微服务框架学习二:Http调用

1. HTTP接口的意义前端

二进制接口使用的是java/hessian序列化协议,不能很好的与其余语言通讯,虽然hessian也是一种跨语言的通用协议,但不少语言没有很好的实现该协议的产品。
因此为了可以与其余语言进行服务通讯,咱们实现了http + json的协议实现,利用json原生的跨语言的特性。java

 

2. 原理简图json

描述:经过Netty暴露http服务端口,接收到http请求,经过HttpDecoder将其解析为HttpRequest,经过JSONDecoder提取service请求信息,生成Request请求对象,从而adapt到binary协议实现,交由request processor处理,返回Response结果,最终经过JSONEncoder编码为JSON格式数据,再经过HttpEncoder生成HttpResponse返回给Http接口调用方。数组

 

3. 协议格式安全

假定:有服务暴露接口ExampleService,服务名称:http://service.huifu.com/ExampleService/exampleService_1.0.0,定义了服务方法public String sayHello(String message),则HTTP访问协议描述以下:框架

请求协议:eclipse

Name Value Description
URL http://{machine-ip}:{http-port}/rpc.json HTTP服务的访问地址,注意http端口号为pegasus端口号+1000,即:pegasus配置为8888,http端口就是9888
Method POST 访问方式,推荐使用POST方式
Encoding UTF-8 HTTP请求编码
Params _service

http://service.huifu.com/ExampleService/exampleService_1.0.0maven

访问的服务名称
_method

sayHello测试

访问的服务接口方法名称
_param {"message" : "kitty"}

传递到接口方法的参数值,使用json格式,形如:{"param1" : "value1", "param2" : {"f1" : 1, "f2" : "vvv"}},其中param1,param2为方法的参数名称,param1为string类型,param2为javabean类型,f1和f2为该javabean的属性名。ui

如:调用方法为sayHello(String message),则对应json为{"message":"kitty"}。若是调用方法为:sayHello(RequestDTO userDTO),RequestDTO中有一个属性为name,则对应的json为{"userDTO":{"name":"kitty"}}。

响应协议:

Name Value Description
格式 {"type" : "xxx", "result" : "xxx", "error" : "xxx"} HTTP服务接口的响应内容为JSON格式
type "service" | "service-exception" | "exception"

"service":业务处理正常返回
"service-exception":业务处理抛出异常
"exception":框架处理抛出异常 

result type = "service"时,服务方法的返回值,为JSON格式

若方法返回值为简单类型:
"result" : "hello world"
若为复杂类型:
"result" : "{"f1" : "v1", "f2" : "v2"}" 

error

type = "service-exception" | "exception"时,返回的错误信息,为JSON格式

形如{"type" : "com.huifu.xx.BizException", "message" : "order_no is not supported"}
 
 
4. 注意事项
  1. 关于服务方法参数名
    默认状况下,pegasus经过asm读取服务实现类的方法签名信息,获取方法的参数名,但因重构或其余缘由,该方法的参数名有可能被修改,从而致使原有的http接口使用方没法正常调用;
    因此推荐在有可能提供http访问的服务方法上经过@Param注解定义参数名,这样就能够防止参数名被修改的状况,如void sayHello(@Param("message") String message),此时该方法就能够安全地重构为void sayHello(@Param("message") String msg),而不影响原有调用方;
    注意:在使用的过程当中发现,经过javassist获取参数名,若是使用javac编译器编译的class文件(maven默认使用),可能会出现没法读取参数名或读取错误; 咱们遇到的几个问题case改用eclipse的ecj编译器后均可以fix,但咱们没法去测试覆盖全部的代码case,所以若方法涉及到http调用时,请使用@Param的方式显式提供参数名; 
    btw: maven使用ecj做为编译器的详细配置以下 (1.0.2中已经使用asm替换javassist,该问题已FIXED) 

  2. 关于服务传输对象(DTO)的定义
    因为使用json格式传递方法参数值,就牵扯到json ==> javabean的转换过程,而这个过程要求能明确地知道全部java对象及其属性的类型,因此在建立服务协议DTO对象时,若是这个对象牵扯到http方式的调用,那么就须要明肯定义其类型,好比集合类型或数组,必须有明确的元素类型信息,因此Map, List, Set, Object[]都是没法转换的,必须是Map<String, String>, List<Order>, Set<Integer>, Order[]等有明确的元素类型声明的集合类和数组,即混合类型的集合http方式不支持;
    因为json对象没法表示对象引用,因此在定义DTO时,应避免引用的方式,好比parent下定义了list<child>,child中又定义一个parent引用刚才的parent对象,这种方式不容许。
    因为json对象的key必定是string类型,因此对于DTO中的Map,只能是Map<String, xx>, Map<Integer, xx>, Map<BigDecimal, xx>, Map<Boolean, xx>,Map<Date, xx>这样的Map类型,而Map<JavaBean, xx>这种类型则没法经过http接口调用,由于json没法表示这样的key。json转map:{"k1", "v1"} => map {"k1", "v1"}。
    对于有Map<JavaBean, JavaBean>这种类型的dto,若是又须要经过http方式访问,那么请重构该map类型为List方式,好比List<MapBean>, MapBean中有JavaBean的key和JavaBean类型的value属性,即经过List<Entry>的方式实现Map,此时能够经过json表示,即[{"key1" : {xxx}, "value1" : {xxx}}, {"key2" : {xxx}, "value2" : {xxx}}]。
  3. 关于客户端调用能够按照正常的http方式访问服务接口,因为目前各语言尚未本身封装的客户端,因此集群方面只能经过前端架设软负载的方式实现,后期考虑各语言封装本身的客户端,内部维护到各服务提供者的http长链接,并按期地经过服务注册中心的http接口更新服务提供者列表信息,搭配按期心跳探测移除不可用的链接,以便最终摒弃软负载。经过http方式访问服务接口时,须要在客户端设置http请求的超时时间,以便在超过设定的超时时间后,客户端会当即抛出超时异常,并中断此次请求,不然会一直等待服务端返回,一旦服务端业务逻辑处理僵死,将没法返回http响应,并一直维持该http链接,形成资源没法释放;
相关文章
相关标签/搜索