vlambda博客
学习文章列表

云计算技术自学(九)-微服务架构

一、微服务架构的概念

      微服务架构,Microservices Architecture,缩写为MSA,是一种架构模式,它提倡将单一应用程序划分为一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API)。每个服务都围绕着具体业务进行构建,并且能够被独立地部署在生产环境、预发布环境等。另外,应尽量避免统一的、集中式的服务管理机制,对具体一个服务而言,应根据应用上下文,选择合适的语言、工具对其进行构建。

避免或解决的问题:集中式服务管理机制带来的性能瓶颈问题、单点故障问题、开发缓慢问题等。

      主要特征包括如下:由分布式服务组成、围绕业务进行划分、业务数据独立(无状态服务)、快速演化和迭代。

      主要优势包括:

     1、是每个微服务组件都是简单灵活的,能够独立部署。应用不需要一个庞大的应用服务器来支撑。

     2、可以由一个小团队负责更专注专业,相应的也就更高效可靠。

     3、微服务之间是松耦合的,微服务内部是高内聚的,每个微服务很容易按需扩展。

     4、微服务架构与语言工具无关,自由选择合适的语言和工具,高效的完成业务目标即可。

1.1 微服务和SOA

     SOA于1993年在Gartner报告中首次提出,其主要应对的是大规模软件开发的软件危机问题,将功能定义为了高内聚低耦合的独立服务,服务之间通过交互和协调完成业务的整体逻辑,服务之间通过服务总线(ESB)或流程管理进行连接和通信。

      SOA的核心技术组件包括UDDI、WSDL、SOAP协议,其中UDDI是服务注册和服务发现的协议,WSDL则是对服务进行描述,SOAP则是服务之间调用的协议。

       微服务和SOA虽然都强调服务化和敏捷化,但两者之间有区别,主要区别如下:

1、SOA强调重用,微服务强调重写或迭代开发

SOA中,为了避免重复造轮子,定义了相关服务发现、路由和调用的协议和架构,服务层次之间有很强的逻辑调用关系和规则。微服务则是要求更低的耦合度,便于对模块进行更好的剥离和敏捷重写,模块之间不用采用统一语言和框架,是可以独立部署的。

2、SOA进行水平分层服务,微服务则是垂直分层服务

SOA以水平分层服务为主,不同层级之间明确调用逻辑,也便于层级之间通过ESB进行集中管控;微服务则是按照独立业务处理进行分割,微服务之间调用没有总线、十分松散,采用HTTP/REST等进行通信交互

3、SOA适合自顶向下构建,微服务则是自底向上

SOA需要进行整体的服务规划和服务依赖制定,但微服务则可以从小、少开始,逐步增多

1.2 微服务和云原生

      云原生的概念由Pivotal公司于2015年首次提出,其最初定义中,云原生架构具有面向微服务、自服务敏捷、基于API协作、抗脆弱等特征。CNCF给出元原生三大特征为容器化封装、动态管理、面向微服务。

目前公认的云原生包括四个层面:

1、微服务:轻量级HTTP API接口,可独立进行部署、迭代

2、DevOps:开发运维一体,快速部署、自动发布

3、持续交付

4、容器化:作为微服务的载体

二、微服务架构的设计

2.1 架构设计原则

     微服务架构设计的原则包括:AFK扩展拆分、前后端分离、REST通信风格。

一、AFK扩展拆分

AKF扩展立方体,是一个叫AKF的公司的技术专家抽象总结的应用扩展的三个维度(如下图所示)。理论上按照这三个扩展模式,可以将一个单体系统,进行无限扩展,需要基于不同业务进行扩展拆分。

X 轴 :指的是水平复制,很好理解,就是讲单体系统多运行几个实例,做个集群加负载均衡的模式。

Z 轴 :是基于类似的数据分区,比如一个互联网打车应用突然上线,用户量激增,集群模式撑不住了,那就按照用户请求的地区进行数据分区,北京、上海、四川等多建几个集群。

Y 轴 :就是所说的微服务的拆分模式,就是基于不同的业务拆分。


二、前后端分离

前后端代码进行分离,并采用物理分离方式部署。

三、无状态服务

如果一个数据需要被多个服务共享,才能完成一笔交易,那么这个数据被称为状态。进而依赖这个“状态”数据的服务被称为有状态服务,反之称为无状态服务。

状态服务原则并不是说在微服务架构里就不允许存在状态,表达的真实意思是要把有状态的业务服务改变为无状态的计算类服务,那么状态数据也就相应的迁移到对应的“有状态数据服务”中。


