近日,个推服务端技术专家李白受邀参与SegmentFault D-Day 线上技术直播活动,与来自头部互联网企业的后端技术专家们共探 “后端架构演进之路”。李白以“API网关演进之路”为主题,分享了个推基于golang进行API网关建设的实践经验和深度思考。
★以下为李白演讲干货整理:
API网关之源起
API网关是随“微服务”概念而兴起的一种架构模式。在微服务拆分过程中,原本庞大的单体应用和业务系统被拆分成许多微服务系统进行独立维护和部署,导致API规模成倍增长,API治理难度日益增加;同时,子系统通过API对外提供能力时,还会存在通用能力重复建设的问题。因此,使用API网关统一发布和管理API逐渐成为一种架构趋势。
在个推,公司的消息推送、金融风控等业务核心系统均是基于微服务架构,部分系统也存在自建的网关模块。随着不同系统之间的依赖越来越多,高效进行接口治理的需求日益迫切。因此,个推很早就引入了统一的API网关,来解决权限控制、流控、服务降级、灰度发布、版本管理等一系列问题。
1、个推早期API网关
2015年,SpringCloud的诞生极大促进了微服务架构的发展和流行。个推也是在这一时期,依赖于SpringCloud gateway构建了自己真正意义上的API网关。
SpringCloud在生态上比较友好,有很多可以开箱即用的监控和容错组件,但因为不支持流量和安全治理,所以无法很好地满足后续公司进行API治理的需求;尤其是个推后来建设数据中台,更亟需一个统一的API网关来承担数据中台的入口流量。此外,在开发语言上,SpringCloud只支持Java,适用性有限;且存在性能不理想、运维难度大等不足。
因此,为了实现更强大的API治理能力、简化接入和运维方式,同时也为了更好地适应公司数据中台建设需求,个推选择自研API网关。
2、个推自研API网关
2.1 自研目标
个推建设API网关的几个关键目标:
✦ 1) 要能够对API完整生命周期进行统一治理
比如,在API设计上,要统一规范,规定API必须有归属服务和标签,做到API设计上的隔离;设计完成后要能够直接调试,自动生成测试代码;发布时,对API流量要做到精细化控制,支持服务的灰度发布;在API运行过程中,要能够对API调用情况进行全方位的监控和告警,对出现问题的服务能够及时地熔断隔离;下线后能够及时回收资源。
✦ 2)需要有完善的功能组件,来处理再一次请求过程
在整个请求链路中,我们设计实现了链路追踪、日志、鉴权、限流、熔断、插件等一系列核心功能。
✦ 3)保证“三高”的同时,用户迁移和接入要简单
这一点其实是说API网关要便于运维和使用,同时要提供各种指标检测功能,且支持自动容错和弹性扩容。
2.2 技术选型
基于以上的目标设计和技术调研之后,我们选择将golang作为自研API网关的主力开发语言。之所以选择golang,有以下几个原因:
✦ 1)个推在做多机房容灾建设和数据拆分迁移过程中,设计了gproxy-codis和gproxy-es,并已经搭建了一套proxy集群来路由不同的用户。这些proxy到目前为止运行良好,一些大的集群单机QPS超过6W。在这些项目的开发过程中,我们积累了不少的开发经验和基础组件,里面的很多轮子都是可以直接复用的。因此,我们使用golang并基于现有的这些组件,开发一套gproxy-http就相对省力得多。
✦ 2)golang语言本身天然支持高并发,开发速度快,最重要是节省机器成本。
✦ 3 )golang作为云原生框架使用最多的语言,golang上的技术沉淀也是为个推云原生建设铺路。
2.3 设计与实现
确认目标和技术选型之后,接下来就是一些具体的设计与实现。
✦ 1) 整体架构
个推API网关的整体架构设计,如图所示:
首先是一个Web管理平台,API的创建、发布和后续管理都可在管理平台上配置完成;配置完成后,会下发到配置中心通知到网关,同时网关也会定时拉取全量的API配置;然后是网关的一些核心组件,比如插件引擎,主要是执行配置的一些插件。转发引擎也是API网关的核心模块,个推的转发引擎支持http、grpc,同时还有个推自研的gcf协议,而在数据中台的业务场景下,也支持了kafka的数据推送能力。
✦ 2) 插件服务
从图中可以看到,个推API网关整体架构里有一个独立的插件服务。这个设计的核心原因是golang是类C语言,打包后是可执行文件,而golang的原生插件是直接编译好的,不支持更新和卸载,所以也不能在界面上直接新增和更新插件。基于此原因,我们使用Java开发了一个插件服务,利用Java动态的语言特性,以灵活地支持插件的新增、更新和卸载。
网关通过grpc与插件服务通信,性能上有一定损耗。为了尽可能减少性能的损耗,我们把加密、特定的序列化相关插件都使用golang的一个原生插件去实现,一些业务自定义比较强的组件则推荐使用Java插件服务。
✦ 3) 资源隔离
资源隔离是实现系统高可用的常用手段,在隔离设计上主要有集群和线程池的隔离。
API网关主要支持服务集群隔离,通过这种集群级别的隔离,在上层可以支持多租户,如果再彻底一点,在LB层面可以把网关集群也隔离出去。另外,服务的灰度发布也是通过网关这种集群隔离来实现的。具体过程是,在升级时,用户可以在界面上配置流量转发规则和集群,通过流量回放,将部分测试流量导入到灰度集群,或把线上真实流量按比例转发给灰度集群,确保没有问题后再全量发布。
线程的隔离主要体现在数据服务,主要功能是把数据API化,也就是在界面通过简单的配置就可以把MySQL、ES、Hbase的数据通过API提供出去,不用开发人员动手写CRUD或者客户端代码,非常方便。目前个推数据中台业务场景的大部分流量都是请求数据服务,因此我们设计了普通线程池、慢线程池和自定义配置的线程池等三类线程池。当请求时长超过慢阈值后,接口会被分配到慢线程池处理,避免慢请求拖垮整个服务。
✦ 4) 服务编排
服务编排也是API网关需要满足的一个常见需求,主要是将多个API做聚合调用,大幅降低调用延迟。这部分功能之前是在网关上的,在网关层面支持的服务编排相对来说较为基础,能够支撑API的并发聚合调用,但难以处理复杂的业务组合,特别是涉及到事务的编排场景。因此,我们决定将这部分功能,抽取出去做独立服务,而后续的链路网关会直接访问服务编排模块,这样也保证了网关整体相对轻量。
✦ 5) 性能优化
针对API网关性能,我们也做了一系列压测和优化,比如,使用开源函数代替或重写内部大量使用的序列化、加解密等函数;大量使用Sync.Pool复用对象,对其内部逻辑进行纯异步处理;自研gnet,替换原生net框架,对网络模型进行优化等。从线上实际运行结果来看,目前个推数据中台中的API平台每天调用量超过10亿次,单机QPS峰值在2W左右,整体性能损耗在10% ,性能表现超过预期。
✦ 6) 易用性设计
通过上述插件机制、隔离手段和性能上的极致优化,我们确保建设的API网关平台整体是高可用且易扩展的。而平台做好以后,还要方便用户接入使用和运维。
因此,为了提升易用性,我们采用纯Web的界面设计,并内置了多个API模板。用户通过简单的配置,就可以创建一个API,例如接口授权有效期、QPS、限额等权限配置,都能通过可视化界面操作完成;创建好API之后,用户还可以直接在界面上调试。
同时,在对外提供API的场景下,用户可以批量导出某个服务下的API文档,非常方便。个推API平台还实现了监控和统计的功能,比如提供API被调用的趋势、整个服务下API调用量和错误统计等数据,对运营和研发人员比较友好。