访问量
访客数

微服务治理详解

2021.06.21 阅读量

什么是微服务架构

In short, the microservice architectural style is an approach to developing a single application as a suite of small services,each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API。 These services are built around business capabilities and independently deployable by fully automated deployment machinery。 There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies。 ——James Lewis and Martin Fowler (2014)

大意:简而言之,微服务体系结构风格是一种将单个应用程序开发为一套小型服务的方法,每个服务运行在自己的进程中,并与轻量级机制(通常是HTTP资源API)通信。 这些服务是围绕业务功能构建的,可以通过全自动部署机制进行独立部署。对这些服务的集中管理是最低限度的,这些服务可能用不同的编程语言编写,并使用不同的数据存储技术。

微服务是一种架构风格,将服务围绕业务功能拆分,一个应用拆分为一组小型服务,每个服务运行在自己的进程内,也就是可独立部署和升级,服务之间使用轻量级HTTP交互,可以由全自动部署机制独立部署,去中心化,服务自治。服务可以使用不同的语言、不同的存储技术。

为什么要使用微服务

说起微服务就不得不说微服务之前的单体应用,有对比才能看的出微服务对比之前的单体应用到底强在哪?为什么我们要抛弃单体应用?

  • 单体应用往往所有的功能打包在一个包里,包含了 DO/DAO,Service,UI等所有逻辑,如果要修改一部分代码就需要全部重新部署,所以改动影响大,风险高。微服务模式弥补了这个缺陷,它将应用合理的拆分,实现敏捷开发和快速部署。
  • 单体应用只能通过在负载均衡器后面放置整个应用程序的多个实例来进行水平扩展,如果想要扩展特定的程序,显然是不行的,则需要使用到微服务模式实现。
  • 因为单体应用所有的功能全部都达到一个包里面,所以耦合性比较强,对于程序员来说代码维护会收到影响,出现了什么问题也不太方便进行排查。
  • 如果没有正确的设计,单体应用的一部分失败可能会级联并导致整个系统崩溃。而这种情况可以使用微服务的熔断、降级的思想来解决。

微服务虽然好处多多,但是缺点就是服务众多,不好管理,这也就是为什么要治理微服务,治理成本高,不利于维护系统,项目成本会升高,所以项目架构设计时应综合考量。

建议设计的原则是业务驱动、设计保障、演进式迭代、保守治疗的方式。搞不清楚,有争议的地方先尽量不要拆,如果确实要拆,要经过业务分析后慎重设计,把真正相对独立的部分拆分出来,可以借鉴 DDD 的方式。拆了以后要观察微服务的接口是否稳定,针对业务需求的变更微服务的模块是否可以保持相对稳定,是否可以独立演进。

DDD: Domain-driven design的简称,译为领域驱动设计,是一种通过将实现连接到持续进化的模型来满足复杂需求的软件开发方法。
核心思想:DDD其实是面向对象方法论的一个升华。无外乎是通过划分领域(聚合根、实体、值对象)、领域行为封装到领域对象(充血模式)、内外交互封装到防腐层、职责封装到对应的模块和分层,从而实现了高内聚低耦合。

单体架构如何转向微服务架构

从单体架构迁移到微服务架构时,避免大规模重写代码,重写代码听起来很好但风险较大,如果大规模重写代码可能会导致程序出现各种各样的bug。(当你承担重建一套全新基于微服务的应用程序不需要使用时候,可以采用重写这种方法)

逐步迁移单体应用的功能,独立出来形成新的微服务,同时需要与旧的单体应用集成,这可以保证系统的正常运行,单体式应用在整个架构中比例逐渐下降直到消失或者成为微服务架构一部分;

虽然在逐步的迁移功能,但是也会有源源不断的需求需要开发,此时该停止让单体式应用继续变大,也就是说当开发新功能时不应该为旧单体应用添加新代码,应该是将新功能开发成独立微服务;

一个巨大的复杂单体应用由成十上百个模块构成,每个都是被抽取对象。决定第一个被抽取模块一般都是挑战,一般最好是从最容易抽取的模块开始,这会让开发者积累足够经验,这些经验可以为后续模块化工作带来巨大好处。

转换模块成为微服务一般很耗费时间,一般可以根据获益程度来排序,一般从经常变化模块开始会获益最大。一旦转换一个模块为微服务,就可以将其开发部署成独立模块,从而加速开发进程。比如,抽取一些消耗内存资源较大的代码,将其弄成一个服务,然后可以将其部署在大内存主机上。同样的,将对计算资源很敏感的算法应用抽取出来也是非常有益的,这种服务可以被部署在有很多 CPU 的主机上。 有三种策略可以考虑:将新功能以微服务方式实现;将表现层与业务数据访问层分离;将现存模块抽取变成微服务。

首先要将抽离的相关功能封装成相关几个方法、几个类、几个包中;定义好模块和单体应用之间的几个大概的接口,从程序内部开始调用;一旦完成相关的接口,也就将此模块转换成独立微服务。为了实现,必须写代码使得单体应用和微服务之间通过使用进程间通信机制的API来交换信息;服务和单体应用整合的API代码就成为了容灾层;

然后将抽离的功能转化为独立的服务进行部署,将其整合成一个微服务基础框架。每抽取一个服务,就朝着微服务方向前进一步,随着时间推移,单体应用将会越来越简单,用户就可以增加更多独立的微服务。

服务治理

服务治理是指在分布式系统、微服务架构中,通过技术和策略管理、监控、优化各个服务之间的交互,来确保整个系统的高效、稳定和可靠运行。 它不仅包括技术实现,还涉及设计规范和策略,目的是为了解决服务调用、资源分配和故障处理等关键问题。

服务治理治的是什么

拿城市交通规划举例,在进行城市交通规划之前首先要做的第一个事情是收集信息,要能够知道这个城市发生了什么,所以在各个路口需要安装采集探头,记录车来车往的信息。有了信息以后就需要对信息进行分析了,那么就需要可视化的图形界面,能够一眼就看出什么地方出了问题,通往哪个工厂的路坏了。发现了问题就要解决问题了,限制一下拥堵路段的流量,把去往一个公园的车辆导向到另外一个类似的公园。最后,如果把城市作为一个国家来考虑,那么每个进入这个城市的车辆都需要进行检查,看看有没有携带违禁品,最后给这些不熟悉道路的外地车规划路线。通过上面这个思考的过程,我们发现要对一个城市进行治理的时候,第一要采集信息,然后要能够对采集的信息进行监控和分析,最后根据分析的结果采取对应的治理策略。另外从整体安全的角度考虑还需要一个守门人。

