如何用代理平台解决微服务的一些痛点
为什么要做代理平台
微服务架构越来越流行,在一个上百号人开发的项目中,使用微服务的方式,大量模块之间通过接口调用,随之也带来了许多问题:
- 接口不能及时提供造成阻塞:往往客户端需要等待后台接口进入测试阶段,才能开始进行开发。一些刚入门的客户端开发(如web前端开发),并没有自行伪造接口数据的能力。
- 通信数据格式混乱:json、xml、protobuf等各种方式都有,方式相同而数据结构又不统一,主调和被调方都需要自行封装和解析不同格式的数据
- 日志未收敛汇集 : 日志分散在各个模块,未跟请求链路绑定,异常发生时的故障现场不好还原
- 各模块重复造轮子:各接口需要自己实现鉴权、限频、验证调用者等通用能力
为解决上述问题,我们约定一套调用规范,并将所有调用收归一处进行代理中转。因此,需要实现一套高可用的代理平台。
代理平台特性
先说一下几个角色
- 第三方(服务) -- 接口提供方,被调方
- 调用方(应用) -- 主调方 ,它可以是一个客户端如浏览器、手机APP,也可以是另一个接口提供方(第三方、服务)。
- 代理平台 -- 请求转发平台。
- 代理管理平台 -- 可视化配置及查询调用方、第三方、请求日志等数据,
- 鉴权平台 -- 基于 cookie、token等方式的含登录、权限控制的用户中心
代理平台主要有以下特性
松耦合、消除客户端开发等待。
代理平台除可以正常转发请求外,还可以伪装响应,即代理平台直接按预定规则随机生成响应给调用方,并不请求第三方。
统一输入输出格式
严格控制输入格式、标准化输出结构。第三方可以不按结构响应,但是需要在代理管理平台定义转换规则。如果很多事情按照约定来,可以少很多工作。这也是许多框架所倡导的约定优于配置。
规范CGI文档
通过统一的页面,统一录入接口说明、参数说明、响应头及字段说明等等。客户端开发甚至不需要跟后台开发沟通,即可完成开发任务。同时也提高接口的复用性。可以提供相应的注解和注释包,开发者在代码中引入注解或者注释,可以自动完成接口数据在平台上的录入、更新等操作。
输入初步验证
在代理平台定义好入参规则,代理平台在收到请求时,统一对输入参数进行格式验证、必填验证等等。对恶意输入请求进行拦截并记录。
频率控制
代理平台对每个接口提供基于分钟频次或者超频验证码的调用频率控制能力
接口性能统计
统计从平台向第三方发起请求,到平台完全收到第三方的响应的时间,做为第三方接口的性能。
请求日志
目标请求接口,来源ip、请求报文、响应报文、成功状态、耗时、所属的调用树等等整个请求完整的上下文数据,都记录在案。并且,请求到达接口提供方之后,如果第三方处理出现异常,可以将异常日志反向上报到代理平台,由代理平台来关联这次请求的日志。
还原调用树(调用链路)
一个调用方请求到达第三方后,有可能级联发起更多的请求。如果这些请求全部经过代理平台。那么代理平台可以记录链路数据,后续在代理管理平台上可视化的还原、呈现整个调用链路,清楚标识每个被调节点的耗时、是否成功等数据,有助于排查故障、性能瓶颈等问题
统一鉴权
登录态检查和接口权限控制可以全部收归代理平台。如果请求转发到了第三方接口,说明用户已登录并对此接口有权限,第三方开发者只需要从头部取封装好的userkey,到统一鉴权中心换取用户信息即可.
协议头规范化及过滤
为防止后台响应一些不规范的头,或者一些危险的头进行过滤。并且平台可以统一处理OPTIONS请求,将预定的协商头响应给客户端,可以灵活配置跨域需求。
来源IP过滤
对接口可以提供IP白名单或者黑名单的功能,合法来源ip的请求才能被转发到后台
多协议支持
需要能够支持多种接口协议,如HTTP、FTP、SMTP等等应用层协议,也需要能直接支持TCP长连接、UDP数据报等协议。并能做到部分协议转换,调用方无感知的使用跟第三方接口不同的协议通信。
关联测试用例
每个接口可以集成测试用例,类似于Advance REST Client或PostMan,定时以用例对接口进行输入输出调用测试。这可能需要接口提供方提供专门的测试接口。
服务存活检查,多server时提供负载均衡
第三方可以提供部署服务的多个server ip,由代理管理平台来进行存活检查,但server不可用时从缓存中踢掉此server,代理平台对多个server ip实现负载均衡。server故障时向真实接口方发出告警。
对第三方接口压测
代理平台本身需要具备较强的并发能力。但更重要的是接口本身支持高吞吐量访问。代理平台可以对第三方接口进行压测,以推进第三方优化接口性能及并发能力。
实现方式
nginx+lua+confd + etcd
confd + etcd 用来实现实时动态更新nginx配置,当有接口新增或修改时,动态生成server或者location配置。nginx来实现请求转发。lua脚本来实现一些诸如日志上报、鉴权检查、协议头检查等等逻辑。
这种方式在接口量较小时是个不错的选择。但是当接口数量达到成千上万的级别后,动态生成的nginx配置会非常大,不利于问题排查,且容易生成冲突、错误的nginx配置,致使nginx进程异常。并且上面提到的一些复杂的目标,如还原调用链路、输入参数验证、关联测试用例等,此方式也很难实现。
camel
apache camel是一个协议转换的框架,可以很方便实现协议转换、请求路由转发。下面是代理平台及代理管理平台的架构及请求处理过程示意。
其中
clinet 即为调用方,使用平台提供的各种语言(js、c、java、php、python等)的sdk,可以对调用方无感知的包装一些验证、标识头部。这也是还原调用链路的基础。如果不使用sdk,同样可以向代理平台发起请求。SDK主要做提供两个功能:1、透传标识头、来源路径头信息等等。2、向代理管理平台上报此次请求处理出现的异常日志。
cgi_proxy 即是基于camel开发的代理平台。主要实现验证请求合法性(appkey)、登录态验证与权限控制、协议转换、参数验证、请求转发、头部特殊字段处理、格式化响应、请求日志记录等等。日志由异步线程写入到消息队列。转发请求需要的数据从redis中获取,redis中存储了接口提供方真实接口地址、参数规则、合法appkey等信息。因为在client看来,请求的处理时间是平台处理时间+真实接口处理时间,只有平台处理足够快,才能减少性能损失,所以cgi_proxy 不从任何接口查询数据,也不直接操作db,只从redis查询数据,这样可以很好的减少性能损失。
log_coll收集消息队列中的请求日志落地存储,并按时对全平台接口调用情况进行统计,生成运营数据。当每日的调用量达到百万、千万级别时,比db分表更好的手段是由此进程定时迁移旧的数据。
manager 即代理管理平台。定义一套接口模型,将各种后台接口按模型规范并存储至db。将核心的数据缓存到redis。在页面实现可视的接口编辑、日志查询等能力