Dubbo——HTTP 协议JSON-RPCProtocol 还有一个实现分支是 AbstractProxyProtocol,如下图所示:
从图中我们可以看到:gRPC、HTTP、WebService、Hessian、Thrift 等协议对应跨语言rpcgo的 Protocol 实现,都是继承自 AbstractProxyProtocol 抽象类 。
目前互联网跨语言rpcgo的技术栈百花齐放,很多公司会使用 Node.js、Python、Rails、Go 等语言来开发 一些 Web 端应用,同时又有很多服务会使用 Java 技术栈实现 , 这就出现了大量的跨语言调用的需求 。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-webservice 等模块支持的 XML-RPC、WebService 等协议与 JSON-RPC 一样,都是基于文本的协议,只不过 JSON 的格式比 XML、WebService 等格式更加简洁、紧凑 。与 Dubbo 协议、Hessian 协议等二进制协议相比 , JSON-RPC 更便于调试和实现,可见 JSON-RPC 协议还是一款非常优秀的远程调用协议 。
在 Java 体系中 , 有很多成熟的 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 类 , 作为最基础的数据对象:
接下来创建一个 UserService 接口作为服务接口,其中定义了 5 个方法 , 分别用来创建 User、查询 User 以及相关信息、删除 User:
UserServiceImpl 是 UserService 接口的实现类,其中使用一个 ArrayList 集合管理 User 对象,具体实现如下:
整个用户管理业务的核心大致如此 。下面我们来看服务端如何将 UserService 与 JSON-RPC 关联起来 。
首先,创建 RpcServlet 类 , 它是 HttpServlet 的子类 , 并覆盖了 HttpServlet 的 service() 方法 。我们知道,HttpServlet 在收到 GET 和 POST 请求的时候,最终会调用其 service() 方法进行处理;HttpServlet 还会将 HTTP 请求和响应封装成 HttpServletRequest 和 HttpServletResponse 传入 service() 方法之中 。这里的 RpcServlet 实现之中会创建一个 JsonRpcServer,并在 service() 方法中将 HTTP 请求委托给 JsonRpcServer 进行处理:
最后,创建一个 JsonRpcServer 作为服务端的入口类 , 在其 main() 方法中会启动 Jetty 作为 Web 容器,具体实现如下:
这里使用到的 web.xml 配置文件如下:
完成服务端的编写之后 , 下面再继续编写 JSON-RPC 的客户端 。在 JsonRpcClient 中会创建 JsonRpcHttpClient , 并通过 JsonRpcHttpClient 请求服务端:
在 AbstractProxyProtocol 的 export() 方法中,首先会根据 URL 检查 exporterMap 缓存,如果查询失败 , 则会调用 ProxyFactory.getProxy() 方法将 Invoker 封装成业务接口的代理类,然后通过子类实现的 doExport() 方法启动底层的 ProxyProtocolServer,并初始化 serverMap 集合 。具体实现如下:
在 HttpProtocol 的 doExport() 方法中,与前面介绍的 DubboProtocol 的实现类似,也要启动一个 RemotingServer 。为了适配各种 HTTP 服务器,例如,Tomcat、Jetty 等 , Dubbo 在 Transporter 层抽象出了一个 HttpServer 的接口 。
dubbo-remoting-http 模块的入口是 HttpBinder 接口,它被 @SPI 注解修饰,是一个扩展接口 , 有三个扩展实现 , 默认使用的是 JettyHttpBinder 实现,如下图所示:
HttpBinder 接口中的 bind() 方法被 @Adaptive 注解修饰 , 会根据 URL 的 server 参数选择相应的 HttpBinder 扩展实现 , 不同 HttpBinder 实现返回相应的 HttpServer 实现 。HttpServer 的继承关系如下图所示:
这里以 JettyHttpServer 为例简单介绍 HttpServer 的实现,在 JettyHttpServer 中会初始化 Jetty Server,其中会配置 Jetty Server 使用到的线程池以及处理请求 Handler:
可以看到 JettyHttpServer 收到的全部请求将委托给 DispatcherServlet 这个 HttpServlet 实现,而 DispatcherServlet 的 service() 方法会把请求委托给对应接端口的 HttpHandler 处理:
了解了 Dubbo 对 HttpServer 的抽象以及 JettyHttpServer 的核心之后,回到 HttpProtocol 中的 doExport() 方法继续分析 。
在 HttpProtocol.doExport() 方法中会通过 HttpBinder 创建前面介绍的 HttpServer 对象,并记录到 serverMap 中用来接收 HTTP 请求 。这里初始化 HttpServer 以及处理请求用到的 HttpHandler 是 HttpProtocol 中的内部类,在其他使用 HTTP 协议作为基础的 RPC 协议实现中也有类似的 HttpHandler 实现类,如下图所示:
在 HttpProtocol.InternalHandler 中的 handle() 实现中,会将请求委托给 skeletonMap 集合中记录的 JsonRpcServer 对象进行处理:
skeletonMap 集合中的 JsonRpcServer 是与 HttpServer 对象一同在 doExport() 方法中初始化的 。最后,我们来看 HttpProtocol.doExport() 方法的实现:
介绍完 HttpProtocol 暴露服务的相关实现之后 , 下面再来看 HttpProtocol 中引用服务相关的方法实现,即 protocolBindinRefer() 方法实现 。该方法首先通过 doRefer() 方法创建业务接口的代理,这里会使用到 jsonrpc4j 库中的 JsonProxyFactoryBean 与 Spring 进行集成 , 在其 afterPropertiesSet() 方法中会创建 JsonRpcHttpClient 对象:
下面来看 doRefer() 方法的具体实现:
在 AbstractProxyProtocol.protocolBindingRefer() 方法中,会通过 ProxyFactory.getInvoker() 方法将 doRefer() 方法返回的代理对象转换成 Invoker 对象,并记录到 Invokers 集合中,具体实现如下:
本文重点介绍了在 Dubbo 中如何通过“HTTP 协议JSON-RPC”的方案实现跨语言调用 。首先介绍了 JSON-RPC 中请求和响应的基本格式,以及其实现库 jsonrpc4j 的基本使用;接下来我们还详细介绍了 Dubbo 中 AbstractProxyProtocol、HttpProtocol 等核心类,剖析了 Dubbo 中“HTTP 协议JSON-RPC”方案的落地实现 。
主流的RPC框架有哪些?RPC是远程过程调用的简称,广泛应用在大规模分布式应用中,作用是有助于系统的垂直拆分,使系统更易拓展 。Java中的RPC框架比较多,各有特色,广泛使用的有RMI、Hessian、Dubbo等 。RPC还有一个特点就是能够跨语言 。
1、RMI(远程方法调用)
JAVA自带的远程方法调用工具,不过有一定的局限性,毕竟是JAVA语言最开始时的设计,后来很多框架的原理都基于RMI,RMI的使用如下:
对外接口
【跨语言rpcgo 跨语言检测结果是什么】span style="font-size:12px;"public interface IService extends Remote {
public String queryName(String no) throws RemoteException;
}/span
服务实现
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
// 服务实现
public class ServiceImpl extends UnicastRemoteObject implements IService {
/**
*/
private static final long serialVersionUID = 682805210518738166L;
/**
* @throws RemoteException
*/
protected ServiceImpl() throws RemoteException {
super();
}
/* (non-Javadoc)
* @see com.suning.ebuy.wd.web.IService#queryName(java.lang.String)
*/
@Override
public String queryName(String no) throws RemoteException {
// 方法的具体实现
System.out.println("hello"no);
return String.valueOf(System.currentTimeMillis());
}
}
RMI客户端
[java] view plain copy
import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
// RMI客户端
public class Client {
public static void main(String[] args) {
// 注册管理器
Registry registry = null;
try {
// 获取服务注册管理器
registry = LocateRegistry.getRegistry("127.0.0.1",8088);
// 列出所有注册的服务
String[] list = registry.list();
for(String s : list){
System.out.println(s);
}
} catch (RemoteException e) {
}
try {
// 根据命名获取服务
IService server = (IService) registry.lookup("vince");
// 调用远程方法
String result = server.queryName("ha ha ha ha");
// 输出调用结果
System.out.println("result from remote : "result);
} catch (AccessException e) {
} catch (RemoteException e) {
} catch (NotBoundException e) {
}
}
}
RMI服务端
[java] view plain copy
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
// RMI服务端
public class Server {
public static void main(String[] args) {
// 注册管理器
Registry registry = null;
try {
// 创建一个服务注册管理器
registry = LocateRegistry.createRegistry(8088);
} catch (RemoteException e) {
}
try {
// 创建一个服务
ServiceImpl server = new ServiceImpl();
// 将服务绑定命名
registry.rebind("vince", server);
System.out.println("bind server");
} catch (RemoteException e) {
}
}
}
2、Hessian(基于HTTP的远程方法调用)
基于HTTP协议传输,在性能方面还不够完美,负载均衡和失效转移依赖于应用的负载均衡器,Hessian的使用则与RMI类似,区别在于淡化了Registry的角色,通过显示的地址调用,利用HessianProxyFactory根据配置的地址create一个代理对象,另外还要引入Hessian的Jar包 。
3、Dubbo(淘宝开源的基于TCP的RPC框架)
基于Netty的高性能RPC框架,是阿里巴巴开源的 , 总体原理如下:
微服务跨语言调用(摘?。?/h2>微服务架构已成为目前互联网架构的趋势,关于微服务的讨论,几乎占据跨语言rpcgo了各种技术大会的绝大多数版面 。国内使用最多的服务治理框架非阿里开源的 dubbo 莫属,千米网也选择了 dubbo 作为微服务治理框架 。另一方面 , 和大多数互联网公司一样,千米的开发语言是多样的,大多数后端业务由 java 支撑,而每个业务线有各自开发语言的选择权,便出现了 nodejs,python , go 多语言调用的问题 。
跨语言调用是一个很大的话题,也是一个很有挑战的技术活,目前业界经常被提及的解决方案有如下几种,不妨拿出来老生常谈一番:
当跨语言rpcgo我们再聊跨语言调用时我们在聊什么?纵观上述几个较为通用 , 成熟的解决方案,可以得出结论:解决跨语言调用的思路无非是两种:
如果一个新型的团队面临技术选型,我认为上述的方案都可以纳入参考,可考虑到遗留系统的兼容性问题
旧系统的迁移成本
这也关键的选型因素 。我们做出的第一个尝试,便是在 RPC 协议上下功夫 。
通用协议的跨语言支持
springmvc的美好时代
springmvc
springmvc
在没有实现真正的跨语言调用之前 , 想要实现“跨语言”大多数方案是使用 http 协议做一层转换,最常见的手段莫过于借助 springmvc 提供的 controller/restController , 间接调用 dubbo provider 。这种方案的优势和劣势显而易见
通用协议的支持
事实上,大多数服务治理框架都支持多种协议,dubbo 框架除默认的 dubbo 协议之外,还有当当网扩展的 rest协议和千米网扩展的 json-rpc 协议可供选择 。这两者都是通用的跨语言协议 。
rest 协议为满足 JAX-RS 2.0 标准规范,在开发过程中引入了 @Path,@POST,@GET 等注解 , 习惯于编写传统 rpc 接口的人可能不太习惯 rest 风格的 rpc 接口 。一方面这样会影响开发体验,另一方面,独树一帜的接口风格使得它与其跨语言rpcgo他协议不太兼容,旧接口的共生和迁移都无法实现 。如果没有遗留系统,rest 协议无疑是跨语言方案最简易的实现,绝大多数语言支持 rest 协议 。
和 rest 协议类似 , json-rpc 的实现也是文本序列化http 协议 。dubbox 在 restful 接口上已经做出了尝试,但是 rest 架构和 dubbo 原有的 rpc 架构是有区别的,rest 架构需要对资源(Resources)进行定义 , 需要用到 http 协议的基本操作 GET、POST、PUT、DELETE 。在我们看来,restful 更合适互联网系统之间的调用,而 rpc 更适合一个系统内的调用 。使用 json-rpc 协议使得旧接口得以兼顾,开发习惯仍旧保留,同时获得了跨语言的能力 。
千米网在早期实践中采用了 json-rpc 作为 dubbo 的跨语言协议实现,并开源了基于 json-rpc 协议下的 python 客户端 dubbo-client-py 和 node 客户端 dubbo-node-client,使用 python 和 nodejs 的小伙伴可以借助于它们直接调用 dubbo-provider-java 提供的 rpc 服务 。系统中大多数 java 服务之间的互相调用还是以 dubbo 协议为主,考虑到新旧协议的适配 , 在不影响原有服务的基础上,我们配置了双协议 。
dubbo 协议主要支持 java 间的相互调用,适配老接口;json-rpc 协议主要支持异构语言的调用 。
定制协议的跨语言支持
微服务框架所谓的协议(protocol)可以简单理解为:报文格式和序列化方案 。服务治理框架一般都提供了众多的协议配置项供使用者选择 , 除去上述两种通用协议 , 还存在一些定制化的协议,如 dubbo 框架的默认协议:dubbo 协议以及 motan 框架提供的跨语言协议:motan2 。
motan2协议的跨语言支持
motan2
motan2
motan2 协议被设计用来满足跨语言的需求主要体现在两个细节中—MetaData 和 motan-go 。在最初的 motan 协议中,协议报文仅由 Header Body 组成,这样导致 path,param,group 等存储在 Body 中的数据需要反序列得到,这对异构语言来说是很不友好的,所以在 motan2 中修改了协议的组成;weibo 开源了 motan-go ,motan-php , motan-openresty ,并借助于 motan-go 充当了 agent 这一翻译官的角色 , 使用 simple 序列化方案来序列化协议报文的 Body 部分(simple 序列化是一种较弱的序列化方案) 。
agent
agent
仔细揣摩下可以发现这么做和双协议的配置区别并不是大 , 只不过这里的 agent 是隐式存在的 , 与主服务共生 。明显的区别在于 agent 方案中异构语言并不直接交互 。
dubbo协议的跨语言支持
dubbo 协议设计之初只考虑到了常规的 rpc 调用场景,它并不是为跨语言而设计,但跨语言支持从来不是只有支持、不支持两种选择,而是要按难易程度来划分 。是的,dubbo 协议的跨语言调用可能并不好做,但并非无法实现 。千米网便实现了这一点,nodejs 构建的前端业务是异构语言的主战场 , 最终实现了 dubbo2.js , 打通了 nodejs 和原生 dubbo 协议 。作为本文第二部分的核心内容 , 重点介绍下我们使用 dubbo2.js 干了什么事 。
Dubbo协议报文格式
dubbo协议
dubbo协议
dubbo协议报文消息头详解:
magic:类似java字节码文件里的魔数,用来判断是不是 dubbo 协议的数据包 。魔数是常量 0xdabb
flag:标志位, 一共8个地址位 。低四位用来表示消息体数据用的序列化工具的类型(默认 hessian),高四位中,第一位为 1 表示是 request 请求,第二位为 1 表示双向传输(即有返回 response),第三位为 1 表示是心跳 ping 事件 。
status:状态位, 设置请求响应状态,dubbo 定义了一些响应的类型 。具体类型见com.alibaba.dubbo.remoting.exchange.Response
invoke id:消息 id, long 类型 。每一个请求的唯一识别 id(由于采用异步通讯的方式,用来把请求 request 和返回的 response 对应上)
body length:消息体 body 长度, int 类型,即记录 Body Content 有多少个字节
body content:请求参数,响应参数的抽象序列化之后存储于此 。
协议报文最终都会变成字节,使用 tcp 传输,任何语言只要支持网络模块,有类似 Socket 之类的封装,那么通信就不成问题 。那,跨语言难在哪儿?以其他语言调用 java 来说,主要有两个难点:
ps:dubbo 协议通讯demo()
跨语言rpcgo的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于跨语言检测结果是什么、跨语言rpcgo的信息别忘了在本站进行查找喔 。
推荐阅读
- java恶搞表白代码,java编程表白
- SAP清账币别,sap中清账的逻辑是什么?
- 小说创作辅助器安卓版下载,小说创作辅助平台
- oracle怎么查库存 查看oracle库使用大小
- 如何营销清吧,清吧营销员应该怎么做
- 鸿蒙能上安卓游戏吗,鸿蒙可以玩游戏吗
- .net软件开发逻辑题,软件开发基础net
- 递归函数python 递归函数Python用集合定义自然数
- 电视屏幕倒着图像怎么调,电视屏幕倒过来了怎么恢复