我们公司是杭州的一家电商公司,公司内的技术体系较多,主要语言有了JAVA/PHP/Node,其中在19年的时候,公司制定了去PHP化的计划,将后端逻辑沉淀到Java服务化当中,而部分服务化调用相关业务则需要Node扛起,而与Java进行通信则需要经过Dubbo,由此我们以Consumer的角色来探索与研究如何用Node调用Dubbo.
Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
连接个数 | 连接方式 | 传输协议 | 传输方式 | 序列化 | 适用范围 | 适用场景 | |
---|---|---|---|---|---|---|---|
dubbo | 单连接 | 长连接 | TCP | NIO 异步传输 | Hessian 二进制序列化 | 传入传出参数数据包较小,消费者比提供者个数多,单一消费者无法压满提供者 | 常规远程服务方法调用 |
rmi | 多连接 | 短连接 | TCP | 同步传输 | Java 标准二进制序列化 | 传入传出参数数据包大小混合,消费者与提供者个数差不多,可传文件。 | 常规远程服务方法调用,与原生RMI服务互操作 |
hessian | 多连接 | 短连接 | HTTP | 同步传输 | Hessian二进制序列化 | 传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件。 页面传输,文件传输,或与原生hessian服务互操作 | |
http | 多连接 | 短连接 | HTTP | 同步传输 | 表单序列化 | 传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或URL传入参数 需同时给应用程序和浏览器 JS 使用的服务。 | |
rest | 多连接 | 短连接 | HTTP | 同步传输 | 表单序列化 | 同http,适用于更加符合rest规范的服务 | 同http |
目前引用服务有两个方案,分别是
直接引用服务,顾名思义就是绕开注册中心获取我们所想要的服务提供者,由于绕开了注册中心,自然也无法做到服务发现,而且由于单点问题,无法做到负载均衡以及高可用, 所以生产环境不推荐使用此模式的
.
但是由于其开发上的便利性,在开发环境/测试环境仍可以尝试使用此模式.
由上图所示,开发同学联调过程中,需要在项目工程中对指定服务开发同学的机器进行直连,而其他没有指定的服务将会默认走注册中心.为了避免对工程代码的侵入性,我们会在工程中建立应对不同环境的dubbo.properies,而dubbo.properies不会加入到工程的版本控制当中,主要用于解决不同环境下的服务直连问题.其中服务的控制粒度可以精确到具体的服务.
通过注册中心发现引用服务,Dubbo常用的引用服务方式,可以做到服务自动发现,负载均衡.正式环境调用基本基于此模式.其中注册中心实现有很多种,例如Zookeeper/Redis/Multicast.官方推荐Zookeeper.
服务请求体结构,是在对dubbo在注册中心上注册信息的抽象之后的一层封装,一方面可以提升开发人员的开发效率,另外降低开发人员自身手动拼接请求的错误率.
基于上述基于协议所分析,我们目前协议将只会锁定在dubbo/rest,那么我们先看来这两个协议在注册中心注册的信息是什么样子的.
dubbo://192.168.1.2:10880/com.service.ProductService?dubbo=2.8&methods=getById,getByName rest://192.168.1.2:10081/service/com.service.ProductService?dubbo=2.8&methods=getById,getByName
我们对这两个协议公共部分进行提取一下
dubbo:// com.service.ProductService
基于上述服务结构构成的分析,dubbo和rest服务请求结构构成大体类似,我们对不同的协议请求的可以做如下定义.
// 1. dubbo协议的请求体定义 services.ProductService = (dubbo) => dubbo.proxyService({ dubboInterface: 'com.service.ProductService', methods: { getById(id) { return [java.Long(id)]; }, getByName(name) { return [java.String(name)]; } }, }); 复制代码
// rest 请求体定义 services.ProductService = (dubbo) => dubbo.proxyService({ dubboInterface: 'com.service.ProductService', methods: { getById(id) { return { method: 'get', query: [parseInt(id)] }; }, getByName(name) { return [String(name)]; } }, }); 复制代码
两者最大不同点在于参数定义上的不同,dubbo需要强制转换为强类型,而rest不需要.
我们在对服务定义完成之后,接下来就会面临一个使用上的问题,最直接的方法就是为每个工程每个服务新建一个服务文件,但是一用就会发现一个问题请求定义的文件分散在不同工程,无法进行统一维护升级,维护成本较高.
我们第一个反应是每个服务抽象出来,各自成为一个独立的NPM包,譬如MemberService我们可以抽象成为 @dubbo-service/member-service
,这样就可以解决文件分散在不同工程导致的维护问题.