1.3 分布式微服务架构

1.3.1 微服务概述

微服务是在2014年由Martin Fowler大神提出的。首先,可以肯定的是SOA和微服务是一脉相承的。Martin Fowler提出这一概念可以说把SOA的理念继续升华,精进了一步。微服务的核心思想是在应用开发领域,使用一系列微小服务来实现单个应用,或者说微服务的目的是有效地拆分应用,实现敏捷开发和部署,可以使用不同的编程语言编写。

1.3.2 SOA与微服务

从实现的方式来看,两者都具有中立性,与语言无关,协议跨平台。相比SOA,微服务框架将能够带来更大的敏捷性,并为构建应用提供更轻量级、更高效率的开发。而SOA更适合大型企业中的业务过程编排和应用集成。

从服务粒度来看,既然是微服务,必然更倡导服务的细粒度,重用组合,甚至是每个操作(或方法)都是独立开发的服务,足够小到不能再进行拆分。而SOA没有这么极致的要求,只需要接口契约的规范化,内部实现可以更粗粒度。

从部署方式来看,传统的SOA服务粒度比较大,多数会采用将多个服务合并打成War包的方案。而微服务则打开了这个黑盒子,把应用拆分成一个一个的单个服务,应用Docker技术,不依赖任何服务器和数据模型,是一个全栈应用,可以通过自动化方式独立部署,每个服务运行在自己的进程中,通过轻量的通信机制联系,经常是基于HTTP或者RPC的,这些服务基于业务能力构建,能实现集中化管理。

另外,微服务是去ESB(总线)、去中心化、分布式的,而SOA还是以ESB总线为核心,大量的WS标准实现。

1.3.3 微服务架构的特点

微服务的主要特点有:

  • 单一职责:与面向对象原则中的单一职责原则类似,需要确保每个微服务只做一件事情。
  • 独立部署、升级、扩展和替换:每个服务都可以单独部署及重新部署而不影响整个系统。微服务能以独立进程进行部署(对于重要服务而言),也可将多个微服务合设到同一个进程中,进行高密度部署(对于非核心服务)。服务可以被部署到物理机器上,也可以通过Docker技术实现容器级部署,降低部署成本,提高资源利用率。
  • 支持异构/多种语言:每个服务的实现细节都与其他服务无关,这使得服务之间能够解耦,团队可以针对每个服务选择最合适的开发语言、工具和方法。
  • 服务无状态:所有的微服务都尽量保证无状态或者有状态的可以做状态转移,例如session等数据,可以转移到Redis集群中。
  • 微服务间采用统一的通信模式,如RPC、REST等。
  • 隔离化:每个微服务相互隔离,互不影响。每个微服务运行在自己的进程中,某一个服务出现问题不会影响其他服务。
  • 自动化管理:需要对微服务提供自动化部署和监控预警的能力,实现真正的DevOps。

1.3.4 微服务架构的缺点

1. 复杂度高

微服务间通过REST、RPC等形式交互,需要考虑被调用方故障、过载、消息丢失等各种异常情况,代码逻辑更加复杂。

对于微服务间的事务性操作,因为不同的微服务采用不同的数据库,所以无法利用数据库本身的事务机制保证一致性,需要引入二阶段提交等分布式事务技术。

2. 运维复杂,成本高

在采用微服务架构时,系统由多个独立运行的微服务构成,需要一个设计良好的监控系统对各个微服务的运行状态进行监控。运维人员需要对系统有细致的了解才能够更好地运维系统,微服务架构的引入会带来运维成本的上升。

3. 影响性能

微服务之间通过REST、RPC等形式进行跨进程交互,增加网络IO,通信的时延会受到较大的影响。

4. 依赖更加复杂

微服务架构模式下,应用的改变将会波及多个服务。比如,在完成一个需求时需要修改服务A、B、C,而A依赖B,B依赖C。在单体应用中,只需要改变相关模块,整合变化,部署就好了。对比之下,微服务架构模式就需要考虑相关改变对不同服务的影响。比如,需要更新服务C,然后是B,最后才是A。

1.3.5 微服务架构全景图

微服务架构平台技术体系非常庞大,这里只能简单列举平台基础的技术,具体内容如图1-5所示。

图1-5 微服务架构简单全景图

  • 日志系统(日志中心):主要用于收集和管理微服务应用产生的日志,快速帮助开发人员定位异常,同时还可以在日志系统中搜索历史日志。日志配合告警系统,可以按照日志信息的等级(error、info等)、日志的某一个具体的字段设置告警规则,通过短信、企业微信、邮箱提醒开发人员,帮助开发人员及时发现并解决问题。
  • 监控中心:主要用于实时监控微服务运行情况,比如CPU、内存、QPS、成功率等。设置各项指标的阈值,当微服务应用程序的某一个指标达到设置的阈值时,发出告警提醒开发人员及时处理问题。
  • 配置中心:主要用于统一管理微服务的配置,开发人员或者运维人员通过配置中心可以实时动态更新微服务的配置参数,不需要重启系统,配置的参数即可生效。
  • 网关:主要用于给前端调用提供统一的入口。
  • 部署中心:主要用于编译并打包微服务源码并将其部署到Docker容器中,技术可以选择Jenkins(慢慢淘汰)和GitHub CI(主流)。
  • 注册中心:主要用于管理微服务相关的配置信息,如服务提供者信息,常用的技术有ZooKeeper等。
  • 消息中心:主要用于微服务之间相互解耦,常用的技术有Kafka、RabbitMQ等。大型互联网企业都有自己的消息中心,可以在消息中心管理topic、查看消息队列的消费情况、查看消息队列的消费速度和堆积情况等。如果队列出现消息堆积,那么还会结合监控中心进行告警通知。
  • 追踪中心:主要用于管理微服务的调用轨迹。
  • 容器化:容器化技术促进微服务架构落地,目前流行的技术主要是Docker技术。
  • 应用层:在应用层中主要相关的业务服务有用户服务、订单服务、产品服务等,各个微服务由不同的开发团队管理,每个团队可以选择适合自己业务开发的语言和技术框架,开发语言如Java、Go、PHP,技术架构如目前流行的微服务脚手架Spring Boot。

