原文链接:bbs.huaweicloud.com/blogs/41880…

翻开一个简略的REST接口:

@RestSchema(schemaId = "ProviderController")
@RequestMapping(path = "/")
public class ProviderController {
  @PostMapping("/benchmark")
  public DataModel sayHello(@RequestHeader("wait") int wait, @RequestBody DataModel dataModel) {
    if (wait > 0) {
      Thread.sleep(wait);
    }
    return dataModel;
  }
}

契约:

openapi: 3.0.1
info:
  title: swagger definition for org.apache.servicecomb.samples.ProviderController
  version: 1.0.0
servers:
- url: /
paths:
  /benchmark:
    post:
      operationId: sayHello
      parameters:
      - name: wait
        in: header
        required: true
        schema:
          type: integer
          format: int32
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DataModel'
          application/protobuf:
            schema:
              $ref: '#/components/schemas/DataModel'
          text/plain:
            schema:
              $ref: '#/components/schemas/DataModel'
        required: true
        x-name: dataModel
      responses:
        "200":
          description: response of 200
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DataModel'
            application/protobuf:
              schema:
                $ref: '#/components/schemas/DataModel'
            text/plain:
              schema:
                $ref: '#/components/schemas/DataModel'
components:
  schemas:
    ChildDataModel:
      type: object
      properties:
        numInt:
          type: integer
          format: int32
        numLong:
          type: integer
          format: int64
        numDouble:
          type: number
          format: double
        numFloat:
          type: number
          format: float
        data:
          type: string
      x-java-class: org.apache.servicecomb.samples.ChildDataModel
    DataModel:
      type: object
      properties:
        data:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/ChildDataModel'
      x-java-class: org.apache.servicecomb.samples.DataModel

可以看到 Java Chassis运用了Open API 3.0.1的协议标准,Request Bodyresponses添加了application/protobuftext/plain的支撑。 这种运用形式,奠定了Java Chassis 3多序列化支撑方法的基础,细心的读者应该很快可以发现这种方法与其他支撑多序列化框架之间的差异。

  • Spring Boot

支撑多序列化方法,需求完成HttpMessageConverter接口,并在Controller声明支撑的Content-Type。 假如运用Spring Fox或者Spring Doc等 Open API东西生成契约,契约内容只会包含Controller声明的序列化类型。 Spring Boot存在隐式的契约情况,这意味着契约并不能彻底代表Controller服务的能力。 假如需求对接口添加或者减少序列化支撑,都需求修正代码。

  • Dubbo

Dubbo 需求在 Provider 运用dubbo:protocol声明序列化方法, 在Consumer运用dubbo:reference声明序列化方法。 因为 Dubbo 是根据IDL的契约体系, 在运用 RPC 的场景下,可以经过配置动态调整序列化方法。REST支撑在 Dubbo 是彻底独立的单元, 序列化方法也独立于 RPC 接口,RPCREST不能互操作。

Open API根据REST的语义,来支撑IDL的语义。 Java Chassis可以更加直观的支撑经过第三方东西以HTTP协议族拜访微服务, 只需求按照契约的描绘结构HTTP的报文。 在编码侧, Java Chassis的客户端可以运用 REST 语义的接口,如RestOperations,也可以运用 RPC 语义的接口拜访服务端。

定义服务端接口:

public interface ProviderService {
  DataModel sayHello(int wait, DataModel dataModel);
}

经过RPC拜访:

@RestSchema(schemaId = "ConsumerController")
@RequestMapping(path = "/")
public class ConsumerController {
  @RpcReference(schemaId = "ProviderController", microserviceName = "provider")
  private ProviderService providerService;
  @PostMapping("/benchmark")
  public DataModel sayHello(@RequestHeader("wait") int wait, @RequestBody DataModel dataModel) {
    return providerService.sayHello(wait, dataModel);
  }
}

Java Chassis以OpenAPI为基础的Edge Service部件,可以完成恳求在通信协议、序列化方法上的自动转化。比方将HTTP协议转Highway协议、application/json转application/protobuf等。

根据Java Chassis Benchmark,做一个简略功能测验。该测验对比了两种场景:

Java Chassis 3技能解密:多种序列化方法支撑

场景一的默许配置:

servicecomb:
  rest:
    parameter:
      default-request-encoding: "application/json"
      default-response-encoding: "application/json"

场景二的默许配置:

servicecomb:
  rest:
    parameter:
      default-request-encoding: "application/protobuf"
      default-response-encoding: "application/protobuf"

测验结果参考下表。该数据首要用于阐明序列化差异,因而省去了测验环境的描绘。下表的均匀时延统计了测验客户端核算的恳求-响应时刻的均匀值。

版别 数据单位 等待时刻 线程数 执行次数 执行时刻 均匀时延
protobuffer 1 0 10 1000 6642 6
protobuffer 100 0 10 1000 9418 9
protobuffer 1000 0 10 1000 25205 24
protobuffer 1 10 10 1000 15432 15
protobuffer 100 10 10 1000 15965 15
protobuffer 1000 10 10 1000 25926 25
protobuffer 1 100 10 1000 105727 105
protobuffer 100 100 10 1000 106376 106
protobuffer 1000 100 10 1000 114452 114
jason 1 0 10 1000 6736 6
jason 100 0 10 1000 15063 14
jason 1000 0 10 1000 69757 68
jason 1 10 10 1000 16632 16
jason 100 10 10 1000 20033 19
jason 1000 10 10 1000 66104 65
jason 1 100 10 1000 104868 104
jason 100 100 10 1000 107439 107
jason 1000 100 10 1000 132786 131

从这组数据可以看出:

  • 在数据量比较小的场景下,运用jsonproto-buffer功能差异很小。 在数据量比较大的情况下,proto-buffer的功能显着好于json
  • 在事务时延比较大(>100ms)的时分, 序列化的时延可以忽略。

不同的序列化方法除了功能差异,在可保护方面也会存在很大的差异。比方proto-buffer在兼容性方面的表现会比json差,当修正接口定义的时分, 比方添加特点、删除特点、修正特点等,proto-buffer更容易导致兼容性问题,做好兼容性防范对大都用户而言,都是比较困难的事情。

支撑多协议、多序列化方法的别的一个考虑,是对接留传体系。对接留传体系会担负很多前史债务,使得新体系本身规划违背预期的方向。在 Java Chassis 多序列化方法的挑选上, 只提供了目前广泛运用的jsonproto-buffer支撑, 而没有挑选支撑其他序列化计划。 以架构的耐性去处理留传体系问题,是 Java Chassis坚持的一个重要规划理念,对接留传体系或者保持与留传体系的兼容,不是它的首要规划方针。

Java Chassis 3技能解密:多种序列化方法支撑

在序列化方法挑选上,简略的总结如下:

  • 运用REST协议是绝大大都场景的最优挑选,可以最好的兼顾功能、可靠性、耐性等方面的要求。
  • 关于数据量比较大,事务时延很低(<100ms),并且事务比较稳定,事务接口不需求频频变动的场景,可以选用proto-buffer来优化功能,按需调整。

客户故事:某个客户的关键中心体系关于时延要求很高,因而需求选用私有协议和序列化方法来提高功能。可是关于一些非中心体系,需求运用REST接口,方便日常开发、调试。Java Chassis的解耦规划使得客户无需对代码进行任何改造,就可以满意两方面的要求。