HTTP协议
含义:超文本传输协议(Hypertext Transfer Protocol)
包含:
-
请求行/状态行
请求行包括:方法名、URL、协议版本
状态行包括:协议版本、状态码、状态码描述
状态码:
1xx:信息类
2xx:成功
3xx:重定向
4xx:客户端错误
5xx:服务端错误
-
请求头/响应头
-
请求体/响应体
请求流程:
业务层——服务治理层/中间层——路由层——协议编(解)码层——传输层
各版本的不足:
-
HTTP1
- 队头阻塞
- 传输效率低
- 明文传输不安全
-
HTTP2
- 多路复用
- 头部压缩
- 二进制协议
-
QUIC
- 基于UDP实现
- 解决队头阻塞
- 加密减少握手次数
- 支持快速启动
HTTP框架的设计与实现
分层设计
应用层设计:
context、Request、Response、Handler提供合理的 API
- 可理解性
- 简单性
- 冗余性
- 兼容性
- 可测性
- 可见性
中间件设计:
中间件需求:
- 配合 Handler 实现一个完整的请求处理生命周期
- 拥有预处理逻辑与后处理逻辑
- 可以注册多中间件
- 对上层模块用户逻辑模块易用
洋葱模型:
核心逻辑与通用逻辑分离
使用场景:
- 日志记录
- 性能统计
- 安全控制
- 事务处理
- 异常处理
调用链
适用场景:
- 不调用 Next:初始化逻辑且不需要在同一调用栈
- 调用 Next:后处理逻辑或需要在同一调用栈上
路由设计:
框架路由实际上就是为 URL 匹配对应的处理函数(Handlers),构建前缀匹配树
-
静态路由
-
参数路由
-
路由修复
-
冲突路由以及优先级
-
匹配 HTTP 方法
使用路由映射表 Map:
key: Method(String)
value:前缀树+头结点(*node)
-
多处理函数:方便添加中间件
在每个节点上使用一个 list 存储 handler
协议层设计:
抽象出合适的接口:需要在连接上读写数据
网络层设计:
网络模型
BIO、NIO
性能修炼之道
主要分为两方面的优化:针对网络库的优化和针对协议的优化
针对网络库的优化:
go net
- 存下全部 Header
- 减少系统调用次数
- 能够复用内存
- 能够多次读
go net with bufio
绑定一块缓冲区
netpoll
- 存下全部 Header
- 拷贝出完整的 Body
netpoll with nocopy peek
- 分配足够大的 buffer
- 限制最大 buffer size
不同网络库优势:
go net
- 流式友好
- 小包性能高
netpoll
- 中大包性能高
- 时延低
针对协议的优化:
Headers 解析
快速解析:
- 通过 Header key 首字母快速筛除掉完全不可能的 key
- 解析对应 value 到独立字段
- 使用 byte slice 管理对应 header 存储,方便复用
热点资源池化
RequestContext进行池化
优点:减少了内存分配、提高了内存复用,降低了GC压力,使得性能提升。
缺点:额外的 Reset 逻辑;请求内有效;问题定位难度增加
课后作业:
RPC框架
RPC 需要解决的问题
- 函数映射
- 数据转换成字节流
- 网络传输
RPC模型
RPC由五个部分组成:User、User-Stub、RPC-Runtime、Server-Stub、Server
一次RPC的完整过程
IDL(interface description language)文件
IDL 通过一种中立的方式来描述接口,使得在不同平台上运行的对象和用不同语言编写的程序可以相互通信
生成代码
通过编译器工具把 IDL 文件转换成语言对应的静态库
编解码
从内存中表示到字节序列的转换称为编码,反之为解吗,也交佐序列化和反序列化
通信协议
规范了数据在网络中传输内容和格式。除必须的请求/响应数据外,通常还会包含额外的元数据
网络传输
通常基于成熟的网络库走 TCP/UDP 传输
RPC的好处
- 单一职责,有利于分工协作和运维开发
- 可扩展性强,资源使用率更优
- 故障隔离,服务的整体可靠性更高
RPC带来的问题
- 服务宕机,对方怎么处理?
- 在调用过程中发生网络异常,如何保证消息的可达性?
- 请求量突增导致服务无法及时处理,有哪些应对措施?