Java 遠端通訊技術及原理分析

陶邦仁的部落格發表於2016-05-19

在分散式服務框架中,一個最基礎的問題就是遠端服務是怎麼通訊的,在Java領域中有很多可實現遠端通訊的技術,例如:RMI、MINA、ESB、Burlap、Hessian、SOAP、EJB和JMS等,這些名詞之間到底是些什麼關係呢,它們背後到底是基於什麼原理實現的呢,瞭解這些是實現分散式服務框架的基礎知識,而如果在效能上有高的要求的話,那深入瞭解這些技術背後的機制就是必須的了。

1 基本原理

要實現網路機器間的通訊,首先得來看看計算機系統網路通訊的基本原理,在底層層面去看,網路通訊需要做的就是將流從一臺計算機傳輸到另外一臺計算機,基於傳輸協議和網路IO來實現,其中傳輸協議比較出名的有tcp、udp等等,tcp、udp都是在基於Socket概念上為某類應用場景而擴充套件出的傳輸協議,網路IO,主要有bio、nio、aio三種方式,所有的分散式應用通訊都基於這個原理而實現,只是為了應用的易用,各種語言通常都會提供一些更為貼近應用易用的應用層協議。

2 訊息模式

歸根結底,企業應用系統就是對資料的處理,而對於一個擁有多個子系統的企業應用系統而言,它的基礎支撐無疑就是對訊息的處理。與物件不同,訊息本質上是一種資料結構(當然,物件也可以看做是一種特殊的訊息),它包含消費者與服務雙方都能識別的資料,這些資料需要在不同的程式(機器)之間進行傳遞,並可能會被多個完全不同的客戶端消費。訊息傳遞相較檔案傳遞與遠端過程呼叫(RPC)而言,似乎更勝一籌,因為它具有更好的平臺無關性,並能夠很好地支援併發與非同步呼叫。

對於Web Service與RESTful而言,則可以看做是訊息傳遞技術的一種衍生或封裝。

2.1 訊息通道(Message Channel)模式

我們常常運用的訊息模式是Message Channel(訊息通道)模式,如圖所示。

訊息通道作為在客戶端(消費者,Consumer)與服務(生產者,Producer)之間引入的間接層,可以有效地解除二者之間的耦合。只要實現規定雙方需要通訊的訊息格式,以及處理訊息的機制與時機,就可以做到消費者對生產者的“無知”。事實上,該模式可以支援多個生產者與消費者。例如,我們可以讓多個生產者向訊息通道傳送訊息,因為消費者對生產者的無知性,它不必考慮究竟是哪個生產者發來的訊息。

雖然訊息通道解除了生產者與消費者之間的耦合,使得我們可以任意地對生產者與消費者進行擴充套件,但它又同時引入了各自對訊息通道的依賴,因為它們必須知道通道資源的位置。要解除這種對通道的依賴,可以考慮引入Lookup服務來查詢該通道資源。例如,在JMS中就可以通過JNDI來獲取訊息通道Queue。若要做到充分的靈活性,可以將與通道相關的資訊儲存到配置檔案中,Lookup服務首先通過讀取配置檔案來獲得通道。

訊息通道通常以佇列的形式存在,這種先進先出的資料結構無疑最為適合這種處理訊息的場景。微軟的MSMQ、IBM MQ、JBoss MQ以及開源的RabbitMQ、Apache ActiveMQ都通過佇列實現了Message Channel模式。因此,在選擇運用Message Channel模式時,更多地是要從質量屬性的層面對各種實現了該模式的產品進行全方位的分析與權衡。例如,訊息通道對併發的支援以及在效能上的表現;訊息通道是否充分地考慮了錯誤處理;對訊息安全的支援;以及關於訊息持久化、災備(fail over)與叢集等方面的支援。

因為通道傳遞的訊息往往是一些重要的業務資料,一旦通道成為故障點或安全性的突破點,對系統就會造成災難性的影響。

此處也順帶的提下jndi的機制,由於JNDI取決於具體的實現,在這裡只能是講解下jboss的jndi的實現了:

在將物件例項繫結到jboss jnp server後,當遠端端採用context.lookup()方式獲取遠端物件例項並開始呼叫時,jboss jndi的實現方法是從jnp server上獲取物件例項,將其序列化回本地,然後在本地進行反序列化,之後在本地進行類呼叫。

