zoukankan      html  css  js  c++  java
  • 一般只用 20% 的代码就可以解决 80% 的问题。但要想解决剩下 20% 的问题的话,则需要额外 80% 的代码。

    sniper/thought.md at master · bilibili/sniper https://github.com/bilibili/sniper/blob/master/thought.md

    RPC 协议

    有了语言,接下来就要确定通信协议。首先不要使用 REST 风格接口。 REST 中看不中用。REST 的核心是资源和状态,所有的变更都对应状态的转变。

    对于简单的场景,REST 看似完美,如:GET /user/123 表示查询。

    但如果是发送一条短信呢?一种方案是使用 POST /sms 表示创建一条短信资源,另一种方案则是 POST /sms:send 直接发送。

    但不管哪种方式,都不如 RPC 调用直观,其原因有二:

    • 一是 http 的方法(GET, POST, PUT, DELETE 等)太少,基本都是面向静态资源的,表达能力有限
    • 二是将业务过程转成资源状态变化本身就比较烧脑,而且存在无法转化的场景

    REST 还有一个比较大的问题就是 url 中有数字 id,统计 prometheus 监控指标的时候必须做归一化处理。

    所以,不用 REST。

    Weisai RPC

    这得从原来在 L 部门用的 Weisai-RPC 说起。该 RPC 基于 TCP 传输,消息结构如下:

    typedef struct swoole_message {
        uint32_t header_magic;     // magic 字段 默认2233
        uint32_t header_ts;        // unix时间戳
        uint32_t header_check_sum; // 校验和, 暂未定义, 默认为0
        uint32_t header_version;   // 版本号
        uint32_t header_reserved;  // 保留字段, 默认0, live-api转发时设置为1
        uint32_t header_seq;       // 序列号
        uint32_t header_len;       // body长度
        char cmd[32];              // 命令字符串
                                   // 格式 {message_type}controller.method,
                                   // message_type 0 request, 1 response
                                   // 长度没满右端补充, 超过自动右端截断.
        char* body;                // 可变 长度为header_len 格式为JSON:
                                   // {"header":..., "body":....}
    } rpc_message_t;

    典型的面向 c 语言的设计,方便 c 语言解析,但不太灵活。

    比如,cmd 字段只有 32 字节,也就是说接口名字最多只能是 32 字节。还有 body 是字符串,但实际传输的是 JSON,需要二次解析。使用结构化二进制消息就是为了提高解析速度,但这种改进跟 JSON 解码相比又可以忽略。所以,这种混合型的设计除了看上去比较复杂以外,确实没什么优点了。

    因为没有采用 HTTP 协议,后来不得不在 body 中定义了 header 字段用来传输 HTTP 请求的 header。像 nginx, curl, tcpdump 这样的标准也基本上无法正常使用。为此,还专门引入了一个接入层负责 RPC 和 HTTP 之间的相互转换。

    切实体会到了 Weisai-RPC 的不便之后,我决定业务 RPC 协议只用 HTTP 传输,原则上不使用二进制消息格式。

    关于 gRPC

    说到 HTTP 就不得不说说 gRPC。gRPC 是 Google 开放的一种 RPC 协议,其主要特性:

    • 只支持 protobuf 编码
    • 强依赖 HTTP2 协议
    • 支持 stream 接口
    • 每个消息都有五字节的二进制前缀 其他细节请参考 PROTOCOL-HTTP2

    protobuf 本身是支持 JSON 的,不明白为什么 gRPC 的实现不支持。而支持 stream 接口则是 gRPC 的一大特色,使 gRPC 能够胜任诸如语音实时识别等场景。但这一类场景是比较少见的。我们绝大多数业务场景都是一问一答的。为了实现这个 stream 特性,gRPC 不得不依赖 HTTP2,不得不自行定义了一种有固定五字节头的消息格式。与此同时,gRPC 也就放弃了 HTTP 协议原生的压缩功能,也没法使用 HTTP 协议的 content-length 头传递消息长度。这也是 gRCP 消息五字节头的功能所在,头一个字节表示是否压缩,后四个字节表示消息长度。

    有个所谓的 2-8 原则:

    一般只用 20% 的代码就可以解决 80% 的问题。但要想解决剩下 20% 的问题的话,则需要额外 80% 的代码。

    gRPC 的 stream 接口就是剩下的 20% 的问题。

    gRPC 还有个 web 支持的问题。浏览器的 js 无法使用 HTTP2 的特性,所以不能直接与 gRPC 服务通信。于是有了 grpc-web,还有 grpc-gateway

    所以,如果没有 stream 接口需求,则完全没有必要使用 gRPC;如果真的有这类需求,也不可能太多,直接使用原生 TCP/WebSocket 协议开发也不是难事。

    最终我们选择了 twirp。twirp 可以看作是简化版的 gRPC,同样用 protobuf 描述,不依赖 HTTP2,同时支持 protobuf 和 JSON,没有五字节的二进制前缀。但我们对原生的 twirp 做了修改,形成了自己的版本,主要改动就是添加了对 www-form-urlencoded 编码格式的支持,这是移动端的历史包袱导致的,没办法。

    现在的移动端使用 www-form-urlencoded 编码,更加简单;管理后台使用 JSON 编码,更加灵活。如果对性能有要求也可以使用 protobuf 编码,但没目前没有用,估计也不会有人喜欢用。

  • 相关阅读:
    Docker
    dcoker-componse-2
    MyBatis的基本使用
    SpringMVC实现文件上传和下载
    CF817E Choosing The Commander
    CSP 2020 游记
    COCI2014-2015 Contest#1 题目选做
    CF590D Top Secret Task
    LuoguP1937 [USACO10MAR]Barn Allocation G
    CF741C Arpa’s overnight party and Mehrdad’s silent entering
  • 原文地址:https://www.cnblogs.com/rsapaper/p/14096380.html
Copyright © 2011-2022 走看看