背景
在平常的业务开发中遇到了两个场景:
1.由于业务用的rpc框架是thrift,代码也是都是用thrift在写,有一天突然接到个需要前端要用http访问接口的需求,于是花了几天时间把所有的thrift接口又用Controller封装一层。由于跨语言,且对方不使用thrift,就需要你提供Http接口
2.写完thrift为了自测,需要再写个TestController验证代码是否正确,整个流程是否跑通,非常麻烦。
这两个场景大家遇到的比较多,所以要是能一写完thrift接口就能直接转换为http接口,那样就好了。
放眼整个互联网中,在互联网快速迭代的大潮下,越来越多的公司选择nodejs、django、rails这样的快速脚本框架来开发web端应用,而对于我们来说公司选择的后端语言是Java,这就产生了大量的跨语言的调用需求。其实对于thrift来说是支持很多语言的,但是给每次给其他语言开发都需要开发对应的客户端,并且还有很多rpc框架并不是像thrift一样支持这么多语言的,所以现在微服务都推出了service mesh(http://www.servicemesh.cn/),但是这个依然很新,有需要尝试的其实可以起尝试一下。http、json是天然合适作为跨语言的标准,各种语言都有成熟的类库,所以如何把像thrift这种tcp rpc框架转换成http,对于多语言支持是比较重要的。
RESTful or JSONRPC
RESTful
最开始想的是如何把thrift接口映射成RESTful,因为这个更加符合互联网http的标准,但是TCP rpc 对比RESTful有根本的区别,RESTful的核心是资源,并且利用Http协议中的各种方法GET,POST,OPTION等等对资源进行操作,如果想把thrift每个接口一一映射上,这个难度有点大,毕竟两个产生不出来任何关联,这个时候就需要每个接口进行配置映射,起成本不亚于我重写一套Controller了,所以RESTful这个方案基本被否决了。
JSONRPC
JSON-RPC是一个无状态且轻量级的远程过程调用(RPC)协议。它允许运行在基于socket,http等诸多不同消息传输环境的同一进程中。
JSONRPC本质上也是个RPC,定位和thrfit类似,不需要进行过多的协议映射。所以我们选择了使用JSONRPC,进行Http的转换。
JSONRPC请求对象
发送一个请求对象至服务端代表一个rpc调用, 一个请求对象包含下列成员:
jsonrpc
指定JSON-RPC协议版本的字符串,必须准确写为“2.0”
method
包含所要调用方法名称的字符串,以rpc开头的方法名,用英文句号(U+002E or ASCII 46)连接的为预留给rpc内部的方法名及扩展名,且不能在其他地方使用。
params
调用方法所需要的结构化参数值,该成员参数可以被省略。
id
已建立客户端的唯一标识id,值必须包含一个字符串、数值或NULL空值。如果不包含该成员则被认定为是一个通知。该值一般不为NULL[1],若为数值则不应该包含小数[2]。
服务端必须回答相同的值如果包含在响应对象。 这个成员用来两个对象之间的关联上下文。
[1] 在请求对象中不建议使用NULL作为id值,因为该规范将使用空值认定为未知id的请求。另外,由于JSON-RPC 1.0 的通知使用了空值,这可能引起处理上的混淆。
[2] 使用小数是不确定性的,因为许多十进制小数不能精准的表达为二进制小数。
通知
没有包含“id”成员的请求对象为通知, 作为通知的请求对象表明客户端对相应的响应对象并不感兴趣,本身也没有响应对象需要返回给客户端。服务端必须不回复一个通知,包含那些批量请求中的。
由于通知没有返回的响应对象,所以通知不确定是否被定义。同样,客户端不会意识到任何错误(例如参数缺省,内部错误)。
参数结构
rpc调用如果存在参数则必须为基本类型或结构化类型的参数值,要么为索引数组,要么为关联数组对象。
索引:参数必须为数组,并包含与服务端预期顺序一致的参数值。
关联名称:参数必须为对象,并包含与服务端相匹配的参数成员名称。没有在预期中的成员名称可能会引起错误。名称必须完全匹配,包括方法的预期参数名以及大小写。
响应对象
当发起一个rpc调用时,除通知之外,服务端都必须回复响应。响应表示为一个JSON对象,使用以下成员:
jsonrpc
指定JSON-RPC协议版本的字符串,必须准确写为“2.0”
result
该成员在成功时必须包含。
当调用方法引起错误时必须不包含该成员。
服务端中的被调用方法决定了该成员的值。
error
该成员在失败是必须包含。
当没有引起错误的时必须不包含该成员。
该成员参数值必须为5.1中定义的对象。
id
该成员必须包含。
该成员值必须于请求对象中的id成员值一致。
若在检查请求对象id时错误(例如参数错误或无效请求),则该值必须为空值。
响应对象必须包含result或error成员,但两个成员必须不能同时包含。
错误对象
当一个rpc调用遇到错误时,返回的响应对象必须包含错误成员参数,并且为带有下列成员参数的对象:
code
使用数值表示该异常的错误类型。 必须为整数。
message
对该错误的简单描述字符串。 该描述应尽量限定在简短的一句话。
data
包含关于错误附加信息的基本类型或结构化