通過這個機制,就可以知道了,本地其實是必須有繫結到jboss上的物件例項的class的,否則反序列化的時候肯定就失敗了,而遠端通訊需要做到的是在遠端執行某動作,並獲取到相應的結果,可見純粹基於JNDI是無法實現遠端通訊的。

但JNDI也是實現分散式服務框架一個很關鍵的技術點,因為可以通過它來實現透明化的遠端和本地呼叫,就像ejb,另外它也是個很好的隱藏實際部署機制(就像datasource)等的方案。

2.2 釋出者-訂閱者(Publisher-Subscriber)模式

一旦訊息通道需要支援多個消費者時,就可能面臨兩種模型的選擇:拉模型與推模型。拉模型是由訊息的消費者發起的,主動權把握在消費者手中,它會根據自己的情況對生產者發起呼叫。如圖所示:

拉模型的另一種體現則由生產者在狀態發生變更時,通知消費者其狀態發生了改變。但得到通知的消費者卻會以回撥方式,通過呼叫傳遞過來的消費者物件獲取更多細節訊息。

在基於訊息的分散式系統中,拉模型的消費者通常以Batch Job的形式,根據事先設定的時間間隔,定期偵聽通道的情況。一旦發現有訊息傳遞進來,就會轉而將訊息傳遞給真正的處理器(也可以看做是消費者)處理訊息,執行相關的業務。

推模型的主動權常常掌握在生產者手中,消費者被動地等待生產者發出的通知,這就要求生產者必須瞭解消費者的相關資訊。如圖所示:

對於推模型而言,消費者無需瞭解生產者。在生產者通知消費者時,傳遞的往往是訊息(或事件),而非生產者自身。同時,生產者還可以根據不同的情況,註冊不同的消費者,又或者在封裝的通知邏輯中,根據不同的狀態變化,通知不同的消費者。

兩種模型各有優勢。拉模型的好處在於可以進一步解除消費者對通道的依賴,通過後臺任務去定期訪問訊息通道。壞處是需要引入一個單獨的服務程式,以Schedule形式執行。而對於推模型而言,訊息通道事實上會作為消費者觀察的主體,一旦發現訊息進入,就會通知消費者執行對訊息的處理。無論推模型,拉模型,對於訊息物件而言,都可能採用類似Observer模式的機制,實現消費者對生產者的訂閱,因此這種機制通常又被稱為Publisher-Subscriber模式,如圖所示:

通常情況下,釋出者和訂閱者都會被註冊到用於傳播變更的基礎設施(即訊息通道)上。釋出者會主動地瞭解訊息通道,使其能夠將訊息傳送到通道中;訊息通道一旦接收到訊息,會主動地呼叫註冊在通道中的訂閱者,進而完成對訊息內容的消費。

對於訂閱者而言,有兩種處理訊息的方式。一種方式是廣播機制,這時訊息通道中的訊息在出列的同時,還需要複製訊息物件,將訊息傳遞給多個訂閱者。例如,有多個子系統都需要獲取從CRM系統傳來的客戶資訊,並根據傳遞過來的客戶資訊,進行相應的處理。此時的訊息通道又被稱為Propagation通道。另一種方式則屬於搶佔機制,它遵循同步方式,在同一時間只能有一個訂閱者能夠處理該訊息。實現Publisher-Subscriber模式的訊息通道會選擇當前空閒的唯一訂閱者,並將訊息出列,並傳遞給訂閱者的訊息處理方法。

目前,有許多訊息中介軟體都能夠很好地支援Publisher-Subscriber模式,例如JMS介面規約中對於Topic物件提供的MessagePublisher與MessageSubscriber介面。RabbitMQ也提供了自己對該模式的實現。微軟的MSMQ雖然引入了事件機制,可以在佇列收到訊息時觸發事件,通知訂閱者。但它並非嚴格意義上的Publisher-Subscriber模式實現。由微軟MVP Udi Dahan作為主要貢獻者的NServiceBus,則對MSMQ以及WCF做了進一層包裝,並能夠很好地實現這一模式。

2.3 訊息路由(Message Router)模式

