第三届字节跳动青训营讲师非常用心给大家整理了课前、中、后的学习内容,同学们自我评估,选择性查漏补缺,便于大家更好的跟上讲师们的节奏,祝大家学习愉快,多多提问交流~
第十三节:深入浅出 RPC 框架
概述
本节课程主要分为四个方面:
RPC 相关的基本概念
RPC 框架的分层设计
衡量 RPC 框架的一些核心指标
字节内部 RPC 框架 Kitex 实践分享
课前部分主要罗列课程中涉及到的概念。对于不熟悉的概念,同学们可以提前查询预习;
课中部分主要罗列每一部分的关键思路,帮助同学们跟上课程的进度;
课后部分是一些问题,帮助同学们在课后梳理本课程的重点。
课前
RPC 的基本概念
RPC的概念模型:User、User-Stub、RPC-Runtime、Server-Stub、Server
来自论文《Implementing Remote Procedure Calls》
IDL(Interface Definition Language) 文件
Thrift
Protobuf
生成代码
编解码(序列化/反序列化)
通信协议
应用层协议
网络通信
TCP
UDP
blocking IO
unblocking IO
IO multiplexing
signal driven IO
asynchronous IO
IO 网络模型
传输层协议
RPC 框架分层设计
编解码层
兼容性
通用型
性能
语言特定格式
文本格式
二进制编码
TLV 编码:Thrift 使用 TLV 编码
Varint 编码:Protobuf 使用 Varint 编码
数据格式:
选项:
传输协议层
以 Thrift 的 THeader 协议为例讲解
特殊结束符
变长协议:length+body
消息切分
协议构造
网络通信层
吞吐高
延迟低
网络库
核心指标
RPC 框架的核心指标
稳定性
BackupRequest
负载均衡
重试
熔断
限流
超时
保障策略
请求成功率
长尾请求
易用性
开箱即用
周边工具
扩展性
观测性
Log
Metric
Tracing
内置观测性服务
高性能
字节内部 Kitex 实践分享
Kitex 整体架构
自研网络库 Netpoll
性能优化:
网络库优化
编解码优化
合并部署
课中
基本概念
相比本地函数调用,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 实践分享
框架文档 Kitex
自研网络库 Netpoll,背景:
a. 原生库无法感知连接状态 b. 原生库存在 goroutine 暴涨的风险
扩展性:支持多协议,也支持灵活的自定义协议扩展
性能优化,参考 字节跳动 Go RPC 框架 KiteX 性能优化实践
a. 网络优化
i. 调度优化 ii. LinkBuffer 减少内存拷贝,从而减少 GC iii. 引入内存池和对象池
b. 编解码优化
i. Codegen:预计算提前分配内存,inline,SIMD等ii. JIT:无生产代码,将编译过程移到了程序的加载(或首次解析)阶段,可以一次性编译生成对应的 codec 并高效执行
合并部署
a. 微服务过微,引入的额外的传输和序列化开销越来越大 b. 将强依赖的服务统计部署,有效减少资源消耗
课后
行业内各个流行的 RPC 框架的优劣对比
从第三章节 RPC 的核心指标来看,Kitex 还有哪些功能是欠缺或者需要加强的?
了解微服务的新趋势 ServiceMesh,以及 RPC 框架和 ServiceMesh 的关系
关于 RPC 框架,业界有哪些新的趋势和概念?
Netpoll 的优势在哪?相比其他高性能网络库例如 Netty 还有什么不足?
Flatbuffer 和 Cap'n Proto 等编解码协议为什么高性能?
参考文献
官方文档 Kitex Netpoll
字节跳动 Go RPC 框架 KiteX 性能优化实践_架构_字节跳动技术团队_InfoQ精选文章
字节跳动微服务架构体系演进_架构_字节跳动技术团队_InfoQ精选文章
第十四节:HTTP 框架修炼之道
概述
本节课程主要分为四个方面:
HTTP 协议相关知识
HTTP 框架的设计与实现
HTTP 框架的优化手段
企业实践
课前部分主要罗列课程中涉及到的概念。对于不熟悉的概念,同学们可以提前查询预习;课后部分是一些问题,帮助同学们在课后梳理本课程的重点。
课前
HTTP 协议
HTTP 协议出现背景
HTTP 协议是什么
HTTP 协议有什么
可参考百度百科
尝试写一个 hello world 服务器
可尝试用 gin 写一个 hello world 程序,达到以下效果
HTTP 框架中常见概念
框架路由:根据请求的 URI 选择对应的处理函数。
命名参数:形如 :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)
首先匹配 HTTP 方法
静态路由: 精确匹配注册的路由,如:/a/b/c、/a/b/d
参数路由:
路由修复: 如果只注册了 /a/b,但是访问的 URI 是 /a/b/,那可以提供自动重定向到 /a/b 能力;同样,如果只注册了 /a/b/,但是访问的 URI 是 /a/b,那可以提供自动重定向到 /a/b/ 能力
冲突路由:同时注册 /a/b 和 /:id/b,并设定优先级。比如:当请求 URI 为 /a/b 时,优先匹配静态路由 /a/b
什么是框架中间件,可参考 gin,kratos
Golang
sync.Pool 用法
网络库
C10K Problem
Select,Poll,Epoll
Epoll ET、LT 区别
字节跳动自研网络库 netpoll,netpoll-examples
SIMD
SIMD 是什么,可参考维基百科
Improving performance with SIMD intrinsics in three use cases
课后作业
为什么 HTTP 框架做要分层设计?分层设计有哪些优势与劣势。
现有开源社区 HTTP 框架有哪些优势与不足。
中间件还有没有其他实现方式?可以用伪代码说明。
完成基于前缀路由树的注册与查找功能?可以用伪代码说明。
路由还有没有其他的实现方式?
第十五节:微服务架构原理与治理实践
概述
本课程内容主要分为以下4个方面:
微服务架构介绍
微服务架构的背景由来、架构概览、基本要素
微服务架构原理及特征
微服务架构的基本组件、工作原理、流量特征
核心服务治理功能
核心的服务治理功能,包括流量治理、服务均衡、稳定性治理
字节跳动服务治理实践
字节跳动在微服务架构稳定性治理中,对请求重试策略的探索及实践
为了帮助大家更好地预习及理解本节课程,该学员手册列出了课前、课中、及课后这三个阶段所涉及到的专业内容大纲,其中课前部分供同学们提前预习参考,课中部分给出了课程大纲,帮助同学们整理思路,课后部分列出一些扩展性的问题让同学们进一步延伸思考。
课前
微服务架构介绍
系统架构的演进历史
单体架构
垂直应用架构
分布式架构
SOA架构
微服务架构
微服务架构的三大要素
服务治理
可观测性
安全
微服务架构原理及特征
微服务架构中的基本概念及组件
服务、实例......
服务间通信
RPC、HTTP
服务注册及服务发现
核心服务治理功能
服务发布
蓝绿部署
灰度发布(金丝雀发布)
流量治理
负载均衡
Round Robin
Ring Hash
Random
稳定性治理
限流
熔断
过载保护
降级
字节跳动服务治理实践
请求重试的意义
请求重试的难点
课中
微服务架构介绍
系统架构的演进历史
单体架构
All in one process
垂直应用架构
按照业务线垂直划分
分布式架构
抽出与业务无关的公共模块
SOA架构
面向服务
微服务架构
彻底的服务化
微服务架构概览
网关
服务配置和治理
链路追踪和监控
微服务架构的三大要素
服务治理(本课程内容)
服务注册
服务发现
负载均衡
扩缩容
流量治理
稳定性治理
可观测性
日志采集
日志分析
监控打点
监控大盘
异常报警
链路追踪
安全
身份验证
认证授权
访问令牌
审计
传输加密
黑产攻击
微服务架构原理及特征
微服务架构中的基本概念及组件
服务
一组具有相同逻辑的运行实体
实例
一个服务中的每个运行实体
实例与进程的关系
没有必然对应关系,一般一对一或者一对多
常见的实例承载形式
进程、VM、k8s pod......
服务间通信
微服务之间通过网络进行通信
常见的通信协议包括 HTTP、RPC
服务注册及服务发现
基本问题
服务间调用中,如何指定下游服务实例的地址?
简单方案
本地 DNS 存在缓存,导致延迟
DNS 没有负载均衡
不支持服务探活检查
DNS 不能指定端口
没有任何动态能力
有多个实例下游实例怎么办?
直接指定 ip:port?
使用 DNS?
服务注册发现
新增一个统一的服务注册中心,用于存储服务名到服务实例之间的映射关系
旧服务实例下线前,从服务注册中心删除该实例,下线流量
新服务实例上线后,在服务注册中心注册该实例,上线流量
微服务流量特征
统一网关入口
外网通信多数采用 HTTP,内网通信多数采用 RPC(Thrift, gRPC)
核心服务治理功能
服务发布
何为服务发布
让一个服务升级运行新的代码的过程
服务发布难点
服务不可用
服务抖动
服务回滚
蓝绿部署
将服务分成两个部分,分别先后发布
简单、稳定
但需要两倍资源
灰度发布(金丝雀发布)
先发布少部分实例,接着逐步增加发布比例
不需要增加资源
回滚难度大,基础设施要求高
流量治理
流量控制
在微服务架构中,可以从各个维度对端到端的流量在链路上进行精确控制
控制维度
地区维度
集群维度
实例维度
请求维度
负载均衡
Round Robin
Random
Ring Hash
Least Request
稳定性治理
限流
限制服务处理的最大 QPS,拒绝过多请求
熔断
中断请求路径,增加冷却时间从而让故障实例尝试恢复
过载保护
在负载高的实例中,主动拒绝一部分请求,防止实例被打挂
降级
服务处理能力不足时,拒绝低级别的请求,只响应线上高优请求
字节跳动服务治理实践
请求重试的意义
降低错误率
降低长尾延时
容忍暂时性错误
避开下游故障实例
网络抖动、下游负载高、下游机器宕机......
重试是有意义的,可以避免偶发性的错误,提高 SLA
通常没有重试意义
本地函数调用
远程函数调用
重试的意义
请求重试的难点
假设调用时间一共1s,经过多少时间开始重试?
随着调用链路的增加,重试次数呈指数级上升
POST 请求可以重试吗?
幂等性
重试风暴
超时设置
重试策略
对于可能超时(或延时高)的请求,重新向另一个下游实例发送一个相同的请求,并等待先到达的响应
返回特殊的 status code,表示“请求失败,但别重试”
设定一个重试比例阈值(例如 1%),重试次数占所有请求比例不超过该阈值
限制重试比例
防止链路重试
Hedged Requests
重试效果验证
字节跳动重试组件能够极大限制重试发生的链路放大效应
课后
结合 CAP 等原理,思考微服务架构有哪些缺陷?
微服务是否拆分得越“微”越好?为什么?
Service Mesh 这一架构是为了解决微服务架构的什么问题?
有没有可能有这样一种架构,从开发上线运维体验上是微服务,但实际运行又类似单体服务?
参考文献
A Design Analysis of Cloud-based Microservices Architecture at Netflix
字节跳动微服务架构体系演进
微服务架构的一知半解
原文:https://juejin.cn/post/709966539865561500