Jak funguje Spring HTTP invoker?

Ikaros42

Jak funguje Spring HTTP invoker?
« kdy: 24. 07. 2013, 12:58:17 »
Zdravím,

docela mě zaujal HTTP invoker, který je součástí Spring framework a zajímalo by mě, jak vnitřně funguje. Vím že při použití serializace Java objektů je během deserializace nutná přítomnost té dané třídy, v případě HTTP invokeru mám ale na straně klienta jen rozhraní třídy, samotná třída je kdesi daleko na serveru. Spouští se tedy ona třída na vzdálené JVM nebo se nějakým způsobem dopraví do lokální části aplikace a zde se spustí?

Díky


Re:Jak funguje Spring HTTP invoker?
« Odpověď #1 kdy: 24. 07. 2013, 13:24:34 »
Je to obecný princip, který se používá skoro ve všech remoting frameworcích v Javě. Na klientovi máte jen rozhraní a na serveru jeho implementaci. K tomu rozhraní se na klientovi (většinou) automaticky vytvoří umělá třída (stub). Když na ní zavoláte metodu, zabalí ta uměle vytvořená třída parametry volání, pošle je na server, tam se vykoná implementace té třídy, výsledek se zabalí a pošle zpět na klienta. Na klientovi se výsledek rozbalí a vrátí se jako návratová hodnota volané metody. Serializují se tedy parametry volané metody a návratová hodnota – a příslušné třídy tedy samozřejmě musí být na obou stranách. Jinak celé se to dělá právě proto, aby se ten výkonný kód spustil na serveru.

Javista

Re:Jak funguje Spring HTTP invoker?
« Odpověď #2 kdy: 24. 07. 2013, 13:45:20 »
Je to obecný princip, který se používá skoro ve všech remoting frameworcích v Javě. Na klientovi máte jen rozhraní a na serveru jeho implementaci. K tomu rozhraní se na klientovi (většinou) automaticky vytvoří umělá třída (stub). Když na ní zavoláte metodu, zabalí ta uměle vytvořená třída parametry volání, pošle je na server, tam se vykoná implementace té třídy, výsledek se zabalí a pošle zpět na klienta. Na klientovi se výsledek rozbalí a vrátí se jako návratová hodnota volané metody. Serializují se tedy parametry volané metody a návratová hodnota – a příslušné třídy tedy samozřejmě musí být na obou stranách. Jinak celé se to dělá právě proto, aby se ten výkonný kód spustil na serveru.

Ještě se místo návratové hodnoty případně musí serializovat i exception (a rozlišit vzdáleně vzniklou exception od různých exception způsobených přenosem - z toho mohou být nepěkné chyby). Není moc těžké si něco takového napsat sám, klíčem je že ta umělá třída (stub) se tvoří takhle: http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html

Róbert Novotný

Re:Jak funguje Spring HTTP invoker?
« Odpověď #3 kdy: 24. 07. 2013, 14:05:48 »
Tak, ako povedali kolegovia. Rozdiel medzi HTTP Invokerom a bežným RMI je len v protokole. Spring vezme vašu správu (= objekt), zaserializuje ju štandardným Java serializačným mechanizmom, vloží ho do HTTP POSTu a pošle na server. Server to deserializuje, urobí biznis logiku, výsledok zaserializuje a pošle naspäť. HTTP je totiž menej náchylné na firewallovanie a podobné veci než bežný Java mechanizmus, navyše nemusíte púšťať separátny server, ale sa to integruje rovno do Spring DispatcherServletu.

Samozrejme, triedy musia byť na oboch stranách.

Bežné RMI podporuje aj serializáciu samotnej definície triedy (čiže tá nemusí byť na oboch stranách), to však nie je prípad HTTP Invokera.

Róbert Novotný

Re:Jak funguje Spring HTTP invoker?
« Odpověď #4 kdy: 24. 07. 2013, 14:53:17 »
Ešte poznámka: všetky triedy, ktoré lietajú po kábli, musia byť a) Serializable b) byť na oboch stranách. Interfejs pre zverejnenú službu musí byť tiež na oboch stranách.

1. Interfejs FoodService s List<Food> list() musí byť na oboch stranách
2. Ak je napr. Food interfejsom a server posiela v skutočnosti Burger implements Food, aj Food, aj Burger, musia byť na oboch stranách.


Ikaros42

Re:Jak funguje Spring HTTP invoker?
« Odpověď #5 kdy: 24. 07. 2013, 15:01:02 »
Je to obecný princip, který se používá skoro ve všech remoting frameworcích v Javě. Na klientovi máte jen rozhraní a na serveru jeho implementaci. K tomu rozhraní se na klientovi (většinou) automaticky vytvoří umělá třída (stub). Když na ní zavoláte metodu, zabalí ta uměle vytvořená třída parametry volání, pošle je na server, tam se vykoná implementace té třídy, výsledek se zabalí a pošle zpět na klienta. Na klientovi se výsledek rozbalí a vrátí se jako návratová hodnota volané metody. Serializují se tedy parametry volané metody a návratová hodnota – a příslušné třídy tedy samozřejmě musí být na obou stranách. Jinak celé se to dělá právě proto, aby se ten výkonný kód spustil na serveru.

Ještě se místo návratové hodnoty případně musí serializovat i exception (a rozlišit vzdáleně vzniklou exception od různých exception způsobených přenosem - z toho mohou být nepěkné chyby). Není moc těžké si něco takového napsat sám, klíčem je že ta umělá třída (stub) se tvoří takhle: http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html

Moc díky za rady a za objasnění principů, rozhodl jsem se napsat si vlastní primitivní verzi abych si to vyzkoušel. Dejme tomu že mám na klientu i serveru rozhraní Car, přičemž má jen jednu metodu int getConsumption(int kilometres). Na straně serveru mám i příslušnou implementaci rozhraní. Na klientu jsem si vytvořil proxy třídu rozhraní Car a předal jí svůj InvocationHandler. Chápu správně, že v jeho metodě invoke(...) musím serveru poslat jméno volané třídy, metody a její parametry (třeba ve formátu test.Car[TAB]getConsumption[TAB]42), tam si pomocí reflexe vytvořit patřičnou instanci, zavolat metodu a výsledek odeslat zpět klientskému InvocationHandleru, který jej vrátí mé proxy třídě?

Díky

Róbert Novotný

Re:Jak funguje Spring HTTP invoker?
« Odpověď #6 kdy: 24. 07. 2013, 15:17:14 »
V takýchto jednoduchých prípadoch tie proxy za vás spraví Spring:

Klient:
Kód: [Vybrat]
HttpInvokerProxyFactoryBean httpInvokerProxyFactoryBean = new HttpInvokerProxyFactoryBean();
httpInvokerProxyFactoryBean.setServiceUrl("http://localhost:8080/car.service");
httpInvokerProxyFactoryBean.setServiceInterface(Car.class);
httpInvokerProxyFactoryBean.afterPropertiesSet();

Car car = (Car) httpInvokerProxyFactoryBean.getObject();

int result = car.getConsumption(100);

Ikaros42

Re:Jak funguje Spring HTTP invoker?
« Odpověď #7 kdy: 24. 07. 2013, 15:19:20 »
V takýchto jednoduchých prípadoch tie proxy za vás spraví Spring:

Klient:
Kód: [Vybrat]
HttpInvokerProxyFactoryBean httpInvokerProxyFactoryBean = new HttpInvokerProxyFactoryBean();
httpInvokerProxyFactoryBean.setServiceUrl("http://localhost:8080/car.service");
httpInvokerProxyFactoryBean.setServiceInterface(Car.class);
httpInvokerProxyFactoryBean.afterPropertiesSet();

Car car = (Car) httpInvokerProxyFactoryBean.getObject();

int result = car.getConsumption(100);

Děkuji, samozřejmě vím o této možnosti, šlo mi o to zkusit si to napsat sám a pochopit princip.

vrtak

Re:Jak funguje Spring HTTP invoker?
« Odpověď #8 kdy: 24. 07. 2013, 16:08:39 »
Děkuji, samozřejmě vím o této možnosti, šlo mi o to zkusit si to napsat sám a pochopit princip.

Pro pochopeni principu je lepsi a) neco si o tom precist nebo b) podivat se na existujici implementaci (napr. ve Spring zdrojacich) nez si neco sam bastlit.

Róbert Novotný

Re:Jak funguje Spring HTTP invoker?
« Odpověď #9 kdy: 24. 07. 2013, 16:49:56 »
Mne sa tiež vidí, že robiť to ručne znamená riešiť problémy na mnohých frontoch naraz: jednak musíte pracovať s tou proxy, jednak riešiť deserializáciu a serializáciu (+ výnimky) a jednak riešiť HTTP transport. a zároveň tým aj tak asi neurobíte lepšiu prácu než Spring.

Ako som pozeral do zdrojákov Springu, tie dokonca dynamické proxy priamo nevyužívajú: namiesto toho stavajú na projekte AOP z AOPAlliance. Pozrite do triedy org.springframework.aop.framework.ProxyFactory pre východisko.

pepicek

Re:Jak funguje Spring HTTP invoker?
« Odpověď #10 kdy: 25. 07. 2013, 09:13:27 »
musím serveru poslat jméno volané třídy, metody a její parametry (třeba ve formátu test.Car[TAB]getConsumption[TAB]42), tam si pomocí reflexe vytvořit patřičnou instanci, zavolat metodu
Samozrejme to zalezi na Tobe (kdyz si to zkousis;), vetsinou ale nevytvaris novou instanci, ale nejaka na serveru uz existuje. Takze misto tridy posilas spis identifikator te instance (i kdyz v pripade jedne instance na tridu je identifikatorem i trida): pepikuvTrabant[TAB]getConsumption[TAB]42.

Re:Jak funguje Spring HTTP invoker?
« Odpověď #11 kdy: 25. 07. 2013, 09:19:05 »
Chápu správně, že v jeho metodě invoke(...) musím serveru poslat jméno volané třídy, metody a její parametry (třeba ve formátu test.Car[TAB]getConsumption[TAB]42), tam si pomocí reflexe vytvořit patřičnou instanci, zavolat metodu a výsledek odeslat zpět klientskému InvocationHandleru, který jej vrátí mé proxy třídě?
Pokud si to chcete vyzkoušet, nepotřebujete žádný InvocationHandler, ale jednoduše ve třídě implementující na klientovi rozhraní Car přímo zavolejte HTTP požadavek. Vytváření proxy bych zkoušel až v druhém kroku, i to volání přes HTTP je dost práce.

Tímhle si vyzkoušíte ten princip komunikace, ale pro pochopení Spring Remotingu je to podle mne vlastně zbytečné. To, jak se vytvářejí proxy a data posílají přes síť, je totiž asi jedna z mála věcí, kterou u toho Spring Remotingu řešit nemusíte a funguje to samo. Řešit budete hlavně to, jak to používat ve své aplikaci a jak to celé nakonfigurovat. Spring totiž používá spoustu výchozích nastavení, třeba jméno třídy se nepřenáší v požadavku, místo toho je název beany odvozený ze jména třídy součástí URL. Takže když budete postupovat přesně podle návodu a použijete přesně stejná jména, ale jakmile něco přejmenujete, přestane vám to fungovat, protože  od toho jména bylo odvozeno další jméno někde úplně jinde. A pochopit Spring Remoting znamená pochopit hlavně tyhle vazby, který kontext je potomkem kterého, která třída se automaticky zveřejní a proč, které interceptory se na ní budou aplikovat atd.