無論是Message Channel模式,還是Publisher-Subscriber模式,佇列在其中都扮演了舉足輕重的角色。然而,在企業應用系統中,當系統變得越來越複雜時,對效能的要求也會越來越高,此時對於系統而言,可能就需要支援同時部署多個佇列,並可能要求分散式部署不同的佇列。這些佇列可以根據定義接收不同的訊息,例如訂單處理的訊息,日誌資訊,查詢任務訊息等。這時,對於訊息的生產者和消費者而言,並不適宜承擔決定訊息傳遞路徑的職責。事實上,根據S單一職責原則,這種職責分配也是不合理的,它既不利於業務邏輯的重用,也會造成生產者、消費者與訊息佇列之間的耦合,從而影響系統的擴充套件。

既然這三種物件(元件)都不宜承擔這樣的職責,就有必要引入一個新的物件專門負責傳遞路徑選擇的功能,這就是所謂的Message Router模式,如圖所示:

通過訊息路由,我們可以配置路由規則指定訊息傳遞的路徑,以及指定具體的消費者消費對應的生產者。例如指定路由的關鍵字,並由它來繫結具體的佇列與指定的生產者(或消費者)。路由的支援提供了訊息傳遞與處理的靈活性,也有利於提高整個系統的訊息處理能力。同時,路由物件有效地封裝了尋找與匹配訊息路徑的邏輯,就好似一個調停者(Meditator),負責協調訊息、佇列與路徑定址之間關係。

3 應用級協議

遠端服務通訊,需要達到的目標是在一臺計算機發起請求,另外一臺機器在接收到請求後進行相應的處理並將結果返回給請求端,這其中又會有諸如one way request、同步請求、非同步請求等等請求方式,按照網路通訊原理,需要實現這個需要做的就是將請求轉換成流,通過傳輸協議傳輸至遠端,遠端計算機在接收到請求的流後進行處理,處理完畢後將結果轉化為流,並通過傳輸協議返回給呼叫端。

原理是這樣的,但為了應用的方便,業界推出了很多基於此原理之上的應用級的協議,使得大家可以不用去直接操作這麼底層的東西,通常應用級的遠端通訊協議會提供:

  1. 為了避免直接做流操作這麼麻煩,提供一種更加易用或貼合語言的標準傳輸格式;
  2. 網路通訊機制的實現,就是替你完成了將傳輸格式轉化為流,通過某種傳輸協議傳輸至遠端計算機,遠端計算機在接收到流後轉化為傳輸格式,並進行儲存或以某種方式通知遠端計算機。

所以在學習應用級的遠端通訊協議時,我們可以帶著這幾個問題進行學習:

  1. 傳輸的標準格式是什麼?
  2. 怎麼樣將請求轉化為傳輸的流?
  3. 怎麼接收和處理流?
  4. 傳輸協議是?

不過應用級的遠端通訊協議並不會在傳輸協議上做什麼多大的改進,主要是在流操作方面,讓應用層生成流和處理流的這個過程更加的貼合所使用的語言或標準,至於傳輸協議則通常都是可選的,在java領域中知名的有:RMI、XML-RPC、Binary-RPC、SOAP、CORBA、JMS、HTTP,來具體的看看這些遠端通訊的應用級協議。

3.1 RMI(遠端方法呼叫)

RMI是個典型的為java定製的遠端通訊協議,我們都知道,在single vm中,我們可以通過直接呼叫java object instance來實現通訊,那麼在遠端通訊時,如果也能按照這種方式當然是最好了,這種遠端通訊的機制成為RPC(Remote Procedure Call),RMI正是朝著這個目標而誕生的。

RMI 採用stubs 和 skeletons 來進行遠端物件(remote object)的通訊。stub 充當遠端物件的客戶端代理,有著和遠端物件相同的遠端介面,遠端物件的呼叫實際是通過呼叫該物件的客戶端代理物件stub來完成的,通過該機制RMI就好比它是本地工作,採用tcp/ip協議,客戶端直接呼叫服務端上的一些方法。優點是強型別,編譯期可檢查錯誤,缺點是隻能基於JAVA語言,客戶機與伺服器緊耦合。

來看下基於RMI的一次完整的遠端通訊過程的原理:

  1. 客戶端發起請求,請求轉交至RMI客戶端的stub類;
  2. stub類將請求的介面、方法、引數等資訊進行序列化;
  3. 基於socket將序列化後的流傳輸至伺服器端;
  4. 伺服器端接收到流後轉發至相應的skelton類;
  5. skelton類將請求的資訊反序列化後呼叫實際的處理類;
  6. 處理類處理完畢後將結果返回給skelton類;
  7. Skelton類將結果序列化,通過socket將流傳送給客戶端的stub;
  8. stub在接收到流後反序列化,將反序列化後的Java Object返回給呼叫者。

