关于微服务架构的思考、设计和实践
本文章基于以下CC协议进行知识共享:署名(BY)-非商业性使用(NC)-禁止演绎(ND)
为什么选择微服务
在之前的启嘉网后端中,我们使用传统的单体架构 .Net + PHP (中间层) 进行开发。但是在实际的开发过程中,遇到了以下几点问题:
- 功能模块越来越多的时候,项目维护越来越困难,在推送版本更新时,经常出现未知问题导致整站崩溃。
- 一些高负载的功能模块无法抽离,导致负载均衡较难实施。
- 无意识写出的高耦合代码,导致产品需求变更时,连带修改太多。
- 开发人员出现变动时(如请假、离职),较难调整开发安排及排期。
而使用微服务架构的话,各个功能模块(服务)可以单独部署、单独更新,即便出现问题,也可以保证其他模块的正常使用。同时,也可以将高负载的微服务抽离到性能更高的服务器上进行优化。其次,微服务的设计思想所带来的低耦合,对产品设计、开发都比较有利。最后,在后端人员出现变动时,可以随时抽离人数更多的前端人员,使用 NodeJS / Python / Golang 等任意一种语言开发微服务,来保证排期的正常进行。
架构设计概述
如上图所示,微服务部分主要分为 3 层 —— 网关层、服务发现层、应用层。
网关层
客户端请求(浏览器)首先经过 负载均衡服务器
(用于提升网关层的并发能力)。由 网关层
分析请求所涉及的微服务,然后调用 服务发现层
的接口,判断服务存活状态,并对多实例的微服务进行负载均衡。获取到微服务实例的地址后,进行 RPC 远程调用,然后将结果返回给客户端。
应用层
应用层即实际的业务逻辑部分,由基于 NodeJS / Pytonn / Golang 等任意框架或语言开发的微服务组成。服务间通信使用谷歌的 gRPC 框架进行远程调用。应用层的服务在启动时会调用 服务发现层
的接口进行服务注册,同时,在服务不可用时,服务发现层
会自动剔除该节点来保证服务的稳定运行。虽然说以启嘉网目前的规模,使用 RPC 还是 HTTP 并不会产生较大的性能差异,但是为了适配未来的发展和学习研究等目的,我们仍然选择了 gRPC。
服务发现层
网上主流的解决方案中,通常是使用 gRPC + etcd 这样的组合,并由 etcd
进行服务注册与发现的工作。但是由于时间关系,在方案落地过程中,我没有太多的时间去完全学习所有的技术栈组合。因此决定使用自己擅长的 NodeJS 自行开发,同时由于 JS 是动态语言,那么在构建 服务发现层
和 网关层
这样不确定(动态)因素较多的应用时,不必像 Golang 一样频繁的使用反射来实现相关功能。
落地实施
在启嘉网这边落地微服务有几个原则:
- 不让架构过分入侵业务代码。
- 优先使用类库而不是框架。
- 渐进式。
为了实现以上几点,我们针对 NodeJS / Python / Goloang 封装了基础库,将服务发现层的相关操作对外简化,来达到 不让架构过分入侵业务代码
,如 Golang 建立服务的所有操作仅需要4行代码,其中包括检测服务器自身环境、自动寻找可用地址和端口等等。
1 | func main() { |
优先使用类库而不是框架
实际也是一个不入侵业务代码的体现,但是更重要的是希望能简化处理,不要因为使用某个框架而学习框架带来的很多抽象概念。
最后,由于项目功能模块比较多,想快速完美的切换到微服务架构是基本不可能的。因此,需要 渐进式
,用细分化的微服务逐渐替换掉原有单体应用中的功能。这部分只需要网关层判断一下即可。