图1-6只是简单列举微服务中重要的组成部分,企业中用的技术更为复杂,其他子系统更为繁多。

图1-6 微服务架构各部分协调与分工

图1-6只是微服务架构简单的全景图,接下来我们了解各个部分是如何协调与分工的。

  • 开发人员将代码提交到代码仓库(GitHub)。
  • 部署中心从配置中心获取服务相关的配置参数。
  • 部署中心将应用程序和配置文件一同复制到Docker镜像中,并上传镜像到镜像仓库。
  • 服务发布时,从镜像仓库下载指定的镜像,启动并运行容器,容器启动后,会自动将配置信息写入注册中心。
  • 用户通过浏览器或者移动端访问应用系统时,首先请求进入网关。
  • 网关通过服务名称从服务注册中心获取服务所在的IP地址和端口,根据服务地址(IP地址和端口)以反向代理的方式,结合一定的负载均衡策略调用具体的容器。
  • 服务在容器运行过程中会产生大量的日志,这些日志会被收集到日志系统中进行管理,监控中心可以监控容器的运行情况,并通过可视化的报表展示数据(如Grafana技术),追踪中心提供图形化界面查看服务之间的调用轨迹以及所产生的调用时延迟等。

1.3.6 微服务类型

我们根据服务的作用以及特点,将其分为4种类型:基础服务、业务服务、前置服务、组合服务。不同服务迁移的优先级不同。

  • 基础服务:基础组件,与具体的业务无关,比如短信服务、邮件服务等。这种服务最容易拆出来做微服务,是第一优先级分离出来的服务。
  • 业务服务:一些垂直的业务系统,只处理单一的业务类型,比如评论服务、点赞服务、Feed服务等。这类服务职责比较单一,根据业务情况来选择是否迁移,是第二优先级分离出来的服务。
  • 前置服务:前置服务一般为服务的接入或者输出服务,比如网站的前端服务、App的服务接口等,这是第三优先级分离出来的服务。
  • 组合服务:组合服务涉及具体的业务,比如订单服务,需要调用很多垂直的业务服务,这类服务一般放到最后进行微服务化架构改造,因为这类服务最为复杂,除非涉及大的业务逻辑变更,否则不会轻易进行迁移。

1.3.7 微服务拆分原则与步骤

微服务拆分是一个渐进的过程,不能一步到位。服务拆分之前,需要确认公司业务是否适合,是否需要进行微服务化改造,毕竟很多传统的垂直系统是不适合走微服务这一套的;需要梳理系统的业务,对不同的业务进行分类;同时需要加强团队成员的微服务架基础知识的配置,比如Spring Boot技术、Spring Cloud技术等。

在进行微服务改造的过程中,优先对新业务系统进行微服务化,前期可以只有少量的项目进行微服务化改造,随着大家对技术的熟悉度增加,可以加大微服务改造的范围。这里总结几个微服务拆分的步骤:

(1)梳理业务

梳理出业务模块以及模块之间的依赖关联关系。这一过程需要相关的业务人员一起评估。

(2)优先对公共业务进行服务化

优先对公共业务进行服务化,如用户服务、邮箱服务、消息服务等。

(3)对业务服务进行服务化

对业务服务进行服务化,切分的服务之间尽量不要有任何的关联,开始服务化时,先粗粒度地进行服务的划分,之后再慢慢根据业务的情况进行细粒度服务的切分,不必追求一步到位。微服务拆分后,服务之间的依赖关系复杂,如果循环调用,升级的时候就很头疼,不知道应该先升级哪个,后升级哪个,难以维护。

  • 基础服务层以及基础业务服务层主要做数据库的操作和一些简单的业务逻辑,不允许调用其他任何服务。
  • 组合服务层可以调用基础服务层完成复杂的业务逻辑,也可以调用组合服务层,但不允许循环调用,也不允许调用Controller层服务。
  • Controller层服务可以调用组合业务层服务,不允许被其他服务调用。

(4)微服务的领域模型设计

微服务拆分完成后,需要设计每个服务设计的数据库表、表与表之间的关系。数据库表设计需要文档化,方便相关开发人员查阅。

(5)定义微服务接口

数据库表设计完成后,需要定义服务接口,让外部调用。服务接口的入参、出参、方法的名称以及注释等都需要仔细思考,必要时还需要开会评审。服务接口也需要文档化,方便调用者查阅。