根據原理來回答下之前學習應用級協議帶著的幾個問題:

  1. 傳輸的標準格式是什麼?是Java ObjectStream。
  2. 怎麼樣將請求轉化為傳輸的流?基於Java序列化機制將請求的java object資訊轉化為流。
  3. 怎麼接收和處理流?根據採用的協議啟動相應的監聽埠,當有流進入後基於Java序列化機制將流進行反序列化,並根據RMI協議獲取到相應的處理物件資訊,進行呼叫並處理,處理完畢後的結果同樣基於java序列化機制進行返回。
  4. 傳輸協議是?Socket。

3.2 XML-RPC

RPC使用C/S方式,採用http協議,傳送請求到伺服器,等待伺服器返回結果。這個請求包括一個引數集和一個文字集,通常形成“classname.methodname”形式。優點是跨語言跨平臺,C端、S端有更大的獨立性,缺點是不支援物件,無法在編譯器檢查錯誤,只能在執行期檢查。

XML-RPC也是一種和RMI類似的遠端呼叫的協議,它和RMI的不同之處在於它以標準的xml格式來定義請求的資訊(請求的物件、方法、引數等),這樣的好處是什麼呢,就是在跨語言通訊的時候也可以使用。

來看下XML-RPC協議的一次遠端通訊過程:

  1. 客戶端發起請求,按照XML-RPC協議將請求資訊進行填充;
  2. 填充完畢後將xml轉化為流,通過傳輸協議進行傳輸;
  3. 接收到在接收到流後轉換為xml,按照XML-RPC協議獲取請求的資訊並進行處理;
  4. 處理完畢後將結果按照XML-RPC協議寫入xml中並返回。

同樣來回答問題:

  1. 傳輸的標準格式是?標準格式的XML。
  2. 怎麼樣將請求轉化為傳輸的流?將XML轉化為流。
  3. 怎麼接收和處理流?通過監聽的埠獲取到請求的流,轉化為XML,並根據協議獲取請求的資訊,進行處理並將結果寫入XML中返回。
  4. 傳輸協議是?Http。

3.3 Binary-RPC

Binary-RPC看名字就知道和XML-RPC是差不多的了,不同之處僅在於傳輸的標準格式由XML轉為了二進位制的格式。

同樣來回答問題:

  1. 傳輸的標準格式是?標準格式的二進位制檔案。
  2. 怎麼樣將請求轉化為傳輸的流?將二進位制格式檔案轉化為流。
  3. 怎麼接收和處理流?通過監聽的埠獲取到請求的流,轉化為二進位制檔案,根據協議獲取請求的資訊,進行處理並將結果寫入XML中返回。
  4. 傳輸協議是?Http。

3.4 SOAP

SOAP原意為Simple Object Access Protocol,是一個用於分散式環境的、輕量級的、基於XML進行資訊交換的通訊協議,可以認為SOAP是XML RPC的高階版,兩者的原理完全相同,都是http+XML,不同的僅在於兩者定義的XML規範不同,SOAP也是Webservice採用的服務呼叫協議標準,因此在此就不多加闡述了。

Web Service提供的服務是基於web容器的,底層使用http協議,類似一個遠端的服務提供者,比如天氣預報服務,對各地客戶端提供天氣預報,是一種請求應答的機制,是跨系統跨平臺的。就是通過一個servlet,提供服務出去。

首先客戶端從伺服器獲得WebService的WSDL,同時在客戶端生成一個代理類(Proxy Class),這個代理類負責與WebService伺服器進行Request和Response。當一個資料(XML格式的)被封裝成SOAP格式的資料流傳送到伺服器端的時候,就會生成一個程式物件並且把接收到這個Request的SOAP包進行解析,然後對事物進行處理,處理結束以後再對這個計算結果進行SOAP包裝,然後把這個包作為一個Response傳送給客戶端的代理類(Proxy Class),同樣地,這個代理類也對這個SOAP包進行解析處理,繼而進行後續操作。這就是WebService的一個執行過程。

Web Service大體上分為5個層次:

  1. Http傳輸通道;
  2. XML的資料格式;
  3. SOAP封裝格式;
  4. WSDL的描述方式;
  5. UDDI UDDI是一種目錄服務,企業可以使用它對Webservices進行註冊和搜尋;

