您好,登錄后才能下訂單哦!
太陽紅彤彤,花兒五顏六色,各位讀者朋友好,又來到了分享 Dubbo 知識點的時候了。說到 Dubbo 框架支持的協議,你的第一反應是什么?大概會有 Dubbo 默認支持的 dubbo 協議,以及老生常談的由當當貢獻給 Dubbo 的 rest 協議,或者是今天的主角 http。截止到目前,Dubbo 最新版本演進到了 2.7.3,已經支持了:dubbo,hessain,http,injvm,jsonrpc,memcached,native-thrift,thrift,redis,rest,rmi,webservice,xml 等協議,有些協議的使用方式還沒有補全到官方文檔中。原來 Dubbo 支持這么多協議,是不是有點出乎你的意料呢?
這么多 RPC 協議,可能有人會產生如下的疑問:rest,jsonrpc,webservice 不都是依靠 http 通信嗎?為什么還單獨有一個 http 協議?先不急著回答這個問題,而是引出今天的話題,先來介紹下 Dubbo 框架中所謂的 http 協議。
<!-- more -->
在 Dubbo 使用 http 協議和其他協議基本一樣,只需要指定 protocol 即可。
<dubbo:protocol name="http" port="8080" server="jetty" />
server 屬性可選值:jetty,tomcat,servlet。
配置過后,當服務消費者向服務提供者發起調用,底層便會使用標準的 http 協議進行通信。可以直接在 https://github.com/apache/dubbo-samples 中找到官方示例,其中的子模塊:dubbo-samples-http 構建了一個 http 協議調用的例子。
為避免大家誤解,特在此聲明:本文中,所有的 http 協議特指的是 dubbo 中的 http 協議,并非那個大家耳熟能詳的通用的 http 協議。
從默認的 dubbo 協議改為 http 協議是非常簡單的一件事,上面便是使用者視角所看到的全部的內容了,接下來我們將會探討其底層實現原理。
翻看 Dubbo 的源碼,找到 HttpProtocol 的實現,你可能會吃驚,基本就依靠 HttpProtocol 一個類,就實現了 http 協議
要知道實現自定義的 dubbo 協議,有近 30 個類!http 協議實現的如此簡單,背后主要原因有兩點:
Spring 提供的 HttpInvoker 是何方神圣呢?的確是一個比較生僻的概念,但并不復雜,簡單來說,就是使用 Java 序列化將對象轉換成字節,通過 http 發送出去,在 server 端,Spring 能根據 url 映射,找到容器中對應的 Bean 反射調用的過程,沒見識過它也不要緊,可以通過下面的示例快速掌握這一概念。
本節內容可參見 Spring 文檔:https://docs.spring.io/spring/docs/4.3.24.RELEASE/spring-framework-reference/htmlsingle/#remoting-httpinvoker-server
下面的示例將會展示如何使用 Spring 原生的 HttpInvoker 實現遠程調用。
public class AccountServiceImpl implements AccountService {
@Override
public Account findById(int id) {
Account account = new Account(id, new Date().toString());
return account;
}
}
@Bean
AccountService accountService(){
return new AccountServiceImpl();
}
@Bean("/AccountService")
public HttpInvokerServiceExporter accountServiceExporter(AccountService accountService){
HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
exporter.setService(accountService);
exporter.setServiceInterface(AccountService.class);
return exporter;
}
暴露服務的代碼相當簡單,需要注意兩點:
org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
是 Spring 封裝的一個服務暴露器,它會以 serviceInterface 為公共接口,以 service 為實現類向外提供服務。http://localhost:8080/AccountService
@Configuration
public class HttpProxyConfig {
@Bean("accountServiceProxy")
public HttpInvokerProxyFactoryBean accountServiceProxy(){
HttpInvokerProxyFactoryBean accountService = new HttpInvokerProxyFactoryBean();
accountService.setServiceInterface(AccountService.class);
accountService.setServiceUrl("http://localhost:8080/AccountService");
return accountService;
}
}
@SpringBootApplication
public class HttpClientApp {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(HttpClientApp.class, args);
AccountService accountService = applicationContext.getBean(AccountService.class);
System.out.println(accountService.findById(10086));
}
}
消費者端引用服務同樣有兩個注意點:
org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean
是 Spring 封裝的一個服務引用器,serviceInterface 指定了生成代理的接口,serviceUrl 指定了服務所在的地址,與之前配置的服務暴露者的路徑需要對應。對于 Spring HttpInvoker 的底層實現,就沒必要深究了,但大家肯定還是會好奇一些細節:dubbo 中的 http 報文體是怎么組織的?如何序列化對象的?
我們使用 wireshark 可以抓取到客戶端發送的請求以及服務端響應的報文。
追蹤報文流,可以看到詳細的請求和響應內容
從 ContentType: application/x-java-serialized-object
和報文 Body 部分的 ASCII 碼可以看出,使用的是 Java Serialize 序列化。我們將 Body 部分導出成文件,使用 Java Serialize 反序列化響應,來驗證一下它的廬山真面目:
使用 Java Serialize 可以正常反序列化報文,得到結果是 Spring 內置的包裝類 RemoteInvocationResult,里面裝飾著實際的業務返回結果。
Dubbo 提供的眾多協議有各自適用的場景,例如
所有協議的具體使用場景和其特性,我可能會單獨寫文章來分析,而如今我們要思考的是 dubbo 提供 http 協議到底解決什么問題,什么場景下用戶會考慮使用 dubbo 的 http 協議。
我個人認為 dubbo 現如今的 http 協議比較雞肋,原生 http 通信的優勢在于其通用性,基本所有語言都有配套的 http 客戶端和服務端支持,但是 dubbo 的 http 協議卻使用了 application/x-java-serialized-object
的格式來做為默認的 payload,使得其喪失了跨語言的優勢。可能有讀者會反駁:HttpInvoker 支持配置序列化格式,不能這么草率的詬病它。但其實我們所關注的恰恰是默認實現,正如 dubbo:// 協議也可以配置 fastjson 作為序列化方案,但是我們同樣不認為 dubbo:// 協議是一個優秀的跨語言方案,理由是一樣的。當然,評價一個應用層協議是否是優秀的,是否適合做 mesh 等等,需要多種方向去分析,這些我不在本文去分析。
說到底,本文花了一定的篇幅向大家介紹了 dubbo 的 http 協議,到頭來卻是想告訴你:這是一個比較雞肋的協議,是不是有些失望呢?不要失望,dubbo 可能在 2.7.4 版本廢棄現有的 http 協議,轉而使用 jsonrpc 協議替代,其實也就是將 jsonrpc 協議換了個名字而已,而關于 jsonrpc 的細節,我將會在下一篇文章中介紹,屆時,我也會分析,為什么 jsonrpc 比現有的 http 協議更適合戴上 http 協議的帽子,至于現有的 http 協議,我更傾向于稱之為:spring-httpinvoker 協議。
總結,dubbo 現有 http 協議的意義是什么?如果你習慣于使用 Spring HttpInvoker,那或許現有的 http 協議還有一定的用處,但從 dubbo 交流群和 Spring 文檔介紹其所花費的篇幅來看,它還是非常小眾的。同時也可以讓我們更好地認識協議發展的歷史,知道一個協議為什么存在,為什么會被淘汰。
當然,我說了不算,最終還是要看 dubbo 社區的決策,如果你對這個遷移方案感興趣,想要參與討論,歡迎大家在 dubbo 社區的郵件列表中發表你的見解
Topic:[Proposal] replace the protocol="http" with protocol="jsonrpc"
歡迎關注我的微信公眾號:「Kirito的技術分享」,關于文章的任何疑問都會得到回復,帶來更多 Java 相關的技術分享。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。