用同样的思路来思考服务治理,网关就是整个整体的守门人,日志采集,追踪工具,服务注册发现都是用来采集信息的,然后需要监控平台来展现这些采集的信息,并进行监控和分析。最后根据分析的结果采取治理策略,有的服务快撑不住了要限流,有的服务坏了要熔断,并且还能够及时的调整这些服务的配置。

分布式架构-011

服务注册与发现

服务注册与发现是微服务架构中的核心概念。在微服务架构中,服务数量众多,服务间通信变得尤为重要。为了实现动态的服务间通信,每个服务都需要将自己的信息注册到注册中心,以便其他服务能够发现并调用。

  • 服务注册:服务提供者在启动时将自己的信息(如IP地址、端口号等)注册到注册中心。
  • 服务发现:服务消费者从注册中心查询所需服务的地址信息,实现动态调用。

当服务器启动的时候,会把当前自己服务器的信息,比如:服务地址通讯地址等,以别名方式注册到注册中心上。另一方消费者服务提供者,以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用。 RPC远程调用框架核心设计思想在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系。在任何RPC远程框架中,都会有一个注册中心存放服务地址相关信息。

服务熔断降级

由于分布式架构中应用程序依赖关系可能非常多,每个依赖关系在某些时候将不可避免地失败,针对服务调用失败,为了缩小调用失败的影响,引入了服务熔断降级的思想。

多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”。

  • 服务熔断:熔断机制是应对雪崩效应的一种微服务链路保护机制,当链路的某个微服务不可用或者响应时间太长时,将快速的返回设置好的提示信息。
  • 服务降级:服务降级一般是指在服务器压力剧增的时候,根据实际业务使用情况以及流量,对一些服务和页面有策略的不处理或者用一种简单的方式进行处理,从而释放服务器资源的资源以保证核心业务的正常高效运行。

服务熔断是应对系统服务雪崩的一种保险措施,给出的一种特殊降级措施。而服务降级则是更加宽泛的概念,主要是对系统整体资源的合理分配以应对压力。服务熔断可看作特殊降级。

服务网关

微服务背景下,一个系统被拆分为多个服务,但是像安全认证、流量控制、日志、监控等功能是每个服务都需要的,没有网关的话,我们就需要在每个服务中单独实现,这使得我们做了很多重复的事情并且没有一个全局的视图来统一管理这些功能。

分布式架构-005

网关是作为一个用来保护、增强和控制对API服务访问的角色。网关是一个处于应用程序或服务之前的系统,用来管理授权、访问控制和流量限制等,这样 REST API 接口服务就被网关保护起来,对所有的调用者透明。 因此,隐藏在网关后面的业务系统就可以专注于创建和管理服务,而不用去处理这些策略性的基础设施。

服务调用

服务调用可以说是微服务中最关键的,如果没有服务调用不会出现熔断降级、也不会出现服务负载、服务注册,分布式的服务治理可以说是围绕服务调用展开的。

常见的服务之间的调用方式有两种:

  • RPC远程过程调用:定义数据式,基于原生TCP通信,速度快,效率高。早期的WedService,现在热门的Dubbo,都是RPC的典型代表;
  • Http调用:Http是一种网络传输协议,基于TCP,规定了数据传输的格式。现在客户端浏览器与服务器端通信基本都是采用HTTP协议,也可以用来进行远程服务调用。缺点是消息封装臃肿,优势是对服务的提供和调用方没有任何技术限定,自由灵活,更符合为服务理念;

现在热门的Rest风格,就可以通过HTTP协议来实现,如果公司全部采用Java技术栈,那么使用Dubbo作为微服务框架是一个不错的选择;如果公司的技术栈多样化,而且你更青睐Spring家族,那么SpringCloud搭建微服务是不二之选。

服务负载均衡

负载均衡,英文名称为Load Balance,其含义就是指将工作任务进行平衡、分摊到多个操作单元上进行运行,例如FTP服务器、Web服务器、企业核心应用服务器和其它主要任务服务器等,从而协同完成工作任务。

当前服务与服务之间进行相互调用时,在分布式架构下应用都是集群部署,所以这个时候就需要进行服务负载,即将收到的请求分摊到对应的服务器上,从而达到系统的高可用。

常见负载均衡算法:

  • 随机分配:随机选择一台服务器来分配任务。它保证了请求的分散性达到了均衡的目的。同时它是没有状态的不需要维持上次的选择状态和均衡因子。但是随着任务量的增大,它的效果趋向轮询后也会具有轮询算法的部分缺点;
  • 轮询分配:将任务分配给此时具有最小连接数的节点,因此它是动态负载均衡算法。一个节点收到一个任务后连接数就会加1,当节点故障时就将节点权值设置为0,不再给节点分配任务;
  • 最小连接分配:将任务分配给此时具有最小连接数的节点,因此它是动态负载均衡算法。一个节点收到一个任务后连接数就会加1,当节点故障时就将节点权值设置为0,不再给节点分配任务;
  • hash分配:对请求中的关键信息进行hash计算,hash值相同的请求分配到同一台服务器,具体原理可查看HashMap分配原理;
  • 根据性能分配:根据服务器的响应时间来进行任务分配,优先将新任务分配给响应最快的服务器;

服务配置中心

在分布式微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。

配置中心应运而生。配置中心,顾名思义,就是来统一管理项目中所有配置的系统。对于单机版称之为配置文件,而对于分布式集群系统称之为配置中心。 它的作用是负责统一管理不同环境和集群的配置,解耦应用与配置,同时支持版本发布与管理。

微服务解决方案

随着微服务模式的使用,服务之间的调用带来的问题有很多,如数据一致性、网络波动、缓存、事务等问题。 根据服务治理的思想,并针对这一系列的问题,目前主流一站式微服务解决方案有SpringCloudSpringCloud Alibaba及以Dubbo构成的体系。

功能模块 Spring Cloud Dubbo Spring Cloud Alibaba
服务注册与发现 Eureka、Consul、Zookeeper Zookeeper、Nacos Nacos
熔断与限流 Resilience4j、Hystrix(已停更,逐步替换) 无(需外部集成) Sentinel(限流、熔断、降级)
负载均衡 Ribbon、Spring Cloud LoadBalancer、Feign Dubbo Nacos、Spring Cloud LoadBalancer
配置管理 Spring Cloud Config、Consul 无(可与 Nacos、Apollo 集成) Nacos
API 网关 Spring Cloud Gateway、Zuul(已停更) 无(需外部集成) Spring Cloud Gateway
分布式追踪 Spring Cloud Sleuth + Zipkin / Jaeger Skywalking、Zipkin Spring Cloud Sleuth + Zipkin / Skywalking
服务调用 Feign、RestTemplate Dubbo Feign、RestTemplate、Dubbo
分布式事务 Seata、TCC-Transaction、SAGA TCC、XA 等协议 Seata(支持 AT、TCC、SAGA 模式)
通信协议 HTTP/REST、gRPC(需扩展支持) Dubbo 协议(基于高效 RPC 调用) HTTP/REST、Dubbo 协议
适用场景 面向全栈微服务治理,适合通用微服务架构 高性能 RPC 调用,适合低延迟、高并发场景 增强版 Spring Cloud,适合国内复杂分布式场景
生态成熟度 完善,社区活跃 成熟,广泛应用于国内企业 活跃,逐渐成为国内微服务治理首选

