【Go 语言框架与实现 学习资料】第三届字节跳动青训营 - 后端专场

语言: CN / TW / HK

theme: juejin

第三届字节跳动青训营讲师非常用心给大家整理了课前、中、后的学习内容,同学们自我评估,选择性查漏补缺,便于大家更好的跟上讲师们的节奏,祝大家学习愉快,多多提问交流~

第十三节:深入浅出 RPC 框架

概述

本节课程主要分为四个方面:

  1. RPC 相关的基本概念
  1. RPC 框架的分层设计
  1. 衡量 RPC 框架的一些核心指标
  1. 字节内部 RPC 框架 Kitex 实践分享

课前部分主要罗列课程中涉及到的概念。对于不熟悉的概念,同学们可以提前查询预习;

课中部分主要罗列每一部分的关键思路,帮助同学们跟上课程的进度;

课后部分是一些问题,帮助同学们在课后梳理本课程的重点。

课前

RPC 的基本概念

  • IDL(Interface Definition Language) 文件

    • Thrift
    • Protobuf
  • 生成代码
  • 编解码(序列化/反序列化)
  • 通信协议

    • 应用层协议
  • 网络通信

    • IO 网络模型

      • blocking IO
      • unblocking IO
      • IO multiplexing
      • signal driven IO
      • asynchronous IO
    • 传输层协议

      • TCP
      • UDP

RPC 框架分层设计

  • 编解码层

    • 数据格式:

      • 语言特定格式

      • 文本格式

      • 二进制编码

        • TLV 编码:Thrift 使用 TLV 编码
        • Varint 编码:Protobuf 使用 Varint 编码
    • 选项:

      • 兼容性
      • 通用型
      • 性能
  • 传输协议层

    • 消息切分

      • 特殊结束符
      • 变长协议:length+body
    • 协议构造

      • 以 Thrift 的 THeader 协议为例讲解
  • 网络通信层

    • 网络库

    • 核心指标

      • 吞吐高
      • 延迟低

RPC 框架的核心指标

  • 稳定性

    • 保障策略

      • 熔断
      • 限流
      • 超时
    • 请求成功率

      • 负载均衡
      • 重试
    • 长尾请求

      • BackupRequest
  • 易用性

    • 开箱即用
    • 周边工具
  • 扩展性
  • 观测性

    • Log
    • Metric
    • Tracing
    • 内置观测性服务
  • 高性能

字节内部 Kitex 实践分享

  • 合并部署

课中

基本概念

  • 相比本地函数调用,RPC调用需要解决的问题

    • 函数映射
    • 数据转换成字节流
    • 网络传输
  • 一次 RPC 的完整过程
  • RPC 带来的问题将由 RPC 框架来解决

    • 服务宕机如何感知?
    • 遇到网络异常应该如何应对?
    • 请求量暴增怎么处理?

RPC 框架分层设计

编解码层

  • 数据格式

    • 语言特定格式:例如 java.io.Serializable
    • 文本格式:例如 JSON、XML、CSV 等
    • 二进制编码:常见有 Thrift 的 BinaryProtocol,Protobuf,实现可以有多种形式,例如 TLV 编码 和 Varint 编码
  • 选型考察点

    • 兼容性

    • 通用型

    • 性能

      • 空间开销
      • 时间开销
  • 生成代码和编解码层相互依赖,框架的编解码应当具备扩展任意编解码协议的能力

协议层

  • 以 Thrift 的 THeader 协议为例

    - LENGTH 字段 32bits,包括数据包剩余部分的字节大小,不包含 LENGTH 自身长度 - HEADER MAGIC 字段16bits,值为:0x1000,用于标识 协议版本信息,协议解析的时候可以快速校验 - FLAGS 字段 16bits,为预留字段,暂未使用,默认值为 0x0000 - SEQUENCE NUMBER 字段 32bits,表示数据包的 seqId,可用于多路复用,最好确保单个连接内递增 - HEADER SIZE 字段 16bits,等于头部长度字节数/4,头部长度计算从第14个字节开始计算,一直到 PAYLOAD 前(备注:header 的最大长度为 64K) - PROTOCOL ID 字段 uint8 编码,取值有: - ProtocolIDBinary = 0 - ProtocolIDCompact = 2 - NUM TRANSFORMS 字段 uint8 编码,表示 TRANSFORM 个数 - TRANSFORM ID 字段 uint8 编码,表示压缩方式 zlib or snappy - INFO ID 字段 uint8 编码,具体取值参考下文,用于传递一些定制的 meta 信息 - PAYLOAD 消息内容

  • 协议解析

