跨语言调用原理 跨语言通信
Dubbo——HTTP 协议 + JSON-RPC
Protocol 还有一个实现分支是 AbstractProxyProtocol,如下图所示:
跨语言调用原理 跨语言通信
跨语言调用原理 跨语言通信
从图中我们可以看到:gRPC、HTTP、WebServ、Hessian、Thrift 等协议对应的 Protocol 实现,都是继承自 AbstractProxyProtocol 抽象类。
目前互联网的技术栈百花齐放,很多公司会使用 Node.js、Python、Rails、Go 等语言来开发 一些 Web 端应用,同时又有很多服务会使用 Ja 技术栈实现,这就出现了大量的跨语言调用的需求。Dubbo 作为一个 RPC 框架,自然也希望能实现这种跨语言的调用,目前 Dubbo 中使用“HTTP 协议 + JSON-RPC”的方式来达到这一目的,其中 HTTP 协议和 JSON 都是天然跨语言的标准,在各种语言中都有成熟的类库。
下面就重点来分析 Dubbo 对 HTTP 协议的支持。首先,会介绍 JSON-RPC 的基础,并通过一个示例,快速入门,然后介绍 Dubbo 中 HttpProtocol 的具体实现,也就是如何将 HTTP 协议与 JSON-RPC 结合使用,实现跨语言调用的效果。
Dubbo 中支持的 HTTP 协议实际上使用的是 JSON-RPC 协议。
JSON-RPC 是基于 JSON 的跨语言远程调用协议。Dubbo 中的 dubbo-rpc-xml、dubbo-rpc-webserv 等模块支持的 XML-RPC、WebServ 等协议与 JSON-RPC 一样,都是基于文本的协议,只不过 JSON 的格式比 XML、WebServ 等格式更加简洁、紧凑。与 Dubbo 协议、Hessian 协议等二进制协议相比,JSON-RPC 更便于调试和实现,可见 JSON-RPC 协议还是一款非常的远程调用协议。
在 Ja 体系中,有很多成熟的 JSON-RPC 框架,例如 jsonrpc4j、jpoxy 等,其中,jsonrpc4j 本身体积小巧,使用方便,既可以使用,也可以与 Spring 无缝,非常适合基于 Spring 的项目。
下面先来看看 JSON-RPC 协议中请求的基本格式:
JSON-RPC请求中各个字段的含义如下:
在 JSON-RPC 的服务端收到调用请求之后,会查找到相应的方法并进行调用,然后将方法的返回值整理成如下格式,返回给客户端:
JSON-RPC响应中各个字段的含义如下:
Dubbo 使用 jsonrpc4j 库来实现 JSON-RPC 协议,下面使用 jsonrpc4j 编写一个简单的 JSON-RPC 服务端示例程序和客户端示例程序,并通过这两个示例程序说明 jsonrpc4j 最基本的使用方式。
首先,需要创建服务端和客户端都需要的 domain 类以及服务接口。先来创建一个 User 类,作为最基础的数据对象:
接下来创建一个 UserServ 接口作为服务接口,其中定义了 5 个方法,分别用来创建 User、查询 User 以及相关信息、删除 User:
UserServImpl 是 UserServ 接口的实现类,其中使用一个 ArrayList 管理 User 对象,具体实现如下:
整个用户管理业务的核心大致如此。下面我们来看服务端如何将 UserServ 与 JSON-RPC 关联起来。
首先,创建 RpcServlet 类,它是 HttpServlet 的子类,并覆盖了 HttpServlet 的 serv() 方法。我们知道,HttpServlet 在收到 GET 和 POST 请求的时候,最终会调用其 serv() 方法进行处理;HttpServlet 还会将 HTTP 请求和响应封装成 HttpServletRequest 和 HttpServletResponse 传入 serv() 方法之中。这里的 RpcServlet 实现之中会创建一个 JsonRpc,并在 serv() 方法中将 HTTP 请求委托给 JsonRpc 进行处理:
,创建一个 JsonRpc 作为服务端的入口类,在其 main() 方法中会启动 Jetty 作为 Web 容器,具体实现如下:
这里使用到的 web.xml 配置文件如下:
完成服务端的编写之后,下面再继续编写 JSON-RPC 的客户端。在 JsonRpc 中会创建 JsonRpcHttp,并通过 JsonRpcHttp 请求服务端:
在 AbstractProxyProtocol 的 export() 方法中,首先会根据 URL 检查 exporterMap 缓存,如果查询失败,则会调用 ProxyFactory.getProxy() 方法将 Invoker 封装成业务接口的类,然后通过子类实现的 doExport() 方法启动底层的 ProxyProtocol,并初始化 serverMap 。具体实现如下:
在 HttpProtocol 的 doExport() 方法中,与前面介绍的 DubboProtocol 的实现类似,也要启动一个 Remoting。为了适配各种 HTTP ,例如,Tomcat、Jetty 等,Dubbo 在 Transporter 层抽象出了一个 Http 的接口。
dubbo-remoting- 模块的入口是 HttpBinder 接口,它被 @SPI 注解修饰,是一个扩展接口,有三个扩展实现,默认使用的是 JettyHttpBinder 实现,如下图所示:
HttpBinder 接口中的 bind() 方法被 @Adaptive 注解修饰,会根据 URL 的 server 参数选择相应的 HttpBinder 扩展实现,不同 HttpBinder 实现返回相应的 Http 实现。Http 的继承关系如下图所示:
这里以 JettyHttp 为例简单介绍 Http 的实现,在 JettyHttp 中会初始化 Jetty ,其中会配置 Jetty 使用到的线程池以及处理请求 Handler:
可以看到 JettyHttp 收到的全部请求将委托给 DispatcherServlet 这个 HttpServlet 实现,而 DispatcherServlet 的 serv() 方把请求委托给对应接端口的 HttpHandler 处理:
了解了 Dubbo 对 Http 的抽象以及 JettyHttp 的核心之后,回到 HttpProtocol 中的 doExport() 方法继续分析。
在 HttpProtocol.doExport() 方法中会通过 HttpBinder 创建前面介绍的 Http 对象,并记录到 serverMap 中用来接收 HTTP 请求。这里初始化 Http 以及处理请求用到的 HttpHandler 是 HttpProtocol 中的内部类,在其他使用 HTTP 协议作为基础的 RPC 协议实现中也有类似的 HttpHandler 实现类,如下图所示:
在 HttpProtocol.InternalHandler 中的 handle() 实现中,会将请求委托给 skeletonMap 中记录的 JsonRpc 对象进行处理:
skeletonMap 中的 JsonRpc 是与 Http 对象一同在 doExport() 方法中初始化的。,我们来看 HttpProtocol.doExport() 方法的实现:
介绍完 HttpProtocol 暴露服务的相关实现之后,下面再来看 HttpProtocol 中引用服务相关的方法实现,即 protocolBindinRefer() 方法实现。该方法首先通过 doRefer() 方法创建业务接口的,这里会使用到 jsonrpc4j 库中的 JsonProxyFactoryBean 与 Spring 进行集成,在其 afterPropertiesSet() 方法中会创建 JsonRpcHttp 对象:
下面来看 doRefer() 方法的具体实现:
在 AbstractProxyProtocol.protocolBindingRefer() 方法中,会通过 ProxyFactory.getInvoker() 方法将 doRefer() 方法返回的对象转换成 Invoker 对象,并记录到 Invokers 中,具体实现如下:
本文重点介绍了在 Dubbo 中如何通过“HTTP 协议 + JSON-RPC”的方案实现跨语言调用。首先介绍了 JSON-RPC 中请求和响应的基本格式,以及其实现库 jsonrpc4j 的基本使用;接下来我们还详细介绍了 Dubbo 中 AbstractProxyProtocol、HttpProtocol 等核心类,剖析了 Dubbo 中“HTTP 协议 + JSON-RPC”方案的落地实现。
微服务跨语言调用(摘选)
微服务架构已成为目前互联网架构的趋势,关于微服务的讨论,几乎占据了各种技术大会的绝大多数版面。国内使用最多的服务治理框架非阿里开源的 dubbo 莫属,千米网也选择了 dubbo 作为微服务治理框架。另一方面,和大多数互联网公司一样,千米的开发语言是多样的,大多数后端业务由 ja 支撑,而每个业务线有各自开发语言的选择权,便出现了 nodejs,python,go 多语言调用的问题。
grpc原理
1)需要使用protobuf定义接口,即.proto文件
2)然后使用compile工具生成特定语言的执行代码,比如JAVA、C/C++、Python等。类似于thrift,为了解决跨语言问题。
3)启动一个端,server端通过侦听指定的port,来等待链接请求,通常使用Netty来构建,GRPC内置了Netty的支持。
4)启动一个或者多个端,也是基于Netty,通过与建立TCP长链接,并发送请求;Request与Response均被封装成HTTP2的stream Frame,通过Netty Channel进行交互。
对于GRPC的“鼓吹”,本文不多表述,截止到今日,GRPC仍然处于开发阶段,尚没有release版本,而且特性也很多需要补充;GRPC基于protobuf 3.x,但是protobuf 3.x也没有release版本;虽然HTTP2协议已成定局,但尚未被主流web容器包括支持,这意味着GRPC在HTTP负载均衡方面尚有欠缺;最终,在短期内我们还不能在production环境中实施,可以做技术储备。不过GRPC的缺点,在将来将会成为它的优点,我们需要时间等待它的成熟。
1)GRPC尚未提供连接池
2)尚未提供“服务发现”、“负载均衡”机制
3)因为基于HTTP2,绝大部多数HTTP 、Nginx都尚不支持,即Nginx不能将GRPC请求作为HTTP请求来负载均衡,而是作为普通的TCP请求。(nginx将会在1.9版本支持)
4)GRPC尚不成熟,易用性还不是很理想;就本人而言,我还是希望GRPC能够像hessian一样:无IDL文件,无需代码生成,接口通过HTTP表达。
5)Spring容器尚未提供整合。
在实际应用中,GRPC尚未完全提供连接池、服务自动发现、进程内负载均衡等高级特性,需要开发人员额外的封装;的问题,就是GRPC生成的接口,调用方式实在是不太便捷(JAVA),最起码与thrift相比还有距,希望未来能够有所改进。
JSON-RPC轻量级远程调用协议介绍及使用
json-rpc是基于json的跨语言远程调用协议。比xml-rpc、webserv等基于文本的协议数据传输格小;相对hessian、ja-rpc等二进制协议便于调试、实现、扩展,是很的一种远程调用协议。眼下主流语言都已有json-rpc的实现框架,ja语言中较好的json-rpc实现框架有jsonrpc4j、jpoxy、json-rpc。三者之中jsonrpc4j既可使用。又可与spring无缝,比较适合于基于spring的项目开发。
json-rpc协议很easy,发起远程调用时向服务端数据传输格式例如以下:
{ "mod": "sayHello", "params": ["Hello JSON-RPC"], "id": 1}
参数说明:
mod: 调用的方法名
params: 方法传入的参数。若无参数则传入 []
id : 调用标识符。用于标示一次远程调用过程
server其收到调用请求,处理方法调用,将方法效用结果效应给调用方;返回数据格式:
参数说明:
result: 方法返回值。若无返回值。则返回null。
若调用错误,返回null。
error :调用时错误,无错误返回null。
id : 调用标识符,与调用方传入的标识符一致。
以上就是json-rpc协议规范,很easy,小巧。便于各种语言实现。
2.1、server端Ja调用演示样例
jsonrpc4jserver端ja演示样例:
2.2、Jaclient调用演示样例
jsonrpc4j的Jaclient调用演示样例:
2.3、JaScriptclient调用演示样例
基于jsonrpcjs的JaScriptclient调用演示样例:
2.4、直接GET请求进行调用
无需不论什么client。仅仅需手工拼接参数进行远程调用,请求URL例如以下:
参数说明:
mod : 方法名
params :调用参数。json的数组格式[], 将参数需先进行编码,再进行base64编码
id : 调用标识符,随意值。
json-rpc是一种很轻量级的跨语言远程调用协议。实现及使用简单。
仅需几十行代码,就可以实现一个远程调用的client。方便语言扩展client的实现。
server端有php、ja、python、ruby、等语言实现,是很不错的及轻量级的远程调用协议。
dubbo跨语言的研究(c++)
目前来看jni反而是一个比较通用的方法,
c++的服务端使用jni对服务进行包装,通过服务总线(dubbo)发布服务,最终打包成一个jar包,启动服务。
c++客户端也使用jni方式通过服务总线(dubbo)调用服务。
我简单研究了githup中的dubbo-python、dubbo-node-client、dubbo-php-client。
基本都是这样做的:
问题,这些各个语言的dubbo库,都只是客户端的,对于服务端没有支持。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系 836084111@qq.com 删除。