SpringCloud体系

SpringCloud它并不是一个框架,而是很多个框架。它是分布式微服务架构的一站式解决方案,是多种微服务架构落地技术的集合体,俗称微服务全家桶。

分布式架构-001

分布式架构-010

Eureka

Eureka is a REST (Representational State Transfer) based service that is primarily used in the AWS cloud for locating services for the purpose of load balancing and failover of middle-tier servers。 We call this service, the Eureka Server。 Eureka also comes with a Java-based client component,the Eureka Client, which makes interactions with the service much easier。 The client also has a built-in load balancer that does basic round-robin load balancing。 —https://github.com/Netflix/eureka

大意:Eureka是一个REST (Representational State Transfer)服务,它主要用于AWS云,用于定位服务,以实现中间层服务器的负载平衡和故障转移,我们称此服务为Eureka服务器。Eureka也有一个基于java的客户端组件,Eureka客户端,这使得与服务的交互更加容易,同时客户端也有一个内置的负载平衡器,它执行基本的循环负载均衡。

Eureka采用了Client / Server 的设计架构,提供了完整的服务注册和服务发现,可以和 SpringCloud 无缝集成。Eureka Sever作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。

分布式架构-002

  • Eureka Server 提供服务注册服务;各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
  • Eureka Client 通过注册中心进行访问;它是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的使用轮询负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳默认周期为30秒。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除,默认90秒。
    • Service Provider:服务提供方,将自身服务注册到Eureka,从而使服务消费方能够找到;
    • Service Consumer:服务消费方,从Eureka获取注册服务列表,从而能够消费服务;

默认使用下,Eureka有一个自我保护机制,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。 但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。

自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使Eureka集群更加的健壮、稳定的运行。 如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制:

  • Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务;
  • Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用;
  • 当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中;

简而言之,某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存。所以Eureka属于AP

Hystrix

当微服务系统的一个服务出现故障时,故障会沿着服务的调用链路在系统中疯狂蔓延,最终导致整个微服务系统的瘫痪,这就是“雪崩效应”。为了防止此类事件的发生,微服务架构引入了“断路器”的一系列服务容错和保护机制。

“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

SpringCloud Hystrix 是基于 Netflix 公司的开源组件 Hystrix 实现的,它提供了熔断器功能,能够有效地阻止分布式微服务系统中出现联动故障,以提高微服务系统的弹性。SpringCloud Hystrix 具有服务降级、服务熔断、线程隔离、请求缓存、请求合并以及实时故障监控等强大功能。

熔断机制是为了应对雪崩效应而出现的一种微服务链路保护机制。 当微服务系统中的某个微服务不可用或响应时间太长时,为了保护系统的整体可用性,熔断器会暂时切断请求对该服务的调用,并快速返回一个友好的错误响应。这种熔断状态不是永久的,在经历了一定的时间后,熔断器会再次检测该微服务是否恢复正常,若服务恢复正常则恢复其调用链路。

分布式架构-003

在熔断机制中涉及了三种熔断状态:

  • 熔断打开:请求不再进行调用当前服务,内部定时一般为MTTR(平均故障处理时间),当打开时长达到所设的时长则进入半熔断状态;
  • 熔断半开:部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断;
  • 熔断关闭:熔断关闭不会对服务进行熔断;

Hystrix 实现服务熔断的步骤如下:

  1. 当服务的调用出错率达到或超过 Hystix 规定的比率(默认为 50%)后,熔断器进入熔断开启状态。
  2. 熔断器进入熔断开启状态后,Hystrix 会启动一个休眠时间窗,在这个时间窗内,该服务的降级逻辑会临时充当业务主逻辑,而原来的业务主逻辑不可用。
  3. 当有请求再次调用该服务时,会直接调用降级逻辑快速地返回失败响应,以避免系统雪崩。
  4. 当休眠时间窗到期后,Hystrix 会进入半熔断转态,允许部分请求对服务原来的主业务逻辑进行调用,并监控其调用成功率。
  5. 如果调用成功率达到预期,则说明服务已恢复正常,Hystrix 进入熔断关闭状态,服务原来的主业务逻辑恢复;否则 Hystrix 重新进入熔断开启状态,休眠时间窗口重新计时,继续重复第 2 到第 5 步。

Hystrix工作流程

分布式架构-004

  1. 创建HystrixCommandHystrixObserableCommand对象。
  2. 命令执行:
    • 其中HystrixCommand实现了下面前两种执行方式:
      • execute:同步执行,从依赖的服务返回一个单一的结果对象或是在发生错误的时候抛出异常。
      • queue:异步执行,直接返回一个Future对象,其中包含了服务执行结束时要返回的单一结果对象。
    • HystrixObservableCommand实现了后两种执行方式:
      • obseve():返回Observable对象,它代表了操作的多个结果,它是一个Hot Observable,不论“事件源”是否有“订阅者”,都会在创建后对事件进行发布,所以对于Hot Observable的每一个“订阅者”都有可能是从“事件源”的中途开始的,并可能只是看到了整个操作的局部过程。
      • toObservable():同样会返回Observable对象,也代表了操作的多个结果,但它返回的是一个Cold Observable,没有“订间者”的时候并不会发布事件,而是进行等待,直到有“订阅者"之后才发布事件,所以对于Cold Observable 的订阅者,它可以保证从一开始看到整个操作的全部过程。
  3. 判断当前命令的请求缓存功能是被启用的,并且该命令缓存命中,那么缓存的结果会立即以Observable对象的形式返回。
  4. 检查断路器是否为打开状态。如果断路器是打开的,那么Hystrix不会执行命令,而是转接到fallback处理逻辑(第8步);如果断路器是关闭的,检查是否有可用资源来执行命令(第5步)。
  5. 判断线程池/请求队列信号量是否占满。如果命令依赖服务的专有线程地和请求队列,或者信号量已经被占满,那么Hystrix也不会执行命令,而是转接到fallback处理理辑(第8步) 。
  6. Hystrix会根据我们编写的方法来决定采取什么样的方式去请求依赖服务:
    • HystrixCommand.run():返回一个单一的结果,或者抛出异常。
    • HystrixObservableCommand.construct():返回一个Observable对象来发射多个结果,或通过onError发送错误通知。
  7. Hystrix会将“成功”、“失败”、“拒绝”、“超时” 等信息报告给断路器,而断路器会维护一组计数器来统计这些数据。断路器会使用这些统计数据来决定是否要将断路器打开,来对某个依赖服务的请求进行"熔断/短路”。
  8. 当命令执行失败的时候,Hystrix会进入fallback尝试回退处理,我们通常也称波操作为“服务降级”。而能够引起服务降级处理的情况有下面几种:
    • 第4步∶当前命令处于“熔断/短路”状态,断路器是打开的时候。
    • 第5步∶当前命令的线程池、请求队列或者信号量被占满的时候。
    • 第6步∶HystrixObsevableCommand.construct()HytrixCommand.run()抛出异常的时候。
  9. 当Hystrix命令执行成功之后,它会将处理结果直接返回或是以Observable的形式返回给调用方。