网络通信层

  • 阻塞 IO 下,耗费一个线程去阻塞在 read(fd) 去等待用足够多的数据可读并返回。
  • 非阻塞 IO 下,不停对所有 fds 轮询 read(fd) ,如果读取到 n <= 0 则下一个循环继续轮询。

第一种方式浪费线程(会占用内存和上下文切换开销),第二种方式浪费 CPU 做大量无效工作。而基于 IO 多路复用系统调用实现的 Poll 的意义在于将可读/可写状态通知和实际文件操作分开,并支持多个文件描述符通过一个系统调用监听以提升性能。
网络库的核心功能就是去同时监听大量的文件描述符的状态变化(通过操作系统调用),并对于不同状态变更,高效,安全地进行对应的文件操作。

RPC 框架核心指标

稳定性

  • 保障策略

    • 熔断
    • 限流
    • 超时控制

从某种程度上讲超时、限流和熔断也是一种服务降级的手段 。

  • 请求成功率

    • 负载均衡
    • 重试
  • 长尾请求

    • BackupRequest

易用性

  • 开箱即用

    • 合理的默认参数选项、丰富的文档
  • 周边工具

    • 生成代码工具、脚手架工具

扩展性

  • Middleware:middleware 会被构造成一个有序调用链逐个执行,比如服务发现、路由、负载均衡、超时控制等
  • Option:作为初始化参数
  • 核心层是支持扩展的:编解码、协议、网络传输层
  • 代码生成工具也支持插件扩展

观测性

  • 三件套:Log、Metric 和 Tracing

  • 内置观测性服务,用于观察框架内部状态

    • 当前环境变量
    • 配置参数
    • 缓存信息
    • 内置 pprof 服务用于排查问题

高性能

  • 连接池和多路复用:复用连接,减少频繁建联带来的开销
  • 高性能编解码协议:Thrift、Protobuf、Flatbuffer 和 Cap'n Proto 等
  • 高性能网络库:Netpoll 和 Netty 等

字节内部 Kitex 实践分享

  1. 框架文档 Kitex
  1. 自研网络库 Netpoll,背景:

    a. 原生库无法感知连接状态
    b. 原生库存在 goroutine 暴涨的风险

  1. 扩展性:支持多协议,也支持灵活的自定义协议扩展
  1. 性能优化,参考 字节跳动 Go RPC 框架 KiteX 性能优化实践

    a. 网络优化

    i.  调度优化  
    ii.  LinkBuffer 减少内存拷贝,从而减少 GC  
    iii.  引入内存池和对象池
    

    b. 编解码优化

    i.  Codegen:预计算提前分配内存,inline,SIMD等
    ii.  JIT:无生产代码,将编译过程移到了程序的加载(或首次解析)阶段,可以一次性编译生成对应的 codec 并高效执行
    
  1. 合并部署

    a. 微服务过微,引入的额外的传输和序列化开销越来越大
    b. 将强依赖的服务统计部署,有效减少资源消耗

课后

  1. 行业内各个流行的 RPC 框架的优劣对比
  1. 从第三章节 RPC 的核心指标来看,Kitex 还有哪些功能是欠缺或者需要加强的?
  1. 了解微服务的新趋势 ServiceMesh,以及 RPC 框架和 ServiceMesh 的关系
  1. 关于 RPC 框架,业界有哪些新的趋势和概念?
  1. Netpoll 的优势在哪?相比其他高性能网络库例如 Netty 还有什么不足?
  1. Flatbuffer 和 Cap'n Proto 等编解码协议为什么高性能?

参考文献

  1. 官方文档 Kitex Netpoll
  1. 字节跳动 Go RPC 框架 KiteX 性能优化实践_架构_字节跳动技术团队_InfoQ精选文章
  1. 字节跳动微服务架构体系演进_架构_字节跳动技术团队_InfoQ精选文章

第十四节:HTTP 框架修炼之道

概述

本节课程主要分为四个方面:

  1. HTTP 协议相关知识
  1. HTTP 框架的设计与实现
  1. HTTP 框架的优化手段
  1. 企业实践

课前部分主要罗列课程中涉及到的概念。对于不熟悉的概念,同学们可以提前查询预习;课后部分是一些问题,帮助同学们在课后梳理本课程的重点。

课前

