请求分析
约 2169 字大约 7 分钟
2025-05-06
在进行聚合请求实现之前, 我们需要先分析下, 对于一个接口请求都需要哪些信息
核心信息
核心信息是 不可缺少的信息
, 缺少相关信息无法请求成功, 这部分对应http的基础信息
请求url
: 完整的接口地址请求方法
: get/post等请求类型
: content_type 直接决定请求能否被后端接口正确解析
请求信息
请求信息是基于请求的接口具体确定哪些有, 哪些没有的, 没有一定之规, 但是注意, header中一定会存在如下几个信息:
X-Front-Service: Gateway
: 代表请求从网关过来, 是一个固定值X-Front-Request-Id
: 网关的请求ID, 每次请求网关独立生成, 建议后端服务复用这个ID, 这样可以串联起全部服务的日志X-Front-Gateway_Uri
: 因为请求了哪个网关接口而触发当前接口调用, 用于跟踪当前接口都服务于哪些业务场景
下面是一些常规信息, 跟随具体接口具体生成:
请求query
: query信息(按实际要求)请求Body
: Body信息(按实际要求)请求header
: header信息(按实际要求)请求cookie
: cookie信息(按实际要求)
关于query与body的说明
我们经常说的 get 请求没有body, post请求没有query是一种约定的规范, 实际上, get 请求是可以有body的, post请求也是可以有query的, 但是强烈建议不要如此使用, 几不符合规范, 也不符合逻辑直觉, 后患无穷. 但是网关会支持 get 带body, post带query, 原因就是: 遇见过太多不符合规范服务
时间限制
限制单一请求的请求时长
, 是极其重要的诉求, 一般建议对于请求必须配置超时时间.
重试规则
自动重试也是一次请求的基础能力, 但是重试行为本身也是有学问的, 是否重试也是分场景、分情况的, 有如下条件:
- http状态码异常是否需要重试? 哪些异常状态码重试?
- 业务错误码非成功是否重试? 哪些业务错误码重试?
- 超时是否重试?
- 重试几次?
一些经历过的场景
读请求的重试: 一些读接口, 如果请求超时后端服务有缓存, 重试是有几率而且很大几率成功, 重试很可能命中缓存, 而如果没任何缓存, 相同条件, 重试大概率依旧超时, 需要后端接口针对性改进.
写请求重试: 写请求的重试, 相比读请求敏感很多, 如果是超时重试, 需要确定后端接口做好了幂等处理, 否则很容易生产出重复数据或者脏数据, 容易因为数据原因对服务稳定性产生影响.
如何判定重试次数
对于每一个接口, 都是有时间要求的, 那么如何设置重试次数呢? 假定现有一个网关接口A, 后端调用服务接口B, 基于服务监控或历史经验, 服务接口B 300ms能完成处理 98% 的请求, 1000ms 能处理完成100%的请求, 而网关接口划分给服务B的时间为 700ms, 那么我们选择 98%覆盖率即可, 即服务接口B超时时间设置为 300ms , 超时后重试一次
响应数据规则
如果我们是纯粹的业务代码, 调用一个接口, 很明确知道其返回结构与返回类型等信息, 可以定义一个结构体接收相关返回结果,但是网关做的是一种泛化接口请求, 后端的接口可能每一个都有不同的响应结构,因此 需要一种抽象的泛化配置来解决相关问题
, 具体配置如下:
业务状态码字段
: 用于标记请求是否处理成功业务状态码位置
: 从何处读取业务状态码, Body/Header成功的业务状态码
: 哪些业务状态码, 代表处理成功业务状态码描述字段
: 描述当前业务状态码的含义, 不配置影响不大数据字段
: 后端接口返回的数据时哪一个字段
基于以上基本信息, 还需要 自适应不同的响应类型
, 将数据类型统一转化转化为Json进行处理
日志记录
没有日志, 不影响任何服务的运行, 但是, 影响开发者寿命
, 出现异常, 两眼一黑, 头发是真的大把大把掉, 所以, 我一般将日志定义为 次核心
逻辑, 其重要程度甚至在服务稳定性之上, 毕竟服务出现异常, 也需要日志来辅助定位问题, 所以核心能力建设完成之后, 必须快速建立日志体系。
流量控制
流量控制是对一个服务的基础保护, 如果后端服务本身未实现流控, 则网关需要启用流控, 对后端服务实施保护, 防止流量过载导致服务崩溃. 留空的具体实现参见后续章节: 流量控制及其实现
结果缓存
不考虑网关, 即使常规的业务开发, 我们也会对第三方读接口
做一些缓存, 以加快访问速度, 那么, 那些场景适合做接口数据缓存呢? 具有如下特征之一的就可考虑缓存:
- 相同条件下, 接口返回数据一致, 并且该
数据变化频率极低
, 比如系统中常见的用户详情 - 返回数据变化频率虽然较高, 但是
数据更新接受一定延迟的
, 比如常见的APP详情页的各种图片物料, 更新后延迟生效 - 访问第三方付费接口,
可以对数据作缓存
, 比如公司购买第三方付费接口服务, 甚至缓存时间可以稍长, 除去访问加速外, 也可以降低成本
缓存的具体实现参见后续章节: 缓存机制的设计与实现
请求预热
请求预热是**以请求结果需要缓存作为前提
** , 预热是指针对缓存进行预热, 那么什么是预热? 一句话: 在缓存快过期但未过期时, 当前请求通过缓存返回数据, 后台任务通过接口请求最新数据, 并刷新缓存
, 这是一种防止缓存集中过期, 而后大批量请求穿透到后端服务的手段, 也可以保证访问网关接口的响应速度. 具体实现, 参见后续章节: 请求预热及其实现
数据结构
综上, 一个请求配置的完整数据结构如下:
// Request 请求配置(核心配置)
type Request struct {
Ctx context.Context `json:"-"` // 请求上下文
PathParam map[string]string `json:"path_param"` // 替换url中的占位符
Body map[string]any `json:"body"` // 请求Body
Header map[string]any `json:"header"` // 请求Header
Cookie map[string]any `json:"cookie"` // 请求Cookie
Query map[string]any `json:"query"` // 请求query
Static map[string]map[string]any `json:"static"` // 静态参数: location => valName => val
FullUrl string `json:"full_url"` // 完整的请求URL
ContentType string `json:"content_type"` // 请求类型
Method string `json:"method"` // 请求方法
DataField string `json:"data_field"` // 数据字段
CodeField string `json:"code_field"` // 业务状态码字段
MessageField string `json:"message_field"` // code描述字段
DataReceiver any `json:"-"` // 响应data部分数据解析
SuccessHttpCodeList []int `json:"success_http_code_list"` // 哪些http状态码视为成功, 不配置, 默认2xx
SuccessCodeList []string `json:"success_code_list"` // 哪些业务状态码视为成功
ConnectTimeout int64 `json:"connect_timeout"` // 连接超时时间: ms
ReadTimeout int64 `json:"read_timeout"` // 读取超时时间
RetryRule *RequestRetryRule `json:"retry_rule"` // 重试规则
Logger *zap.Logger `json:"-"` // 日志记录器
}
// RequestRetryRule 重试规则配置
type RequestRetryRule struct {
RetryCount int `json:"retry_count"` // 重试次数
RetryTimeInterval int64 `json:"retry_time_interval"` // 重试的时间间隔 1 - 10 之间, 单位毫秒
RetryHttpCodeList []int64 `json:"retry_http_code_list"` // 哪些http状态码需要重试
RetryBusinessCodeList []string `json:"retry_business_code_list"` // 哪些业务状态码需要重试
}
// RequestOption 请求一些可选项, 可以视作是一些扩展配置, 对接口请求能力的增强
type RequestOption struct {
CacheInstance abstract.ICache `json:"-"` // 数据结果缓存实例
RateLimiter abstract.RateLimiter `json:"-"` // 流控实例
ResponseParser abstract.IResponse `json:"-"` // 返回结果解析, 不配置使用内置实现
}