四、REST通信风格

RESTful是一种架构的规范与约束、原则,基于HTTP,可使用XML或JSON进行定义。其将资源统一用URL进行标识,方便的进行新增、变更、删除。

采用REST通信风格,一方面是无状态HTTP,扩展能力强;另一方面和语言无关。

2.2 常见设计模式

1、聚合器微服务设计模式

这是一种最常见也最简单的设计模式。

云计算技术自学(九)-微服务架构


聚合器调用多个服务实现应用程序所需的功能。它可以是一个简单的Web页面,将检索到的数据进行处理展示。它也可以是一个更高层次的组合微服务,对检索到的数据增加业务逻辑后进一步发布成一个新的微服务,这符合DRY原则。另外,每个服务都有自己的缓存和数据库。如果聚合器是一个组合服务,那么它也有自己的缓存和数据库。聚合器可以沿X轴和Z轴独立扩展。

2、代理微服务设计模式

这是聚合模式的一个变种,如下图所示。

云计算技术自学(九)-微服务架构


在这种情况下,客户端并不聚合数据,但会根据业务需求的差别调用不同的微服务。代理可以仅仅委派请求,也可以进行数据转换工作。

3、链式微服务设计模式

这种模式在接收到请求后会产生一个经过合并的响应,如下图所示:

云计算技术自学(九)-微服务架构


在这种情况下,服务A接收到请求后会与服务B进行通信,类似地,服务B会同服务C进行通信。所有服务都使用同步消息传递。在整个链式调用完成之前,客户端会一直阻塞。

因此,服务调用链不宜过长,以免客户端长时间等待。

4、分支微服务设计模式

这种模式是聚合器模式的扩展,允许同时调用两个微服务链,如下图所示。

云计算技术自学(九)-微服务架构


5、数据共享微服务设计模式

      自治是微服务的设计原则之一,就是说微服务是全栈式服务。但在重构现有的“单体应用(monolithic application)”时,SQL数据库反规范化可能会导致数据重复和不一致。

      因此,在单体应用到微服务架构的过渡阶段,可以使用这种设计模式,如下图所示。

云计算技术自学(九)-微服务架构


       在这种情况下,部分微服务可能会共享缓存和数据库存储。不过,这只有在两个服务之间存在强耦合关系时才可以。对于基于微服务的新建应用程序而言,这是一种反模式。

6、异步消息传递微服务设计模式

      虽然REST设计模式非常流行,但它是同步的,会造成阻塞。因此部分基于微服务的架构可能会选择使用消息队列代替REST请求/响应,如下图所示。

云计算技术自学(九)-微服务架构


三、关键技术    

3.1 微服务拆分

    微服务拆分时应考虑如下原则:高内聚低耦合、以业务模型切入、演进式拆分。

   目前的拆分维度主要有如下三种:

     1、纵向和横向拆分:纵向根据业务维度进行,标准是按照业务的关联程度来决定,关联比较密切的业务适合拆分为一个微服务,而功能相对比较独立的业务适合单独拆分为一个微服务;横向则是功能独立性拆分,按照是否有公共的被多个其他服务调用,且依赖的资源独立不与其他业务耦合。

     2、考虑维护、组织、发布的角度:拆分前后维护成本要更低(人力、物力和时间)、符合组织架构、便于软件频繁发布

     3、在考虑业务需求下,综合考虑稳定、可靠、性能。

     稳定性:将系统中的业务模块按照稳定性进行排序。稳定的、不经常修改的划分一块;将不稳定的,经常修改的划分为一个独立服务。比如日志服务、监控服务都是相对稳定的服务,可以归到一起。这个很类似上面提到的2/8原则,80%的业务是稳定的。

      可靠性:将系统中的业务模块按照可靠性进行排序。对可靠性要求比较高的核心模块归在一起,对可靠性要求不高的非核心模块归在一块。

性能:将系统中的业务模块按照对性能的要求进行优先级排序。把对性能要求较高的模块独立成一个服务,对性能要求不高的放在一起。

3.2 服务网关

     服务网关或API网关是一个处于应用程序或服务(提供 REST API 接口服务)之前的系统,用来管理授权、访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的调用者透明。

      其主要功能包括:请求接入口,业务聚合点,进行安全验证和路由过滤等,对API服务和策略进行统一管理。

3.3 访问限流

      限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量已达到保护系统的目的。一般来说系统的吞吐量是可以被测算的,为了保证系统的稳定运行,一旦达到的需要限制的阈值,就需要限制流量并采取一些措施以完成限制流量的目的。比如:延迟处理,拒绝处理,或者部分拒绝处理等等。

