Node 调用 dubbo 服务的探索及实践

1. 背景介绍

咱们公司是杭州的一家电商公司,公司内的技术体系较多,主要语言有了JAVA/PHP/Node,其中在19年的时候,公司制定了去PHP化的计划,将后端逻辑沉淀到Java服务化当中,而部分服务化调用相关业务则须要Node扛起,而与Java进行通讯则须要通过Dubbo,由此咱们以Consumer的角色来探索与研究如何用Node调用Dubbo.javascript

调用链路


2.Dubbo简介

2.1 什么是dubbo

Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。java

2.2 流程图

调用流程图

  • Provider : 暴露服务的服务提供方。
  • Consumer : 调用远程服务的服务消费方。
  • Registry: 服务注册与发现的注册中心。
  • Monitor: 统计服务的调用次调和调用时间的监控中心。
  • Container: 服务运行容器。

3. 具体实现

3.1 协议选择

官方虽然支持了不少种协议,可是真正适合于咱们的协议并非不少,譬如rmi协议主要是面向Java工程之间的调用,并不适合用于异构语言RPC场景,因此咱们接下来只针对部分协议进行分析,并结合咱们的实际的业务场景,最后来筛选出适合咱们的协议.git

链接个数 链接方式 传输协议 传输方式 序列化 适用范围 适用场景
dubbo 单链接 长链接 TCP NIO 异步传输 Hessian 二进制序列化 传入传出参数数据包较小,消费者比提供者个数多,单一消费者没法压满提供者 常规远程服务方法调用
rmi 多链接 短链接 TCP 同步传输 Java 标准二进制序列化 传入传出参数数据包大小混合,消费者与提供者个数差很少,可传文件。 常规远程服务方法调用,与原生RMI服务互操做
hessian 多链接 短链接 HTTP 同步传输 Hessian二进制序列化 传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件。 页面传输,文件传输,或与原生hessian服务互操做
http 多链接 短链接 HTTP 同步传输 表单序列化 传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或URL传入参数 需同时给应用程序和浏览器 JS 使用的服务。
rest 多链接 短链接 HTTP 同步传输 表单序列化 同http,适用于更加符合rest规范的服务 同http

3.1.1 协议选择 - dubbo

从使用场景上来看,dubbo协议是比较符合咱们实际业务需求的,因为其数据包相较于Http协议体积小不少,传输速度也会更快,另外咱们能够经过socket与provider创建长链接,能够减小反复建连带来的没必要要的网络开销.使用此协议,咱们须要注意的几个点是github

  • 本地负载均衡策略
  • 与Provider创建TCP长链接
  • Hessian协议解析

小结 : 若是想要采用此协议链接Provider,可使用dubbo官方推荐dubbo2.js后端

3.1.2 协议选择 - rest

此协议是基于http,因此对consumer端就基本上没有了限制.并且此协议咱们在拼接好参数以后,能够直接经过浏览器或者是HTTP请求工具便可查看结果,使用友好程度上来说,会优于dubbo. consumer端能够直接使用request库请求,也不存在协议解析以及socket状态维护的问题,消费端的代码实现难度也会比较小.使用此协议,咱们须要注意的几个点 :浏览器

  • 须要使用keep-alive来与provider保持建连,不然http反复断开重连会带来不少没必要要的网络开销
  • 本地负载均衡策略
  • 此协议在dubbo 2.6.x+ 才支持

小结 : 性能上虽不及dubbo.但对开发人员相对友好,能够结合业务自身场景进行选择.网络

3.1.3 补充

dubbo是支持一个服务以多种协议注册,好比一个服务能够同时注册dubbo://rest://,若是你想用http,可是目前公司所暴露出来的协议只支持dubbo,能够和提供服务的同窗商量一下,额外再添加一个http协议成本也是能在接收范围内的.负载均衡

3.2 如何引用服务

目前引用服务有两个方案,分别是框架

  • 直接引用
  • 经过注册中心引用服务

3.2.1 直接引用服务

直接引用服务,顾名思义就是绕开注册中心获取咱们所想要的服务提供者,因为绕开了注册中心,天然也没法作到服务发现,并且因为单点问题,没法作到负载均衡以及高可用,因此生产环境不推荐使用此模式的异步

可是因为其开发上的便利性,在开发环境/测试环境仍能够尝试使用此模式.

直连provider

由上图所示,开发同窗联调过程当中,须要在项目工程中对指定服务开发同窗的机器进行直连,而其余没有指定的服务将会默认走注册中心.为了不对工程代码的侵入性,咱们会在工程中创建应对不一样环境的dubbo.properies,而dubbo.properies不会加入到工程的版本控制当中,主要用于解决不一样环境下的服务直连问题.其中服务的控制粒度能够精确到具体的服务.

直连代码片断

3.2.2 经过注册中心引用服务

经过注册中心发现引用服务,Dubbo经常使用的引用服务方式,能够作到服务自动发现,负载均衡.正式环境调用基本基于此模式.其中注册中心实现有不少种,例如Zookeeper/Redis/Multicast.官方推荐Zookeeper.

经过注册中心引用服务

3.3 服务请求结构的定义

服务请求体结构,是在对dubbo在注册中心上注册信息的抽象以后的一层封装,一方面能够提高开发人员的开发效率,另外下降开发人员自身手动拼接请求的错误率.

3.3.1 服务的构成

咱们基于dubbo/rest两种协议,来分析一下这两种协议在注册中心注册包含哪些信息.

  • dubbo : dubbo://192.168.1.2:10880/com.service.ProductService?dubbo=2.8&methods=getById,getByName
  • rest : rest://192.168.1.2:10081/service/com.service.ProductService?dubbo=2.8&methods=getById,getByName

咱们对这两个协议公共部分进行提取一下

  • protocol : 协议类型.例如 dubbo://.
  • host : provider主机地址
  • port : provider对外暴露服务的端口
  • interface : 对外暴露服务名称,在java中是采用包名 + 服务名称构成,例如com.service.ProductService
  • dubboVersion: dubbo版本
  • method:服务对外暴露的方法,一个服务会同时包含多个方法.
  • query:还有一个就是请求参数列表,此项是在java服务定义的,在注册信息中没法体现.

3.3.2 请求体的定义

基于上述服务结构构成的分析,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不须要.

3.4 服务定义的维护

咱们在对服务定义完成以后,接下来就会面临一个使用上的问题,最直接的方法就是为每一个工程每一个服务新建一个服务文件,可是一用就会发现一个问题请求定义的文件分散在不一样工程,没法进行统一维护升级,维护成本较高.

维护成本

咱们第一个反应是每一个服务抽象出来,各自成为一个独立的NPM包,譬如MemberService咱们能够抽象成为@dubbo-service/member-service,这样就能够解决文件分散在不一样工程致使的维护问题.

解决维护问题

3.4.1 后续问题

事情到这里,咱们已经解决了服务如何统必定义的问题,可是仍然没有解决统一管理与维护的问题.如 :

  • 维护人员职责划分问题.NPM包的维护工做该交给服务提供方仍是服务调用方?
  • 项目迁移成本问题.若是涉及到项目迁移问题,可能会涉及到不少现有的Java服务初始化的问题,而手动去定义服务工程量巨大,如何下降迁移成本问题是咱们不得不要面临的问题.

4 最后

若是你以为此篇文章对你有帮助,就顺手点个赞吧~ 很是感谢

有什么疑问能够直接评论回复或者私信我,我会尽我所能回复你~

相关文章
相关标签/搜索