Dubbo的使用背景
隨著公司的業(yè)務(wù)復雜度日漸上漲腥刹,技術(shù)架構(gòu)的發(fā)展從單體到分布式脑溢,是一種順勢而為的架構(gòu)演進喻奥,也是一種被逼無奈的技術(shù)變革席纽。和傳統(tǒng)的單體架構(gòu)相比,分布式多了一個遠程服務(wù)之間的通信撞蚕,不管是soa還是微服務(wù)润梯,他們本質(zhì)上都是對于業(yè)務(wù)服務(wù)的提煉和復用。遠程服務(wù)之間的調(diào)用是實現(xiàn)分布式的關(guān)鍵因素甥厦。而在遠程通信這個領(lǐng)域纺铭,其實有很多的技術(shù),比如 Java 的 RMI刀疙、WebService舶赔、 Hessian、Dubbo谦秧、Thrift 等RPC框架竟纳,現(xiàn)在接觸得比較多的應(yīng)該就是RPC框架 Dubbo 以及應(yīng)用協(xié)議Http。其實每一個技術(shù)都是在某一個階段產(chǎn)生它的價值疚鲤,隨著架構(gòu)的變化以及需求的變化锥累,技術(shù)的解決方案也在變。
RPC
RPC集歇,全稱為Remote Procedure Call桶略,即遠程過程調(diào)用,是一種計算機通信協(xié)議诲宇。
比如現(xiàn)在有兩臺機器:A機器和B機器际歼,并且分別部署了應(yīng)用A和應(yīng)用B。假設(shè)此時位于A機器上的A應(yīng)用想要調(diào)用位于B機器上的B應(yīng)用提供的函數(shù)或是方法姑蓝,由于A應(yīng)用和B應(yīng)用不在一個內(nèi)存空間里面鹅心,所以不能直接調(diào)用,此時就需要通過網(wǎng)絡(luò)來表達調(diào)用的方式和傳輸調(diào)用的數(shù)據(jù)它掂。也即所謂的遠程調(diào)用巴帮。
如何實現(xiàn)一個RCP框架呢
主要有以下幾個步驟:
1、建立通信
首先要解決通訊的問題:即A機器想要調(diào)用B機器虐秋,首先得建立起通信連接榕茧。主要是通過在客戶端和服務(wù)器之間建立網(wǎng)絡(luò)連接,遠程過程調(diào)用的所有相關(guān)的數(shù)據(jù)都在這個連接里面進行傳輸交換客给。這就需要考慮到底層網(wǎng)絡(luò)通信協(xié)議的處理用押。
2、服務(wù)尋址
解決尋址的問題:即A機器上的應(yīng)用A要調(diào)用B機器上的應(yīng)用B靶剑,那么此時對于A來說如何告知底層的RPC框架所要調(diào)用的服務(wù)具體在哪里呢蜻拨?
通常情況下我們需要提供B機器(主機名或IP地址)以及特定的端口池充,然后指定調(diào)用的方法或者函數(shù)的名稱以及入?yún)⒊鰠⒌刃畔ⅲ@樣才能完成服務(wù)的一個調(diào)用缎讼。
3收夸、網(wǎng)絡(luò)傳輸
3.1、序列化
當A機器上的應(yīng)用發(fā)起一個RPC調(diào)用時血崭,調(diào)用方法和其入?yún)⒌刃畔⑿枰ㄟ^底層的網(wǎng)絡(luò)協(xié)議如TCP傳輸?shù)紹機器卧惜,由于網(wǎng)絡(luò)協(xié)議是基于二進制的,所有我們傳輸?shù)膮?shù)數(shù)據(jù)都需要先進行序列化(Serialize)或者編組(marshal)成二進制的形式才能在網(wǎng)絡(luò)中進行傳輸夹纫。然后通過尋址操作和網(wǎng)絡(luò)傳輸將序列化或者編組之后的二進制數(shù)據(jù)發(fā)送給B機器咽瓷。
3.2、反序列化
當B機器接收到A機器的應(yīng)用發(fā)來的請求之后舰讹,又需要對接收到的參數(shù)等信息進行反序列化操作(序列化的逆操作)茅姜,即將二進制信息恢復為內(nèi)存中的表達方式,然后再找到對應(yīng)的方法(尋址的一部分)進行本地調(diào)用(一般是通過生成代理Proxy去調(diào)用, 通常會有JDK動態(tài)代理月匣、CGLIB動態(tài)代理钻洒、Javassist生成字節(jié)碼技術(shù)等),之后得到調(diào)用的返回值锄开。
4航唆、服務(wù)調(diào)用
B機器進行本地調(diào)用(通過代理Proxy)之后得到了返回值,此時還需要再把返回值發(fā)送回A機器院刁,同樣也需要經(jīng)過序列化操作,然后再經(jīng)過網(wǎng)絡(luò)傳輸將二進制數(shù)據(jù)發(fā)送回A機器粪狼,而當A機器接收到這些返回值之后退腥,則再次進行反序列化操作,恢復為內(nèi)存中的表達方式再榄,最后再交給A機器上的應(yīng)用進行相關(guān)處理(一般是業(yè)務(wù)邏輯處理操作)狡刘。
通常,經(jīng)過以上四個步驟之后困鸥,一次完整的RPC調(diào)用算是完成了嗅蔬,另外可能因為各種原因需要有容錯策略。
這些工作本身應(yīng)該是通用的疾就,應(yīng)該是一個中間件服務(wù)澜术。為整個公司提供遠程通信的服務(wù)。而不應(yīng)該由業(yè)務(wù)開發(fā)人員來自己去實現(xiàn)猬腰,所以才有了這樣的RPC框架鸟废,使得我們調(diào)用遠程方法時就像調(diào)用本地方法那么簡單,不需要關(guān)心底層的通信邏輯。
到此為止姑荷,只是實現(xiàn)了遠程通信的功能盒延,但是當企業(yè)開始大規(guī)模的服務(wù)化以后缩擂,遠程通信帶來的弊端就越來越明顯了。比如:
- 服務(wù)鏈路變長了添寺,如何實現(xiàn)對服務(wù)鏈路的跟蹤和監(jiān)控胯盯。
- 服務(wù)的大規(guī)模集群使得服務(wù)之間需要依賴第三方注冊中心來解決服務(wù)的
發(fā)現(xiàn)和服務(wù)的感知問題。 - 服務(wù)通信之間的異常计露,需要有一種保護機制防止一個節(jié)點故障引發(fā)大規(guī)模
的系統(tǒng)故障博脑,所以要有容錯機制。 - 服務(wù)大規(guī)模集群會是的客戶端需要引入負載均衡機制實現(xiàn)請求分發(fā)薄坏。
Dubbo 的基本使用
創(chuàng)建三個項目
dubbo-consumer :使用服務(wù)
dubbo-client : 提供接口
dubbo-server:提供服務(wù)
添加jar包依賴
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
dubbo-client
打包提交至maven本地倉庫中趋厉,為dubbo-consumer 和 dubbo-server提供接口。
定義接口
public interface UserService {
/**
*
*/
String login(String username, String password);
}
dubbo-server
實現(xiàn)dubbo-client中定義的接口胶坠,提供服務(wù)君账。
public class LoginServiceImpl implements LoginService{
@Override
public String login(String username, String password) {
if(username.equals("admin")&&password.equals("admin")){
return "SUCCESS";
}
return "FAILED";
創(chuàng)建配置文件發(fā)布服務(wù),具體的配置參數(shù)可在dubbo文檔查看
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 提供方應(yīng)用信息,用于計算依賴關(guān)系 -->
<dubbo:application name="dubbo-server" />
<!--配置注冊中心,如果是 zookeeper 集群沈善,則配置的方式是
address=”zookeeper ://ip:host?backup=ip:host,ip:host” -->
<dubbo:registry check="${check-rpc:false}" address="zookeeper://192.168.13.102:2181" protocol="zookeeper" id="zkreg" >
<dubbo:parameter key="qos.enable" value="false"/>
</dubbo:registry>
<!-- 用 dubbo 協(xié)議在 7895 端口暴露服務(wù) -->
<dubbo:protocol name="dubbo" port="7895" />
<!---->
<dubbo:consumer check="${check-rpc:false}" timeout="2000" registry="zkreg" group="test" retries="0"/>
<!-- 和本地bean一樣實現(xiàn)服務(wù) -->
<bean id="LoginService" class="com.learn.practice.LoginServiceImpl" />
<dubbo:service interface="com.learn.practice.LoginService" ref="loginService" registry="zkreg"
timeout="5000" group="test" version="1.0.0"/>
</beans>
dubbo-consumer
@Service
public class ApiUserService{
//調(diào)用dubbo服務(wù)提供的loginService
@Resource
private LoginService loginService;
public void login(){
loginService.login();
}
}
調(diào)用方的配置中心
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="dubbo-consumer" />
<dubbo:registry check="${check-rpc:false}" address="zookeeper://192.168.13.102:2181" protocol="zookeeper" id="zkreg" >
<dubbo:parameter key="qos.enable" value="false"/>
</dubbo:registry>
<dubbo:protocol name="dubbo" port="9006" />
<dubbo:reference interface="com.learn.practice.LoginService" check="false" id="userService" timeout="20000" registry="zkreg" group="test" version="1.0.0" />
</beans>
以上是dubbo 的基礎(chǔ)使用過程乡数,但是dubbo不僅僅是一個RPC框架。
Dubbo的多協(xié)議支持
dubbo 對于 RPC 通信協(xié)議的支持闻牡,不僅僅是原生的 dubbo 協(xié)議净赴,它還支持著 rmi暖释、hessian酌心、http、webservice东且、thrift割以、rest金度。
有了多協(xié)議的支持,使得其他 rpc 框架的應(yīng)用程序可以快速的切入到 dubbo 生態(tài)中严沥。 同時猜极,對于多協(xié)議的支持,使得不同應(yīng)用場景的服務(wù)消玄,可以選擇合適的協(xié)議來發(fā)布服務(wù)跟伏,并不一定要使用 dubbo 提供的長連接方式。