3.5 JMS

JMS是實現java領域遠端通訊的一種手段和方法,基於JMS實現遠端通訊時和RPC是不同的,雖然可以做到RPC的效果,但因為不是從協議級別定義的,因此我們不認為JMS是個RPC協議,但它確實是個遠端通訊協議,在其他的語言體系中也存在著類似JMS的東西,可以統一的將這類機制稱為訊息機制,而訊息機制呢,通常是高併發、分散式領域推薦的一種通訊機制,這裡的主要一個問題是容錯。

JMS是Java的訊息服務,JMS的客戶端之間可以通過JMS服務進行非同步的訊息傳輸。JMS支援兩種訊息模型:Point-to-Point(P2P)和Publish/Subscribe(Pub/Sub),即點對點和釋出訂閱模型

來看JMS中的一次遠端通訊的過程:

  1. 客戶端將請求轉化為符合JMS規定的Message;
  2. 通過JMS API將Message放入JMS Queue或Topic中;
  3. 如為JMS Queue,則傳送中相應的目標Queue中,如為Topic,則傳送給訂閱了此Topic的JMS Queue。
  4. 處理端則通過輪訓JMS Queue,來獲取訊息,接收到訊息後根據JMS協議來解析Message並處理。

同樣來回答問題:

  1. 傳輸的標準格式是?JMS規定的Message。
  2. 怎麼樣將請求轉化為傳輸的流?將引數資訊放入Message中即可。
  3. 怎麼接收和處理流?輪訓JMS Queue來接收Message,接收到後進行處理,處理完畢後仍然是以Message的方式放入Queue中傳送或Multicast。
  4. 傳輸協議是?不限。

基於JMS也是常用的實現遠端非同步呼叫的方法之一。

4 之間的區別

4.1 RPC與RMI

  1. RPC跨語言,而RMI只支援Java。
  2. RMI呼叫遠端物件方法,允許方法返回Java物件以及基本資料型別,而RPC不支援物件的概念,傳送到RPC服務的訊息由外部資料表示 (External Data Representation, XDR) 語言表示,這種語言抽象了位元組序類和資料型別結構之間的差異。只有由 XDR 定義的資料型別才能被傳遞,可以說 RMI 是物件導向方式的Java RPC。
  3. 在方法呼叫上,RMI中,遠端介面使每個遠端方法都具有方法簽名。如果一個方法在伺服器上執行,但是沒有相匹配的簽名被新增到這個遠端介面上,那麼這個新方法就不能被RMI客戶方所呼叫。在RPC中,當一個請求到達RPC伺服器時,這個請求就包含了一個引數集和一個文字值,通常形成“classname.methodname”的形式。這就向RPC伺服器表明,被請求的方法在為 “classname”的類中,名叫“methodname”。然後RPC伺服器就去搜尋與之相匹配的類和方法,並把它作為那種方法引數型別的輸入。這裡的引數型別是與RPC請求中的型別是匹配的。一旦匹配成功,這個方法就被呼叫了,其結果被編碼後返回客戶方。
  4. RPC本身沒有規範,但基本的工作機制是一樣的,即:serialization/deserialization+stub+skeleton,寬泛的講,只要能實現遠端呼叫,都是RPC,如:rmi .net-remoting ws/soap/rest hessian xmlrpc thrift potocolbuffer。
  5. 在Java裡提供了完整的sockets通訊介面,但sockets要求客戶端和服務端必須進行應用級協議的編碼交換資料,採用sockets是非常麻煩的。一個代替Sockets的協議是RPC(Remote Procedure Call), 它抽象出了通訊介面用於過程呼叫,使得程式設計者呼叫一個遠端過程和呼叫本地過程同樣方便。RPC 系統採用XDR來編碼遠端呼叫的引數和返回值。但RPC並不支援物件,所以,物件導向的遠端呼叫RMI(Remote Method Invocation)成為必然選擇。採用RMI,呼叫遠端物件和呼叫本地物件同樣方便。RMI 採用JRMP(Java Remote Method Protocol)通訊協議,是構建在TCP/IP協議上的一種遠端呼叫方法。

4.2 JMS與RMI

  1. 採用JMS服務,物件是在物理上被非同步從網路的某個JVM 上直接移動到另一個JVM 上(是訊息通知機制),而RMI物件是繫結在本地JVM 中,只有函式引數和返回值是通過網路傳送的(是請求應答機制)。
  2. RMI一般都是同步的,也就是說,當client呼叫Server的一個方法的時候,需要等到對方的返回,才能繼續執行client端,這個過程呼叫本地方法感覺上是一樣的,這也是RMI的一個特點。JMS 一般只是一個點發出一個Message到Message Server,發出之後一般不會關心誰用了這個message。所以,一般RMI的應用是緊耦合,JMS的應用相對來說是鬆散耦合應用。

4.3 Webservice與RMI

RMI是在tcp協議上傳遞可序列化的java物件,只能用在java虛擬機器上,繫結語言,客戶端和服務端都必須是java。webservice沒有這個限制,webservice是在http協議上傳遞xml文字檔案,與語言和平臺無關。

4.4 Webservice與JMS

Webservice專注於遠端服務呼叫,jms專注於資訊交換。

大多數情況下Webservice是兩系統間的直接互動(Consumer Producer),而大多數情況下jms是三方系統互動(Consumer Producer)。當然,JMS也可以實現request-response模式的通訊,只要Consumer或Producer其中一方兼任broker即可。

JMS可以做到非同步呼叫完全隔離了客戶端和服務提供者,能夠抵禦流量洪峰;WebService服務通常為同步呼叫,需要有複雜的物件轉換,相比SOAP,現在JSON,rest都是很好的http架構方案;

JMS是java平臺上的訊息規範。一般jms訊息不是一個xml,而是一個java物件,很明顯,jms沒考慮異構系統,說白了,JMS就沒考慮非java的東西。但是好在現在大多數的jms provider(就是JMS的各種實現產品)都解決了異構問題。相比WebService的跨平臺各有千秋吧。

5 可選實現技術

目前java領域可用於實現遠端通訊的框架或library,知名的有:JBoss-Remoting、Spring-Remoting、Hessian、Burlap、XFire(Axis)、ActiveMQ、Mina、Mule、EJB3等等,來對每種做個簡單的介紹和評價,其實呢,要做分散式服務框架,這些東西都是要有非常深刻的瞭解的,因為分散式服務框架其實是包含了解決分散式領域以及應用層面領域兩方面問題的。

當然,你也可以自己根據遠端網路通訊原理(transport protocol+Net IO)去實現自己的通訊框架或library。

那麼在瞭解這些遠端通訊的框架或library時,會帶著什麼問題去學習呢?

  1. 是基於什麼協議實現的?
  2. 怎麼發起請求?
  3. 怎麼將請求轉化為符合協議的格式的?
  4. 使用什麼傳輸協議傳輸?
  5. 響應端基於什麼機制來接收請求?
  6. 怎麼將流還原為傳輸格式的?
  7. 處理完畢後怎麼回應?

5.1 Spring-Remoting

Spring-remoting是Spring提供java領域的遠端通訊框架,基於此框架,同樣也可以很簡單的將普通的spring bean以某種遠端協議的方式來發布,同樣也可以配置spring bean為遠端呼叫的bean。

  1. 是基於什麼協議實現的?作為一個遠端通訊的框架,Spring通過整合多種遠端通訊的library,從而實現了對多種協議的支援,例如rmi、http+io、xml-rpc、binary-rpc等。
  2. 怎麼發起請求?在Spring中,由於其對於遠端呼叫的bean採用的是proxy實現,發起請求完全是通過服務介面呼叫的方式。
  3. 怎麼將請求轉化為符合協議的格式的?Spring按照協議方式將請求的物件資訊轉化為流,例如Spring Http Invoker是基於Spring自己定義的一個協議來實現的,傳輸協議上採用的為http,請求資訊是基於java序列化機制轉化為流進行傳輸。
  4. 使用什麼傳輸協議傳輸?支援多種傳輸協議,例如rmi、http等等。
  5. 響應端基於什麼機制來接收請求?響應端遵循協議方式來接收請求,對於使用者而言,則只需通過spring的配置方式將普通的spring bean配置為響應端或者說提供服務端。
  6. 怎麼將流還原為傳輸格式的?按照協議方式來進行還原。
  7. 處理完畢後怎麼回應?處理完畢後直接返回即可,spring-remoting將根據協議方式來做相應的序列化。

5.2 Hessian