Gateway

SpringCloud Gateway 属于 SpringCloud 生态系统中的网关,其诞生的目标是为了替代老牌网关 Zuul。准确点来说,应该是 Zuul 1.x。SpringCloud Gateway 起步要比 Zuul 2.x 更早。 为了提升网关的性能,SpringCloud Gateway 基于 Spring WebFlux 。Spring WebFlux 使用 Reactor 库来实现响应式编程模型,底层基于 Netty 实现同步非阻塞的 I/O。

分布式架构-012

Gateway 核心组件:

  • Route:路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如断言为true则匹配该路由;
  • Predicate:断言参考的是Java8的java.util.function。Predicate,开发人员可以匹配HTTP请求中的所有内容,例如请求头或请求参数,如果请求与断言相匹配则进行路由;
  • Filter:过滤指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改;

工作流程

分布式架构-013

  • 路由判断:客户端的请求到达网关后,先经过 Gateway Handler Mapping 处理,这里面会做断言(Predicate)判断,看下符合哪个路由规则,这个路由映射后端的某个服务。
  • 请求过滤:然后请求到达 Gateway Web Handler,这里面有很多过滤器,组成过滤器链(Filter Chain),这些过滤器可以对请求进行拦截和修改,比如添加请求头、参数校验等等,有点像净化污水。然后将请求转发到实际的后端服务。这些过滤器逻辑上可以称作 Pre-Filters,Pre 可以理解为“在…之前”。
  • 服务处理:后端服务会对请求进行处理。
  • 响应过滤:后端处理完结果后,返回给 Gateway 的过滤器再次做处理,逻辑上可以称作 Post-Filters,Post 可以理解为“在…之后”。
  • 响应返回:响应经过过滤处理后,返回给客户端。

客户端的请求先通过匹配规则找到合适的路由,映射到具体的服务,然后请求经过过滤器处理后转发给具体的服务,服务处理后,再次经过过滤器处理,最后返回给客户端。

对于公司业务以 Java 为主要开发语言的情况下,SpringCloud Gateway 通常是个不错的选择,其优点有,简单易用、成熟稳定、与 SpringCloud 生态系统兼容、Spring 社区成熟等等。 不过,SpringCloud Gateway 也有一些局限性和不足之处, 一般还需要结合其他网关一起使用比如 OpenResty。并且,其性能相比较于 Kong 和 APISIX,还是差一些。如果对性能要求比较高的话,SpringCloud Gateway 不是一个好的选择。

OpenFeign

Feign is a declarative web service client。 It makes writing web service clients easier。 To use Feign create an interface and annotate it。 It has pluggable annotation support including Feign annotations and JAX-RS annotations。 Feign also supports pluggable encoders and decoders。 Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters used by default in Spring Web。 Spring Cloud integrates Ribbon and Eureka, as well as Spring Cloud LoadBalancer to provide a load-balanced http client when using Feign。

大意:Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。SpringCloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。

Feign与OpenFeign:

  • Feign是SpringCloud组件中的一个轻量级RESTful的HTTP服务客户端,Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务;
  • OpenFeign是SpringCloud在Feign的基础上支持了SpringMVC的注解,如@RequesMapping等等。OpenFeign的@Feignclient可以解析SpringMVc的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务;

通过使用OpenFeign可以使代码变得更加简洁,减轻程序员负担:

 // 伪代码
 @Component
 // 括号中,为在注册中心注册的服务名称
 @FeignClient("CLOUD-PAYMENT-SERVICE")
 public interface PaymentFeignService {
 
     @GetMapping("/payment/get/{id}")
     CommonResult<Payment> getPayment(@PathVariable("id") Long id);
 
     @PostMapping("/payment/timeout")
     String feignTimeoutTest();
 }

OpenFeign工作原理:

分布式架构-008

  1. 使用注解@FeignClient注册FactoryBean到IOC容器, 最终产生了一个虚假的实现类代理;
  2. 使用Feign调用接口的地方,最终拿到的都是一个假的代理实现类;
  3. 所有发生在代理实现类上的调用,都被转交给Feign框架,翻译成HTTP的形式发送出去,并得到返回结果,再翻译回接口定义的返回值形式;

Ribbon

SpringCloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具,是一个内置软件负载平衡器的进程间通信(远程过程调用)库。主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项,如连接超时、重试等。

本地负载与服务端负载:

  • Nginx是服务器负载均衡,客户端所有请求都会交给Nginx,然后由Nginx实现转发请求。即负载均衡是由服务端实现的;
  • Ribbon是本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术;

Ribbon其实是一个软负载均衡的客户端组件,它可以和其他所需请求的客户端结合使用,例如与Eureka结合:

分布式架构-007

消费方和服务方在注册中心注册服务,当消费方发起请求时,Ribbon会去注册中心寻找请求的服务名,即服务方集群,Ribbon默认负载算法会根据接口第几次请求 % 服务器集群总数量算出实际消费方服务器的位置,每次服务重启动后rest接口计数从1开始。

模拟Ribbon默认负载均衡算法:

public interface ILoadBalance {
    ServiceInstance instance(List<ServiceInstance> instances); 
}
@Component
public class MyLoadBalance implements ILoadBalance{

    /**
     * 轮询索引
     */
    private final AtomicInteger index = new AtomicInteger(0);

    /**
     * 负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务重启动后rest接口计数从1开始。
     * @param instances 服务器集群数量
     * @return 实际服务器的下标
     */
    @Override
    public ServiceInstance instance(List<ServiceInstance> instances) {
        return instances.get(incrementAndGet() % instances.size());
    }

