Java分布式開發


  分布式概念的引入是基於性能的提升,應用的可靠性而提出的。所謂Java分布式,即是在使用Java語言進行企業級應用開發的過程中,采用分布式技術解決業務邏輯的高並發、高可用性的一些架構設計方案。

1. RPC技術介紹

    我們知道Web Servie實現了服務器端如何向客戶端提供服務。常見的三種方法:RPC 所謂的遠程過程調用(面向方法);SOA所謂的面向服務的架構(面向消息);REST所謂的Representational state transfer(面向資源)。如果說RPC是基於方法調用,那么SOA則是基於消息,基於方法調用通常會與特定的程序語言耦合起來,而后者則與具體的實現語言無關,所以在一定程度上得到大公司的支持。

  RPC(Remote Procedure Call Protocol),遠程過程調用協議。它是一種協議,程序可使用這種協議向網絡中的另一台計算機上的程序請求服務,由於使用RPC的程序不必了解支持通信的網絡協議的情況,因此RPC提高了程序的互操作性。該協議允許運行於一台計算機的程序調用另一台計算機的子程序,而程序員無需額外地為這個交互作用編程。通俗的說,使用了RPC服務之后,就可以像調用一個本地方法一樣去調用一個運行於網絡中的其他計算機的方法。

上圖是windows中的RPC服務調用,可以在windows系統中的服務選項中查看到。當前使用RPC協議的技術有很多,如阿里巴巴的hsf、dubbo(開源)、Facebook的thrift(開源)、Google grpc(開源)、Twitter的finagle(開源)等。在大型互聯網公司,公司的系統都由成千上萬大大小小的服務組成,各服務部署在不同的機器上,由不同的團隊負責。我們在開發一個服務的時候面臨兩個問題:1)要搭建一個新服務,免不了需要依賴他人的服務,而現在他人的服務都在遠端,怎么調用?2)其它團隊要使用我們的新服務,我們的服務該怎么發布以便他人調用?

上圖所示是RPC調用的詳細流程圖

1)服務消費方(client)調用以本地調用方式調用服務; 2)client stub接收到調用后負責將方法、參數等組裝成能夠進行網絡傳輸的消息體; 3)client stub找到服務地址,並將消息發送到服務端; 4)server stub收到消息后進行解碼; 5)server stub根據解碼結果調用本地的服務; 6)本地服務執行並將結果返回給server stub; 7)server stub將返回結果打包成消息並發送至消費方; 8)client stub接收到消息,並進行解碼; 9)服務消費方得到最終結果。   RPC的目標就是要2~8這些步驟都封裝起來,讓用戶對這些細節透明。

在RPC中,怎么做到透明化遠程服務調用?怎么封裝通信細節才能讓用戶像以本地調用方式調用遠程服務呢?對java來說就是使用代理!java代理有兩種方式:1) jdk 動態代理;2)字節碼生成。盡管字節碼生成方式實現的代理更為強大和高效,但代碼維護不易,大部分公司實現RPC框架時還是選擇動態代理方式。

/**
* 服務提供方
**/
public interface SMSService {
     String sendSMS(String msg);
}

public class SMSServiceImpl implements SMSService {
     @Override
     public String sendSMS(String msg) {
         String result = “您好,” + msg + "測試短信,勿回復。 " ;
         System.out.println(result);
         return result;
     }
}

/**
* 服務消費方
**/
public class Test {
2     public static void main(String[] args) {
3         SMSService smsService = new SMSServiceImpl ();
4         smsService.sendSMS("Sucre");
5     }
6 }

下面簡單介紹下動態代理怎么實現我們的需求。我們需要實現RPCProxyClient代理類,代理類的invoke方法中封裝了與遠端服務通信的細節,消費方首先從RPCProxyClient獲得服務提供方的接口,當執行smsService.sendSMS("Sucre")方法時就會調用invoke方法。

public class RPCProxyClient implements java.lang.reflect.InvocationHandler{
     private Object obj;
 
     public RPCProxyClient(Object obj){
         this.obj=obj;
     }
 