Hessian是由caucho提供的一個基於binary-RPC實現的遠端通訊library。

  1. 是基於什麼協議實現的?基於Binary-RPC協議實現。
  2. 怎麼發起請求?需通過Hessian本身提供的API來發起請求。
  3. 怎麼將請求轉化為符合協議的格式的?Hessian通過其自定義的序列化機制將請求資訊進行序列化,產生二進位制流。
  4. 使用什麼傳輸協議傳輸?Hessian基於Http協議進行傳輸。
  5. 響應端基於什麼機制來接收請求?響應端根據Hessian提供的API來接收請求。
  6. 怎麼將流還原為傳輸格式的?Hessian根據其私有的序列化機制來將請求資訊進行反序列化,傳遞給使用者時已是相應的請求資訊物件了。
  7. 處理完畢後怎麼回應?處理完畢後直接返回,hessian將結果物件進行序列化,傳輸至呼叫端。

5.3 Burlap

Burlap也是有caucho提供,它和hessian的不同在於,它是基於XML-RPC協議的。

  1. 是基於什麼協議實現的?基於XML-RPC協議實現。
  2. 怎麼發起請求?根據Burlap提供的API。
  3. 怎麼將請求轉化為符合協議的格式的?將請求資訊轉化為符合協議的XML格式,轉化為流進行傳輸。
  4. 使用什麼傳輸協議傳輸?Http協議。
  5. 響應端基於什麼機制來接收請求?監聽Http請求。
  6. 怎麼將流還原為傳輸格式的?根據XML-RPC協議進行還原。
  7. 處理完畢後怎麼回應?返回結果寫入XML中,由Burlap返回至呼叫端。

5.4 XFire、Axis

XFire、Axis是Webservice的實現框架,WebService可算是一個完整的SOA架構實現標準了,因此採用XFire、Axis這些也就意味著是採用webservice方式了。

  1. 是基於什麼協議實現的?基於SOAP協議。
  2. 怎麼發起請求?獲取到遠端service的proxy後直接呼叫。
  3. 怎麼將請求轉化為符合協議的格式的?將請求資訊轉化為遵循SOAP協議的XML格式,由框架轉化為流進行傳輸。
  4. 使用什麼傳輸協議傳輸?Http協議。
  5. 響應端基於什麼機制來接收請求?監聽Http請求。
  6. 怎麼將流還原為傳輸格式的?根據SOAP協議進行還原。
  7. 處理完畢後怎麼回應?返回結果寫入XML中,由框架返回至呼叫端。

5.5 ActiveMQ

ActiveMQ是JMS的實現,基於JMS這類訊息機制實現遠端通訊是一種不錯的選擇,畢竟訊息機制本身的功能使得基於它可以很容易的去實現同步/非同步/單向呼叫等,而且訊息機制從容錯角度上來說也是個不錯的選擇,這是Erlang能夠做到容錯的重要基礎。

  1. 是基於什麼協議實現的?基於JMS協議。
  2. 怎麼發起請求?遵循JMS API發起請求。
  3. 怎麼將請求轉化為符合協議的格式的?不太清楚,猜想應該是二進位制流。
  4. 使用什麼傳輸協議傳輸?支援多種傳輸協議,例如socket、http等等。
  5. 響應端基於什麼機制來接收請求?監聽符合協議的埠。
  6. 怎麼將流還原為傳輸格式的?同問題3。
  7. 處理完畢後怎麼回應?遵循JMS API生成訊息,並寫入JMS Queue中。

5.6 Mina

Mina是Apache提供的通訊框架,在之前一直沒有提到網路IO這塊,之前提及的框架或library基本都是基於BIO的,而Mina是採用NIO的,NIO在併發量增長時對比BIO而言會有明顯的效能提升,而java效能的提升,與其NIO這塊與OS的緊密結合是有不小的關係的。

  1. 是基於什麼協議實現的?基於純粹的Socket+NIO。
  2. 怎麼發起請求?通過Mina提供的Client API。
  3. 怎麼將請求轉化為符合協議的格式的?Mina遵循java序列化機制對請求物件進行序列化。
  4. 使用什麼傳輸協議傳輸?支援多種傳輸協議,例如socket、http等等。
  5. 響應端基於什麼機制來接收請求?以NIO的方式監聽協議埠。
  6. 怎麼將流還原為傳輸格式的?遵循java序列化機制對請求物件進行反序列化。
  7. 處理完畢後怎麼回應?遵循Mina API進行返回。

MINA是NIO方式的,因此支援非同步呼叫是毫無懸念的。