    public final int incrementAndGet() {
        int current = 0;
        int next = 0;
        do {
            current = index.get();
            // 当最大数量超过 Integer。MAX_VALUE 归0
            next = current >= 2147483647 ? 0 : current + 1;
        }while (!index.compareAndSet(current, next));
        return next;
    }
}
 @Resource
 private RestTemplate restTemplate;

 @Resource
 private DiscoveryClient discoveryClient;

 @Resource
 private ILoadBalance iLoadBalance;

 @GetMapping("/myLoadBalance")
 public String myLoadBalanceTest() {

     List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
     ServiceInstance instance = iLoadBalance.instance(instances);

     URI uri = instance.getUri();
     String finalUri = String.format("%s/%s",uri, PaymentConstant.PAYMENT_GETPORT_API);

     log.info("自定义负载均衡,请求地址:{}", finalUri);

     return restTemplate.getForObject(finalUri, String.class);
 }

Config

SpringCloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。

分布式架构-009

SpringCloud Config是配置中心的一种,它分为服务端和客户端两部分:

  • 服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密/解密信息等访问接口;Config-Server端集中式存储/管理配置文件,并对外提供接口方便Config-Client访问,接口使用HTTP的方式对外提供访问;
  • 客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息,配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容;

使用SpringCloud Config非常简单,需要在服务端和客户端分别进行改造:

  1. 引入依赖就不说了,首先将分布式中系统中的配置放到git或svn上,在服务端配置文件里配置好git或svn用户名密码、配置文件所在的分支及配置文件的目录,再将服务端启动类加上@EnableConfigServer注解,就大功告成了;
  2. 放在git或svn上的配置文件名称要符合规则,才能通过配置中心访问得到该文件。一些常用的配置文件规则,其中,label:分支profiles:环境(dev/test/prod)application:服务名
     ## http://127.0.0.1:3344/master/config-dev.yml
     1. /{label}/{application}-{profile}.yml
    
     ## http://127.0.0.1:3344/config-dev.yml
     2. /{application}-{profile}.yml
    
     ## http://127.0.0.1:3344/config/dev/master
     3. /{application}/{profile}[/{label}]
    
  3. 在客户端也要引入依赖和进行相关配置:配置中心地址、分支名称、配置文件名称、环境。需要注意的是要将客户端模块下的application.yml文件改为bootstrap.yml,再将主启动类加上@EnableEurekaClient注解;

    当配置客户端启动时,它绑定到配置服务器(通过spring.cloud.config.uri引导配置属性),并使用远程属性源初始化Spring Environment。 这种行为的最终结果是,所有想要使用Config Server的客户端应用程序都需要bootstrap.yml或一个环境变量

    applicaiton.yml是用户级的资源配置项,而bootstrap.yml是系统级的,优先级更加高,在加载配置时优先加载bootstrap.yml

当将SpringCloud Config客户端服务端都配置好之后,修改配置时会发现修改的配置文件不能实时生效。针对这个问题,可以将服务重启或者调用actuator的刷新接口使其生效,使用之前需要引入actuator的依赖:

# 使用SpringCloud Config修改完配置后,调用刷新接口使客户端配置生效
curl -X POST "http://localhost:3355/actuator/refresh"

为了避免手动的调用刷新接口,可以使用SpringCloud Bus配合SpringCloud Config实现配置的动态刷新。

SpringCloud Alibaba体系

Spring Cloud Alibaba 是一个为 SpringCloud 提供的增强解决方案,专注于微服务架构的构建和治理,特别是在中国市场的使用场景。 此项目包含开发分布式应用服务的必需组件,方便开发者通过 SpringCloud 编程模型轻松使用这些组件来开发分布式应用服务。

Nacos

Nacos 是一个由阿里巴巴开源的动态服务发现、配置管理和服务治理平台,专为微服务架构设计。它提供服务注册与发现功能,允许服务实例自动注册并被其他服务发现,从而简化微服务之间的通信。 Nacos 还支持动态配置管理,应用的配置可以实时更新,无需重启应用,提升了系统的灵活性。Nacos 提供健康检查机制,监控服务的可用性,并支持多数据源存储选项,以满足不同的业务需求。 它与 Spring Cloud Alibaba 深度集成,形成了一整套高效的微服务治理解决方案,广泛应用于云原生和分布式系统中。

Nacos 服务注册流程:

  1. 当服务提供者(服务实例)启动时,它会初始化 Nacos 客户端;
  2. 服务提供者通过 Nacos 客户端发送注册请求,包括以下信息:服务名称、服务实例的 IP 地址、端口号、权重(可选)、元数据(可选);
  3. Nacos 接收到注册请求后,执行以下操作:
    • 将服务实例信息存储在内存中。
    • 根据配置将服务实例信息持久化存储到数据库(如 MySQL、PostgreSQL 等)。
  4. Nacos 定期对注册的服务实例进行健康检查,保证服务的可用性。健康检查可以使用:HTTP 健康检查、TCP 健康检查、GRPC 健康检查。 如果某个实例被标记为不可用,Nacos 会在一定时间内将其状态更新为“下线”,并在一段时间后将其从注册表中剔除。

Nacos 服务发现流程:

  1. 服务消费者(需要调用服务的应用)在启动时也会初始化 Nacos 客户端;
  2. 服务消费者通过 Nacos 客户端向 Nacos 发送请求,查询指定服务名称的可用实例信息;
  3. Nacos 查询到可用的服务实例后,将其信息以 JSON 格式返回给服务消费者,信息包括:服务实例的 IP 地址、端口号、权重、元数据(如果有);
  4. 服务消费者收到服务实例信息后,使用负载均衡算法(如轮询、随机等)选择一个实例进行调用;
  5. 服务消费者通过 HTTP、gRPC 等协议调用选定的服务实例,实现业务逻辑;

使用Nacos作为配置中心步骤:

  1. 下载安装Nacos,启动Nacos,并创建配置; SpringBoot整合nacos
    server:
      port: 8001
    
    config:
      info: "config info for dev from nacos config center"
    
  2. 集成Nacos客户端,在SpringBoot项目中添加相关依赖,在application.yml配置中配置Nacos地址;
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    
    spring:
      cloud:
        nacos:
          config:
            server-addr: localhost:8848
            file-extension: yaml  # 或 json,根据配置格式选择
    
  3. 在应用中使用@Value@ConfigurationProperties注解来获取Nacos中的配置,使用@RefreshScope注解标记需要动态刷新的Bean;
    @RestController
    @RefreshScope
    public class ValueAnnotationExample {
    
        @Value("${config.info}")
        private String configInfo;
    
        @GetMapping("/config/info")
        public String getConfigInfo() {
            return configInfo;
        }
    
    }
    
  4. 修改 Nacos 控制台中的配置,观察 SpringBoot 应用是否能够动态刷新配置;

