一文詳解Dubbo中的http協(xié)議-創(chuàng)新互聯(lián)

太陽紅彤彤,花兒五顏六色,各位讀者朋友好,又來到了分享 Dubbo 知識點的時候了。說到 Dubbo 框架支持的協(xié)議,你的第一反應(yīng)是什么?大概會有 Dubbo 默認(rèn)支持的 dubbo 協(xié)議,以及老生常談的由當(dāng)當(dāng)貢獻(xiàn)給 Dubbo 的 rest 協(xié)議,或者是今天的主角 http。截止到目前,Dubbo 最新版本演進(jìn)到了 2.7.3,已經(jīng)支持了:dubbo,hessain,http,injvm,jsonrpc,memcached,native-thrift,thrift,redis,rest,rmi,webservice,xml 等協(xié)議,有些協(xié)議的使用方式還沒有補全到官方文檔中。原來 Dubbo 支持這么多協(xié)議,是不是有點出乎你的意料呢?

創(chuàng)新互聯(lián)建站自2013年創(chuàng)立以來,先為昌邑等服務(wù)建站,昌邑等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為昌邑企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。

這么多 RPC 協(xié)議,可能有人會產(chǎn)生如下的疑問:rest,jsonrpc,webservice 不都是依靠 http 通信嗎?為什么還單獨有一個 http 協(xié)議?先不急著回答這個問題,而是引出今天的話題,先來介紹下 Dubbo 框架中所謂的 http 協(xié)議。

<!-- more -->

Dubbo 中的 http 協(xié)議

在 Dubbo 使用 http 協(xié)議和其他協(xié)議基本一樣,只需要指定 protocol 即可。

<dubbo:protocol name="http" port="8080" server="jetty" />

server 屬性可選值:jetty,tomcat,servlet。

配置過后,當(dāng)服務(wù)消費者向服務(wù)提供者發(fā)起調(diào)用,底層便會使用標(biāo)準(zhǔn)的 http 協(xié)議進(jìn)行通信??梢灾苯釉?https://github.com/apache/dubbo-samples 中找到官方示例,其中的子模塊:dubbo-samples-http 構(gòu)建了一個 http 協(xié)議調(diào)用的例子。

為避免大家誤解,特在此聲明:本文中,所有的 http 協(xié)議特指的是 dubbo 中的 http 協(xié)議,并非那個大家耳熟能詳?shù)耐ㄓ玫?http 協(xié)議。

http 協(xié)議的底層原理

從默認(rèn)的 dubbo 協(xié)議改為 http 協(xié)議是非常簡單的一件事,上面便是使用者視角所看到的全部的內(nèi)容了,接下來我們將會探討其底層實現(xiàn)原理。

翻看 Dubbo 的源碼,找到 HttpProtocol 的實現(xiàn),你可能會吃驚,基本就依靠 HttpProtocol 一個類,就實現(xiàn)了 http 協(xié)議

一文詳解 Dubbo 中的 http 協(xié)議

要知道實現(xiàn)自定義的 dubbo 協(xié)議,有近 30 個類!http 協(xié)議實現(xiàn)的如此簡單,背后主要原因有兩點:

  1. remoting 層使用 http 通信,不需要自定義編解碼
  2. 借助了 Spring 提供的 HttpInvoker 封裝了 refer 和 exporter 的邏輯

Spring 提供的 HttpInvoker 是何方神圣呢?的確是一個比較生僻的概念,但并不復(fù)雜,簡單來說,就是使用 Java 序列化將對象轉(zhuǎn)換成字節(jié),通過 http 發(fā)送出去,在 server 端,Spring 能根據(jù) url 映射,找到容器中對應(yīng)的 Bean 反射調(diào)用的過程,沒見識過它也不要緊,可以通過下面的示例快速掌握這一概念。

Spring HttpInvoker

本節(jié)內(nèi)容可參見 Spring 文檔:https://docs.spring.io/spring/docs/4.3.24.RELEASE/spring-framework-reference/htmlsingle/#remoting-httpinvoker-server

下面的示例將會展示如何使用 Spring 原生的 HttpInvoker 實現(xiàn)遠(yuǎn)程調(diào)用。

創(chuàng)建服務(wù)提供者

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;
}

暴露服務(wù)的代碼相當(dāng)簡單,需要注意兩點:

  1. org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter是 Spring 封裝的一個服務(wù)暴露器,它會以 serviceInterface 為公共接口,以 service 為實現(xiàn)類向外提供服務(wù)。
  2. @Bean("/AccountService") 不僅僅指定了 IOC 容器中 bean 的名字,還充當(dāng)了路徑映射的功能,如果本地服務(wù)器暴露在 8080 端口,則示例服務(wù)的訪問路徑為http://localhost:8080/AccountService

創(chuàng)建服務(wù)消費者

@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));
    }
}

消費者端引用服務(wù)同樣有兩個注意點:

  1. org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean是 Spring 封裝的一個服務(wù)引用器,serviceInterface 指定了生成代理的接口,serviceUrl 指定了服務(wù)所在的地址,與之前配置的服務(wù)暴露者的路徑需要對應(yīng)。
  2. HttpInvokerProxyFactoryBean 注冊到容器之中時,會同時生成一個 AccountService 接口的代理類,由 Spring 封裝遠(yuǎn)程調(diào)用的邏輯。

調(diào)用細(xì)節(jié)分析

對于 Spring HttpInvoker 的底層實現(xiàn),就沒必要深究了,但大家肯定還是會好奇一些細(xì)節(jié):dubbo 中的 http 報文體是怎么組織的?如何序列化對象的?

我們使用 wireshark 可以抓取到客戶端發(fā)送的請求以及服務(wù)端響應(yīng)的報文。

一文詳解 Dubbo 中的 http 協(xié)議

追蹤報文流,可以看到詳細(xì)的請求和響應(yīng)內(nèi)容

一文詳解 Dubbo 中的 http 協(xié)議

ContentType: application/x-java-serialized-object和報文 Body 部分的 ASCII 碼可以看出,使用的是 Java Serialize 序列化。我們將 Body 部分導(dǎo)出成文件,使用 Java Serialize 反序列化響應(yīng),來驗證一下它的廬山真面目:

一文詳解 Dubbo 中的 http 協(xié)議

使用 Java Serialize 可以正常反序列化報文,得到結(jié)果是 Spring 內(nèi)置的包裝類 RemoteInvocationResult,里面裝飾著實際的業(yè)務(wù)返回結(jié)果。

http 協(xié)議的意義

Dubbo 提供的眾多協(xié)議有各自適用的場景,例如

  • dubbo://,dubbo 協(xié)議是默認(rèn)的協(xié)議,自定義二進(jìn)制協(xié)議;單個長連接節(jié)省資源;基于 tcp,架構(gòu)于 netty 之上,性能還算可以;協(xié)議設(shè)計上沒有足夠的前瞻性,不適合做 service-mesh 談不上多么優(yōu)雅,但是好歹風(fēng)風(fēng)雨雨用了這么多年,周邊也有不少配套組件例如 dubbo2.js, dubbo-go, dubbo-cpp,一定程度解決了多語言的問題。
  • webservice://,hession://,thrift:// 等協(xié)議,基本是為了適配已有協(xié)議的服務(wù)端/客戶端,借助于 dubbo 框架的 api,可以使用其功能特性,意義不是特別大。
  • redis://,memcached:// 等協(xié)議,并非是暴露給用戶配置的協(xié)議,一般是 dubbo 自用,在注冊中心模塊中會使用到相應(yīng)的擴(kuò)展

所有協(xié)議的具體使用場景和其特性,我可能會單獨寫文章來分析,而如今我們要思考的是 dubbo 提供 http 協(xié)議到底解決什么問題,什么場景下用戶會考慮使用 dubbo 的 http 協(xié)議。

我個人認(rèn)為 dubbo 現(xiàn)如今的 http 協(xié)議比較雞肋,原生 http 通信的優(yōu)勢在于其通用性,基本所有語言都有配套的 http 客戶端和服務(wù)端支持,但是 dubbo 的 http 協(xié)議卻使用了application/x-java-serialized-object的格式來做為默認(rèn)的 payload,使得其喪失了跨語言的優(yōu)勢。可能有讀者會反駁:HttpInvoker 支持配置序列化格式,不能這么草率的詬病它。但其實我們所關(guān)注的恰恰是默認(rèn)實現(xiàn),正如 dubbo:// 協(xié)議也可以配置 fastjson 作為序列化方案,但是我們同樣不認(rèn)為 dubbo:// 協(xié)議是一個優(yōu)秀的跨語言方案,理由是一樣的。當(dāng)然,評價一個應(yīng)用層協(xié)議是否是優(yōu)秀的,是否適合做 mesh 等等,需要多種方向去分析,這些我不在本文去分析。

說到底,本文花了一定的篇幅向大家介紹了 dubbo 的 http 協(xié)議,到頭來卻是想告訴你:這是一個比較雞肋的協(xié)議,是不是有些失望呢?不要失望,dubbo 可能在 2.7.4 版本廢棄現(xiàn)有的 http 協(xié)議,轉(zhuǎn)而使用 jsonrpc 協(xié)議替代,其實也就是將 jsonrpc 協(xié)議換了個名字而已,而關(guān)于 jsonrpc 的細(xì)節(jié),我將會在下一篇文章中介紹,屆時,我也會分析,為什么 jsonrpc 比現(xiàn)有的 http 協(xié)議更適合戴上 http 協(xié)議的帽子,至于現(xiàn)有的 http 協(xié)議,我更傾向于稱之為:spring-httpinvoker 協(xié)議。

總結(jié),dubbo 現(xiàn)有 http 協(xié)議的意義是什么?如果你習(xí)慣于使用 Spring HttpInvoker,那或許現(xiàn)有的 http 協(xié)議還有一定的用處,但從 dubbo 交流群和 Spring 文檔介紹其所花費的篇幅來看,它還是非常小眾的。同時也可以讓我們更好地認(rèn)識協(xié)議發(fā)展的歷史,知道一個協(xié)議為什么存在,為什么會被淘汰。

當(dāng)然,我說了不算,最終還是要看 dubbo 社區(qū)的決策,如果你對這個遷移方案感興趣,想要參與討論,歡迎大家在 dubbo 社區(qū)的郵件列表中發(fā)表你的見解

Topic:[Proposal] replace the protocol="http" with protocol="jsonrpc"

歡迎關(guān)注我的微信公眾號:「Kirito的技術(shù)分享」,關(guān)于文章的任何疑問都會得到回復(fù),帶來更多 Java 相關(guān)的技術(shù)分享。

創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動態(tài)BGP最優(yōu)骨干路由自動選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨有T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動現(xiàn)已開啟,新人活動云服務(wù)器買多久送多久。

網(wǎng)站欄目:一文詳解Dubbo中的http協(xié)議-創(chuàng)新互聯(lián)
標(biāo)題路徑:http://bm7419.com/article28/cdghjp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站建設(shè)、品牌網(wǎng)站制作、微信小程序網(wǎng)站排名、靜態(tài)網(wǎng)站商城網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站托管運營