HTTP 协议

  • HTTP 协议出现背景
  • HTTP 协议是什么
  • HTTP 协议有什么

可参考百度百科

尝试写一个 hello world 服务器

可尝试用 gin 写一个 hello world 程序,达到以下效果

HTTP 框架中常见概念

  • 框架路由:根据请求的 URI 选择对应的处理函数。

    • 首先匹配 HTTP 方法

    • 静态路由: 精确匹配注册的路由,如:/a/b/c、/a/b/d

    • 参数路由:

      • 命名参数:形如 :name这类叫做命名参数,命名参数只匹配单个路径段:
      • ``` Pattern: /user/:user

        /user/gordon match(user = gordon) /user/you match(user = you) /user/gordon/profile no match /user/ no match - 通配参数:形如 **`*action`**这类叫做通配参数,就像名字所暗示的那样,它们匹配所有内容。因此,它们必须始终位于模式的末尾: - Pattern: /src/*filepath

        /src/ match(filepath = "") /src/somefile.go match(filepath = somefile.go) /src/subdir/somefile.go match(filepath = subdie/somefile.go) ```

    • 路由修复: 如果只注册了 /a/b,但是访问的 URI 是 /a/b/,那可以提供自动重定向到 /a/b 能力;同样,如果只注册了 /a/b/,但是访问的 URI 是 /a/b,那可以提供自动重定向到 /a/b/ 能力

    • 冲突路由:同时注册 /a/b 和 /:id/b,并设定优先级。比如:当请求 URI 为 /a/b 时,优先匹配静态路由 /a/b

  • 什么是框架中间件,可参考 ginkratos

Golang

网络库

  • C10K Problem
  • Select,Poll,Epoll
  • Epoll ET、LT 区别

SIMD

课后作业

  1. 为什么 HTTP 框架做要分层设计?分层设计有哪些优势与劣势。
  1. 现有开源社区 HTTP 框架有哪些优势与不足。
  1. 中间件还有没有其他实现方式?可以用伪代码说明。
  1. 完成基于前缀路由树的注册与查找功能?可以用伪代码说明。
  1. 路由还有没有其他的实现方式?

第十五节:微服务架构原理与治理实践

概述

本课程内容主要分为以下4个方面:

  • 微服务架构介绍

    • 微服务架构的背景由来、架构概览、基本要素
  • 微服务架构原理及特征

    • 微服务架构的基本组件、工作原理、流量特征
  • 核心服务治理功能

    • 核心的服务治理功能,包括流量治理、服务均衡、稳定性治理
  • 字节跳动服务治理实践

    • 字节跳动在微服务架构稳定性治理中,对请求重试策略的探索及实践

为了帮助大家更好地预习及理解本节课程,该学员手册列出了课前、课中、及课后这三个阶段所涉及到的专业内容大纲,其中课前部分供同学们提前预习参考,课中部分给出了课程大纲,帮助同学们整理思路,课后部分列出一些扩展性的问题让同学们进一步延伸思考。

课前

微服务架构介绍

  • 系统架构的演进历史

    • 单体架构
    • 垂直应用架构
    • 分布式架构
    • SOA架构
    • 微服务架构
  • 微服务架构的三大要素

    • 服务治理
    • 可观测性
    • 安全

微服务架构原理及特征

  • 微服务架构中的基本概念及组件

    • 服务、实例......
  • 服务间通信

    • RPC、HTTP
  • 服务注册及服务发现

核心服务治理功能

  • 服务发布

    • 蓝绿部署
    • 灰度发布(金丝雀发布)
  • 流量治理
  • 负载均衡

    • Round Robin
    • Ring Hash
    • Random
  • 稳定性治理

    • 限流
    • 熔断
    • 过载保护
    • 降级

字节跳动服务治理实践

  • 请求重试的意义
  • 请求重试的难点

课中

微服务架构介绍

系统架构的演进历史

  • 单体架构

    • All in one process
  • 垂直应用架构

    • 按照业务线垂直划分
  • 分布式架构

    • 抽出与业务无关的公共模块
  • SOA架构

    • 面向服务
  • 微服务架构

    • 彻底的服务化

微服务架构概览

  • 网关
  • 服务配置和治理
  • 链路追踪和监控

微服务架构的三大要素

  • 服务治理(本课程内容)

    • 服务注册
    • 服务发现
    • 负载均衡
    • 扩缩容
    • 流量治理
    • 稳定性治理
  • 可观测性

    • 日志采集
    • 日志分析
    • 监控打点
    • 监控大盘
    • 异常报警
    • 链路追踪
  • 安全

    • 身份验证
    • 认证授权
    • 访问令牌
    • 审计
    • 传输加密
    • 黑产攻击