Sentinel

Sentinel 是阿里巴巴开源的一款流量防护和熔断工具,专为分布式系统设计,提供了限流、熔断降级、系统保护和实时监控等功能,适用于高并发、高可用的分布式微服务架构。

Sentinel 具有以下特征:

  • 丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。
  • 完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  • 广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 SpringCloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
  • 完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。例如定制规则管理、适配数据源等。

Sentinel 的熔断机制通过实时监控接口的异常情况(如请求失败率、响应时间)来判断是否触发熔断,以避免系统雪崩。主要工作原理如下:

  1. 监控数据:对每个接口的请求量、错误率、响应时间等数据进行统计,基于滑动窗口收集短时间内的请求情况。
  2. 熔断触发:根据配置的熔断规则(如错误比例、响应时间等),当统计数据超过预设的阈值时,触发熔断。熔断期间该接口会拒绝请求,直接返回错误响应。
  3. 进入半开状态:经过一段时间后,系统进入“半开”状态,允许部分请求通过。若恢复正常,解除熔断;若依然异常,继续熔断。
  4. 恢复正常:在半开状态下请求正常时,熔断器关闭,接口恢复服务,来保证服务逐步回归稳定。

假设有一个电商系统中的“下单”接口,为了保证系统稳定性,我们配置了以下熔断规则:当“下单”接口的错误率超过 50%,并且在 10 秒内调用量超过 100 时触发熔断,熔断持续时间为 5 秒。Sentinel执行流程如下:

  1. 正常监控:系统正常运行时,Sentinel 监控每次“下单”请求的成功与失败,统计错误率。
  2. 熔断触发:某个时段内由于后端数据库延迟,“下单”接口的错误率在 10 秒内达到了 60%(超过了配置的 50%),且调用量也超过 100。此时,Sentinel 自动触发熔断,拒绝所有“下单”请求,并直接返回错误信息给客户端。
  3. 进入半开状态:5 秒后,Sentinel 进入“半开”状态。此时允许一部分请求通过,如果这些请求成功率较高,系统恢复正常。
  4. 恢复或继续熔断:
    • 如果在半开状态下的请求依旧失败较多,错误率较高,则熔断器重新进入熔断状态,拒绝请求并持续监控。
    • 若通过请求的错误率明显下降,则熔断器关闭,接口完全恢复正常服务。

Seata

Seata是一款分布式事务解决方案,专为微服务架构而设计,主要用于解决分布式环境下的数据一致性问题。Seata 由阿里巴巴开源,具备高性能、易于扩展的特点,在微服务环境下提供简单可靠的分布式事务控制。

Seata的设计目标其一是对业务零侵入,因此从业务无侵入的2PC入手,在传统方案2PC的基础上演进,并解决2PC方案面临的问题。 Seata把一个分布式的事务理解为一个包含了若干分支事务的全局事务,它通过对本地关系型数据库的分支事务的协调来驱动完成全局事务,是工作在应用层的中间件。全局事务的职责是协调其下管辖的分支事务达成一致,要么一起成功提交事务,要么一起回滚失败。此外,通常分支事务本身就是一个关系型数据库的本地事务。 主要优点是性能较好,且不长时间占用连接资源,它以高效并对业务0入侵的方式解决微服务场景下面临的分布式事务问题,目前提供AT模式(即2PC)和TCC模式的分布式事务解决方案。

分布式事务-005

与传统2PC的模型类似,Seata定义了3个组件来协调分布式事务的处理过程: 分布式事务-006

  • TM:Transaction Manager事务管理器,需要嵌入(jar包)应用程序中工作,负责开启一个全局事务,并最终向TC发起全局提交或全局回滚的指令。
  • RM:Resource Manager资源管理器,控制分支事务,负责分支事务注册,状态回报,并接收事务调节器的指令,驱动分支事务的提交和回滚。
  • TC:Transaction Coordinator事务协调器,它是独立的中间件,需要独立运行部署,它维护全局事务的运行状态,接收TM指令发起全局事务的提交与回滚,负责与RM通信协调各个分支事务的提交回滚。

以新用户注册送积分举例: 分布式事务-007

具体执行流程:

  1. 用户服务器的事务管理器向事务协调器申请开启一个全局事务,全局事务的创建成功并生成一个全局唯一XID。
  2. 用户服务器的资源管理器向事务协调器注册分支事务,该分支事务在用户服务执行新增用户逻辑,并将其纳入XID对应全局事务的管辖。

    在执行注册分支事务的时候,这里事务已经提交了,提交后可以释放资源,从而提升程序性能。

  3. 用户服务器执行分支事务,向用户表插入一条记录。
  4. 逻辑执行到远程分布式调用积分服务时,XID在微服务调用链路的上下文中传播。积分服务的资源管理器向事务协调器注册了分支事务,该分支事务执行增加积分的逻辑,并将其纳入XID对应的全局事务的管辖。
  5. 积分服务执行分支事务,向积分记录表插入一条记录,执行完毕后,返回用户服务。
  6. 用户服务分支事务执行完毕。
  7. 事务管理器向事务协调器针对XID的全局提交或者回滚决策。
  8. 事务协调器调度XID下管辖的全部分支事务完成提交或回滚的请求。

Seata实现2PC与传统2PC的差别:

  • 架构方面,传统2PC方案的RM实际上是在数据库层,RM本质上就是数据库自身,通过XA协议实现,而Seata的RM则是以jar包的形式作为中间件层部署在应用程序这一侧的。
  • 两阶段提交方面,传统2PC无论第二阶段的决策是commit还是rollback,事务性资源的锁都要保持到第二阶段才释放。而Seata的做法是在第一阶段就将本地事务提交,这样可以省去第二阶段持有锁的时间,提高整体效率。

Seata两阶段提交协议的演变:

  • 一阶段:Seata会拦截,解析SQL语义,找到SQL要更新的数据,在业务员数据更新前,将其保存为before image,随后执行业务SQL。在业务数据更新后,将其保存为after image,插入UNDO LOG回滚日志;提交前向TC注册分支并生成行锁。 随后本地事务提交,将本地事务提交的结果上报给TC,由TC协调。 分布式事务-008

  • 二阶段-提交:执行提交操作,说明SQL执行顺利,因业务SQL在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。 分布式事务-009

  • 二阶段-回滚:要执行回滚操作,说明SQL执行不顺利,Seata需要回滚一阶段已经执行的业务SQL还原数据。回滚方式是用before image还原数据,但是在还原前还要校验脏写,对比数据库当前业务数据和after image。 对比数据库当前业务数据和after image分布式事务-010

