Nodejs 后端框架如何转型微服务(理论期)

为什么要使用微服务架构

单体的优缺点

单体应用就是将应用程序的所有功能都打包成一个独立的单元,最终以一个 WAR 包或 JAR 包存在,没有外部的任何依赖,里面包含 DAO、Service、UI 等所有的逻辑。单体应用有以下优点:

  • 便于开发:只需借助 IDE 的开发、调试功能即可完成

  • 易于测试:只需要通过单元测试或浏览器即可完成测试

  • 易于部署:打包成单一可执行 jar 包,执行 jar 包即可完成部署

    不幸的是,这种简单的单元有很大的局限性。应用程序随着业务需求的迭代,功能的追加扩展,最终成为一个庞然大物。变得更加复杂,逻辑耦合严重,难以理解,团队开发 人员职责不清,部署困难,回归测试成本巨大,交付效率大大降低,总结下来,单体应用有一下缺点:

  1. 复杂性高

  • 代码难以理解,且复杂性进一步增加随着复杂度的增加,耦合度越来越高,代码牵一发而动全身,代码已经很难修改和重构

  • 团队职责不清晰 高度耦合的单体工程使得逻辑边界模糊不清,新的业务需求开发任务无法有效分配到人,团队人员职责不清晰,沟通成本增加

  1. 交付效率低

  • 构建和部署耗时长,难以定位问题,开发效率低

  • 代码量比较庞大,首先是编译耗时变长,开发调试将大部分时间花在重新编译上,代码量的增加又很难定位bug,在代码合并过程中极易遇到代码冲突;

  • 当我们开发完一个新的功能或者修复一个bug,代码的变更影响是很难预估的,所以每次发布之前都要进去全量功能的回归测试;

  • 全量部署耗时长、影响范围广、风险大,发布频次低 正因为这种全量部署耗时长、影响范围广、风险大,导致我们将很多功能和修复聚集在一起进行开发完成,这导致了产品发布频次降低,新的功和更换的体验能不能及时呈现给用户,甚至被竞争对手赶超。

  1. 伸缩性 (scalable) 差

  • 由于所有模块部署到一起,单体架构IO密集型模块和CPU密集型模块无法独立升级和扩容的,比如图片压缩,加解密这些 都是cpu资源密集的应该升级CPU,而IO密集型的模块比如日志收集服务IO操作比较多需要更大的内存,使用比如SSD性能更好的磁盘。

  1. 可靠性差

  • 一个bug有可能引起整个应用的崩溃 由于所有模块都是部署在一个实例中,一个bug会引起整个应用的崩溃,比如一个不重要的模块的内存泄露就将会导致所有应用实例一个个crash掉

  1. 阻碍技术创新

  • 受技术栈限制,团队成员使用同一框架和语言 受技术栈限制,团队成员必须使用同一框架和语言,不能使用新的语言和框架;

    那么如何解决单体的不足呢,通过迁移到微服务架构来解决,我们看一下什么是微服务

微服务架构的优缺点:

先说优点:

  1. 复杂度可控:在将应用分解的同时,规避了原本复杂度无止境的积累。每一个微服务专注于单一功能,并通过定义良好的接口清晰表述服务边界。由于体积小、复杂度低,每个微服务可由一个小规模开发团队完全掌控,易于保持高可维护性和开发效率。

  2. 独立部署:由于微服务具备独立的运行进程,所以每个微服务也可以独立部署。当某个微服务发生变更时无需编译、部署整个应用。由微服务组成的应用相当于具备一系列可并行的发布流程,使得发布更加高效,同时降低对生产环境所造成的风险,最终缩短应用交付周期。

  3. 技术选型灵活:微服务架构下,技术选型是去中心化的。每个团队可以根据自身服务的需求和行业发展的现状,自由选择最适合的技术栈。由于每个微服务相对简单,当需要对技术栈进行升级时所面临的风险较低,甚至完全重构一个微服务也是可行的。

  4. 容错:当某一组建发生故障时,在单一进程的传统架构下,故障很有可能在进程内扩散,形成应用全局性的不可用。在微服务架构下,故障会被隔离在单个服务中。若设计良好,其他服务可通过重试、平稳退化等机制实现应用层面的容错。

  5. 扩展:单块架构应用也可以实现横向扩展,就是将整个应用完整的复制到不同的节点。当应用的不同组件在扩展需求上存在差异时,微服务架构便体现出其灵活性,因为每个服务可以根据实际需求独立进行扩展。

  6. 功能特定:一个微服务一般完成某个特定的功能,比如消息管理、客户管理等等。每一个微服务都有自己的业务逻辑和适配器。一些微服务还会发布API给其它微服务和应用客户端使用。其它微服务完成一个 Web UI ,运行时,每一个实例可能是一个云 VM 或者是 Docker 容器。