6 RPC框架的發展與現狀

RPC(Remote Procedure Call)是一種遠端呼叫協議,簡單地說就是能使應用像呼叫本地方法一樣的呼叫遠端的過程或服務,可以應用在分散式服務、分散式計算、遠端服務呼叫等許多場景。說起 RPC 大家並不陌生,業界有很多開源的優秀 RPC 框架,例如 Dubbo、Thrift、gRPC、Hprose 等等。下面先簡單介紹一下 RPC 與常用遠端呼叫方式的特點,以及一些優秀的開源 RPC 框架。

RPC 與其它遠端呼叫方式比較,RPC 與 HTTP、RMI、Web Service 都能完成遠端呼叫,但是實現方式和側重點各有不同。

6.1 RPC與HTTP

HTTP(HyperText Transfer Protocol)是應用層通訊協議,使用標準語義訪問指定資源(圖片、介面等),網路中的中轉伺服器能識別協議內容。HTTP 協議是一種資源訪問協議,通過 HTTP 協議可以完成遠端請求並返回請求結果。

HTTP 的優點是簡單、易用、可理解性強且語言無關,在遠端服務呼叫中包括微博有著廣泛應用。HTTP 的缺點是協議頭較重,一般請求到具體伺服器的鏈路較長,可能會有 DNS 解析、Nginx 代理等。

RPC 是一種協議規範,可以把 HTTP 看作是一種 RPC 的實現,也可以把 HTTP 作為 RPC 的傳輸協議來應用。RPC 服務的自動化程度比較高,能夠實現強大的服務治理功能,和語言結合更友好,效能也十分優秀。與 HTTP 相比,RPC 的缺點就是相對複雜,學習成本稍高。

6.2 RPC與RMI

RMI(Remote Method Invocation)是指 Java 語言中的遠端方法呼叫,RMI 中的每個方法都具有方法簽名,RMI 客戶端和伺服器端通過方法簽名進行遠端方法呼叫。RMI 只能在 Java 語言中使用,可以把 RMI 看作物件導向的 Java RPC。

6.3 RPC與Web Service

Web Service 是一種基於 Web 進行服務釋出、查詢、呼叫的架構方式,重點在於服務的管理與使用。Web Service 一般通過 WSDL 描述服務,使用 SOAP通過 HTTP 呼叫服務。

RPC 是一種遠端訪問協議,而 Web Service 是一種體系結構,Web Service 也可以通過 RPC 來進行服務呼叫,因此 Web Service 更適合同一個 RPC 框架進行比較。當 RPC 框架提供了服務的發現與管理,並使用 HTTP 作為傳輸協議時,其實就是 Web Service。

相對 Web Service,RPC 框架可以對服務進行更細粒度的治理,包括流量控制、SLA 管理等,在微服務化、分散式計算方面有更大的優勢。

RPC 可基於 HTTP 或 TCP 協議,Web Service 就是基於 HTTP 協議的 RPC,它具有良好的跨平臺性,但其效能卻不如基於 TCP 協議的 RPC。會兩方面會直接影響 RPC 的效能,一是傳輸方式,二是序列化。

眾所周知,TCP 是傳輸層協議,HTTP 是應用層協議,而傳輸層較應用層更加底層,在資料傳輸方面,越底層越快,因此,在一般情況下,TCP 一定比 HTTP 快。

7 總結

在遠端通訊領域中,涉及的知識點還是相當的多的,例如有:通訊協議(Socket/tcp/http/udp/rmi/xml-rpc etc.)、訊息機制、網路IO(BIO/NIO/AIO)、MultiThread、本地呼叫與遠端呼叫的透明化方案(涉及Java Classloader、Dynamic Proxy、Unit Test etc.)、非同步與同步呼叫、網路通訊處理機制(自動重連、廣播、異常、池處理等等)、Java Serialization (各種協議的私有序列化機制等)、各種框架的實現原理(傳輸格式、如何將傳輸格式轉化為流的、如何將請求資訊轉化為傳輸格式的、如何接收流的、如何將流還原為傳輸格式的等等),要精通其中的哪些東西,得根據實際需求來決定了,只有在瞭解了原理的情況下才能很容易的做出選擇,甚至可以根據需求做私有的遠端通訊協議,對於從事分散式服務平臺或開發較大型的分散式應用的人而言,我覺得至少上面提及的知識點是需要比較瞭解的。

相關文章