Skywalking

SkyWalking 是一款开源的应用性能监控(APM)工具,专注于微服务、云原生应用和分布式系统的监控与管理。它能够通过链路追踪、指标收集和分析,帮助开发者定位和解决分布式系统中的性能瓶颈和故障。

SkyWalking 适用于各类分布式系统,尤其是微服务架构的应用。它能够帮助开发和运维人员更好地监控和优化服务性能,并在故障发生时快速定位问题源头,适用于电商、金融等对系统可靠性要求较高的领域。核心功能如下:

  • 分布式追踪:SkyWalking 能够在微服务调用链路中记录请求的路径,包括请求的起点、经过的服务节点和终点等,帮助定位问题节点。
  • 应用监控:收集系统的各项性能指标,如 CPU、内存、请求量、延迟等,直观呈现应用的健康状况。
  • 报警与告警:支持阈值告警和自定义告警,当性能指标异常时自动触发报警,及时提醒运维人员。
  • 服务依赖分析:绘制服务间的调用关系图,展示微服务架构下的服务依赖,方便分析服务拓扑。

SkyWalking 分布式追踪的核心原理:

  1. 链路追踪的基本概念:
    • Trace:追踪一个请求的全生命周期,它跨越多个服务节点,可以包含多个 span。
    • Span:描述链路中的一次操作或请求,通常是一个服务的处理过程。一个 trace 由多个 span 组成,且 span 之间有顺序关系,表示请求流转的路径。
  2. 追踪数据的采集:
    • 每当一个请求被发送到一个微服务时,SkyWalking 会在请求头中插入唯一标识符(如 Trace ID 和 Span ID)。这些标识符会随着请求在微服务间传递,保持请求的链路关系。
    • 通过在服务端应用中安装 SkyWalking 的代理探针(Agent)或 SDK,SkyWalking 会自动捕获请求的相关信息,包括调用时间、请求的服务、处理的操作等。
  3. 追踪数据的传播:
    • 在分布式系统中,每个微服务的处理请求都会携带 Trace ID 和 Span ID 信息。当请求从一个服务调用到另一个服务时,后续服务会继续传递这些 ID 信息,从而形成一个完整的调用链。
    • SkyWalking 支持通过 HTTP、gRPC 等协议传递追踪信息,通常是通过 HTTP 请求头(如 X-B3-TraceId, X-B3-SpanId)来传递。
  4. 数据的收集与聚合:
    • 每个微服务中的 SkyWalking Agent 会对本地的每个请求生成一个 Span,并在 Span 完成时将其发送到 SkyWalking 后端。
    • 后端会对收集到的 Span 数据进行聚合,将它们组合成完整的 Trace。Trace 包含了所有跨服务的调用信息,并可以形成一个服务间的调用链。

注意事项:

  • 数据存储优化:SkyWalking 生成大量追踪数据,建议使用性能较好的存储方案(如 Elasticsearch)。对于高流量系统,存储方案的性能至关重要,否则会造成数据处理瓶颈。
  • 网络开销:SkyWalking 的探针会收集大量追踪数据并发送至后端,网络延迟可能影响监控的实时性。
  • 性能开销:SkyWalking 的探针会增加一定的系统开销,尽管通常开销不大,但在高并发服务中,需关注探针对服务响应时间的影响,合理配置采样率。
  • 告警规则的合理配置:设置适合系统的告警阈值,避免频繁告警导致“告警疲劳”。建议针对关键性能指标(如响应时间、错误率等)制定精确告警规则。
  • 采样率:在高流量场景下,可以适当降低链路追踪的采样率,以平衡系统监控的全面性和性能。

Dubbo体系

Dubbo 是一个高性能分布式服务框架,致力于解决微服务架构中的服务调用、服务治理和服务注册发现问题。 Dubbo 体系由多个关键组件组成,包括 Dubbo 本身、服务注册中心,如 Zookeeper、Nacos等,共同构成完整的服务治理解决方案。

Dubbo

Dubbo是一个高性能的Java RPC框架,主要用于构建和管理分布式服务,广泛应用于微服务架构中。它提供高效的远程过程调用功能,支持多协议,如Dubbo、HTTP等,和多注册中心,如Zookeeper、Nacos等,实现服务的自动发现和治理。 Dubbo具备负载均衡、容错、动态服务发现以及扩展能力等特性,提升了服务之间的可用性和可靠性。与其他框架如Spring Cloud相比,Dubbo在性能上更具优势,尤其在高并发和低延迟的环境中表现突出。 它适用于大规模分布式系统,尤其在电商平台、金融系统等需要高吞吐量和低延迟的场景中,发挥着重要作用。而对于较小的项目,可能需要权衡其复杂性与功能的匹配程度。

Dubbo的核心组件包括以下几个方面:

  • 服务注册与发现:这个组件负责将服务提供者的服务信息注册到注册中心,并让服务消费者能够发现并调用这些服务。通过注册中心,服务可以在运行时动态管理。常用的注册中心有Zookeeper、Nacos等。注册与发现为分布式系统中的服务管理提供了灵活性和扩展性。
  • 协议层:协议层负责定义服务之间的通信方式。Dubbo默认使用自己的Dubbo协议,同时也支持HTTP、Hessian等协议。协议层通过底层的网络通信机制,确保服务间的数据传输高效且稳定。
  • 序列化层:序列化层将Java对象转换为字节流,以便在网络中传输,在接收端再反序列化回Java对象。Dubbo支持多种序列化方式,包括Java原生序列化、Hessian、JSON、Protobuf等。开发者可以根据实际需求选择不同的序列化方式,优化数据传输的效率和兼容性。
  • 负载均衡层:负载均衡层将请求均匀地分配给多个服务实例。Dubbo支持多种负载均衡策略,比如随机、轮询和加权轮询等。通过合理的负载分配,系统能够更好地利用资源,避免某个服务节点过载。
  • 容错层:容错层负责处理服务调用时可能出现的异常。它通过重试、失败切换等策略,减少系统异常带来的影响。容错机制有助于提高系统的可靠性,保持系统的稳定运行。
  • 服务治理层:服务治理层包括服务限流、熔断和动态配置等功能。限流可以防止系统过载,熔断可以在服务不可用时迅速切断请求,而动态配置则能在不重启服务的情况下调整配置,增强了系统的灵活性和稳定性。
  • 监控与管理层:监控与管理层通过收集服务的性能指标,如调用次数、响应时间、错误率等,帮助开发者实时了解服务的健康状态。通过这些数据,开发者能够快速发现潜在问题并进行优化。
  • 过滤器层:过滤器层让开发者可以在服务调用过程中插入自定义逻辑,比如日志记录、权限校验、数据加密等。它为Dubbo提供了灵活的扩展点,能根据业务需求增强框架的功能。
  • 异步调用层:异步调用层允许服务的调用不阻塞当前线程,提升系统的响应速度和并发处理能力。尤其在高并发场景中,异步调用可以显著提高系统的吞吐量。
  • API层:API层定义了服务的接口,服务提供者根据接口提供服务,消费者通过调用接口访问服务。API层支持版本控制,可以在服务升级时平滑过渡,避免因为接口变动而导致的兼容性问题。
  • 扩展层:Dubbo提供了高度的可扩展性,开发者可以根据需要自定义协议、负载均衡策略、序列化方式等。通过扩展层,Dubbo可以在不同的业务场景中进行灵活调整和优化。

Dubbo RPC调用原理主要包括服务的注册、发现、请求和响应的处理过程,涉及客户端与服务器之间的远程调用。整个过程大致可以分为以下几个步骤:

  1. 服务提供者启动:服务提供者启动时,会将自己提供的服务(接口和实现类)注册到注册中心。服务暴露时,会将服务的接口、方法、调用地址、协议、版本等信息通过注册中心(如Zookeeper)发布出去。这使得服务消费者能够发现并调用该服务。
  2. 服务消费者发现服务:消费者在调用服务时,首先会通过注册中心获取服务提供者的信息。消费者会查询注册中心,获取到服务提供者的地址、协议、负载均衡策略等信息,从而决定如何与提供者进行远程调用。
  3. 代理创建:在消费者端,Dubbo框架会为每个远程服务接口生成一个动态代理。代理对象通过反射机制将方法调用委托给Dubbo客户端的调用框架,而不是直接执行方法体。这个代理对象主要用于将消费者的请求封装为远程调用请求,并将结果进行解封装。
  4. 请求封装与发送:消费者端调用服务方法时,代理对象会把调用信息(方法名、参数、超时时间等)封装成一个RPC请求对象。请求对象会通过指定的协议(如Dubbo协议)进行序列化,然后通过网络传输(一般通过TCP/IP)发送到服务提供者的服务端。
  5. 服务端处理请求:在服务端,Dubbo框架接收到请求后,会通过反序列化将请求数据还原为Java对象,然后查找对应的服务实现(通过反射机制)。服务端调用相应的方法处理请求,执行业务逻辑,并将执行结果封装成RPC响应对象。
  6. 响应返回:服务端将调用结果通过网络返回给消费者。响应数据会进行序列化,通过Dubbo协议传输回消费者。消费者接收到响应后,会对数据进行反序列化,恢复成Java对象,最终将结果返回给调用者。
  7. 负载均衡和容错:Dubbo在RPC调用过程中,会根据配置的负载均衡策略选择服务提供者。如果有多个服务提供者,Dubbo会使用如随机、轮询、加权等策略来进行负载均衡。若某个服务提供者不可用,Dubbo会根据容错机制进行重试或者快速失败,确保服务调用的高可用性。

ZooKeeper

ZooKeeper 是一个开源的分布式协调服务,主要用于在分布式系统中管理和协调大量分布式应用之间的配置和同步问题。它为分布式系统提供了高效的协调机制,帮助处理分布式锁、配置管理、命名服务、集群管理等问题。ZooKeeper 的核心功能包括数据的分布式一致性、实时通知机制和高可靠性。它通过类似文件系统的节点结构来组织数据,并允许客户端对这些节点进行读取、写入和监听操作。

ZooKeeper的核心组件包括以下几个部分:

  • ZooKeeper:ZooKeeper 集群由多个 ZooKeeper 服务端组成,通常会部署为一个主备集群。服务端负责处理客户端的请求、同步数据、保证数据一致性。ZooKeeper 使用了一个“Leader-Follower”模型,其中一个节点充当主节点,处理所有写请求,其他节点作为从节点,同步数据并处理读请求。
  • ZNode:ZooKeeper 中的数据以节点的形式存储。每个ZNode可以包含数据以及一组子节点,ZNode 支持两种类型:持久节点和临时节点。持久节点会一直存在,直到显式删除,而临时节点在客户端会话结束时自动删除。ZNode 的层次结构类似于文件系统的目录结构,通过路径来标识每个节点。
  • 客户端:客户端是与 ZooKeeper 服务端交互的组件,客户端可以向服务端发送请求来读取数据、修改数据、监控节点变动等。客户端通过会话与 ZooKeeper 服务器保持连接,会话有超时机制,超过一定时间未与服务器通信,ZooKeeper 会认为客户端失效,进而删除客户端注册的临时节点。
  • Watcher:Watcher 是 ZooKeeper 提供的事件监听机制。客户端可以通过设置 Watcher 来监控 ZNode 的变化,如数据更新、节点创建、节点删除等事件。当监控的 ZNode 发生变化时,ZooKeeper 会通知客户端。Watchers 仅在首次触发事件时通知,客户端需要重新设置 Watcher 来持续监控。
  • Leader选举与写请求处理:ZooKeeper 在集群中使用 Leader-Follower 模型。所有的写请求都由 Leader 节点处理,Follower 节点主要负责处理读请求。Leader 节点负责协调集群中各个节点的数据同步,保证集群内的数据一致性。当 Leader 节点失效时,ZooKeeper 会通过选举机制选择一个新的 Leader 节点。
  • Quorum:ZooKeeper 集群通过 Quorum来保证数据的一致性。集群中只有超过半数的节点同意的操作才会被提交,这样在集群中的节点出现故障时,仍然能够保持系统的可用性和一致性。

ZooKeeper的服务注册与发现原理主要依赖于分布式协调机制,通过使用 ZooKeeper 的节点和数据来实现服务的注册、发现以及健康检查。以下是详细的注册发现流程:

  • 服务注册:服务启动时,客户端将自己的信息,如服务地址、端口等注册到 ZooKeeper 上。通常是通过在某个指定的根路径(如 /services/)下创建一个临时节点(例如 /services/serviceA/instance1)。 该临时节点包含服务的元数据,如 IP 地址和端口等信息。这些临时节点在服务关闭或宕机时会被自动删除,保证了系统的健康性和一致性。
  • 服务发现:服务消费者通过 ZooKeeper 获取服务的注册信息。消费者通过读取 ZooKeeper 中指定路径的数据(如 /services/serviceA/),获取服务实例的信息。 ZooKeeper 会返回所有在该路径下注册的服务实例。由于这些节点是临时的,当服务实例出现故障或关闭时,节点会自动删除,从而保证消费者只获取到可用的服务实例。

参考文章

发表评论