中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

基于Spring的RPC通訊模型的使用與比較

發布時間:2020-09-29 00:20:02 來源:腳本之家 閱讀:131 作者:JMCui 欄目:編程語言

一、概念和原理

RPC(remote procedure call),遠程過程調用,是客戶端應用和服務端之間的會話。在客戶端,它所需要的一些功能并不在該應用的實現范圍之內,所以應用要向提供這些功能的其他系統尋求幫助。而遠程應用通過遠程服務暴露這些功能。RPC 是同步操作,會阻塞調用代碼的執行,直到被調用的過程執行完畢。

Spring支持多種不同的RPC模型,包括RMI、Caucho的Hessian和Burlap以及Spring自帶的HTTP invoker:

基于Spring的RPC通訊模型的使用與比較

客戶端:

在所有的模型中,服務都是作為 Spring 所管理的 bean 配置到我們的應用中。這是通過一個代理工廠 bean 實現的,這個bean能夠把遠程服務像本地對象一樣裝配到其他bean的屬性中。

客戶端向代理發起調用,就像代理提供了這些服務一樣。代理代表客戶端和遠程服務進行通信,由它負責處理連接的細節并向遠程服務發起調用。

基于Spring的RPC通訊模型的使用與比較

服務端:

Spring 使用遠程導出器(remote exporter)將bean方法發布為遠程服務。

基于Spring的RPC通訊模型的使用與比較

二、RMI

RMI 最初在JDK 1.1被引入到Java平臺中,它為Java開發者提供了一種強大的方法來實現Java程序間的交互。

Spring 提供了簡單的方式來發布RMI服務,在服務端,RmiServiceExporter 可以把任何 Spring 管理的bean發布為RMI服務 ,如圖所示,RmiServiceExporter 把bean包裝在一個適配器類中,然后適配器類被綁定到RMI注冊表中,并且代理到服務類的請求。

基于Spring的RPC通訊模型的使用與比較

/**
   * 服務端:
   * <p>
   * 1、默認情況下,RmiServiceExporter 會嘗試綁定到本地機器1099端口上的RMI注冊表。
   * 2、如果在這個端口沒有發現RMI注冊表,RmiServiceExporter 將會啟動一個注冊表。
   * 3、可重寫注冊表的路徑和端口,這個是個大坑,當你設置了registryHost屬性的時候,源碼中就不創建Registry,而是直接去獲取,可是我們自己也沒有創建,所以就會報連接不上。
   *
   * @param userService
   * @return
   */
  @Bean(name = "rmiServiceExporter")
  public RmiExporter rmiServiceExporter(UserService userService, Environment environment) {
    String registryHost = environment.getProperty("registryHost");
    int registryPort = environment.getProperty("registryPort", Integer.class);
    RmiExporter rmiExporter = new RmiExporter();
    rmiExporter.setService(userService); //要把該bean(即rmiServiceImpl)發布為一個RMI服務
    rmiExporter.setServiceName("RmiService"); //命名RMI 服務
    rmiExporter.setServiceInterface(UserService.class); //指定服務所實現的接口
    rmiExporter.setRegistryHost(registryHost);
    rmiExporter.setRegistryPort(registryPort);
    return rmiExporter;
  }
/**
 * Created by XiuYin.Cui on 2018/5/14.
 * 
 * 解決設置 registryHost 后,報連接拒絕的問題。
 */
public class RmiExporter extends RmiServiceExporter {

  @Override
  protected Registry getRegistry(String registryHost, int registryPort, RMIClientSocketFactory clientSocketFactory,
                  RMIServerSocketFactory serverSocketFactory) throws RemoteException {


    if (registryHost != null) {
      try {
        if (logger.isInfoEnabled()) {
          logger.info("Looking for RMI registry at port '" + registryPort + "' of host [" + registryHost + "]");
        }
        //把spring源代碼中這里try起來,報異常就創建一個
        Registry reg = LocateRegistry.getRegistry(registryHost, registryPort, clientSocketFactory);
        testRegistry(reg);
        return reg;
      } catch (RemoteException ex) {
        LocateRegistry.createRegistry(registryPort);
        Registry reg = LocateRegistry.getRegistry(registryHost, registryPort, clientSocketFactory);
        testRegistry(reg);
        return reg;
      }
    } else {
      return getRegistry(registryPort, clientSocketFactory, serverSocketFactory);
    }
  }
}

