Dubbo 2.7.x 作為 Apache 的孵化版本,除了代碼優化之外,還新增了許多重磅的新特性,本文將會介紹其中最典型的三個新特性: 一、非同步化改造 二、三大中心改造 三、服務治理增強 一、非同步支持優化 我們知道dubbo協議本身支持三種發送請求方式: 單向發送:執行方法不需要返回結果 同步發送 ...
Dubbo 2.7.x 作為 Apache 的孵化版本,除了代碼優化之外,還新增了許多重磅的新特性,本文將會介紹其中最典型的三個新特性:
- 一、非同步化改造
- 二、三大中心改造
- 三、服務治理增強
一、非同步支持優化
我們知道dubbo協議本身支持三種發送請求方式:
單向發送:執行方法不需要返回結果
同步發送:執行方法後,等待結果返回,否則一直阻塞.
非同步發送:也就是當我發送調用後,我不阻塞等待結果,直接返回,將返回的future保存到上下文,方便後期使用。在非同步發送中有兩種方式分別是
future:當請求有響應後,通過future.get()來獲得響應結果,但是future.get()會導致線程阻塞,future從RpcContext獲取。
callback:設置一個回調線程,當接收到響應時,自動執行,不會對當前線程造成阻塞,自定義ResponseFuture支持callback。
2.6.x版本的非同步方式提供了一些非同步能力,包括Consumer端非同步調用、參數回調、事件通知等。但當前的非同步方式存在以下問題:
Future獲取方式不夠直接,只能在RpcContext中進行獲取;
Future只支持阻塞式的get()介面獲取結果。
Future介面無法實現自動回調,而自定義ResponseFuture雖支持callback回調但支持的非同步場景有限,如不支持Future間的相互協調或組合等;
不支持Provider端非同步
那麼在2.7.x版本,由於JDK版本升級到了1.8,引入了JDK1.8 中的CompletableFuture介面,CompletableFuture支持 future 和 callback 兩種調用方式。關於CompletableFuture怎麼被運用到dubbo中我會在後續的文章介紹。引入該介面後,做了以下優化:
支持Provider端非同步
支持直接定義返回CompletableFuture的服務介面。通過這種類型的介面,我們可以更自然的實現Consumer、Provider端的非同步編程。
public interface AsyncService {
CompletableFuture<String> sayHello(String name);
}
如果你不想將介面的返回值定義為Future類型,或者存在定義好的同步類型介面,則可以額外定義一個非同步介面並提供Future類型的方法。
public interface GreetingsService {
String sayHi(String name);
}
@AsyncFor(GreetingsService.class)
public interface GrettingServiceAsync extends GreetingsService {
CompletableFuture<String> sayHiAsync(String name);
}
如果你的原始介面定義不是Future類型的返回值,Provider端非同步也提供了類似Servlet3.0里的Async Servlet的編程介面: RpcContext.startAsync()
public interface AsyncService {
String sayHello(String name);
}
public class AsyncServiceImpl implements AsyncService {
public String sayHello(String name) {
final AsyncContext asyncContext = RpcContext.startAsync();
new Thread(() -> {
asyncContext.write("Hello " + name + ", response from provider.");
}).start();
return null;
}
}
非同步過濾器鏈回調。
二、三大中心改造
三大中心指的:註冊中心,元數據中心,配置中心。
在 2.7 之前的版本,Dubbo 只配備了註冊中心,主流使用的註冊中心為 zookeeper。新增加了元數據中心和配置中心,自然是為瞭解決對應的痛點,下麵我們來詳細闡釋三大中心改造的原因。
元數據改造
元數據是什麼?元數據定義為描述數據的數據,在服務治理中,例如服務介面名,重試次數,版本號等等都可以理解為元數據。在 2.7 之前,元數據一股腦丟在了註冊中心之中,這造成了一系列的問題:
推送量大 -> 存儲數據量大 -> 網路傳輸量大 -> 延遲嚴重
生產者端註冊 30+ 參數,有接近一半是不需要作為註冊中心進行傳遞;消費者端註冊 25+ 參數,只有個別需要傳遞給註冊中心。有了以上的理論分析,Dubbo 2.7 進行了大刀闊斧的改動,只將真正屬於服務治理的數據發佈到註冊中心之中,大大降低了註冊中心的負荷。
同時,將全量的元數據發佈到另外的組件中:元數據中心。元數據中心目前支持 redis(推薦),zookeeper。這也為 Dubbo 2.7 全新的 Dubbo Admin 做了準備,關於新版的 Dubbo Admin,我將會後續準備一篇獨立的文章進行介紹。
示例:使用 zookeeper 作為元數據中心
<dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>
Dubbo 2.6 元數據
dubbo://30.5.120.185:20880/com.alibaba.dubbo.demo.DemoService?
anyhost=true&
application=demo-provider&
interface=com.alibaba.dubbo.demo.DemoService&
methods=sayHello&
bean.name=com.alibaba.dubbo.demo.DemoService&
dubbo=2.0.2&
executes=4500&
generic=false&
owner=kirito&
pid=84228&
retries=7&
side=provider&
timestamp=1552965771067
從本地的 zookeeper 中取出一條服務數據,通過解碼之後,可以看出,的確有很多參數是不必要。
Dubbo 2.7 元數據
在 2.7 中,如果不進行額外的配置,zookeeper 中的數據格式仍然會和 Dubbo 2.6 保持一致,這主要是為了保證相容性,讓 Dubbo 2.6 的客戶端可以調用 Dubbo 2.7 的服務端。如果整體遷移到 2.7,則可以為註冊中心開啟簡化配置的參數:
<dubbo:registry address=“zookeeper://127.0.0.1:2181” simplified="true"/>
Dubbo 將會只上傳那些必要的服務治理數據,一個簡化過後的數據如下所示:
dubbo://30.5.120.185:20880/org.apache.dubbo.demo.api.DemoService?
application=demo-provider&
dubbo=2.0.2&
release=2.7.0&
timestamp=1552975501873
元數據中心的數據可以被用於服務測試,服務 MOCK 等功能。目前註冊中心配置中 simplified 的預設值為 false,因為考慮到了遷移的相容問題,在後續迭代中,預設值將會改為 true。
配置中心支持
衡量配置中心的必要性往往從三個角度出發:
- 分散式配置統一管理
- 動態變更推送
- 安全性
Spring Cloud Config, Apollo, Nacos 等分散式配置中心組件都對上述功能有不同程度的支持。在 2.7 之前的版本中,在 zookeeper 中設置了部分節點:configurators,routers,用於管理部分配置和路由信息,它們可以理解為 Dubbo 配置中心的雛形。在 2.7 中,Dubbo 正式支持了配置中心,目前支持的幾種註冊中心 Zookeeper,Apollo,Nacos(2.7.1-release 支持)。
在 Dubbo 中,配置中心主要承擔了兩個作用
- 外部化配置。啟動配置的集中式存儲
- 服務治理。服務治理規則的存儲與通知
示例:使用 Zookeeper 作為配置中心
<dubbo:config-center address="zookeeper://127.0.0.1:2181"/>
引入配置中心後,需要註意配置項的覆蓋問題。
三、服務治理增強
如果我們把 Dubbo 當做一個服務治理框架,而不僅僅是一個 RPC 框架。在 2.7 中,Dubbo 對其服務治理能力進行了增強,增加了標簽路由的能力,並抽象出了應用路由和服務路由的概念。在最後一個特性介紹中,著重對標簽路由 TagRouter 進行探討。
在服務治理中,路由層和負載均衡層的對比。區別 1,Router:m 選 n,LoadBalance:n 選 1;區別 2,路由往往是疊加使用的,負載均衡只能配置一種。
在很長的一段時間內,Dubbo 社區經常有人提的一個問題是:Dubbo 如何實現流量隔離和灰度發佈,直到 2.7 提供了標簽路由,用戶可以使用這個功能,來實現上述的需求。
標簽路由提供了這樣一個能力,當調用鏈路為 A -> B -> C -> D 時,用戶給請求打標,最典型的打標方式可以藉助 attachment(他可以在分散式調用中傳遞下去),調用會優先請求那些匹配的服務端,如 A -> B,C -> D,由於集群中未部署 C 節點,則會降級到普通節點。
打標方式會收到集成系統差異的影響,從而導致很大的差異,所以 Dubbo 只提供了 RpcContext.getContext().setAttachment() 這樣的基礎介面,用戶可以使用 SPI 擴展,或者 server filter 的擴展,對測試流量進行打標,引導進入隔離環境/灰度環境。新版的 Dubbo Admin 提供了標簽路由的配置項,Dubbo 用戶可以在自己系統的基礎上對標簽路由進行二次擴展,或者借鑒標簽路由的設計,實現自己系統的流量隔離,灰度發佈。