常见限流措施有:计数器法、固定窗口、滑动窗口、Leaky Bucket漏桶、Token Bucket令牌桶。

1、计数器法:在保存一个计数器,处理一个请求,计数器加一,一个请求处理完毕后计数器减一。每次请求进来的时候,先看一眼计数器的值,如果超过阀值则直接拒绝。

2、固定窗口:以一段时间窗口内的访问量作为限流的依据,计数器每过一个时间窗口就自动重置。其规则如下:请求次数小于阀值,允许访问,计数器加1;请求次数大于阀值,拒绝访问;本时间窗口过了之后,计数器自动清零。

3、滑动窗口:相对于固定窗口,滑动窗口除了需要引入计数器外,还需要额外记录时间窗口内每个请求到达的时间点。以时间窗口为1秒为例,规则如下:记录每次请求的时间;统计每次请求的时间向前推1秒这个时间窗口内的请求数,且1秒前的数据可以删除;统计的请求数小于阀值则记录该请求的时间,并允许通过,反之则拒绝该请求。

4、Leaky Bucket漏桶:流量持续进入漏桶中,底部则定速处理请求,如果流量进入的速率高于底部请求被处理的速率,且当桶中的流量超过桶的大小时,流量就会被溢出。漏桶算法的特点是宽进严出,无论请求的速率有多大,底部的处理速度都匀速进行。这种算法的特点有点类似于消息队列的处理机制,一般来说漏桶算法也是由队列来实现的。

5、令牌桶限流:令牌桶与漏桶的原理类似,只是漏桶是底部匀速处理,而令牌桶则是定速的向桶里塞入令牌,然后请求只有拿到了令牌才会被服务器处理。具体规则如下:定速的向桶中放入令牌;令牌数量超过桶的限制,则丢弃;请求来了先向桶中索取令牌,索取成功则通过被处理,否则拒绝;

3.4 通信机制

       微服务之间的通信方式分为同步和异步两种,其中同步有RPC、REST等,异步主要是依靠消息队列进行。消息队列的协议包括AMQP、RabbitMQ、Kafka等。

云计算技术自学(九)-微服务架构


3.5 容错机制

       一个服务依赖的服务可能出错、超时或者宕机,如果没有及时发现和隔离问题,或者在设计中没有考虑如何应对这样的问题,那么很可能在短时间内服务的线程池中的线程被用满、资源耗尽,导致出现雪崩效应。

       主要的微服务容错机制包括如下。

1、舱壁隔离模式

可以对微服务容器进行分组,例如将微服务的每个节点的服务池分为三组:准生产环境、灰度环境和生产环境。准生产环境供内侧使用;灰度环境会跑一些普通流量;大部分生产流量则跑在生产环境中。这样,在一次比较大的重构过程中,就可以充分利用灰度环境的隔离性进行预验证,用普通流量验证重构没有问题后,再上生产环境。

线程池隔离:不同的方法调用进入到不同的线程之中。

2、限流:详细可以见3.3章节。

3、熔断模式:默认情况下,熔断器处于闭合状态,当熔断指标达到阈值时,熔断器状态变为打开状态,此时,所有请求直接返回或抛出异常。过了一段时间后,后端异常服务经过开发运维人员及时解决了问题,则可进入半开启状态,可以按一定规则,允许部分请求通过,试探性的调用被隔离的服务,若请求如是健康状态,则恢复关闭熔断器。

4、回退:一旦发生微服务的调用失败,直接进行fallback机制。

5、失效转移模式:若微服务架构中发生了熔断和限流,则该如何处理被拒绝的请求呢?解决这个问题的模式叫作失效转移模式,通常分为下面几种。

采用快速失败的策略,直接返回使用方错误,让使用方知道发生了问题并自行决定后续处理;

是否有备份服务,如果有备份服务,则迅速切换到备份服务;

失败的服务有可能是某台机器有问题,而不是所有机器有问题,在这种情况下适合使用重试的方法来解决,但是这种方法要求服务提供者的服务实现了幂等性(一个操作,不管做多少次,产生效果或返回的结果都是一样的)。

四、常见微服务架构

       常见的服务框架中的主要技术框架选型如下所示。这里主要是Java语言框架,还有.Net相关微服务框架,包括.NET Core、Service Fabric等。

类型

框架

服务治理

Dubbo、Spring Cloud

API网关

Zuul、traefik、OpenRestry、Kong

服务注册与发现

Zookeeper、Eureka、Consul、etcd

