一、多版本的支持
如何發布服務,需要將需要暴露的服務接口發布出去供客戶端調用,需要在java同級目錄新建一個resources目錄,然后將resoureces目錄標記成Test Resoureces Root,然后在esources目錄下新建MATE-INF.spring目錄,在該目錄下添加配置文件dubbo-server.xml文件
dubbo的服務端配置文件如下
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!--提供方信息,用於計算依賴關系--> <dubbo:application name="dubbo-server" owner="mic"/> <!--注冊中心 暴露服務地址--> <dubbo:registry address="zookeeper://192.168.126.129:2181"/> <!--用dubbo協議在20880 端口暴露服務--> <dubbo:protocol port="20880" name="dubbo"/> <!--聲明需要暴露的服務接口,指定協議為dubbo,設置版本號1.1.1--> <dubbo:service interface="com.gupaoedu.dubbo.IGpHello" ref="gpHelloService" protocol="dubbo" version="1.1.1"/> <!--聲明需要暴露的服務接口,指定協議為dubbo,設置版本號1.1.2--> <dubbo:service interface="com.gupaoedu.dubbo.IDemoService" ref="demoService" protocol="dubbo" version="1.1.2"/> <!--和本地服務一樣實現服務--> <bean id="gpHelloService" class="com.gupaoedu.dubbo.GpHelloImpl"/> <bean id="demoService" class="com.gupaoedu.dubbo.DemoService"/> </beans>
服務端的接口以及實現類
public interface IGpHello {String sayHello(String msg);}
public interface IDemoService {String protocolDemo(String msg);}
public class GpHelloImpl implements IGpHello{ @Override public String sayHello(String msg) {return "Hello:"+msg;} }
public class GpHelloImpl2 implements IGpHello{ @Override public String sayHello(String msg) {return "Hello,i'm server 2:"+msg;} }
編寫Main方法,用spring容器來啟動服務
public class Main { public static void main(String[] args) throws IOException { //默認情況下會使用spring容器來啟動服務 com.alibaba.dubbo.container.Main.main(new String[]{"spring","log4j"});} }
服務啟動過程會進行服務注冊,啟動監聽端口,啟動服務之后
在客戶端通過遠程調用訪問服務端發布的服務,相應的客戶端配置文件 dubbo-client.xml 如下
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!--提供方信息--> <dubbo:application name="dubbo-client" owner="mic"/> <!--注冊中心--> <dubbo:registry id="zokeeper" address="zookeeper://192.168.126.129:2181?register=false" file="d:/dubbo-server"/> <!--聲明需要暴露的服務接口,指定版本號--> <dubbo:reference id="gpHelloService" interface="com.gupaoedu.dubbo.IGpHello" registry="zookeeper" version="1.1.1"/> </beans>
其實我們可以在zookeeper的客戶端可以發現,事實上已經發布了服務方已經發布了兩個不同版本的服務,具體如下
1 dubbo%3A%2F%2F192.168.126.1%3A20880%2Fcom.gupaoedu.dubbo.IDemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-server%26dubbo%3D2.5.3%26interface%3Dcom.gupaoedu.dubbo.IDemoService%26methods%3DprotocolDemo%26owner%3Dmic%26pid%3D22548%26revision%3D1.1.2%26side%3Dprovider%26timestamp%3D1530450331827%26version%3D1.1.2 2 dubbo%3A%2F%2F192.168.126.1%3A20880%2Fcom.gupaoedu.dubbo.IGpHello%3Fanyhost%3Dtrue%26application%3Ddubbo-server%26dubbo%3D2.5.3%26interface%3Dcom.gupaoedu.dubbo.IGpHello%26methods%3DsayHello%26owner%3Dmic%26pid%3D22548%26revision%3D1.1.1%26side%3Dprovider%26timestamp%3D1530450325703%26version%3D1.1.1
我們知道,這兩個版本正是我們在服務發布方設置的不同版本號,同樣的,在消費端,我們可以通過設置指定的版本號獲取相應的版本服務,消費的代碼如下
public class App{ public static void main( String[] args ) throws IOException, InterruptedException { ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("dubbo-client.xml"); context.start(); IGpHello demoService = (IGpHello)context.getBean("gpHelloService");//獲取遠程服務代理 String hello = demoService.sayHello("world");//執行遠程調用方法 System.out.println(hello);//顯示調用結果 } }
在控制台,我們可以看到同樣的服務發布地址url。
二、主機綁定
什么叫主機綁定呢?主機綁定的流程是什么?
在發布一個dubbo服務的時候,會生成一個dubbo://ip:port的協議地址。這就是主機綁定過程,那么這個IP是如何生成的呢?大家可以通過ServiceConfig.java 中的 doExportUrlForlProtocol 方法中找到如下代碼塊。大致的邏輯是,首先從配置文件中獲取,如果失敗再嘗試從本地網卡中獲取host,如果這個也失敗,會繼續執行,直到找到合適的IP地址。
String name = protocolConfig.getName(); if (name == null || name.length() == 0) { name = "dubbo"; } String host = protocolConfig.getHost();//從配置文件中獲取host if (provider != null && (host == null || host.length() == 0)) { host = provider.getHost(); } boolean anyhost = false; if (NetUtils.isInvalidLocalHost(host)) { anyhost = true; try { host = InetAddress.getLocalHost().getHostAddress();//獲取本機的host地址 } catch (UnknownHostException e) { logger.warn(e.getMessage(), e); } if (NetUtils.isInvalidLocalHost(host)) { if (registryURLs != null && registryURLs.size() > 0) {//如果還是沒有獲取到host地址 for (URL registryURL : registryURLs) { try { Socket socket = new Socket(); try { SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort()); socket.connect(addr, 1000); host = socket.getLocalAddress().getHostAddress();//3、 break; } finally { try { socket.close(); } catch (Throwable e) {} } } catch (Exception e) { logger.warn(e.getMessage(), e); } } } if (NetUtils.isInvalidLocalHost(host)) {//4、 host = NetUtils.getLocalHost(); } } }
三、集群容錯
在分析dubbo的集群容錯前,先了解什么是容錯機制?容錯機制指的是某種系統控制在一 定范圍內的一種允許或包容犯錯情況的發生,舉個簡單例 子,我們在電腦上運行一個程序,有時候會出現無響應的情況,然后系統會彈出一個提示框讓我們選擇,是立即結 束還是繼續等待,然后根據我們的選擇執行對應的操作, 這就是“容錯”。
在分布式架構下,網絡、硬件、應用都可能發生故障,由 於各個服務之間可能存在依賴關系,如果一條鏈路中的其 中一個節點出現故障,將會導致雪崩效應。為了減少某一 個節點故障的影響范圍,所以我們才需要去構建容錯服務(這里要說明一點,容錯機制僅僅是處理節點故障的一種機制), 來優雅的處理這種中斷的響應結果 。
Dubbo提供了6種容錯機制,分別如下
1. failsafe 失敗安全,可以認為是把錯誤吞掉(記錄日 志)
2. failover(默認) 重試其他服務器; retries(2) ,缺省的重試次數,不包含第一次
3. failfast 快速失敗, 失敗以后立馬報錯
4. failback 失敗后自動恢復。
5. forking forks. 設置並行數
6. broadcast 廣播,任意一台報錯,則執行的方法報錯
配置方式如下,通過cluster方式,配置指定的容錯方案
<!--聲明需要暴露的服務接口,指定版本號--> <dubbo:reference id="gpHelloService" interface="com.gupaoedu.dubbo.IGpHello" registry="zookeeper" version="1.1.1" cluster="failover"/>
配置的優先級別
客戶端會優於服務端,這里還可以細化,可以細化到方法級別
1. 方法級優先,接口級次之,全局配置再次之。
2. 如果級別一樣,則消費方優先,提供方次之
其中,服務提供方配置,通過URL經由注冊中心傳遞給消 費方
什么應該配置在客戶端,什么應該配置在服務端,retires、loadBlance、cluster(客戶端)、timeout(服務端)
以 timeout 為例,建議由服務提供方設置超時,因為一個方法需要執行多長 時間,服務提供方更清楚,如果一個消費方同時引用多個 服務,就不需要關心每個服務的超時設置。
四、服務降級
降級的目的是為了保證核心服務可用。
降級可以有幾個層面的分類: 自動降級和人工降級; 按 照功能可以分為:讀服務降級和寫服務降級;
1. 對一些非核心服務進行人工降級,在大促之前通過降級 開關關閉哪些推薦內容、評價等對主流程沒有影響的功 能
2. 故障降級,比如調用的遠程服務掛了,網絡故障、或者 RPC服務返回異常。 那么可以直接降級,降級的方案比 如設置默認值、采用兜底數據(系統推薦的行為廣告掛 了,可以提前准備靜態頁面做返回)等等
3. 限流降級,在秒殺這種流量比較集中並且流量特別大的 情況下,因為突發訪問量特別大可能會導致系統支撐不 了。這個時候可以采用限流來限制訪問量。當達到閥值 時,后續的請求被降級,比如進入排隊頁面,比如跳轉 到錯誤頁(活動太火爆,稍后重試等)
dubbo的降級方式: Mock 機制
實現步驟
1. 在client端創建一個TestMock類,實現對應IGpHello 的接口(需要對哪個接口進行 mock,就實現哪個), 名稱必須以Mock結尾
2. 在client端的xml配置文件中,添加如下配置,增加一 個mock屬性指向創建的TestMock
3. 模擬錯誤(設置 timeout),模擬超時異常,運行測試代碼即可訪問到TestMock這個類。當服務端故障解除 以后,調用過程將恢復正常
服務端的類及實現類,以及配置文件,參考第一點的類及實現類以及配置文件,然后再客戶端的配置文件的加上mock,具體見如下截圖。
<!--聲明需要暴露的服務接口,指定版本號--> <dubbo:reference id="gpHelloService" interface="com.gupaoedu.dubbo.IGpHello" registry="zookeeper" version="1.1.1" timeout="1" cluster="failover" mock="com.gupaoedu.dubbo.TestMock"/>
根據Mock機制實現步驟
首先我們再客戶端添加一個TestMock類,實現IGpHello接口,配置文件我已經添加好了,可以看到我這里的配置相對第一點客戶端的配置添加了幾個參數 timeout、failover、mock。這里主要是看mock和timeout配置,因為我們是要去驗證mock機制,而failover是集群容錯的配置,之所以我這里沒有將這個參數去掉,是因為確定這里他們不會有配置沖突,配置好之后,先發布服務端的服務,然后運氣客戶端的App.java.在控制台我們可以看到打印結果,系統繁忙,這個結果說明,我們設置1秒超時,客戶端遠程調用服務超時,報錯了,然后就走到了Mock里。
然后我們再驗證,將超時間加大,設置為100,再運行,此時就不會報錯,就會正常輸出Hello world,輸出結果如下
<!--聲明需要暴露的服務接口,指定版本號--> <dubbo:reference id="gpHelloService" interface="com.gupaoedu.dubbo.IGpHello" registry="zookeeper" version="1.1.1" timeout="100" cluster="failover" mock="com.gupaoedu.dubbo.TestMock"/>
五、總結
好了,總結一下,整理了兩個晚上,沒有整理出我預想的效果,這篇文章主要是以一種開發文檔的形式簡單介紹了dubbo的多版本機制實現、主機綁定、集群容錯處理機制、以及服務降級處理。后續會介紹dubbo的SPI機制,他是dubbo一種核心機制,我也是剛學習dubbo源碼不久,到時候有問題忘大家指正。該篇文章也存在許多不足的地方,比如結構可能不是非常清晰,描述也可能不是非常通俗易懂,后續我會努力矯正,今天就到這了,太困了現在,不寫了睡覺。
歡迎掃碼關注我的微信公眾號,我會不定期的更新一些個人技術文章