接下來,來看看客戶端是怎么使用這些遠程服務的吧!Spring的RmiProxyFactoryBean是一個工廠bean,該bean可以為RMI服務創建代理。該代理代表客戶端來負責與遠程的RMI服務進行通信。客戶端通過服務的接口與代理進行交互,就如同遠程服務就是一個本地的POJO。

基于Spring的RPC通訊模型的使用與比較

@Bean(name = "rmiUserServiceClient")
  public RmiProxyFactoryBean RmiUserServiceClient(){
    RmiProxyFactoryBean rmiProxyFactoryBean = new RmiProxyFactoryBean();
    rmiProxyFactoryBean.setServiceUrl("rmi://127.0.0.1:9999/RmiService");
    rmiProxyFactoryBean.setServiceInterface(UserService.class);
    rmiProxyFactoryBean.setLookupStubOnStartup(false);//不在容器啟動后創建與Server端的連接
    rmiProxyFactoryBean.setRefreshStubOnConnectFailure(true);//連接出錯的時候自動重連
    rmiProxyFactoryBean.afterPropertiesSet();
    return rmiProxyFactoryBean;
  }
 @Resource(name="rmiUserServiceClient")
  private UserService userService;

RMI 的缺陷:

1、RMI很難穿越防火墻,這是因為RMI使用任意端口來交互——這是防火墻通常所不允許的。
2、RMI是基于Java的。這意味著客戶端和服務端必須都是用java開發。因為RMI使用了Java的序列化機制,所以通過網絡傳輸的對象類型必須要保證在調用兩端的Java運行時中是完全相同的版本。

tips:最近發現Dubbo 底層也是用 RMI 實現的,它把 zookeeper 當作注冊表。

三、Hessian 和 Burlap

hessian 和 Burlap 是 Caucho Technology 的兩種基于HTTP的輕量級遠程服務解決方案。借助于盡可能簡單的API和通信協議,它們都致力于簡化Web服務。

hessian,像RMI一樣,使用二進制消息進行客戶端和服務端的交互。但是它與RMI不同的是,它的二進制消息可以移植到其他非Java的語言中。由于它是基于二進制的,所以它在帶寬上更具優勢。

Burlap 是一種基于XML的遠程調用技術,這使得它可以自然而然的移植到任何能夠解析XML的語言上。正因為它基于XML,所以相比起Hessian的二進制格式而言,Burlap可讀性更強。但是和其他基于XML的遠程技術(例如SOAP或XML-RPC)不同,Burlap的消息結構盡可能的簡單。

下面我們會介紹 hessian 的使用。Spring 不推薦使用Burlap,BurlapServiceExporter 在4.0后被廢棄,不再提供支持。5.0 后直接從開發包丟棄了。

服務端,類似于 RmiServiceExporter ,hessian 也有一個HessianServiceExporter 將 Spring 管理的 bean 發布為 Hessian 服務,不同于RMI的是,HessianServiceExporter是一個Spring MVC控制器,它接收Hessian請求(HTTP協議的請求),并將這些請求轉換成對被導出POJO的方法調用。既然是HTTP請求,那我們就必須配置Spring 的DispatcherServlet ,并配置HandlerMapping,將相應的URL映射給HessianServiceExporter。

基于Spring的RPC通訊模型的使用與比較

/**
   * hessian沒有注冊表,不需要設置 serviceName
   */
  @Bean(name = "hessianServiceExporter")
  public HessianServiceExporter hessianServiceExporter(UserService userService) {
    HessianServiceExporter hessianServiceExporter = new HessianServiceExporter();
    hessianServiceExporter.setService(userService);
    hessianServiceExporter.setServiceInterface(UserService.class);
    return hessianServiceExporter;
  }
  /**
   * 需要配置一個URL映射來確保DispatcherServlet把請求轉給HessianServiceExporter
   */
  @Bean(name = "handlerMapping")
  public HandlerMapping handlerMapping() {
    SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
    Properties mappings = new Properties();
    mappings.setProperty("/user.service", "hessianServiceExporter");
    handlerMapping.setMappings(mappings);
    return handlerMapping;
  }