配置中心

Spring Cloud Config、Apollo、Nacos

链路追踪和监控

Zipkin、CAT

4.1 Spring  Cloud

云计算技术自学(九)-微服务架构

   

    Spring Cloud整体架构如上图所示,拍拍贷资深架构师杨波老师就根据自己的实际经验以及对Spring Cloud的深入调研,并结合国内一线互联网大厂的开源项目应用实践结果,认为Spring Cloud技术栈中的有些组件离生产级开发尚有一定距离,最后提出了一个可供中小团队参考的微服务架构技术栈,又被称为“中国特色的微服务架构技术栈1.0“。其中几个重要组件如下。

云计算技术自学(九)-微服务架构


1、Eureka

提供服务注册、服务续约、服务查询、服务下线、服务提出等功能,生产者可以将服务发布和注册,消费者可以查询并请求服务。消费者对于已经在使用的服务,有一定的期限,过期则需要进行续约。

2、Ribbon

提供负载均衡能力,Ribbon运行在消费者侧,由消费者根据一定负载均衡策略进行服务访问。这点和Nginx不同,Nginx为集中部署,前端需求都发送其上后进行后端服务访问。Ribbon提供的策略包括轮询策略、随机策略等。

3、Zuul

网关组件,是系统对外的入口,介于客户端与服务器端之间,用于对请求进行鉴权、限流、 路由、监控等功能。

4、Hytrix

Hytrix是一个库,可通过添加等待时间容限和容错逻辑来帮助控制这些分布式服务之间的交互。分布式环境下,服务之间依赖较多,如果某个服务出现突然并发、资源耗尽时,无法对外提供服务,这回导致请求沿着调用链向上传递,出现雪崩效应。Hytrix主要是通过资源隔离、降级和熔断来避免雪崩。

熔断是当指定时间窗内的请求失败率达到设定阈值时,系统将通过断路器直接将此请求链路断开。降级是为了更好的用户体验,当一个方法调用异常时,通过执行另一种代码逻辑来给用户友好的回复。

4.2 Dubbo

       Dubbo为阿里内部SOA解决方案的开源,2020年发布3.0版本(2011年正式发布2.0.7第一个开源版本),其整体架构如下图所示。在协议层方面,调用方式从初始支持RPC,变为支持REST和Redis;在负载均衡方面,支持Pull-based LB;在注册方面,除了之前的ZooKeeper外,新增支持Eureka。总的来说,其整体向云原生靠近。

云计算技术自学(九)-微服务架构


4.3 两者对比

      Dubbo和Java绑定,属于第一代服务框架;Spring Cloud适合混合开发模式,属于第二代服务框架。两者的主要区别如下。当然,这个对比表显然没有将3.0更新纳入,3.0中Dubbo将支持REST服务调用方式,此外,还支持Service Mesh功能,独立的dubbo-mesh进行路由、负载均衡和熔断,整体将更加完善。

云计算技术自学(九)-微服务架构


4.4 技术演进

      随着技术的不断发展,出现了第三代服务框架sevice mesh。其最早由Buoyant公司在2016年提出,后续又出现了以Linkerd为代表的第一代和以lstio为第二代的Service Mesh技术。

      Service Mesh主要解决的问题:TCP/IP解决了异构系统的通信问题,但是随着微服务、分布式的兴起,微服务之间的依赖和调用关系复杂,给服务分布式的治理带来了极大难度,需要一套分布式的通用语义来满足服务发现、认证、授权、负载均衡、熔断等,这套语义就类似于微服务之间通信中的TCP/IP。早期微服务架构Spring Cloud中虽然定义了这一套分布式的语义,屏蔽了一些通信细节,但是本身管理复杂、具有语言相关性。

      第一代Service Mesh:以Linkerd,Envoy,NginxMesh为代表的代理模式(边车模式),它将分布式服务的通信抽象为单独一层,在这一层中实现负载均衡、服务发现、认证授权、监控追踪、流量控制等分布式系统所需要的功能,作为一个和服务对等的代理服务,和服务部署在一起,接管服务的流量,通过代理之间的通信间接完成服务之间的通信请求。


      第二代Service Mesh:第一代Service Mesh由一系列独立运行的单机代理服务构成,为了提供统一的上层运维入口,演化出了集中式的控制面板,所有的单机代理组件通过和控制面板交互进行网络拓扑策略的更新和单机数据的汇报。这就是以Istio为代表的第二代Service Mesh。和TCP/IP的扁平化分布式处理到SDN集中控制的演变很相似。