     /**
      * 得到被代理對象;
      */
     public static Object getProxy(Object obj){
         return java.lang.reflect.Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                 obj.getClass().getInterfaces(), new RPCProxyClient(obj));
     }
 
     /**
      * 調用此方法執行
      */
     public Object invoke(Object proxy, Method method, Object[] args)
             throws Throwable {
         //結果參數;
         Object result = new Object();
         // ...執行通信相關邏輯
         // ...
         return result;
     }
}

 public class Test {
    public static void main(String[] args) {
        SMSService smsService = (SMSService)RPCProxyClient.getProxy(SMSService.class);
        smsService.sendSMS("Sucre");
    }
}

  在invoke方法中,需要封裝通信細節,而通信的第一步就是要確定客戶端和服務端相互通信的消息結構。客戶端的請求消息結構一般需要包括以下內容:接口名稱,在我們的例子里接口名是“SMSService”,如果不傳,服務端就不知道調用哪個接口了;方法名,一個接口內可能有很多方法,如果不傳方法名服務端也就不知道調用哪個方法;參數類型&參數值,參數類型有很多,比如有bool、int、long、double、string、map、list,甚至如結構體;以及相應的參數值;超時時間;requestID。同理服務端返回的消息結構一般包括以下內容:返回值、狀態code、requestID。

  一旦確定了消息的數據結構后,下一步就是要考慮序列化與反序列化了。什么是序列化?序列化就是將數據結構或對象轉換成二進制串的過程,也就是編碼的過程。什么是反序列化?將在序列化過程中所生成的二進制串轉換成數據結構或者對象的過程。為什么需要序列化?轉換為二進制串后才好進行網絡傳輸嘛!為什么需要反序列化?將二進制轉換為對象才好進行后續處理!現如今序列化的方案越來越多,每種序列化方案都有優點和缺點,它們在設計之初有自己獨特的應用場景,那到底選擇哪種呢?從RPC的角度上看,主要看三點:1)通用性,比如是否能支持Map等復雜的數據結構;2)性能,包括時間復雜度和空間復雜度,由於RPC框架將會被公司幾乎所有服務使用,如果序列化上能節約一點時間,對整個公司的收益都將非常可觀,同理如果序列化上能節約一點內存,網絡帶寬也能省下不少;3)可擴展性,對互聯網公司而言,業務變化飛快,如果序列化協議具有良好的可擴展性,支持自動增加新的業務字段,而不影響老的服務,這將大大提供系統的靈活度。目前互聯網公司廣泛使用Protobuf、Thrift、Avro等成熟的序列化解決方案來搭建RPC框架,這些都是久經考驗的解決方案。

  消息數據結構被序列化為二進制串后,下一步就要進行網絡通信了。目前有兩種常用IO通信模型:1)BIO;2)NIO。一般RPC框架需要支持這兩種IO模型。如何實現RPC的IO通信框架呢?1)使用java nio方式自研,這種方式較為復雜,而且很有可能出現隱藏bug,但也見過一些互聯網公司使用這種方式;2)基於mina,mina在早幾年比較火熱,不過這些年版本更新緩慢;3)基於netty,現在很多RPC框架都直接基於netty這一IO通信框架,省力又省心,比如阿里巴巴的HSF、dubbo,Twitter的finagle等。

  如何發布自己的服務如何讓別人使用我們的服務呢?有同學說很簡單嘛,告訴使用者服務的IP以及端口就可以了啊。確實是這樣,這里問題的關鍵在於是自動告知還是人肉告知。人肉告知的方式:如果你發現你的服務一台機器不夠,要再添加一台,這個時候就要告訴調用者我現在有兩個ip了,你們要輪詢調用來實現負載均衡;調用者咬咬牙改了,結果某天一台機器掛了,調用者發現服務有一半不可用,他又只能手動修改代碼來刪除掛掉那台機器的ip。現實生產環境當然不會使用人肉方式。有沒有一種方法能實現自動告知,即機器的增添、剔除對調用方透明,調用者不再需要寫死服務提供方地址?當然可以,現如今zookeeper被廣泛用於實現服務自動注冊與發現功能。簡單來講,zookeeper可以充當一個服務注冊表(Service Registry),讓多個服務提供者形成一個集群,讓服務消費者通過服務注冊表獲取具體的服務訪問地址(ip+端口)去訪問具體的服務提供者。

 

 

 

 

具體來說,zookeeper就是個分布式文件系統,每當一個服務提供者部署后都要將自己的服務注冊到zookeeper的某一路徑上:

 /{service}/{version}/{ip:port}

比如我們的SMSService部署到兩台機器,那么zookeeper上就會創建兩條目錄:分別為

/SMSService/1.0.0/100.19.20.01:16888
/SMSService/1.0.0/100.19.20.02:16888

zookeeper提供了“心跳檢測”功能,它會定時向各個服務提供者發送一個請求(實際上建立的是一個 Socket 長連接),如果長期沒有響應,服務中心就認為該服務提供者已經“掛了”,並將其剔除,比如100.19.20.02這台機器如果宕機了,那么zookeeper上的路徑就會只剩

/HelloWorldService/1.0.0/100.19.20.01:16888

服務消費者會去監聽相應路徑(/HelloWorldService/1.0.0),一旦路徑上的數據有任務變化(增加或減少),zookeeper都會通知服務消費方服務提供者地址列表已經發生改變,從而進行更新。更為重要的是zookeeper與生俱來的容錯容災能力(比如leader選舉),可以確保服務注冊表的高可用性。

2. RMI技術介紹

3. Restful API方式

4. Filter是基於回調函數,而攔截器是基於動態代理

博文參考:

[1] http://www.cnblogs.com/LBSer


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM