(给ImportNew加星标,提高Java技能)

转自:Kirito的技术分享,作者:kiritomoe

太阳红彤彤,花儿五颜六色,各位读者朋友好,又来到了分享 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 协议。

Dubbo 中的 http 协议

在 Dubbo 使用 http 协议和其他协议基本一样,只需要指定 protocol 即可。

"http" port="8080" server="jetty" />

server 属性可选值:jetty,tomcat,servlet。

配置过后,当服务消费者向服务提供者发起调用,底层便会使用标准的 http 协议进行通信。可以直接在 https://github.com/apache/dubbo-samples 中找到官方示例,其中的子模块:dubbo-samples-http 构建了一个 http 协议调用的例子。

为避免大家误解,特在此声明:本文中,所有的 http 协议特指的是 dubbo 中的 http 协议,并非那个大家耳熟能详的通用的 http 协议。

http 协议的底层原理

从默认的 dubbo 协议改为 http 协议是非常简单的一件事,上面便是使用者视角所看到的全部的内容了,接下来我们将会探讨其底层实现原理。

翻看 Dubbo 的源码,找到 HttpProtocol 的实现,你可能会吃惊,基本就依靠 HttpProtocol 一个类,就实现了 http 协议

要知道实现自定义的 dubbo 协议,有近 30 个类!http 协议实现的如此简单,背后主要原因有两点:

  1. remoting 层使用 http 通信,不需要自定义编解码

  2. 借助了 Spring 提供的 HttpInvoker 封装了 refer 和 exporter 的逻辑

Spring 提供的 HttpInvoker 是何方神圣呢?的确是一个比较生僻的概念,但并不复杂,简单来说,就是使用 Java 序列化将对象转换成字节,通过 http 发送出去,在 server 端,Spring 能根据 Url 映射,找到容器中对应的 Bean 反射调用的过程,没见识过它也不要紧,可以通过下面的示例快速掌握这一概念。

Spring HttpInvoker

本节内容可参见 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 {    @Overridepublic Account findById(int id) {        Account account = new Account(id, new Date().toString());return account;    }}

@BeanAccountService accountService(){return new AccountServiceImpl();}

@Bean("/AccountService")public HttpInvokerServiceExporter accountServiceExporter(AccountService accountService){    HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();    exporter.setService(accountService);    exporter.setServiceInterface(AccountService.class);return exporter;}

暴露服务的代码相当简单,需要注意两点:

  1. org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter 是 Spring 封装的一个服务暴露器,它会以 serviceInterface 为公共接口,以 service 为实现类向外提供服务。

  2. @Bean("/AccountService") 不仅仅指定了 IOC 容器中 bean 的名字,还充当了路径映射的功能,如果本地服务器暴露在 8080 端口,则示例服务的访问路径为 http://localhost:8080/AccountService

创建服务消费者

@Configurationpublic class HttpProxyConfig {    @Bean("accountServiceProxy")public HttpInvokerProxyFactoryBean accountServiceProxy(){        HttpInvokerProxyFactoryBean accountService = new HttpInvokerProxyFactoryBean();        accountService.setServiceInterface(AccountService.class);        accountService.setServiceUrl("http://localhost:8080/AccountService");return accountService;    }}

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

消费者端引用服务同样有两个注意点:

  1. org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean 是 Spring 封装的一个服务引用器,serviceInterface 指定了生成代理的接口,serviceUrl 指定了服务所在的地址,与之前配置的服务暴露者的路径需要对应。

  2. HttpInvokerProxyFactoryBean 注册到容器之中时,会同时生成一个 AccountService 接口的代理类,由 Spring 封装远程调用的逻辑。

调用细节分析

对于 Spring HttpInvoker 的底层实现,就没必要深究了,但大家肯定还是会好奇一些细节:dubbo 中的 http 报文体是怎么组织的?如何序列化对象的?

我们使用 Wireshark 可以抓取到客户端发送的请求以及服务端响应的报文。

追踪报文流,可以看到详细的请求和响应内容

从 ContentType:application/x-java-serialized-object 和报文 Body 部分的 ASCII 码可以看出,使用的是 Java Serialize 序列化。我们将 Body 部分导出成文件,使用 Java Serialize 反序列化响应,来验证一下它的庐山真面目:

使用 Java Serialize 可以正常反序列化报文,得到结果是 Spring 内置的包装类 RemoteInvocationResult,里面装饰着实际的业务返回结果。

http 协议的意义

Dubbo 提供的众多协议有各自适用的场景,例如

  • dubbo://,dubbo 协议是默认的协议,自定义二进制协议;单个长连接节省资源;基于 tcp,架构于 netty 之上,性能还算可以;协议设计上没有足够的前瞻性,不适合做 service-mesh 谈不上多么优雅,但是好歹风风雨雨用了这么多年,周边也有不少配套组件例如 dubbo2.js, dubbo-go, dubbo-cpp,一定程度解决了多语言的问题。

  • webservice://,hession://,thrift:// 等协议,基本是为了适配已有协议的服务端/客户端,借助于 dubbo 框架的 api,可以使用其功能特性,意义不是特别大。

  • redis://,memcached:// 等协议,并非是暴露给用户配置的协议,一般是 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"

推荐阅读

(点击标题可跳转阅读)

Java 调试技能之 dubbo 调试 — telnet

深入 Spring Boot : 快速集成 Dubbo + Hystrix

简单的 HTTP 调用,为什么时延这么大?

看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

好文章,我在看❤️

dubbo 消费者也要暴露端口吗_一文详细解读 Dubbo 中的 http 协议相关推荐

  1. dubbo协议_一文详细解读 Dubbo 中的 http 协议

    太阳红彤彤,花儿五颜六色,各位读者朋友好,又来到了分享 Dubbo 知识点的时候了.说到 Dubbo 框架支持的协议,你的第一反应是什么?大概会有 Dubbo 默认支持的 dubbo 协议,以及老生常 ...

  2. cstring只获取到第一个数_一文讲透 Dubbo 负载均衡之最小活跃数算法

    (给ImportNew加星标,提高Java技能) 作者:why技术(本文来自作者投稿) 本文是对于Dubbo负载均衡策略之一的最小活跃数算法的详细分析.文中所示源码,没有特别标注的地方均为2.6.0版 ...

  3. 处于停机等非正常状态_一文聊透 Dubbo 优雅停机

    1 前言 一年之前,我曾经写过一篇<研究优雅停机时的一点思考>,主要介绍了 kill -9,kill -15 两个 Linux 指令的含义,并且针对性的聊到了 Spring Boot 应用 ...

  4. go 调用其他文件函数_一文读懂Go中软件包概念

    Go编程语言的软件包管理和部署的完整概述 如果您熟悉Java或NodeJS之类的语言,那么您可能非常熟悉软件包. 包不过是带有一些代码文件的目录,该目录从单个引用点公开了不同的变量(功能). 让我解释 ...

  5. 两平面平行方向向量关系_一文读懂 GDT 中的平面度

    本文从以下方面介绍平面度: 1. 数学定义(文末附带向量基础知识) 2. 接触模拟方法 3. 图纸标注 4. 应用场合 5. 取值方法 6 加工方法 7. 测量方法 一.数学定义 马克思有句名言:一门 ...

  6. 服务器千兆网卡接百兆交换机不通_一文搞懂监控工程中百兆交换机和千兆交换机的区别在哪?...

    安防监控系统工程现在都是用的网络摄像机,那么就肯定会经常和网络设备--交换机打交道,很多人在做监控方案的时候犯难,多少台摄像机该选用百兆交换机还是千兆交换机呢?关于这个问题除了需要掌握理论知识还是结合 ...

  7. python数组类型_一文搞懂Python中的所有数组数据类型

    关于我 编程界的一名小小程序猿,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. 联系:hylinux1024@gmail ...

  8. python中row是什么意思_一文搞懂Python中的yield

    关注公众号「Python七号」,及时 get Python 技能. yield 可以实现生成器,可以实现协程. 什么是生成器,什么是协程,如果还不了解,可以继续往下看,概念可以不懂,只要理解它的作用和 ...

  9. python 回归去掉共线性_一文讲解机器学习算法中的共线性问题

    多重共线性是使用线性回归算法时经常要面对的一个问题.在其他算法中,例如决策树和贝叶斯,前者的建模过程是逐步递进,每次拆分只有一个变量参与,这种建模机制含有抗多重共线性干扰的功能:后者干脆假定变量之间是 ...

最新文章

  1. 加速、能耗与对抗攻击:5位顶会作者解析2020 AI系统关键挑战
  2. 重新标注128万张ImageNet图片:多标签,提升模型性能
  3. Codeforces 675C Money Transfers (思维题)
  4. Html5 Video 节点
  5. java开发者工具开源版_JArchitect对Java开源贡献者免费
  6. 图形化界面客户端连接phoenix操作hbase
  7. php+js+return+true,js中return、return false、return true的区别
  8. 三菱PLC(FX5U)与C#通信说明
  9. 9款主流图表控件轻松实现数据可视化
  10. Linux基本操作(实训一)
  11. 面试题单例模式的五种写法(枚举妙用)
  12. 单引号在c语言中作用,我想知道单引号在C语言的具体作用
  13. 云南开放大学《机械制造基础-形考作业1-6(主观题)》
  14. 潇洒郎: 凯酷84机械键盘win键被锁解决方法
  15. 应用程序无法正常启动(0x000007b)的不常见的解决过程
  16. 好文章,转的Java重构
  17. 如何解决VS下载速度慢
  18. 帆软报表决策系统跨域登录
  19. 零基础学习C语言的第一天
  20. Vue - 适配iPhoneX微信浏览器

热门文章

  1. 用于Spring应用程序的Gradle原型
  2. Eclipse在过去十年中的主要成就
  3. Spring陷阱:代理
  4. JBoss Portal上的“ Hello World” portlet
  5. python程序填空题参照代码模板、完善代码_python二级考试操作题11.pdf
  6. MySQL数据库存入日期(java.sql.Date)数据,天数会少一天的问题
  7. Java对象如何实现比较规则
  8. java 中的点_java————形参中的点点点 | 学步园
  9. sqlserver存储过程加锁后怎么解锁_【缺陷周话】第59期:重复加锁
  10. LeetCode 1021 删除最外层的括号