Thrift 個人實戰--RPC服務的發布訂閱實現(基於Zookeeper服務)


 

前言:
  Thrift作為Facebook開源的RPC框架, 通過IDL中間語言, 並借助代碼生成引擎生成各種主流語言的rpc框架服務端/客戶端代碼. 不過Thrift的實現, 簡單使用離實際生產環境還是有一定距離, 本系列將對Thrift作代碼解讀和框架擴充, 使得它更加貼近生產環境. 本文講述如何借用zookeeper來實現中介角色, 使得服務端和客戶端解耦, 並讓RPC服務平台化發展.

基礎架構:
  RPC服務往平台化的方向發展, 會屏蔽掉更多的服務細節(服務的IP地址集群, 集群的擴容和遷移), 只暴露服務接口. 這部分的演化, 使得server端和client端完全的解耦合. 兩者的交互通過ConfigServer(MetaServer)的中介角色來搭線.
  
  注: 該圖源自dubbo的官網
  這邊借助Zookeeper來扮演該角色, server扮演發布者的角色, 而client扮演訂閱者的角色. 

Zookeeper基礎:
  Zookeeper是分布式應用協作服務. 它實現了paxos的一致性算法, 在命名管理/配置推送/數據同步/主從切換方面扮演重要的角色.
  其數據組織類似文件系統的目錄結構:
  
  每個節點被稱為znode, 為znode節點依據其特性, 又可以分為如下類型:
  1). PERSISTENT: 永久節點
  2). EPHEMERAL: 臨時節點, 會隨session(client disconnect)的消失而消失
  3). PERSISTENT_SEQUENTIAL: 永久節點, 其節點的名字編號是單調遞增的
  4). EPHEMERAL_SEQUENTIAL: 臨時節點, 其節點的名字編號是單調遞增的
  注: 臨時節點不能成為父節點
  Watcher觀察模式, client可以注冊對節點的狀態/內容變更的事件回調機制. 其Event事件的兩類屬性需要關注下:
  1). KeeperState: Disconnected,SyncConnected,Expired
  2). EventType: None,NodeCreated,NodeDeleted,NodeDataChanged,NodeChildrenChanged

RPC服務端:
  作為具體業務服務的RPC服務發布方, 對其自身的服務描述由以下元素構成.
  1). product: 產品名稱
  2). service: 服務接口, 采用發布方的類全名來表示
  3). version: 版本號
  借鑒了Maven的GAV坐標系, 三維坐標系更符合服務平台化的大環境.
  *) 數據模型的設計
  具體RPC服務的注冊路徑為: /rpc/{product}/{service}/{version}, 該路徑上的節點都是永久節點
  RPC服務集群節點的注冊路徑為: /rpc/{product}/{service}/{version}/{ip:port}, 末尾的節點是臨時節點
  *) RPC服務節點的配置和行為
  服務端的配置如下所示:

<register>
  <server>{ip:port => Zookeeper的地址列表}</servers>
  <application>{application name => 服務的應用程序名}</application>
</register>

<server>
  <interface>{interface => 服務接口名}</interface>
  <version>{version => 服務版本號}</version>
  <class>{class => interface的具體實現Handler類}</class>
  <port>{提供服務的監聽端口}</port>
</server>

  服務端的注冊邏輯:

Zookeeper zk = new Zookeeper("127.0.0.1:2181", timeout, null);
while ( !application exit ) {
  Stat stat = zk.exists("/rpc/{product}/{service}/{version}/{ip:port}", false);
  if ( stat == null ) {
    zk.create("/rpc/{product}/{service}/{version}/{ip:port}", Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
  }	
  Thread.sleep(wait_timeout);
}	

  注: zookeeper client與zookeeper的斷開會導致臨時節點的丟失, 因此需要重新構建, 這邊采用開啟一個循環線程, 用於定時輪詢.

RPC客戶端:
  客戶端的簡單注冊配置:

<register>
  <server>{ip:port => Zookeeper的地址列表}</servers>
  <application>{application name => 服務的應用程序名}</application>	
</register>

<service>
  <interface>{interface => 服務接口名}</interface>
  <version>{version => 服務版本號}</version>
</sevice>	

  客戶端的代碼:
  1). 初始獲取server列表

Zookeeper zk = new Zookeeper("127.0.0.1:2181", timeout, null);
List<String> childrens = zk.getChildren(path, true);

  2). 注冊Watcher監聽, EventType.NodeChildrenChanged事件, 每次回調, 重新獲取列表

class WatcherWarpper implements Watcher {
  public void process(WatchedEvent event) {
    if ( event.getType() == EventType.NodeChildrenChanged ) {
      List<String> childrens = zk.getChildren(path, true);
      // notify Thrift client, rpc service的server ip:port列表發生了變化	
    }
  }
}

總結: 

  這部分其實涉及thrift點並不多, 但該文確實是rpc服務平台化的理論基礎. 服務端作為服務的發布方, 而客戶端借助zookeeper的watcher機制, 來實現其對服務列表的訂閱更新功能. 從而達到解耦, 邁出服務平台化的一步.

后續:
  后續文章講解Thrift client連接池的實現, 也是比較基礎的一部分, 敬請期待.

 


免責聲明!

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



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