微服务架构原理及特征

微服务架构中的基本概念及组件

  • 服务

    • 一组具有相同逻辑的运行实体
  • 实例

    • 一个服务中的每个运行实体
  • 实例与进程的关系

    • 没有必然对应关系,一般一对一或者一对多
  • 常见的实例承载形式

    • 进程、VM、k8s pod......

服务间通信

  • 微服务之间通过网络进行通信
  • 常见的通信协议包括 HTTP、RPC

服务注册及服务发现

  • 基本问题

    • 服务间调用中,如何指定下游服务实例的地址?
  • 简单方案

    • 直接指定 ip:port?

      • 没有任何动态能力
      • 有多个实例下游实例怎么办?
    • 使用 DNS?

      • 本地 DNS 存在缓存,导致延迟
      • DNS 没有负载均衡
      • 不支持服务探活检查
      • DNS 不能指定端口
  • 服务注册发现

    • 新增一个统一的服务注册中心,用于存储服务名到服务实例之间的映射关系
    • 旧服务实例下线前,从服务注册中心删除该实例,下线流量
    • 新服务实例上线后,在服务注册中心注册该实例,上线流量
  • 微服务流量特征

    • 统一网关入口
    • 外网通信多数采用 HTTP,内网通信多数采用 RPC(Thrift, gRPC)

核心服务治理功能

服务发布

  • 何为服务发布

    • 让一个服务升级运行新的代码的过程
  • 服务发布难点

    • 服务不可用
    • 服务抖动
    • 服务回滚
  • 蓝绿部署

    • 将服务分成两个部分,分别先后发布
    • 简单、稳定
    • 但需要两倍资源
  • 灰度发布(金丝雀发布)

    • 先发布少部分实例,接着逐步增加发布比例
    • 不需要增加资源
    • 回滚难度大,基础设施要求高

流量治理

  • 流量控制

    • 在微服务架构中,可以从各个维度对端到端的流量在链路上进行精确控制
  • 控制维度

    • 地区维度
    • 集群维度
    • 实例维度
    • 请求维度

负载均衡

  • Round Robin
  • Random
  • Ring Hash
  • Least Request

稳定性治理

  • 限流

    • 限制服务处理的最大 QPS,拒绝过多请求
  • 熔断

    • 中断请求路径,增加冷却时间从而让故障实例尝试恢复
  • 过载保护

    • 在负载高的实例中,主动拒绝一部分请求,防止实例被打挂
  • 降级

    • 服务处理能力不足时,拒绝低级别的请求,只响应线上高优请求

字节跳动服务治理实践

  • 请求重试的意义

    • 本地函数调用

      • 通常没有重试意义
    • 远程函数调用

      • 网络抖动、下游负载高、下游机器宕机......
      • 重试是有意义的,可以避免偶发性的错误,提高 SLA
    • 重试的意义

      • 降低错误率
      • 降低长尾延时
      • 容忍暂时性错误
      • 避开下游故障实例
  • 请求重试的难点

    • 幂等性

      • POST 请求可以重试吗?
    • 重试风暴

      • 随着调用链路的增加,重试次数呈指数级上升
    • 超时设置

      • 假设调用时间一共1s,经过多少时间开始重试?
  • 重试策略

    • 限制重试比例

      • 设定一个重试比例阈值(例如 1%),重试次数占所有请求比例不超过该阈值
    • 防止链路重试

      • 返回特殊的 status code,表示“请求失败,但别重试”
    • Hedged Requests

      • 对于可能超时(或延时高)的请求,重新向另一个下游实例发送一个相同的请求,并等待先到达的响应
  • 重试效果验证

    • 字节跳动重试组件能够极大限制重试发生的链路放大效应

课后

  1. 结合 CAP 等原理,思考微服务架构有哪些缺陷?
  1. 微服务是否拆分得越“微”越好?为什么?
  1. Service Mesh 这一架构是为了解决微服务架构的什么问题?
  1. 有没有可能有这样一种架构,从开发上线运维体验上是微服务,但实际运行又类似单体服务?

参考文献

  1. A Design Analysis of Cloud-based Microservices Architecture at Netflix
  1. 字节跳动微服务架构体系演进
  1. 微服务架构的一知半解