1. 程式人生 > >Hessian實現RPC通訊

Hessian實現RPC通訊

RPC即遠端程式呼叫

RPC(Remote Procedure Call)—遠端過程呼叫,它是一種通過網路從遠端計算機程式上請求服務,而不需要了解底層網路技術的協議。RPC協議假定某些傳輸協議的存在,如TCP或UDP,為通訊程式之間攜帶資訊資料。在OSI網路通訊模型中,RPC跨越了傳輸層和應用層。RPC使得開發包括網路分散式多程式在內的應用程式更加容易。

RPC採用客戶機/伺服器模式。請求程式就是一個客戶機,而服務提供程式就是一個伺服器。首先,客戶機呼叫程序傳送一個有程序引數的呼叫        資訊到服務程序,然後等待應答資訊。在伺服器端,程序保持睡眠狀態直到呼叫資訊到達為止。當一個呼叫資訊到達,伺服器獲得程序引數,計算結果,傳送答覆資訊,然後等待下一個呼叫資訊,最後,客戶端呼叫程序接收答覆資訊,獲得程序結果,然後呼叫執行繼續進行。

Hessian是一個輕量級的RPC框架

它基於HTTP協議傳輸,使用Hessian二進位制序列化,對於資料包比較大的情況比較友好。

但是它的引數和返回值都需要實現Serializable介面。

說說hessian到底幹了寫什麼吧

客戶端

1.通過HessianProxyFactory生成代理物件,轉發請求,等待server端迴應

2.將方法以及引數都Serialized序列化,傳給server端

3.因為使用的是http協議,可新增許可權驗證

3145530-32c4ebcbc934459d.png 流程圖

首先建立HessianProxyFacotry物件,構造方法中建立了一個HessianProxyResolver物件,這個物件的lookup方法將用來查詢遠端服務。此外HessianProxyFacotry還有包括許可權驗證方面的支援。

建立了factory之後,接下來就是通過Class物件和遠端服務的URL建立代理物件了。

HessianProxyFactory使用HessianProxy物件作為代理的Handler,也就是說,我們對代理物件的所有操作,都會由這個handler來處理。handler的invoke方法,在進行一些方法名和引數的確認之後,建立HttpURLConnection物件,呼叫sendRequest方法,將方法名和引數用HessianOutput物件(設定序列化的方式)的call方法,寫入到服務端。

protected URLConnection sendRequest(String methodName, Object []args)

    throws IOException

  {

    URLConnection conn = null;

    conn = _factory.openConnection(_url);

    // Used chunked mode when available, i.e. JDK 1.5. 

  if (_factory.isChunkedPost() && conn instanceof HttpURLConnection) {

      try {

        HttpURLConnection httpConn = (HttpURLConnection) conn;

        httpConn.setChunkedStreamingMode(8 * 1024);

          } catch (Throwable e) { }

    }

    addRequestHeaders(conn);

    OutputStream os = null;

    try {

      os = conn.getOutputStream();

    } catch (Exception e) {

      throw new HessianRuntimeException(e);

    }

    try {

      if (log.isLoggable(Level.FINEST)) {

    PrintWriter dbg = new PrintWriter(new LogWriter(log));

    os = new HessianDebugOutputStream(os, dbg);

      }

      AbstractHessianOutput out = _factory.getHessianOutput(os);

      out.call(methodName, args);

      out.flush();

      return conn;

    } catch (IOException e) {

      if (conn instanceof HttpURLConnection)

    ((HttpURLConnection) conn).disconnect();

      throw e;

    } catch (RuntimeException e) {

      if (conn instanceof HttpURLConnection)

    ((HttpURLConnection) conn).disconnect();

      throw e;

    }

  }

服務端拿到請求,進行反序列化,然後將方法呼叫,再將結果序列化之後寫回到connection。所以,客戶端在sendRequest之後,所要做的就是將返回的結果進行解析,看返回的code是不是200:

conn = sendRequest(mangleName, args);

      if (conn instanceof HttpURLConnection) {

    httpConn = (HttpURLConnection) conn;

        int code = 500;

        try {

          code = httpConn.getResponseCode();

        } catch (Exception e) {

        }

        parseResponseHeaders(conn);

        if (code != 200) {

          StringBuffer sb = new StringBuffer();

          int ch;

.....

        AbstractHessianInput in = _factory.getHessianInput(is);

      in.startReply();

      Object value = in.readObject(method.getReturnType());

      if (value instanceof InputStream) {

    value = new ResultInputStream(httpConn, is, in, (InputStream) value);

    is = null;

    httpConn = null;

      }

      else    in.completeReply();

      return value;

解析HessianInput物件,並且從中讀取到結果返回。

伺服器端

1.初始化的時候生成一個HessianServle,在init()的時候生成methodMap;

2.service()方法會呼叫 _objectSkeleton.invoke()實現方法的執行

3.寫會到客戶端

3145530-155a08f87769ab63.png 流程圖

我們所有的工作都圍繞在HessianServlet在展開。該Servlet中有兩個比較重要的方法:init()、service();

init方法初始化服務和服務物件,主要分為3步:

通過home-class或者service-class建立服務端的實現類例項;

if (_homeImpl != null) { }

 else if (getInitParameter("home-class") != null) {

        String className = getInitParameter("home-class");

        Class homeClass = loadClass(className);

        _homeImpl = homeClass.newInstance();

        init(_homeImpl);

      }

else if (getInitParameter("service-class") != null) {

        String className = getInitParameter("service-class");

        Class homeClass = loadClass(className);

        _homeImpl = homeClass.newInstance();

        init(_homeImpl);

      }

else {

    if (getClass().equals(HessianServlet.class))

      throw new ServletException("server must extend HessianServlet");

    _homeImpl = this;

      }

通過home-api或者api-class載入實現類的介面物件;

if (_homeAPI != null) {  }

else if (getInitParameter("home-api") != null) {

        String className = getInitParameter("home-api");

        _homeAPI = loadClass(className);

      }

else if (getInitParameter("api-class") != null) {

        String className = getInitParameter("api-class");

        _homeAPI = loadClass(className);

      }

else if (_homeImpl != null) _homeAPI = _homeImpl.getClass();

init方法還會建立HessianSkeleton物件,這是Hessian服務端的核心功能部分。

HessianSkeleton繼承自AbstractSkeleton,其構造方法,將會從實現類中抽取方法和方法的Method物件,並且儲存到_methodMap中。

對於一個Servlet來說其service方法是對外提供服務的方法:

public void service(ServletRequest request, ServletResponse response)

    throws IOException, ServletException

  {

    HttpServletRequest req = (HttpServletRequest) request;

    HttpServletResponse res = (HttpServletResponse) response;

    if (! req.getMethod().equals("POST")) {

      res.setStatus(500, "Hessian Requires POST");

      PrintWriter out = res.getWriter();

      res.setContentType("text/html");

      out.println("Hessian Requires POST");

      return;

    }

    String serviceId = req.getPathInfo();

    String objectId = req.getParameter("id");

    if (objectId == null)

      objectId = req.getParameter("ejbid");

    ServiceContext.begin(req, serviceId, objectId);

    try {

      InputStream is = request.getInputStream();

      OutputStream os = response.getOutputStream();

      HessianInput in = new HessianInput(is);

      HessianOutput out = new HessianOutput(os);

      if (objectId != null)

    _objectSkeleton.invoke(in, out);

      else    _homeSkeleton.invoke(in, out);

    } catch (RuntimeException e) {

      throw e;

    } catch (ServletException e) {

      throw e;

    } catch (Throwable e) {

      throw new ServletException(e);

    } finally {

      ServiceContext.end();

    }

  }

最主要的是呼叫HessianSkeleton物件的invoke方法。注意,Servlet例項中有兩個HessianSkeleton變數,分別是:_objectSkeleton和 _homeSkeleton,呼叫誰,是由objectid決定的。

invoke方法:

首先從HessianInput物件中獲取到Method資訊,獲取到真正的service物件。

根據反射機制,呼叫service物件的invoke方法,獲取到返回值。

最後呼叫HessianOutput物件將結果寫回到呼叫方。

Spring也為Hessian提供了很友好的支援,通過使用spring-remoting包,我們可以很方便地釋出和呼叫服務。

最原始的實現,我們的服務是通過Servlet來繫結的,而Spring的實現,我們使用了SpringMVC的載入時機,將配置檔案載入。HessianServiceExporter

public classHessianServiceExporterextendsRemoteExporterimplementsHttpRequestHandler,InitializingBean{....}

這個類實現了InitializingBean介面,這是spring-beans包中很重要的一個擴充套件介面。

這個介面的說明如下:

Interface to be implemented by beans that need to react once all their properties have been set by a BeanFactory: for example, to perform custom initialization, or merely to check that all mandatory properties have been set.

也就是說,它會隨著Spring容器(此處為Spring MVC容器)的啟動而被載入。看看HessianServiceExporter的實現:

public void prepare() {

        HessianSkeleton skeleton = null;

        try {

            try {

                Constructor ctor = (class$com$caucho$hessian$server$HessianSkeleton == null?(class$com$caucho$hessian$server$HessianSkeleton = class$("com.caucho.hessian.server.HessianSkeleton")) : class$com$caucho$hessian$server$HessianSkeleton).getConstructor(new Class[]{class$java$lang$Object == null?(class$java$lang$Object = class$("java.lang.Object")):class$java$lang$Object, class$java$lang$Class == null?(class$java$lang$Class = class$("java.lang.Class")):class$java$lang$Class});

                this.checkService();

                this.checkServiceInterface();

                skeleton = (HessianSkeleton)ctor.newInstance(new Object[]{this.getProxyForService(), this.getServiceInterface()});

            } catch (NoSuchMethodException var4) {

                Constructor ctor = (class$com$caucho$hessian$server$HessianSkeleton == null?(class$com$caucho$hessian$server$HessianSkeleton = class$("com.caucho.hessian.server.HessianSkeleton")):class$com$caucho$hessian$server$HessianSkeleton).getConstructor(new Class[]{class$java$lang$Object == null?(class$java$lang$Object = class$("java.lang.Object")):class$java$lang$Object});

                skeleton = (HessianSkeleton)ctor.newInstance(new Object[]{this.getProxyForService()});

            }

        } catch (Throwable var5) {

            throw new BeanInitializationException("Hessian skeleton initialization failed", var5);

        }

        if(hessian2Available) {

            this.skeletonInvoker = new Hessian2SkeletonInvoker(skeleton, this.serializerFactory);

        } else {

            this.skeletonInvoker = new Hessian1SkeletonInvoker(skeleton, this.serializerFactory);

        }

    }

    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        Assert.notNull(this.skeletonInvoker, "HessianServiceExporter has not been initialized");

        if(!"POST".equals(request.getMethod())) {

            throw new HttpRequestMethodNotSupportedException("POST", "HessianServiceExporter only supports POST requests");

        } else {

            try {

                this.skeletonInvoker.invoke(request.getInputStream(), response.getOutputStream());

            } catch (Throwable var4) {

                throw new NestedServletException("Hessian skeleton invocation failed", var4);

            }

        }

    }

在prepare方法中,獲取service和serviceInterface的配置,建立HessianSkeleton物件。 

同時,還實現了HttpRequestHandler,spring-web中的介面。 

又因為實現了HttpRequestHandler介面,所以在handleRequest方法中,可以像HessianServlet的service方法一樣,呼叫Hessian2SkeletonInvoker的invoke方法進行實際的方法呼叫。

spring boot 中Hessian的配置就不多言,Demo就是一個小例子

相關推薦

Hessian實現RPC通訊

RPC即遠端程式呼叫RPC(Remote Procedure Call)—遠端過程呼叫,它是一種通過網路從遠端計算機程式上請求服務,而不需要了解底層網路技術的協議。RPC協議假定某些傳輸協議的存在,如TCP或UDP,為通訊程式之間攜帶資訊資料。在OSI網路通訊模型中,RPC跨越了傳輸

Java通過Hadoop實現RPC通訊簡單例項

一、定義server端程式碼 1.定義一個介面,該介面繼承org.apache.hadoop.ipc.VersionedProtocol介面 import org.apache.hadoop.ipc.VersionedProtocol; /** * 1.伺服器定義介面

用AKKA實現簡單的RPC通訊模型2

上一篇文章,簡單地用AKKA實現了RPC通訊,這篇文章在之前的基礎之上,進行了更多地關於RPC通訊的操作,包括傳一些相關資料,包括心跳機制等 具體功能如下圖: 程式碼結構: WorkerInfo.scala程式碼 package cn.heres.rpc /**

RabbitMq初探——用隊列實現RPC

await 生產 通過 empty 分享 qos load lose ima rabbitmq構造rpc 前言 rpc——remote procedure call 遠程調用。在我接觸的使用過http協議、thrift框架來實現遠程調用。其實消息隊列rabbitmq也

Python-RabbitMQ消息隊列實現rpc

llb author bject roc read uuid tin rip rabbit 客戶端通過發送命令來調用服務端的某些服務,服務端把結果再返回給客戶端 這樣使得RabbitMQ的消息發送端和接收端都能發送消息 返回結果的時候需要指定另一個隊列 服務器端 # -

RocketMQ(二):RPC通訊

clean router 意思 回車 實現 .cn 完整 換行 quest 匠心零度 轉載請註明原創出處,謝謝! RocketMQ網絡部署圖 NameServer:在系統中是做命名服務,更新和發現 broker服務。 Broker-Master:broker 消息主

PHP + Redis 訂閱/發布 實現即時通訊功能

PHP Redis 訂閱/發布 即時通訊 最近比較忙,沒時間更新博客,先暫時記下,待後續補充 Redis Sub/Pub 訂閱/發布 待編輯 PHP + Redis 訂閱/發布 實現即時通訊 待編輯 PHP + Redis 訂閱/發布 實現即時通訊功能

java 實現udp通訊

ket 地址 upd void ESS util dst 服務端 unknown 需求:應用A(通常有多個)和應用B(1個)進行 socket通訊,應用A必須知道應用B的ip地址(在應用A的配置文件中寫死的),這個時候就必須把應用B的ip設成固定ip(但是某些時候如更換路由

徒手擼框架--實現 RPC 遠程調用

swa con code face != nco 語言 dom policy 微服務,已經是每個互聯網開發者必須掌握的一項技術。而 RPC 框架,是構成微服務最重要的組成部分之一。趁最近有時間。又看了看 dubbo 的源碼。dubbo 為了做到靈活和解耦,使用了大量的設計模

Spark內建框架rpc通訊機制及RpcEnv基礎設施-Spark商業環境實戰

本套系列部落格從真實商業環境抽取案例進行總結和分享,並給出Spark原始碼解讀及商業實戰指導,請持續關注本套部落格。版權宣告:本套Spark原始碼解讀及商業實戰歸作者(秦凱新)所有,禁止轉載,歡迎學習。 Spark商業環境實戰及調優進階系列 Spark商業環境實戰-Spark內建框架rpc通訊機制及

一個簡單RPC框架是如何煉成的(IV)——實現RPC訊息的編解碼

之前我們制定了一個很簡單的RPC訊息 的格式,但是還遺留了兩個問題,上一篇解決掉了一個,還留下一個 我們並沒有實現相應的encode和decode方法,沒有基於可以跨裝置的字串傳輸,而是直接的記憶體變數傳遞。 現在的RPC request不支援帶引數的請求命令。如add(a,

Rabbitmq中的RPC通訊機制

具體工作機制: Our RPC will work like this: When the Client starts up, it creates an anonymous exclusive callback queue. For an RPC request, the

golang和c通過unix域實現雙向通訊

go的程式碼如下: package main import ( "fmt" "net" "bufio" "os" "syscall" ) func writeUnix(listener *net.UnixConn,dstAddr *net.UnixAddr) { for {

Java以GET和POST方式實現HTTP通訊

 此程式可以建立HTTP通訊,分別以GET和POST方式向WEB伺服器提交資訊,並接收WEB伺服器返回的響應。 import java.io.*; import java.net.*; public class s311 { public static void main(S

mqtt協議實現即時通訊-activemq nginx.支援JS,JAVA,微信小程式

MQTT協議通訊   簡述:            使用MQTT協議實現後臺推送、及時通訊等功能。本案例實現了web-js端、微信小程式端、Java client端、Java serv

RPC 通訊和 RMI 區別

RPC(Remote Procedure Call Protocol)遠端過程呼叫協議,通過網路從遠端計算機上請求呼叫某種服務。 一個RPC框架包含的要素 RMI (Remote Method Invocation) 遠端方法呼叫。能夠讓在客戶端Java虛擬機器上的物件像呼叫本地物件一

Websocket實現前後臺通訊,demo小測試

  新需求大概如下:使用者登入系統,登入成功之後建立websocket連線,實現通訊   總體思路:前端不是我負責,只是簡單的做個功能,先實現登入,把使用者標識存入HttpSeesion,再建立websocket連線,攔截器HandshakeInterceptor攔截請求,把使用者標識存入Map<St

用udp實現廣播通訊

作者:lycclsltt 原文:https://blog.csdn.net/aspnet_lyc/article/details/34444111 特點: 1.資料傳輸不用建立連線,所以不可靠(符合udp協議的特點) 2.資料的傳送是面向整個子網的,任何一臺在子網內的計算機都可以接收

Http與RPC通訊協議的比較

OSI網路結構的七層模型 各層的具體描述如下:   第七層:應用層     定義了用於在網路中進行通訊和資料傳輸的介面 - 使用者程式;提供標準服務,比如虛擬終端、檔案以及任務的傳輸 和處理;   第六層:表示層   &nbs

ubuntu16.04下ROS作業系統學習(四 / 三)ROS基礎-實現分散式通訊

ROS是分散式的節點,這樣的話我們就可以將程式執行上的節點放到大型機器上面,分擔機器人執行的壓力。接下來我們來看一下怎麼實現分散式通訊,怎麼在多機上執行統一的程式。 ROS是一種分散式軟體框架,節點之間通過鬆耦合的方式進行組合。 那麼我們如何來實現分散式多機通訊呢: 設定IP地址,