2020年总结-用学习过的技术搭建一个简单的微服务框架 + 源码
框架中使用的技术知识
工作快4年了,有时很迷茫,有时很有干劲,学习了一些技术,也忘记了一些技术,即使对一些技术,了解的深度不够,至少自己学习过使用过,那么在面对问题时,不会显得那么无力,解决问题后,也能有更大的收获。
- NET Core基础知识,EF CORE Code First,DB First
- 领域驱动设计理论,三层架构,DDD经典分层架构
- WebApi,Swagger,WebApiClient,Grpc,Exceptionless,Serilog
- Redis,Consul,IdentityServer4,Rabbitmq,Kafka,CAP
- Ocelot,Kong,Docker,Docker-Compose,Jenkins
DDD经典分层架构,与认识
- 根据业务,将问题域逐步分解,把一个大的问题,逐步分解为小的问题,针对细分出的问题,给出相应的解决方案,降低业务的复杂性和系统实现的复杂性
- 领域驱动设计是有门槛的,需要全方位提升,包括,业务知识,沟通能力,了解需求的能力,分析业务的能力,软件建模能力(希望有朝一日能爬过去)
- 个人感觉领域驱动设计最重要的就是,让团队每个人都理解业务,达成共识,过程中留下来的文档,图例,模型,对公司对个人都是一笔财富,提升了团队能力,沉淀了业务知识
- 学习领域驱动设计是一个长期的过程,书本的理论知识中没有明确指出实践的方式,基本上不会有完整的案例,因为实践领域驱动设计的系统都是公司的核心系统,里面包 含了公司大量的业务,以及商业价值,一般不会开源出来分享,需要结合项目,业务,人员,团队,来综合考虑,权衡,团队需要达成共识去实践,在实践中总结,进步
服务间的通讯:WebApiClient,Grpc,EventBus,以及问题
多个服务中需要通讯的时候,我们需要根据场景,来选择不同的通讯手段,每种通讯手段都有好处和坏处,以及异常的情况,需要综合来考虑选择
在下单扣库存的场景中,我们在订单服务中下单完成后,需要扣除商品服务中的sku库存,由于在不同的服务,我们不能保证他们的事务,只能保证最终一致性
WebApiClient,Grpc来实现
WebApiClient的github地址:https://github.com/dotnetcore/WebApiClient
- 如何保证不超卖?扣减库存的并发量比较大怎么办?
- 如何保证生成订单与商品库存的最终一致性?
- 订单服务可能会多次请求商品服务扣减库存的接口,会不会造成多次扣减库存?
商品服务提供RESTful API,GRPC 服务端,扣减库存接口时:利用数据库行锁,和添加扣除的数量不能大于数据库的库存数量的条件(UPDATE t_sku SET Stock=Stock - {sku.SkuQuantity} where Id = '{sku.SkuId}' AND Stock > {sku.SkuQuantity})
重试 + 补偿 ,订单服务保存订单后,使用WebApiClient,调用RESTful API扣减库存接口,使用Grpc 请求服务端扣减库存,根据调用的返回结果,结果失败重试,重试一定次数后,记录日志,回归订单,提示失败,成功则提示下单成功!
商品服务扣减库存的接口需要根据订单服务提供的唯一标识做幂等,Grpc 服务端扣减库存做幂等,幂等可以采用redis 的Hash,和设置Hash的过期时间来做幂等,也可以使用幂等表,新增一张表,用订单服务的标识做唯一索引,我这里使用的是幂等表
EventBus来实现
- 怎么选择消息队列,Rabbitmq 还是 Kafka?
- 下单扣除库存是比较重要的场景,听说消息队列会丢消息?
- 有没有写好的,使用非常简单方便的框架,这样我就能直接搬砖了?
- 用消息队列我还需要考虑幂等吗?
Rabbitmq 的社区比较活跃,官方文档比较详细,有.NET的客户端,性能没有Kafka高,Kafka 的原理架构比rabbitmq 容易理解,kafka的集群更好搭建,目前来说没有性能要求,工作中用的是Rabbitmq
Rabbitmq 提供了发布者确认机制,消费者提供了ACK机制,可以保证不丢消息,消息发布到rabbitmq 服务器,开启了发布者确认,消息持久化到磁盘成功后,会返回持久化的状态,持久化磁盘成功了,代表发布消息成功了,消息者开启ACK,消息消费失败后,会返回消息队列中,多次失败后,可以把消息放到指定的延迟队列中,Rabbitmq 挂了,重启时会,不断的重试,直到成功,也可以失败一定次数后,人工干预解决问题
kafka有生产者消息确认机制,消费者ack机制,kafka 发布消息是可以设置ACK=all,消息需要,Leader持久化磁盘成功,所有的ISR中的Follower都持久化磁盘成功后,才表示真正的成功,消费者可以手动提交当前偏移量,保证不丢消息
杨晓东老师的CAP:https://github.com/dotnetcore/CAP,一个基于本地消息表+消息队列 的分布式事务的解决方案,同样具有 EventBus 的功能,基于本地消息表意味者,多了几次IO,会影响一点性能,但是可靠,使用简直是easy,最好能了解一些原理,这样遇到一些问题,也能解决
用消息队列一定要考虑幂等,很多消息队列都保证至少一次投递消息,可能出现多次投递的情况。幂等方式与上面的一致
Redis
CsRedis的github地址:https://github.com/2881099/csredis
目前接触的数据量,不用考虑缓存穿透、缓存击穿、缓存雪崩的情况,缓存同步策略,也看过一些文章,了解过一些- CsRedis 幂等
- CsRedis 分布式锁
- CsRedis做缓存,加快查询的速度,缓存一些热点数据,比如权限
IdentityServer4
IdentityServer4的文档地址:http://www.identityserver.com.cn/
IdentityServer4 是为ASP.NET Core系列量身打造的一款基于 OpenID Connect 和 OAuth 2.0 认证框架。 对外提供RESTful API接口,需要Token来进行验证,JWT Token中包含一些用户信息,我们可以结合RBAC权限进行授权- 在已有登录的项目中,我们可以使用密码授权模式,获取Token
- 使用混合流模式,结合前端使用oidc-client-js,获取Token
- 使用混合流模式,IdentityServer提供了一套基于 MVC 的样例 UI,可以直接从Github上拉取,引用到项目中,获取Token
Consul
使用Consul来实现服务发现与健康检查
- Consul提供了可视化的界面,我们可以随时查看服务的状态
- Consul有.net的客户端,我们可以在服务启动的时候向consul注册,服务关闭时注销,也可以通过Json文件的方式向consul中注册服务
- Consul的健康检查,会根据你设置的时间来对你的服务发起调用。检查服务是否正常,所以我们需要提供一个接口,表示服务的运行状态
- Consul可以在一个服务中注册多个Ip+端口号,客户端可以根据服务名称获取所有的Ip+端口号,根据算法,实现负载均衡
Ocelot
Ocelot文档地址:https://ocelot.readthedocs.io/en/latest/index.html
Ocelot是一个用.NET Core实现并且开源的API网关,它功能强大,包括了:路由、请求聚合、服务发现、认证、鉴权、限流熔断、并内置了负载均衡器
- Ocelot 可以结合Consul 实现服务发现,实现负载均衡
- Ocelot 所有的请求路由都走Ocelot,可以结合Identityserver4,可以在Ocelot上统一认证授权
Ocelot Swagger,Kong Swagger,遇到过的问题
Swagger 是一个很好用的接口文档,可以帮助我们前后端联调,以及多个项目接口的管理- 使用Ocelot网关,多个服务,怎么统一的使用swagger 来管理
- Swagger访问的统一路径是:IP+端口/swagger,部署网关后,都是8000端口,由于Kong 的Route不能重复,我该怎么来配置?
- 我们公司现在有几十个服务,每个都需要在KongA上配置,很麻烦,怎么办?
Swagger加载时请求一个IP+端口+服务名称+Swagger.json的接口,我们可以在ocelot中,配置单个服务的swagger路由,在Ocelot上配置Swagger,通过选择的服务名称,来路由到指定服务的swagger
我们可以使用KongA可视化界面,给指定的服务配置特定的路由
Swagger访问的统一路径是:IP+端口/swagger,我们可以为每个服务设置IP+端口/{唯一的名称}+swagger,比如订单服务:47.104.83.210:8000/orderSwagger
kong有官方的文档,提供了RESTful API接口,可以调用kong的8001端口来,配置,目前Kong Admin Api 没有官方的.NET客户端,有位大佬开源了Kong.Net,可以帮助我们更快的去实现
Kong.Net的github地址:https://github.com/lianggx/Kong.Net
Docker ,Docker-Compose ,Jenkins
使用Jenkins pipeline 来实现,从git 服务器上拉取代码,发布代码,把代码打包,通过SSH,传输到Linux 服务器,在Linux 服务器执行,docker images ,docker run ,docker-compsoe,部署项目。 使用Docker,Docker-Compose,部署项目,以及安装各种需要的环境,工具。总结
喜欢沟通交流,欢迎大家给出意见,谢谢!
- 基础知识不足,技术的深度不够,没有养成记录知识的习惯,习惯性的忘记了许多东西
- 没有重视业务的重要性,没有沉淀积累业务知识,经常考虑一些技术或者功能实现,忽略了最重要的业务积累
- 做的事件很杂,很散,没有方向,也收获了一些,比如,讨论出的方案,我会思考这样做,是否会有问题,有问题的地方在哪里,提出来,大家讨论
- 学习领域驱动设计,让我明白了业务的重要性,我一直想要提高自己的沟通能力和需求分析能力,却没有方向,十分的迷茫,一无所有,却缺乏从头再来的勇气
源码地址
码云Gitee地址:https://gitee.com/lifeng618/Sample.git