缺点:

  1. 固有的复杂性:微服务应用是分布式系统,由此会带来固有的复杂性。开发者需要在 RPC 或者消息传递之间选择并完成进程间通讯机制。更甚于,他们必须写代码来处理消息传递中速度过慢或者不可用等局部失效问题。

  2. 分区的数据库架构:在微服务架构应用中,需要更新不同服务所使用的不同的数据库。使用分布式交易并不一定是好的选择,不仅仅是因为 CAP 理论,还因为今天高扩展性的 NoSQL 数据库和消息传递中间件并不支持这一需求。

  3. 波及多个服务:最后一个问题在于,微服务架构模式应用的改变将会波及多个服务。比如,假设你在完成一个项目案例,需要修改服务A、B、C,而A依赖B,B依赖C。在单个应用中,你只需要改变相关模块,整合变化,部署就好了。相比之下,微服务架构模式就需要考虑相关改变对不同服务的影响。比如,你需要更新服务C,然后是B,最后才是A,幸运的是,许多改变一般只影响一个服务,而需要协调多服务的改变很少。

微服务架构应该是什么样的

基本组件

微服务架构下,服务调用主要依赖下面几个基本组件:

  • 服务发现/注册中心:注册并维护远程服务及服务提供者的地址,供服务消费者发现和调用。注意一个服务的 IP 不一定是固定的,因为我们需要高可用,会同时存在同一个服务的多个副本,所以需要服务发现。服务发现还会定期检查服务的健康状态。

  • 服务框架/服务网格:用于实现微服务的 RPC 框架,包含服务接口描述及实现方案、向注册中心发布服务等功能。

  • 服务网关:介于客户端与微服务之间的网关层,可以理解为「门卫」的角色,以确保服务提供者对客户端的透明,这一层可以进行反向路由、安全认证、灰度发布、日志监控等前置动作;

  • 服务监控:对服务消费者与提供者之间的调用情况进行监控和数据展示;

  • 服务追踪:记录对每个请求的微服务调用完整链路,以便进行问题定位和故障分析;

  • 服务治理:服务治理就是通过一系列的手段来保证在各种意外情况下,服务调用仍然能够正常进行,这些手段包括熔断、隔离、限流、降级、负载均衡等。

  • 基础设施:分布式消息队列、日志存储、数据库、缓存、文件服务器、搜索集群等,用以提供服务底层的基础数据服务,可以自建,也可以使用阿里云等公有云提供的服务。

怎样将现有架构转型微服务

我们目前的架构类型类似下图,是典型的单体架构,内部模块化设计。服务直接的通信等功能带来了大量耦合

这是我们目前的架构类型

经过一段时间对微服务的探索,感觉必须的部分是网关、服务发现、服务网格。类似下图(但下图的服务网格好像过于简单了

这是一个基本的微服务架构

目前使用的技术栈

经过目前的研究,我们打算先做一个简单的微服务架构,包含了 API 网关、服务注册、服务发现三个板块

  1. API 网关:使用 Kong 作为 API 网关,开源的 Konga UI 作为网关的后台页面

  2. 服务注册:使用 Consul 进行服务注册,里面的一些 agent、client、server 之间的逻辑比较难以理解,但上手难度不大

  3. 服务网格(Service Mesh):使用 Consul Connect 作为服务网格的技术栈,都是 Consul 产品,可以与其他模块相兼容。且使用 Sidecar (边车模式),易懂易上手。

如何对已有后端项目进行拆分解耦

由于之前设计后端的时候已经是模块化设计,所以拆分并不困难。至于解耦,使用上述三个技术栈后,通信就可以交给微服务了,自然解耦。

目前基本功能还没有完全实现,只是调研和简单上手了一下相关技术栈。期待微服务架构的正式应用

Life is a Rainmeter