本地搭建dubbo伺服器
⑴ bbo和zookeeper
bbo 是一個遠程調用服務的分布式框架,可以實現遠程通訊、動態配置、地址路由等等功能。比如在入門demo里的暴露服務,使得遠程調用的協議可以使用dobbo協議( bbo://x.x.x.x )或者其它協議,可以配置zookeeper集群地址,實現軟負載均衡並配置均衡方式等。在不搭配注冊中心的時候,它也是可以實現服務端和調用端的通信的,這種方式是點對點通信的,所謂「沒有中間商」。但是如果配置服務發布和調用端過多特別是集群的方式提供服務的時候,就會暴露許多的問題:增加節點需要修改配置文件、服務端機器宕機後不能被感知等。它可以通過集成注冊中心,來動態地治理服務發布和服務調用。相當於把服務注冊和發布推送的功能分攤給了(zookeeper)注冊中心。
Dubbo實現服務調用是通過RPC的方式,即客戶端和服務端共用一個介面(將介面打成一個jar包,在客戶端和服務端引入這個jar包),客戶端面向介面寫調用,服務端面向介面寫實現,中間的網路通信交給框架去實現。
咱們來看下Spring 配置聲明暴露服務,provider.xml文件
再來看服務消費者,consumer.xml文件
這就是典型的點對點的服務調用。當然我們為了高可用,可以在consumer.xml中配置多個服務提供者,並配置響應的負載均衡策略。
配置多個服務調用者在comsumer.xml的bbo:reference標簽的url屬性中加入多個地址,中間用分號隔開即可;配置負載均衡策略在comsumer.xml的bbo:reference標簽中增加loadbalance屬性即可,值可以為如下四種類型:
那麼目前的架構有什麼問題呢?
1.當服務提供者增加節點時,需要修改配置文件。
2.當其中一個服務提供者宕機時,服務消費者不能及時感知到,還會往宕機的服務發送請求。
這個時候就需要引入注冊中心了,Dubbo目前支持4種注冊中心(multicast、zookeeper、redis、simple)推薦使用Zookeeper注冊中心,要使用注冊中心,只需要將provider.xml和consumer.xml更改為如下:
如果zookeeper是一個集群,則多個地址之間用逗號分隔即可
把consumer.xml中配置的直連的方式去掉
注冊信息在zookeeper中如何保存?
啟動上面服務後,我們觀察zookeeper的根節點多了一個bbo節點及其他,圖示如下
最後一個節點中服務的地址,為什麼把最後一個節點標成綠色?因為最後一個節點是臨時節點,而其他節點是持久節點,這樣,當服務宕機時,這個節點就會自動消失,不再提供服務,服務消費者也不會再請求。如果部署多個DemoService,則providers下面會有好幾個節點,一個節點保存一個DemoService的服務地址。
其實一個zookeeper集群能被多個應用公用,因為不同的框架會在zookeeper上建不同的節點,互不影響。如bbo會創建一個/bbo節點,storm會創建一個/storm節點。
zookeeper 介紹:
zookeeper是 Apacahe Hadoop 的子項目,是一個樹型的目錄服務,支持變更推送,適合作為 Dubbo 服務的注冊中心,工業強度較高,可用於生產環境,並推薦使用。
流程說明:
支持以下功能:
補充:
bbo的協議使用什麼序列化框架?
bbo有多種協議,不同的協議默認使用不同的序列化框架。比如bbo協議默認使用 Hessian2 序列化(說明:Hessian2 是阿里在 Hessian 基礎上進行的二次開發,起名為Hessian2)。rmi協議默認為 java 原生序列化,http協議默認為為 json。
bbo的通信方式?
採用單一長連接和NIO非同步通信,基於Hessian2作為序列化協議。適合於小數據量(每次請求在100kb以內)大並發的服務調用,以及服務消費者機器數遠大於服務提供者機器數的情況。具體實現是消費者使用 NettyClient,提供者使用 NettyServer,Provider 啟動的時候,會開啟埠監聽,使用我們平時啟動 Netty 一樣的方式。而 Client 在 Spring getBean 的時候,會創建 Client,調用遠程方法的時候,將數據通過DubboCodec編碼發送到 NettyServer,然後 NettServer 收到數據後解碼,並調用本地方法,並返回數據,完成一次完美的 RPC 調用。
zookeeper選舉機制?
zookeeper選舉演算法默認的是FastLeaderElection,選舉機制的概念:
1.伺服器ID:比如有三台伺服器,編號分別是1、2、3,編號越大在選擇演算法中的權重越大。
2.事務ID:伺服器中存放的最大數據ID(致使ZooKeeper節點狀態改變的每一個操作都將更新事務id,即時間戳),值越大說明數據越新,在選舉演算法中數據越新權重越大。
3.邏輯時鍾,或者叫投票的次數,同一輪投票過程中的邏輯時鍾值是相同的。每投完一次票這個數據就會增加,然後與接收到的其它伺服器返回的投票信息中的數值相比,根據不同的值做出不同的判斷。
選舉狀態:LOOKING:競選狀態;FOLLOWING:隨從狀態,同步leader狀態,參與投票;OBSERVING:觀察狀態,同步leader狀態,不參與投票;LEADING:領導者狀態。
初次選舉簡述:
目前有5台伺服器,每台伺服器均沒有數據,它們的編號分別是1,2,3,4,5,按編號依次啟動,它們的選擇舉過程如下:
1.伺服器1啟動,給自己投票,然後發投票信息,由於其它機器還沒有啟動所以它收不到反饋信息,伺服器1的狀態一直屬於Looking。
2.伺服器2啟動,給自己投票,同時與之前啟動的伺服器1交換結果,由於伺服器2的編號大所以伺服器2勝出,但此時投票數沒有大於半數,所以兩個伺服器的狀態依然是LOOKING。
3.伺服器3啟動,給自己投票,同時與之前啟動的伺服器1,2交換信息,由於伺服器3的編號最大所以伺服器3勝出,此時投票數為3正好大於半數,所以伺服器3成為領導者,伺服器1,2成為小弟。
4.伺服器4啟動,給自己投票,同時與之前啟動的伺服器1,2,3交換信息,盡管伺服器4的編號大,但之前伺服器3已經勝出,所以伺服器4隻能成為小弟。
5.伺服器5啟動,後面的邏輯同伺服器4成為小弟。
如果中間有節點掛掉,只要有半數以上節點存活,就可以正常服務,如果leader掛掉,則所有節點處於Looking狀態 ,各自依次發起投票,投票包含自己的伺服器ID和最新事務ID,如果發現別人的事務id比自己大,也就是數據比自己新,那麼就重新發起投票,投票給目前已知最大的事務id所屬節點(事務id一樣,則數據id大的勝出)。每次投票後,伺服器都會統計投票數量,判斷是否有某個節點得到半數以上的投票。如果存在這樣的節點,該節點將會成為准Leader,狀態變為Leading。其他節點的狀態變為Following。
引用:
https://www.cnblogs.com/iisme/p/10620125.html
⑵ Dubbo——HTTP 協議 + JSON-RPC
Protocol 還有一個實現分支是 AbstractProxyProtocol,如下圖所示:
從圖中我們可以看到:gRPC、HTTP、WebService、Hessian、Thrift 等協議對應的 Protocol 實現,都是繼承自 AbstractProxyProtocol 抽象類。
目前互聯網的技術棧百花齊放,很多公司會使用 Node.js、Python、Rails、Go 等語言來開發 一些 Web 端應用,同時又有很多服務會使用 Java 技術棧實現,這就出現了大量的跨語言調用的需求。Dubbo 作為一個 RPC 框架,自然也希望能實現這種跨語言的調用,目前 Dubbo 中使用「HTTP 協議 + JSON-RPC」的方式來達到這一目的,其中 HTTP 協議和 JSON 都是天然跨語言的標准,在各種語言中都有成熟的類庫。
下面就重點來分析 Dubbo 對 HTTP 協議的支持。首先,會介紹 JSON-RPC 的基礎,並通過一個示例,快速入門,然後介紹 Dubbo 中 HttpProtocol 的具體實現,也就是如何將 HTTP 協議與 JSON-RPC 結合使用,實現跨語言調用的效果。
Dubbo 中支持的 HTTP 協議實際上使用的是 JSON-RPC 協議。
JSON-RPC 是基於 JSON 的跨語言遠程調用協議。Dubbo 中的 bbo-rpc-xml、bbo-rpc-webservice 等模塊支持的 XML-RPC、WebService 等協議與 JSON-RPC 一樣,都是基於文本的協議,只不過 JSON 的格式比 XML、WebService 等格式更加簡潔、緊湊。與 Dubbo 協議、Hessian 協議等二進制協議相比,JSON-RPC 更便於調試和實現,可見 JSON-RPC 協議還是一款非常優秀的遠程調用協議。
在 Java 體系中,有很多成熟的 JSON-RPC 框架,例如 jsonrpc4j、jpoxy 等,其中,jsonrpc4j 本身體積小巧,使用方便,既可以獨立使用,也可以與 Spring 無縫集合,非常適合基於 Spring 的項目。
下面先來看看 JSON-RPC 協議中請求的基本格式:
JSON-RPC請求中各個欄位的含義如下:
在 JSON-RPC 的服務端收到調用請求之後,會查找到相應的方法並進行調用,然後將方法的返回值整理成如下格式,返回給客戶端:
JSON-RPC響應中各個欄位的含義如下:
Dubbo 使用 jsonrpc4j 庫來實現 JSON-RPC 協議,下面使用 jsonrpc4j 編寫一個簡單的 JSON-RPC 服務端示常式序和客戶端示常式序,並通過這兩個示常式序說明 jsonrpc4j 最基本的使用方式。
首先,需要創建服務端和客戶端都需要的 domain 類以及服務介面。先來創建一個 User 類,作為最基礎的數據對象:
接下來創建一個 UserService 介面作為服務介面,其中定義了 5 個方法,分別用來創建 User、查詢 User 以及相關信息、刪除 User:
UserServiceImpl 是 UserService 介面的實現類,其中使用一個 ArrayList 集合管理 User 對象,具體實現如下:
整個用戶管理業務的核心大致如此。下面我們來看服務端如何將 UserService 與 JSON-RPC 關聯起來。
首先,創建 RpcServlet 類,它是 HttpServlet 的子類,並覆蓋了 HttpServlet 的 service() 方法。我們知道,HttpServlet 在收到 GET 和 POST 請求的時候,最終會調用其 service() 方法進行處理;HttpServlet 還會將 HTTP 請求和響應封裝成 HttpServletRequest 和 HttpServletResponse 傳入 service() 方法之中。這里的 RpcServlet 實現之中會創建一個 JsonRpcServer,並在 service() 方法中將 HTTP 請求委託給 JsonRpcServer 進行處理:
最後,創建一個 JsonRpcServer 作為服務端的入口類,在其 main() 方法中會啟動 Jetty 作為 Web 容器,具體實現如下:
這里使用到的 web.xml 配置文件如下:
完成服務端的編寫之後,下面再繼續編寫 JSON-RPC 的客戶端。在 JsonRpcClient 中會創建 JsonRpcHttpClient,並通過 JsonRpcHttpClient 請求服務端:
在 AbstractProxyProtocol 的 export() 方法中,首先會根據 URL 檢查 exporterMap 緩存,如果查詢失敗,則會調用 ProxyFactory.getProxy() 方法將 Invoker 封裝成業務介面的代理類,然後通過子類實現的 doExport() 方法啟動底層的 ProxyProtocolServer,並初始化 serverMap 集合。具體實現如下:
在 HttpProtocol 的 doExport() 方法中,與前面介紹的 DubboProtocol 的實現類似,也要啟動一個 RemotingServer。為了適配各種 HTTP 伺服器,例如,Tomcat、Jetty 等,Dubbo 在 Transporter 層抽象出了一個 HttpServer 的介面。
bbo-remoting-http 模塊的入口是 HttpBinder 介面,它被 @SPI 註解修飾,是一個擴展介面,有三個擴展實現,默認使用的是 JettyHttpBinder 實現,如下圖所示:
HttpBinder 介面中的 bind() 方法被 @Adaptive 註解修飾,會根據 URL 的 server 參數選擇相應的 HttpBinder 擴展實現,不同 HttpBinder 實現返回相應的 HttpServer 實現。HttpServer 的繼承關系如下圖所示:
這里以 JettyHttpServer 為例簡單介紹 HttpServer 的實現,在 JettyHttpServer 中會初始化 Jetty Server,其中會配置 Jetty Server 使用到的線程池以及處理請求 Handler:
可以看到 JettyHttpServer 收到的全部請求將委託給 DispatcherServlet 這個 HttpServlet 實現,而 DispatcherServlet 的 service() 方法會把請求委託給對應接埠的 HttpHandler 處理:
了解了 Dubbo 對 HttpServer 的抽象以及 JettyHttpServer 的核心之後,回到 HttpProtocol 中的 doExport() 方法繼續分析。
在 HttpProtocol.doExport() 方法中會通過 HttpBinder 創建前面介紹的 HttpServer 對象,並記錄到 serverMap 中用來接收 HTTP 請求。這里初始化 HttpServer 以及處理請求用到的 HttpHandler 是 HttpProtocol 中的內部類,在其他使用 HTTP 協議作為基礎的 RPC 協議實現中也有類似的 HttpHandler 實現類,如下圖所示:
在 HttpProtocol.InternalHandler 中的 handle() 實現中,會將請求委託給 skeletonMap 集合中記錄的 JsonRpcServer 對象進行處理:
skeletonMap 集合中的 JsonRpcServer 是與 HttpServer 對象一同在 doExport() 方法中初始化的。最後,我們來看 HttpProtocol.doExport() 方法的實現:
介紹完 HttpProtocol 暴露服務的相關實現之後,下面再來看 HttpProtocol 中引用服務相關的方法實現,即 protocolBindinRefer() 方法實現。該方法首先通過 doRefer() 方法創建業務介面的代理,這里會使用到 jsonrpc4j 庫中的 JsonProxyFactoryBean 與 Spring 進行集成,在其 afterPropertiesSet() 方法中會創建 JsonRpcHttpClient 對象:
下面來看 doRefer() 方法的具體實現:
在 AbstractProxyProtocol.protocolBindingRefer() 方法中,會通過 ProxyFactory.getInvoker() 方法將 doRefer() 方法返回的代理對象轉換成 Invoker 對象,並記錄到 Invokers 集合中,具體實現如下:
本文重點介紹了在 Dubbo 中如何通過「HTTP 協議 + JSON-RPC」的方案實現跨語言調用。首先介紹了 JSON-RPC 中請求和響應的基本格式,以及其實現庫 jsonrpc4j 的基本使用;接下來我們還詳細介紹了 Dubbo 中 AbstractProxyProtocol、HttpProtocol 等核心類,剖析了 Dubbo 中「HTTP 協議 + JSON-RPC」方案的落地實現。