国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Spring

2019-11-14 22:16:11
字體:
來源:轉載
供稿:網友
SPRing - 幾種RPC模型的使用與比較

Spring中,用JMS搞RPC時會用到:

  • org.springframework.jms.remoting.JmsInvokerServiceExporter
  • org.springframework.jms.remoting.JmsInvokerProxyFactoryBean

spring在實現RPC的幾種方式上都提供了風格一致的支持。在這里我打算把幾種RPC模型記錄下來并作比較。

  • RMI
  • Hessian/Burlap
  • HTTP Invoker
  • JAX-WS

RMI

先從最基本的RMI開始。RMI相關的API早在JDK1.1時就有了,我在這里簡單描述一下RMI的原生實現(代碼可以從別的地方參考)。

  • 聲明一個遠程接口,接口必須繼承java.rmi.Remote,方法需要拋java.rmi.RemoteException
  • 為遠程接口提供實現,實現類需要繼承UnicastRemoteObject。
  • 或者可以使用rmi相關命令創建skelton和stub。
  • 啟動一個RMI注冊表并注冊。

如果是spring實現RMI,方法會簡單很多。我們只需要用到兩個類:

  • org.springframework.remoting.rmi.RmiServiceExporter
  • org.springframework.remoting.rmi.RmiproxyFactoryBean

我簡單定義一下接口和實現類:

package pac.testcase.ws;public interface MyService {    public boolean inviteMeIn();    public String welcome();}

package pac.testcase.ws.impl;import pac.testcase.ws.MyService;public class MyServiceImpl implements MyService{    public boolean inviteMeIn() {        return true;    }    public String welcome() {        return "Everybody is welcome!!";    }}

簡簡單單,不需要繼承其他任何東西,非常pojo。

下面是spring相關配置:

<bean id="myService" class="pac.testcase.ws.impl.MyServiceImpl" /><bean class="org.springframework.remoting.rmi.RmiServiceExporter"    p:service-ref="myService"    p:serviceName="welcomeService"    p:serviceInterface="pac.testcase.ws.MyService"/>

將我們的pojo導出為RMI服務,在這里我采用默認配置。地址在默認情況時如下:

/** * Set the host of the registry for the exported RMI service, * i.e. {@code rmi://HOST:port/name} * <p>Default is localhost. */public void setRegistryHost(String registryHost) {    this.registryHost = registryHost;}/** * Set the port of the registry for the exported RMI service, * i.e. {@code rmi://host:PORT/name} * <p>Default is {@code Registry.REGISTRY_PORT} (1099). * @see java.rmi.registry.Registry#REGISTRY_PORT */public void setRegistryPort(int registryPort) {    this.registryPort = registryPort;}

客戶端方面使用RmiProxyFactoryBean,被代理的服務就像一個簡單的bean一樣:

<bean id="clientSideService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"    p:serviceUrl="rmi://localhost:1099/welcomeService"    p:serviceInterface="pac.test.RemoteService"/>

配置中的pac.test.RemoteService就是那個簡單的bean,根據客戶端的需要,在這里重新定義一下。

package pac.test;public interface RemoteService {    public String welcome();}

這樣就可以在服務端調用了,不用做什么Naming.lookup(serviceUrl)之類的操作,遠程調用變得透明。

applicationContext context = new ClassPathxmlApplicationContext("classpath:applicationContext.xml");RemoteService service = (RemoteService)context.getBean("clientSideService");System.out.println(service.welcome());

RMI雖然簡單高效,但使用RMI會存在一些問題,比如java序列化的版本問題或者防火墻問題(RMI不是基于HTTP的)。

Hessian / Burlap

Hessian和Burlap,現在進Caucho的網站都幾乎見不到這方面的內容了。我也不知道有沒有人還會用這兩個東東,雖然去年出了一個版本,但上一個版本是在2010年。剛才在群里問了一下有沒有人用,結果還真有人用Hessian,他們是C#和Java做通信。Burlap性能更令人頭疼,不知道還有沒有人提及。雖然不知道使用情況如何,但也在這里簡單記錄一下,拓展一下思維。

Hessian和Burlap都是由Caucho提供的,Hessian是Resin的一部分。這兩個東西就像同一件事物的兩個部件,比如像這樣的槍+鏈鋸?

Hessian是binary transport protocol,但與RMI不同的是他不是java序列化對象,所以他可以和其他語言的程序通信,比如C++、C#、Python、Ruby什么的。Burlap是基于XML的,自然也可以支持很多不同的語言。當然,同樣地傳輸內容下,XML的傳輸量會大一些。如果要說有什么好處的話也只有可讀性了。

實在懶得添加依賴再提供原生實現,但他并不復雜。Creating a Hessian service using Java has four steps:

  • Create an Java interface as the public API
  • Create a client using HessianProxyFactory
  • Create the Service implementation class
  • Configure the service in your servlet engine.

在這里我主要記錄一下如何在spring中導出與調用Hessian service。正如上面所說,我需要把服務配置到servlet engine中;服務端和客戶端都需要添加一個dependency:

<dependency>    <groupId>com.caucho</groupId>    <artifactId>hessian</artifactId>    <version>4.0.33</version></dependency>

正好我這邊有個使用springMVC的應用,我就在這個基礎上導出Hessian service。

<servlet>    <servlet-name>springServlet</servlet-name>    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>    <init-param>        <param-name>contextConfigLocation</param-name>        <param-value>/WEB-INF/spring-mvc.xml</param-value>    </init-param>    <load-on-startup>1</load-on-startup></servlet><servlet-mapping>    <servlet-name>springServlet</servlet-name>    <url-pattern>/</url-pattern></servlet-mapping>

簡單寫一個接口與實現:

package pac.king.common.rpc;public interface MyHessianService {    public String justHadEnoughParties();}

package pac.king.common.rpc.impl;import pac.king.common.rpc.MyHessianService;public class MyHessianServiceImpl implements MyHessianService {    public String justHadEnoughParties() {        return "Please save me..";    }}

我在spring-mvc.xml中曾經做了如下配置,并在*Controller中使用了RequestMapping注解去給URL做映射。但這并不妨礙我導出Hessian service再為其映射一個URL:

<context:component-scan base-package="pac.king.controller"    use-default-filters="false">    <context:include-filter type="annotation"        expression="org.springframework.stereotype.Controller" /></context:component-scan><bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">    <property name="mappings">        <value>            /service=myHessianService        </value>    </property></bean>

導出Hessian service:

<bean id="myHessianServiceImpl" class="pac.king.common.rpc.impl.MyHessianServiceImpl" /><bean id="myHessianService" class="org.springframework.remoting.caucho.HessianServiceExporter"    p:service-ref="myHessianServiceImpl"    p:serviceInterface="pac.king.common.rpc.MyHessianService"/>

現在可以調用了,我需要在客戶端聲明一個接口(pac.test.HessianService),再用代理去調用:

<bean id="myHessianClient" class="org.springframework.remoting.caucho.HessianProxyFactoryBean"    p:serviceUrl="http://localhost:8080/runtrain/service"    p:serviceInterface="pac.test.HessianService"/>

調用:

ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");HessianService service = (HessianService)context.getBean("myHessianClient");System.out.println(service.justHadEnoughParties());

console輸出:

對于Burlap,幾乎與Hessian的配置沒什么區別;只需要把HessianServiceExporter改為BurlapServiceExporter,并將HessianProxyFactoryBean改為BurlapProxyFactoryBean即可。

RMI使用Java的序列化,而Hessian/Burlap則為了不同語言之間通信而使用私有的序列化。如果我需要基于HTTP,但我并不需要多語言支持,我只想用Java...

HttpInvoker

我應該說這是基于Http的RMI嗎?雖然看起來兩全其美,但也存在讓人"遺憾"的地方,(事實上不怎么遺憾的說,我曾經做過沒有Spring的項目,連持久層框架都是自己實現,做得越久越痛苦...)他沒有所謂"原生"的實現,他是Spring的一部分,只能在Spring應用中使用。

Spring為這些RPC通信模型提供的相關類在命名上都有一致,都是:

  • 服務端:*ServiceExporter
  • 客戶端:*ProxyFactoryBean

自然地,HttpInvoker將用到

  • org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
  • org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean

基于HttpInvoker的服務也像Hessian那樣由DispatcherServlet進行分發。鑒于很多相同的地方,我打算繼續使用在上一篇中用Hessian通信的接口和實現類。

我幾乎不用做任何工作,URL映射也不需要修改,我只需要將服務端的配置修改一下:

<bean id="myHessianServiceImpl" class="pac.king.common.rpc.impl.MyHessianServiceImpl" /><!-- <bean id="myHessianService" class="org.springframework.remoting.caucho.HessianServiceExporter"    p:service-ref="myHessianServiceImpl"    p:serviceInterface="pac.king.common.rpc.MyHessianService"/> --><bean id="myHessianService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter"    p:service-ref="myHessianServiceImpl"    p:serviceInterface="pac.king.common.rpc.MyHessianService"/>

相應地,客戶端也只需要修改一下class:

<!-- <bean id="myHessianClient" class="org.springframework.remoting.caucho.HessianProxyFactoryBean"    p:serviceUrl="http://localhost:8080/runtrain/service"    p:serviceInterface="pac.test.HessianService"/> --><bean id="myHessianClient" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"    p:serviceUrl="http://localhost:8080/runtrain/service"    p:serviceInterface="pac.test.HessianService"/>

這樣就保證了效率又解決了防火墻的問題,后來我聽有人說,他們用Hessian做跨語言通信時,基于Http這個特征并不能解決防火墻的問題。不知道他們具體情況如何,似乎沒說到一塊兒...

看了Hessian之后突然感覺Web service這種東西好笨重啊(雖然也有一些方法可以克服部分問題)。既然有Hessian,那為什么還要用Web service這種東西呢?我突然開始懷疑起他存在的意義。搜了一下,結果都是比較RPC通信模型的效率,沒有人說他們為什么還要用(都應該有存在的意義吧)...如果僅僅是效率的話都用Hessian不就得了?帶著這個問題我逛了逛stackoverflow,然后我得到了下面幾種答案。

  • 多數人手中拿著錘子的時候,他們傾向于將所有問題都當作釘子,他們通常不會試著去尋找別的工具。導致Web service泛濫的原因也是這個。
  • 我覺得你應該重新看看Web service的優勢(結果有人說了跨語言和SOA...果然關鍵還是相對什么做比較...)
  • Web service比那些non-xml的通信方式慢?這種相對的速度問題更多的取決于業務需求和你自己的代碼實現(這個說法也同樣適用于反射)。

最后我還是沒有得到讓我滿意的答案,倒是復習了Web service...很多類似場景下人們都將Web service視為"standard"option。既然如此...那就看看Web service吧。

JAX-WS

看來使用web service是不可避免的。我曾對這個有些抵觸,因為他給我印象總是麻煩+慢(后來雖然方便了許多,但還是很慢)。然后再去搜索"advantages of web service"什么的試著再讓自己接受他。簡單記錄一下如何用Spring導出Endpoint。

假設我想在有一個Spring應用,我需要把一個Pojo或者一部分方法導出為Web Service。但這會有一個問題——Endpoint的生命周期是由JAX-WS runtime來管理(The lifecycle of such an endpoint instance will be managed by the JAX-WS runtime),Spring context中的Bean無法autowire到Endpoint中,而我要導出的那些東東都用到了Spring管理的Bean。

對此,我們有兩個解決方法:

  • org.springframework.web.context.support.SpringBeanAutowiringSupport
  • JaxWsServiceExporter

我上面括號中的那段話是引用的SpringBeanAutowiringSupport的javaDoc。使用該類的典型案例就是bean注入到JAX-WS endpoint類中(人家注釋上寫的),任何一個生命周期不是由Spring來管理的場景都可以用到他。而我們只需要繼承這個類,也就是說創建一個實例時會調用父類的無參構造方法,我們來看看他的構造方法:

/** * This constructor performs injection on this instance, * based on the current web application context. * <p>Intended for use as a base class. * @see #processInjectionBasedOnCurrentContext */public SpringBeanAutowiringSupport() {    processInjectionBasedOnCurrentContext(this);}/** * Process {@code @Autowired} injection for the given target object, * based on the current web application context. * <p>Intended for use as a delegate. * @param target the target object to process * @see org.springframework.web.context.ContextLoader#getCurrentWebApplicationContext() */public static void processInjectionBasedOnCurrentContext(Object target) {    Assert.notNull(target, "Target object must not be null");    WebApplicationContext cc = ContextLoader.getCurrentWebApplicationContext();    if (cc != null) {        AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();        bpp.setBeanFactory(cc.getAutowireCapableBeanFactory());        bpp.processInjection(target);    }    else {        if (logger.isDebugEnabled()) {            logger.debug("Current WebApplicationContext is not available for processing of " +                    ClassUtils.getShortName(target.getClass()) + ": " +                    "Make sure this class gets constructed in a Spring web application. Proceeding without injection.");        }    }}

那就試試看:

@Service@WebService(serviceName="testMyService")public class MyServiceEndpoint extends SpringBeanAutowiringSupport{    @Autowired    MyService myService;    @WebMethod    public String sayHiFarAway(String name){        return myService.sayHiTo(name);    }}

接著發布一下:

ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:applicationContext*.xml");Endpoint.publish("http://localhost:8080/myservices", (MyServiceEndpoint)context.getBean(MyServiceEndpoint.class));

調用:

javax.xml.ws.Service service = javax.xml.ws.Service.create(url, new QName("http://endpoint.king.pac/","testMyService"));QName q = new QName("http://endpoint.king.pac/","MyServiceEndpointPort");MyClientService client = service.getPort(q,MyClientService.class);System.out.println(client.sayHiFarAway("King"));

寫一個EndPoint還要繼承和業務無關的類,讓人不爽...而且發布和調用都麻煩。那試試SimpleJaxWsServiceExporter,只需要簡單的配置就可以導出一個EndPoint。但是他也有需要注意的地方,引用一下該類的javaDoc:

Note that this exporter will only work if the JAX-WS runtime actually supports publishing with an address argument, i.e. if the JAX-WS runtime ships an internal HTTP server. This is the case with the JAX-WS runtime that's inclued in Sun's JDK 1.6 but not with the standalone JAX-WS 2.1 RI.

SimpleJaxWsServiceExporter會自動detect所有被WebService注解的類,因此只需要在配置中聲明即可;此時Endpoint地址直接使用默認的localhost:8080:

<bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter" />

接著改善一下客戶端的調用,使用JaxWsPortProxyFactoryBean:

<bean id="clientSide" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean"    p:wsdlDocumentUrl="http://localhost:8080/testMyService?wsdl"    p:serviceName="testMyService"    p:portName="MyServiceEndpointPort"    p:serviceInterface="pac.king.endpoint.MyClientService"    p:namespaceUri="http://endpoint.king.pac/"/>

這樣就可以像使用普通bean一樣使用service了:

MyClientService client = (MyClientService)context.getBean("clientSide");System.out.println(client.sayHiFarAway("King"));

用起來方便了不少,但仍然無法改變一個事實:

Web service requests are larger than requests encoded with a binary protocol.

還有就是http/https的問題。如果使用不當,我可能需要多做些工作去處理HTTP做不來的事情,而這時候RMI又非常適合。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 抚远县| 若羌县| 成都市| 漳州市| 西贡区| 思茅市| 和龙市| 望都县| 邵阳市| 城市| 牟定县| 镇雄县| 洛阳市| 平南县| 罗田县| 雷山县| 云阳县| 丰顺县| 蒙城县| 金山区| 肃北| 博爱县| 太谷县| 仁怀市| 桦甸市| 西青区| 武陟县| 明溪县| 利辛县| 宁陵县| 靖安县| 烟台市| 永顺县| 丹江口市| 孟津县| 师宗县| 开鲁县| 建阳市| 宝应县| 蒙山县| 海兴县|