API网关
API网关主要起到了隔离内外网、身份验证、路由、限流等作用。我用一个生活的例子搭地铁比喻来描述下:过闸前我们需要经过安检保证客流的安全性,上下班高峰期还会排队进行限流,我们还可以通过看指示牌或者询问工作人员了解到应该往什么方向走,这就是路由。
我们团队选型了Kong和KongA作为我们的API网关,Kong是一个在Nginx运行的Lua应用程序,由lua-nginx-module实现。Kong和OpenResty一起打包发行,其中已经包含了lua-nginx-module。基本功能有路由、负载均衡、基本认证、限流、跨域、日志等功能,其他功能例如jwt认证可以通过插件进行扩展。
有人会问为什么不用Ocelot?回答这个问题之前,我首先声明我尊敬Ocelot项目与其开发者。
1.易用性。需要二次开发,虽然对.Net开发者来说能接受,但不利于运维。
2.性能。社区很多测试数据,据我了解就是kong 11K,Ocelot 3.5K,四舍五入3倍性能差,作为流量的入口,性能这块我还是比较注重的。
3.可扩展性,Kong很多功能可以通过插件式按需使用与开发。
服务描述
我们团队采用了Swagger,以此来衔接前后端开发的接口对接,省去了编写接口文档的成本,此外也支持接口调试,让开发效率提高不少。我们的服务都是以HTTP协议提供,对外API用RESTful风格,对内统一以POST的RPC风格提供。
服务注册中心
服务注册,服务在发布后自动把IP地址与端口注册进服务中心;服务发现,通过调用服务中心的接口获取到某服务IP地址与端口的列表。我们团队选用Consul+Consul Tamplate+nginx,Consul是基于GO语言开发的开源工具,主要面向分布式,服务化的系统提供服务注册、服务发现和配置管理的功能。Consul的核心功能包括:服务注册/发现、健康检查、Key/Value存储、多数据中心和分布式一致性保证等特性。
Consul作为服务注册中心的存在,但是我们服务发现只能拿到IP列表,我们使用RPC调用时还是得做负载均衡算法,于是使用了Consul Tamplate把服务列表同步到nginx的配置,那么RPC框架就无需集成负载均衡算法经过nginx路由。
开始选型我并没有选择Consul Tamplate,而是选择了fabio的这个中间件。fabio是一个应用于Consul的轻量级、零配置负载均衡路由器,开始用的时候部署起来很方便、很简单。后来上了Skywalking分布式链路跟踪系统,只要经过fabio路由的都无法把调用链串起来,虽然将就的用是没什么问题,但是Skywalking的调用链日志无法很好的展示出来就会影响日后的问题排查。我当时花了两天时间研究与issue提问,并没有很好的结果,所以最后另外选择了Consul Tamplate+nginx。
服务通信
RPC框架主要三大核心,序列化、通信细节隐藏、代理。协议支持分TCP和HTTP,当然还有两者兼容+集成MQ的。我们选择了WebApiClient做客户端,服务端仍是.Net Core WebAPI,主要考虑到WebAPIClient的轻量、易用,而且和Skywalking、Consul集成方便。我当时用的时候时.Net Core 2.2版本,gRPC并没有集成进来。
此外我也选择过ServiceStack,ServiceStack的技术栈很全,缺点是依赖得很深,当时试用的时候,它所以依赖的一个底层包ServiceStack.Common的某个类与WebAPI冲突了,所以对于不熟悉该框架的我断定存在依赖污染,无论我需要还是不需要都统统依赖进来了,然而我只是希望要一个简单的RPC框架。此外还需要破解。
Surging也作为我当时选型的目标,开始也是我抱着最大希望的,因为描述得很牛逼,什么都是全得。然而深入去用的时候,没有一个完整的文档,入门demo也不友好,说实话我驾驭不住只能放弃。
服务跟踪
市面上的分布式链路跟踪系统基本上都是根据谷歌的dapper论文实现的,基本上都分三大块,UI、收集器、代理(探针),原理大概是把涉及的服务链路的RequestID串起来。
我们团队选择了SkyWalking作为了项目的分布式链路跟踪系统,原因很简单:易用,无侵入,集成良好。
实施到我们项目的时候我做了点扩展,把Reqeust、Response、Header、异常给记录了下来,并过滤了部分不需要记录的路径。
分布式事务
只要在分布式系统,分布式事务必不可缺。
分布式事务
分类 | 理论 | 案例 | 中间件 |
强一致性 | ACID | 二阶段提交 | msdtc |
最终一致性 | BASE | 本地消息表 | CAP |
本地消息表是eBay在N年前提出的方案,而CAP以该思想实现的一门框架,原理大概是,本地业务表与消息凭据表作为一个事务持久化,通过各种补偿手段保证MQ消息的可靠性,包括MQ正常发布与消费。
我花了多天的时间专门测试了该框架可靠性,的确有保证。然而有个地方我认为可以优化,Retry的查询语句条件可以更加严谨点,只需要负责相应的Group进行Retry就好,没必要全部都查询出来,因为这个问题我在测试环境与本地环境共同调试时,刚好两个环境的Group不一致,导致会Retry失败的问题。
限流与熔断
我的架构图有两个网关:入站API网关 和 出站API网关
限流是针对外部流量的控制,保护了下游服务避免了流量冲击后雪崩,可以通过入站API网关的限流开关与策略控制。
熔断是保护上游服务避免因为下游的异常而且拖垮,更多是针对不可控的第三方依赖,例如微信、支付宝等。可以通过出站API网关的熔断开关与策略控制。
框架源码
写到这里,本篇的分享差不多要结束了,我将开源我们公司的工具库,有需要的朋友可以去使用。
- Sikiro.Tookits -公共基础库
- Sikiro.Nosql.Redis-StackExchange.Redis的基本封装
- Sikiro.Nosql.Mongo-mongodb驱动封装更新、排序等支持lambda
- Sikiro.MicroService.Extension-RPC注册,微服务框架-服务注册,终端跟踪忽略
- Sikiro.Chloe.Extension-支持多数据、事务封装、分页、IOC
- Sikiro.Chloe.Cap-把Chloe,ORM与CAP整合
- SkyApm.Diagnostics.AspNetCore|SkyApm.Diagnostics.HttpClient 修改了两
- 个库的-XXXXXTracingDiagnosticProcessor的代码,主要增加request、response、header的记录。
额外说明下DotNetCore.CAP.MySql,这个是我从CAP源码拷贝过来然后改了MySql.Data的依赖,原本CAP.MySql是用的MySqlConnector,和我的Chloe.ORM冲突了。
开源项目的库使用:保证包名不修改的前提下,增加版本号引入项目就可以覆盖。