客戶端,類似于RmiProxyFactoryBean ,Hessian 也有一個代理工廠Bean——HessianProxyFactoryBean,來創建代理與遠程服務進行通信:

@Bean(name = "hessianUserServiceClient")
  public HessianProxyFactoryBean hessianUserServiceClient(){
    HessianProxyFactoryBean proxy = new HessianProxyFactoryBean();
    proxy.setServiceUrl("http://127.0.0.1:8080/user.service");
    proxy.setServiceInterface(UserService.class);
    return proxy;
  }
  @Resource(name="hessianUserServiceClient")
  private UserService userService; 

Hessian 的缺陷:

hessian 和 Burlap 都是基于HTTP的,它們都解決了RMI所頭疼的防火墻滲透問題。但是當傳遞過來的RPC消息中包含序列化對象時,RMI就完勝 Hessian 和 Burlap 了。因為 Hessian 和 Burlap 都采用了私有的序列化機制,而RMI使用的是Java本身的序列化機制。

四、HttpInvoker

RMI 和 Hessian 各有自己的缺陷,一方面,RMI使用Java標準的對象序列化機制,但是很難穿透防火墻。另一方面,Hessian和Burlap能很好地穿透防火墻,但是使用私有的對象序列化機制。就這樣,Spring的HTTP invoker應運而生了。HTTP invoker是一個新的遠程調用模型,作為Spring框架的一部分,能夠執行基于HTTP的遠程調用,并使用Java的序列化機制。

HttpInvoker 的使用和 Hessian 很類似,HttpInvokerServiceExporter 也是一個Spring MVC 控制器,也是通過DispatcherServlet 將請求分發給它...

基于Spring的RPC通訊模型的使用與比較

/*Http Invoker*/
  @Bean(name = "httpInvokerServiceExporter")
  public HttpInvokerServiceExporter httpInvokerServiceExporter(UserService userService){
    HttpInvokerServiceExporter httpInvokerServiceExporter = new HttpInvokerServiceExporter();
    httpInvokerServiceExporter.setService(userService);
    httpInvokerServiceExporter.setServiceInterface(UserService.class);
    return httpInvokerServiceExporter;
  }
  /**
   * 需要配置一個URL映射來確保DispatcherServlet把請求轉給HessianServiceExporter
   */
  @Bean(name = "handlerMapping")
  public HandlerMapping handlerMapping() {
    SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
    Properties mappings = new Properties();
    mappings.setProperty("/user.service", "hessianServiceExporter");
    mappings.setProperty("/userInvoker.service", "httpInvokerServiceExporter");
    handlerMapping.setMappings(mappings);
    return handlerMapping;
  }

客戶端,像 RmiProxyFactoryBean 和 HessianProxyFactoryBean 一樣,HttpInvoker 也提供了一個代理工廠Bean——HttpInvokerProxyFactoryBean,用于創建HttpInvoker代理來與遠程服務通信:

@Bean(name = "httpInvokerUserServiceClient")
  public HttpInvokerProxyFactoryBean httpInvokerUserServiceClient(){
    HttpInvokerProxyFactoryBean proxy = new HttpInvokerProxyFactoryBean();
    proxy.setServiceUrl("http://127.0.0.1:8080//userInvoker.service");
    proxy.setServiceInterface(UserService.class);
    return proxy;
  }

參考資料:《Spring 實戰第四版》

演示源代碼鏈接:https://github.com/JMCuixy/SpringForRpc

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

通城县| 宁津县| 屏南县| 芜湖市| 黎城县| 象州县| 葫芦岛市| 锡林郭勒盟| 宣化县| 上林县| 句容市| 祥云县| 肥乡县| 射阳县| 色达县| 镇巴县| 睢宁县| 常山县| 邵东县| 黔江区| 洛隆县| 宽甸| 井陉县| 桦川县| 东宁县| 吕梁市| 怀安县| 凤翔县| 湘潭县| 交城县| 南江县| 大方县| 海门市| 攀枝花市| 印江| 陆丰市| 安新县| 开化县| 乐山市| 高碑店市| 西平县|