Spring Cloud
微服务概念?组件?Spring Cloud组件?√ 优缺点?流行的微服务解决方案及区别?
微服务(Microservices)是一种软件架构风格,将一个大型应用程序划分为一组小型、自治且松耦合的服务。每个微服务负责执行特定的业务功能,并通过轻量级通信机制(如HTTP)相互协作。每个微服务可以独立开发、部署和扩展,使得应用程序更加灵活、可伸缩和可维护。
微服务架构演进方向:单体式->服务化SOA(Service-Oriented Architecture,面向服务的架构)->微服务
SOA是一种设计原则。关注服务的重用性和组合性,但没有规定服务的大小
微服务是对SOA思想的一种具体实践方式,但不等同于SOA
- 注册中心:基于spring-cloud-commons的discovery的DiscoveryClient接口,实现统一的客户端的注册发现。
- Spring Cloud Netflix:spring-cloud-netflix-eureka-server和spring-cloud-netflix-eureka-client ,基于Eureka实现、Consul
- Spring Cloud Alibaba:spring-cloud-alibaba-nacos-discovery,基于Nacos实现。
- spring-cloud-zookeeper-discovery,基于Zookeeper实现。
- 配置中心:
- Spring Cloud Netflix:Spring Cloud Config基于Git、SVN作为存储、consul
- Spring Cloud Alibaba:spring-cloud-alibaba-nacos-config基于Nacos实现。
- Apollo携程开源的配置中心。Spring Cloud最成熟的配置中心的选择。
- 远程调用:用于在不同的微服务之间进行通信和协作
- RESTful API:RestTemplate、Feign
- RPC(远程过程调用):如Dubbo、gRPC
- API网关:作为微服务架构的入口,统一暴露服务,并提供路由、负载均衡、安全认证等功能
- Spring Cloud Netflix:spring-cloud-netflix-zuul基于Zuul1实现。Netflix最新开源的网关服务是Zuul2 ,基于响应式的网关服务。、spring-cloud-gateway ,基于Spring Webflux实现
- Spring Cloud Alibaba:Gateway、Apisix等
- 分布式事务:保证跨多个微服务的一致性和原子性操作
- Spring Cloud Alibaba:Seata
- 熔断器:用于防止微服务之间的故障扩散,提高系统的容错能力。
- Spring Cloud Netflix:spring-cloud-netflix-hystrix基于 Hystrix 实现。
- Spring Cloud Alibaba:spring-cloud-alibaba-sentinel基于 Sentinel 实现。、Resilience4j
- 限流和降级:用于防止微服务过载,对请求进行限制和降级处理。
- Spring Cloud Netflix:Hystrix
- Spring Cloud Alibaba:Sentinel
- 分布式追踪和监控:用于跟踪和监控微服务的请求流程和性能指标。
- Spring Cloud Netflix:Spring Cloud Sleuth + Zipkin基于Zipkin实现
- Spring Cloud Alibaba:SkyWalking监控链路和JVM等、Sentinel Dashboard
- 负载均衡:基于spring-cloud-commons的loadbalancer的ServiceInstanceChooser接口实现统一的服务的选择。并且,负载均衡组件在选择需要调用的服务之后,还提供调用该服务的功能,具体方法见LoadBalancerClient接口的execute方法。
- spring-cloud-netflix-ribbon,基于Ribbon实现。
- spring-cloud-loadbalancer,提供简单的负载均衡功能。
优点
- 每一个服务足够内聚,代码容易理解
- 开发效率提高,一个服务只做一件事
- 微服务能够被小团队单独开发
- 微服务是松耦合的,是有功能意义的服务
- 可以用不同的语言开发,面向接口编程
- 易于与第三方集成
- 微服务只是业务逻辑的代码,不会和 HTML、CSS 或者其他界面组合
缺点
- 分布式系统的复杂性
- 多服务运维难度,随着服务的增加,运维的压力也在增大
- 系统部署依赖
- 服务间通信成本
- 数据一致性和事务管理
- 系统集成测试
- 性能监控
- 团队沟通和协作成本
特点 | Dubbo | Spring Cloud Netflix | Spring Cloud Alibaba |
---|---|---|---|
开发语言 | Java | Java | Java |
服务治理 | 提供完整的服务治理功能 | 提供部分服务治理功能 | 提供完整的服务治理功能 |
服务注册与发现 | ZooKeeper/Nacos | Eureka/Consul | Nacos |
负载均衡 | 自带负载均衡策略 | Ribbon | Ribbon\Dubbo负载均衡策略 |
服务调用 | RPC方式 | RestTemplate/Feign | Feign/RestTemplate/Dubbo |
熔断器 | Sentinel | Hystrix | Sentinel/Resilience4j |
配置中心 | Apollo | Spring Cloud Config | Nacos Config |
API网关 | Higress/APISIX | Zuul/Gateway | Spring Cloud Gateway |
分布式事务 | Seata | 不支持分布式事务 | Seata |
限流和降级 | Sentinel | Hystrix | Sentinel |
分布式追踪和监控 | Skywalking | Spring Cloud Sleuth + Zipkin | SkyWalking或Sentinel Dashboard |
微服务网格 | Dubbo Mesh | 不支持微服务网格 | Service Mesh(Nacos+Dubbo Mesh) |
社区活跃度 | 相对较高 | 目前较低 | 相对较高 |
孵化和成熟度 | 孵化较早,成熟度较高 | 成熟度较高 | 孵化较新,但迅速发展 |
服务注册与发现作用?为什么要有服务注册与发现?服务发现模式?举例?Eureka、ZooKeeper、Nacos的区别?√
管理和维护分布式系统中微服务的地址和元数据的组件。用于实现服务注册和发现
- 服务注册:各个服务在启动时向注册中心注册自己的网络地址、服务实例信息和其他相关元数据。这样,其他服务就可以通过注册中心获取到当前可用的服务列表。
- 服务发现:客户端通过向注册中心查询特定服务的注册信息,获得可用的服务实例列表。这样客户端就可以根据需要选择合适的服务进行调用,实现了服务间的解耦。
- 负载均衡:对同一服务的多个实例进行负载均衡,将请求分发到不同的实例上,提高整体的系统性能和可用性。
- 故障恢复:监测和检测服务的状态,当服务实例发生故障或下线时,可以及时更新注册信息,从而保证服务能够正常工作。
- 服务治理:进行服务的配置管理、动态扩缩容、服务路由、灰度发布等操作,实现对服务的动态管理和控制
屏蔽、解耦服务之间相互依赖的细节。服务之间的远程调用必须要知道IP、端口信息。调用方直接配置被调用方的IP、端口,这种调用直接依赖IP、端口的方式存在依赖,如被调用的IP、端口变化后,调用方也要同步修改。通过服务发现,将服务的IP、端口转化成服务名来调用。
对微服务进行动态配置。微服务架构中,服务众多、服务之间的相互依赖错综复杂,无论是服务主动停止、意外挂掉,还是因为流量增加对服务实现扩容,这些服务或状态上的动态变化,都需要尽快的通知到被调用方,被调用方采取相应的策略。对于服务注册与发现要实时管理者服务的数据和状态,包括服务的注册上线、服务主动下线,异常服务的踢出。服务发现将服务IP、端口等细节通过一个服务名抽象给调用者,并动态管理者各个微服务的状态检测、状态更新,服务上线,下线等,这些都是微服务治理的基础,包括,负载均衡,链路跟踪。
客户端发现模式:客户端决定相应服务实例的网络位置,并且对请求实现负载均衡。客户端查询服务注册表,后者是一个可用服务实例的数据库;然后使用负载均衡算法从中选择一个实例,并发出请求
服务端发现模式:客户端通过负载均衡器向某个服务提出请求,负载均衡器查询服务注册表,并将请求转发到可用的服务实例。如同客户端发现,服务实例在服务注册表中注册或注销。
特性 | Eureka | ZooKeeper | Nacos |
---|---|---|---|
开发公司 | Netflix | Apache 基金会 | 阿里巴巴 |
CAP | AP(可用性和分区容忍性) | CP(一致性和分区容忍性) | AP、CP |
功能 | 服务注册与发现 | 分布式协调、配置管理、分布式锁 | 服务注册与发现、配置管理、服务管理 |
定位 | 适用于构建基于 HTTP 的微服务架构 | 通用的分布式协调服务框架 | 适用于微服务和云原生应用 |
访问协议 | HTTP | TCP | HTTP/DNS |
自我保护 | 支持 | - | 支持 |
数据存储 | 内嵌数据库、多个实例形成集群 | ACID 特性的分布式文件系统 ZAB 协议 | 内嵌数据库、MySQL 等 |
健康检查 | Client Beat | Keep Alive | TCP/HTTP/MYSQL/Client Beat |
特点 | 简单易用、自我保护机制 | 高性能、强一致性 | 动态配置管理、流量管理、灰度发布等 |
- ZooKeeper:leader+follower,leader写,同步到follower,follower读,保证顺序一致性,主动推送,leader崩溃的时候,为了保证数据一致性,尽量不要读到不一致的数据,此时要重新选举leader以及做数据同步,此时集群会短暂的不可用
- Eureka:peer-to-peer,各节点都能写也都能读,每个节点通过异步复同步给其他节点,所以可能数据不一致,任何一个节点宕机,其他节点正常工作,可用性高
- Nacos:基于raft算法。还提供了配置管理、元数据管理和流量管理等功能,并且提供了一个可视化的控制台管理界面。
Eureka介绍?实现原理?工作流程?√Eureka Server高可用?缓存机制?自我保护模式?解决方法?怎么知道最近的服务,就近机房?
由两个组件组成:Eureka服务端,服务注册中心,支持集群部署;Eureka 客户端,Java客户端,用来处理服务注册与发现。
在应用启动时,Eureka客户端向服务端注册自己的服务信息,同时将服务端的服务信息缓存到本地。客户端会和服务端周期性的进行心跳交互,以更新服务租约和服务信息。
服务注册与发现: 服务实例启动时向Eureka Server发送注册请求,将自己的信息注册到注册中心。Eureka Server会将这些信息保存在内存中,并提供REST接口供其他服务查询。服务消费者可以通过查询服务实例列表来获取可用的服务提供者实例
服务健康检查: 通过心跳机制来检测服务实例的健康状态。服务实例会定期向Eureka Server发送心跳表明自己的存活状态。如果Eureka Server在一定时间内没有收到某个服务实例的心跳,则会将其标记为不可用,并从服务列表中移除,下线实例。
服务负载均衡: Eureka客户端在调用其他服务时,会从本地缓存中获取服务的注册信息。如果缓存中没有对应的信息,则会向Eureka Server发送查询请求。Eureka Server会返回一个可用的服务实例列表给客户端,客户端可以使用负载均衡算法选择其中一个进行调用。
Eureka 工作流程
Eureka Server 启动成功,等待服务端注册如果配置了集群,Eureka Servers 之间会通过 Replicate 同步注册表信息,每个服务器都维护完整的服务注册表。
Eureka Client 启动时根据配置的 Eureka Server 地址去注册中心注册服务客户端向注册中心注册自己并成为可用服务。
Eureka Client 定时全量或者增量从注册中心获取服务注册表,并将获取到的信息缓存到本地客户端以一定频率同步服务注册表,以便本地缓存最新信息。
Eureka Client 会每 30 秒向 Eureka Server 发送一次心跳请求,证明客户端服务正常确保注册表中的实例是健康的。
当 Eureka Server 90 秒内没有收到 Eureka Client 的心跳,注册中心认为该节点失效,会注销该实例。超时未收到心跳后,Eureka Server 将该实例标记为下线并从注册表中移除。
单位时间内 Eureka Server 统计到大量 Eureka Client 没有上送心跳,可能为网络异常,进入自我保护机制在自我保护模式中,Eureka Server 暂时不剔除未上报心跳的客户端。
当 Eureka Client 心跳请求恢复正常之后,Eureka Server 自动退出自我保护模式网络恢复后,自我保护模式结束,正常执行剔除逻辑。
服务调用时,Eureka Client 会先从本地缓存找寻调取的服务通过本地缓存提升调用性能,减少对注册中心的依赖。
如果获取不到,Eureka Client 从注册中心刷新注册表,再同步到本地缓存 在缓存失效或服务信息不完整时,主动更新注册表。
Eureka Client 程序关闭时向 Eureka Server 发送取消请求,Eureka Server 将实例从注册表中删除客户端主动注销,确保服务列表的实时性。
多实例部署: 通过将多个Eureka Server实例部署在不同的节点上,当其中一个实例发生故障时,其他实例仍然可以提供服务,并保持注册信息的一致性。
服务注册信息的复制: 当一个服务实例向Eureka Server注册时,每个Eureka Server实例都会复制其他实例的注册信息,以保持数据的一致性。当某个Eureka Server实例发生故障时,其他实例可以接管其工作,保证整个系统的正常运行。
自我保护机制: ~,防止由于网络抖动或其他原因导致的误剔除,提高系统的稳定性
![eurekacache.png](https://b.bdstatic.com/comment/I4MgmLj55Sgosm2EdtLMDQ70a7c92f073b2623dc4a104375c35b6a.png)
当Eureka Server节点在短时间内丢失了过多实例的连接时(比如网络故障或频繁的启动关闭客户端)就会进入自我保护模式,保护服务注册表中的信息,不再删除服务注册表中的数据(即不会注销任何微服务),当网络故障恢复后,就会自动退出自我保护模式
解决方法
等待Eureka Server自动恢复等待网络恢复(或者没有频繁的启动与关闭实例)
依次重启Eureka Server:对Eureka Server做负载均衡,无效的实例会被清除。
关闭Eureka自我保护模式
eureka:
server:
enable-self-preservation: false
eviction-interval-timer-in-ms: 4000 # This is not required
通过属性prefer-same-zone-eureka
如果false则只和service-url的第一个注册中心注册并维持心跳检测,只有在第一个注册失败的情况下,才会依次向其它的注册中心注册,总共重试3次,注册失败后每隔一个心跳时间,会再次尝试。
如果true,先通过region取availability-zones内的第一个zone的service-url下的第一个注册中心进行注册和维持心跳,不再向其它的注册中心注册和维持心跳。只有在第一个注册失败的情况下,才会依次向其它的注册中心注册,总共重试3次,注册失败后每隔一个心跳时间,会再次尝试。为了保证服务注册到同一个 zone 的注册中心,一定要注意 availability-zones 的顺序,必须把同一zone写在最前面
eureka:
client:
# 尽量向同一区域的 eureka 注册,默认为true
prefer-same-zone-eureka: true
# 地区
region: tianjin
availability-zones:
tianjin: zone-1,zone-2
service-url:
zone-1: http://IP1:port/eureka/,http://IP2:port/eureka/,http://IP3:port/eureka/
zone-2: http://IP1:port/eureka/,http://IP2:port/eureka/,http://IP3:port/eureka/
配置中心作用?Nacos配置中心原理?长轮询机制?Spring Cloud Config构建过程?Apollo
用于集中管理微服务的配置信息(例如数据库连接地址、服务端口、日志级别等),动态修改配置而不需要重启服务
配置信息存储:默认用内嵌数据库Derby存储配置信息,可换MySQL
注册配置信息:服务启动时,Nacos Client会向Nacos Server注册的配置信息,Nacos把配置信息写入存储,并生成版本号。
获取配置信息:服务运行期间,Nacos Client通过API从Nacos Server获取配置信息。Server根据键查找对应的配置信息并返回给Client
监听配置变化:Nacos Client通过注册监听器的方式实现对配置信息的监听。当配置信息发生变化时,Nacos Server会通知已注册的监听器,并触发相应的回调方法
客户端和服务端的交互分为:推(Push)和拉(Pull),Nacos采用长轮询进行配置的动态刷新。
客户端发起Pull请求,服务端检查配置是否有变更。如果没有变更,则设置一个定时任务,并将当前的客户端连接加入到等待队列中。
在等待期间,如果配置发生变更,服务端会立即返回结果给客户端,完成一次"推送"操作。
如果没有配置变更,等待时间达到预设的超时时间后,服务端会自动返回结果给客户端,即使配置没有变更。
如果在等待期间,通过Nacos Dashboard或API对配置进行了修改,会触发一个事件机制,服务端会遍历等待队列,找到发生变更的配置项对应的客户端连接,并将变更的数据通过连接返回,完成一次"推送"操作。
降低服务端压力,避免了大量的长连接占用内存资源
提供服务器端和客户端。服务器存储后端的默认实现使用 Git ,因此它轻松支持标签版本的配置环境,以及可以访问用于管理内容的各种工具。
这个还是静态的,得配合 Spring Cloud Bus 实现动态的配置更新。
配置服务器:为配置客户端提供其对应的配置信息,配置信息的来源为配置仓库,启动时即拉取配置仓库的信息,缓存到本地仓库中。
配置客户端:除了配置服务器之外的应用服务,启动时从配置服务器拉取其对应的配置信息。
配置仓库:为配置服务器提供配置源信息,配置仓库的实现可以支持多种方式。
构建 Spring cloud config 配置中心
创建Spring Boot项目并添加config-server依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
入口类的main方法类上添加注解 @EnableConfigServer
在application.properties中配置git仓库
# 接口 URL:/{application}/{profile}/{label}
spring.cloud.config.server.git.uri=https://github.com/username/config-repo
spring.cloud.config.server.git.clone-on-start=true
spring.cloud.config.server.git.searchPaths=config-center
构建 Springcloud config 配置中心仓库:在github上设置配置中心,本地创建文件夹wkcto,然后在里面创建文件夹config-center,然后在里面创建四个配置文件。使用 git push 推送到远程仓库,然后启动配置中心,通过/{application}/{profile}/{label}就能访问配置文件
构建Spring cloud config配置中心客户端
创建Spring Boot项目并添加spring-cloud-starter-config依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
HTTP和RPC区别?Feign和Dubbo的区别?Fegin?实现原理?为什么Feign第一次调用耗时很长?解决方法?怎么实现认证传递?怎么做负载均衡?重试机制?Feign 和 Ribbon 的区别?Feign怎么和Ribbon、Eureka整合?Openfegin底层可以使用哪些客户端?√
微服务中基于HTTP风格的远程调用通常用Feign实现,基于RPC的远程调用通常用Dubbo实现
- | HTTP(Hypertext Transfer Protocol超文本传输协议) | RPC(Remote Procedure Call,远程过程调用) |
---|---|---|
定义 | 用于传输超文本的协议。应用层协议,强调网络通信; | 用于实现分布式系统中不同节点之间通信的协议。强调服务之间的远程调用 |
通信方式 | 基于请求-响应模型,客户端发送请求,服务器返回响应。 | 基于方法调用模型,客户端调用远程方法并等待结果。 |
传输协议 | 基于TCP协议,可使用其他传输层协议如TLS/SSL进行安全加密。 | TCP、UDP。 |
数据格式 | 基于文本,JSON、XML | 二进制、JSON、Protocol Buffers等。 |
接口定义 | 使用RESTful风格的接口进行定义,常用的方法有GET、POST、PUT、DELETE等。 | 使用IDL(接口定义语言)进行接口定义,如Protocol Buffers、Thrift等。 |
跨语言性 | 支持跨语言通信,可以使用HTTP作为通信协议实现不同语言之间的通信。 | 支持跨语言通信,可以使用IDL生成不同语言的客户端和服务端代码。 |
灵活性 | 更加灵活,适用于不同类型的应用场景,如Web开发、API调用等。 | 更加高效,适用于需要高性能和低延迟的分布式系统。 |
- | Feign | Dubbo |
---|---|---|
定义 | 声明式的Web服务客户端,用于简化HTTP API的调用。 | 分布式服务框架,用于构建面向服务的微服务架构。 |
通信方式 | 基于HTTP协议,使用RESTful风格的接口进行定义和调用。也可以RPC协议 | 基于RPC协议,支持多种序列化协议如gRPC、Hessian等。也可以HTTP协议 |
服务发现 | 通常结合服务注册中心(如Eureka、Consul)进行服务发现和负载均衡。 | 通过ZooKeeper、Nacos等进行服务注册和发现,并提供负载均衡功能。 |
服务治理 | 不直接提供服务治理功能,需要结合其他组件或框架进行服务治理。 | 提供服务注册与发现、负载均衡、容错机制、服务降级等服务治理功能。 |
跨语言性 | 支持跨语言通信,可以使用HTTP作为通信协议实现不同语言之间的通信。 | 支持跨语言通信,通过Dubbo的IDL生成不同语言的客户端和服务端代码。 |
生态系统 | 集成了Spring Cloud生态系统,与Spring Boot无缝集成。 | 拥有完整的生态系统,包括注册中心、配置中心、监控中心等组件。 |
适用场景 | 适用于构建RESTful风格的微服务架构,特别适合基于HTTP的微服务调用。 | 适用于构建面向服务的微服务架构,提供更全面的服务治理和容错机制。 |
Feign使用RestTemplate实现Http调用,实现了spring-cloud-openfeign
主要特点和功能包括:
声明式API:用注解来定义和描述对远程服务的访问
集成Ribbon负载均衡:根据服务名和可用实例进行动态路由,并分发请求到不同的服务实例上,提高系统的可用性和可伸缩性。
容错机制:支持Hystrix容错框架,可以在调用远程服务时提供容错和断路器功能。当远程服务不可用或响应时间过长时,Feign可以快速失败并返回预设的响应结果,避免对整个系统造成级联故障。
@FeignClient(name = "example", url = "https://api.example.com")
public interface ExampleService {
@GetMapping("/endpoint")
String getEndpointData();
}
Feign会对定义了@FeignClient的接口创建一个动态代理。接口本质调用Feign创建的动态代理
Feign的动态代理会根据接口上@RequestMapping 等注解,来动态构造出你要请求的服务的地址。
最后针对这个地址,发起请求、解析响应。
通过@EnableFeignClients和FeignClient注解 通过动态代理在本地实例化远程接口->封装Request对象并进行编码->使用restTemplate发起http请求并对获取结果进行解码。
Ribbon的懒加载机制,当第一次调用发生时触发加载过程,包括从服务注册中心获取服务列表、建立连接池等操作,这个加载过程会增加首次调用的耗时。
ribbon:
eager-load:
enabled: true
clients: service-1
预热Feign客户端,启动时触发一次无关紧要的调用,来提前加载Ribbon和其他相关组件。相当于提前进行了第一次调用。
使用拦截器传递认证信息。实现RequestInterceptor接口定义拦截器,把认证信息添加到请求头中,然后将其注册到Feign的配置中
@Configuration
public class FeignClientConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate template) {
// 添加认证信息到请求头中
template.header("Authorization", "Bearer " + getToken());
}
};
}
private String getToken() {
// 获取认证信息的逻辑,可以从SecurityContext或其他地方获取
// 返回认证信息的字符串形式
return "your_token";
}
}
//Ribbon+RestTemplate的重试
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
simpleClientHttpRequestFactory.setConnectTimeout(1000);
simpleClientHttpRequestFactory.setReadTimeout(1000);
return new RestTemplate(simpleClientHttpRequestFactory);
}
在此基础上,使用如下配置,即可实现重试:
spring.cloud.loadbalancer.retry.enabled=true
ribbon:
# 同一实例最大重试次数,不包括首次调用
MaxAutoRetries: 1
# 重试其他实例的最大重试次数,不包括首次所选的server
MaxAutoRetriesNextServer: 2
# 是否所有操作都进行重试
OkToRetryOnAllOperations: false
因为 Ribbon 和 Feign 都有重试机制,在整合 Ribbon 的情况下,不使用 Feign 重试,而是使用 Ribbon 的重试。
ribbon:
MaxAutoRetries: 1
MaxAutoRetriesNextServer: 2
OkToRetryOnAllOperations: false
相关Issue可参考:https://github.com/spring-cloud/spring-cloud-netflix/issues/467
Zuul的重试
# 开启了重试
zuul.retryable: true
ribbon:
MaxAutoRetries: 1
MaxAutoRetriesNextServer: 2
OkToRetryOnAllOperations: false
# 指定局部路由开启重试,局部配置优先级更高。
zuul.routes.<routename>.retryable=true
基于HTTP响应码重试
clientName.ribbon.retryableStatusCodes: 404,502
Hystrix的超时时间必须大于超时的时间,否则,一旦Hystrix超时,就没办法继续重试了。
不建议将ribbon.OkToRetryOnAllOperations 设为true。因为一旦启用该配置,则表示重试任何操作,包括POST请求,而由于缓存了请求体,此时可能会影响服务器的资源。
Ribbon和Feign都是使用于调用用其余服务的,不过方式不同。
启动类用的注解不同。Ribbon使用@RibbonClient ;Feign使用@EnableFeignClients 。
服务的指定位置不同。Ribbon在@RibbonClient注解上设置;Feign则是在定义声明方法的接口中用@FeignClient注解上设置
调使用方式不同
Ribbon需要构建Http请求,模拟Http请求而后用RestTemplate发送给其余服务
Feign采使用接口的方式,将需要调使用的其余服务的方法定义成声明方法就可,不需要构建Http请求。不过要注意的是声明方法的注解、方法签名要和提供服务的方法完全一致
首先,调用Feign 创建的动态代理
然后,Feign 调用 Ribbon 发起调用流程。
- 首先,Ribbon 会从 Eureka Client 里获取到对应的服务列表。
- 然后,Ribbon 使用负载均衡算法获得使用的服务。
- ribbon调用对应的服务,调用 Feign ,而 Feign 调用 HTTP 库最终调用使用的服务。
- 因为 Feign 和 Ribbon 都存在使用 HTTP 库调用指定的服务,两者集成后保留Feign的调用,而Ribbon只负责负载均衡功能
Apache HttpClient(默认)线程安全
OkHttp:异步请求、HTTP/2和WebSocket支持,支持连接池复用连接,
//连接池配置参数
maxIdleConnections:最大空闲连接数。
keepAliveDuration:空闲连接的存活时间,超过这个时间后,连接会被关闭。
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class OkHttpExample {
public static void main(String[] args) throws InterruptedException {
ConnectionPool connectionPool = new ConnectionPool(5, 5, TimeUnit.MINUTES);
OkHttpClient client = new OkHttpClient.Builder()
.connectionPool(connectionPool)
.build();
Request request1 = new Request.Builder()
.url("http://www.example.com/page1")
.build();
Request request2 = new Request.Builder()
.url("http://www.example.com/page2")
.build();
try (Response response1 = client.newCall(request1).execute()) {
System.out.println("Response 1 code: " + response1.code());
} catch (IOException e) {
e.printStackTrace();
}
// Simulate some delay
Thread.sleep(2000);
try (Response response2 = client.newCall(request2).execute()) {
System.out.println("Response 2 code: " + response2.code());
} catch (IOException e) {
e.printStackTrace();
}
}
}
负载均衡作用?模式?Ribbon介绍?核心组件?实现原理?负载均衡算法?√缓存机制?重试机制?怎么和Eureka整合?
优化资源使用,最大化吞吐量,最小化响应时间并避免任何单一资源的过载。使用多个组件进行负载平衡而不是单个组件可能会通过冗余来提高可靠性和可用性。负载平衡通常涉及专用软件或硬件,例如多层交换机或域名系统服务器进程。
客户端模式:客户端节点有一份服务端清单,从Eureka服务注册中心获取。在Spring Cloud中使用@LoadBalanced注解开启客户端模式
服务端模式(nginx):维护一个可用的服务端清单,然后通过心跳机制来删除故障的服务端节点以保证清单中都是可以正常访问的服务端节点。负载均衡服务器按照某种配置好的规则从可用服务端清单中选出一台服务器去处理客户端的请求。
Ribbon是Netflix开源的一个客户端负载均衡器.Ribbon通过从服务注册中心获取可用服务列表,并通过负载均衡算法选择合适的服务实例进行请求转发,提供客户端的软件负载均衡算法。
Server封装了服务实例的ip和端口之类
ServerList获取服务实例列表的
ServerListUpdater是用来更新服务注册表的数据
IRule负责负载均衡的算法的
IClientConfig获取到一些配置Ribbon的一些配置
ILoadBalancer主要是用来协调上面提到的各个核心组件的,使得他们能够协调工作
![ribbonprinciple.png](https://b.bdstatic.com/comment/I4MgmLj55Sgosm2EdtLMDQb667e7307d4531649961085458871b44.png)
Round Robin轮询
Random 随机
AvailabilityFilteringRule会优先过滤掉由于多次访问故障而处于断路器跳闸状态被标记为circuit tripped的服务,还有并发的连接数(active connections 超过配置的阈值)超过临界值的服务,然后对剩余的服务列表按照轮训策略进行访问
WeightedResponseTimeRule根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的机率越大,刚启动时统计信息不足使用RoundRobinRule策略,统计信息足切换到WeightedResponseTimeRule
RetryRule先按照RoundRobinRule策略获取服务,如果获取服务失败则在制定时间内进行重试
BestAvailableRule会优先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,选择一个最小的并发请求的server.逐个考察Server,如果Server被tripped则忽略,在选择ActiveRequestsCount最小的server
ZoneAvoidanceRule复合判断server所在区域的性能和server的可用性选择服务器
Ribbon从Eureka Client获取服务列表。然后,Ribbon 使用负载均衡算法获得使用的服务并调用
什么是服务雪崩?防止方法?为什么要使用服务保障?Hystrix作用,原理√隔离策略?缓存机制?作用?
分布式系统中某个服务故障引发连锁反应,导致多个服务不可用甚至整个系统瘫痪的现象
服务高可用部署:通过冗余部署、故障转移等方式来减少单点故障的影响。
限流和熔断:对服务之间的请求进行限流和熔断,以防止过多的请求涌入导致后端服务不可用。
缓存和降级:合理使用缓存来减轻后端服务的负载压力,并在必要时进行服务降级,保证核心功能的可用性。
为了防止雪崩
熔断,控制故障范围。防止雪崩
Hystrix是一个延迟和容错库,旨在隔离远程系统,服务和第三方库的访问点,当出现故障是不可避免的故障时,停止级联故障并在复杂的分布式系统中实现弹性。
旨在通过添加延迟容错和容错逻辑来提高分布式系统的弹性,防止雪崩效应的发生。它通过隔离服务之间的依赖关系、限制并发线程池的大小、实现熔断机制等方式来实现容错。
线程池隔离(支持超时)。tomcat线程会将请求任务交给服务内部线程池的线程处理,线程完成后将调用结果返回给tomcat。从而实现资源隔离,服务内部的线程池的数量就决定了整个服务并发度;当请求的服务网络开销比较大的时候,或者是请求比较耗时的时候使用,因为,可以保证大量的容器(tomcat)线程可用,不会由于服务原因,一直处于阻塞或等待状态,快速失败返回
信号量隔离。限制tomcat访问服务的线程数达到限流目的。请求缓存服务时使用,因为这类服务的返回通常会非常的快,不会占用容器线程太长时间,而且也减少了线程切换的一些开销,提高了缓存服务的效率
减少重复的请求数。降低依赖服务的返回数据始终保持一致。
在同一个用户请求的上下文中,相同依赖服务的返回数据始终保持一致。
请求缓存在run()和construct()执行之前生效,所以可以有效减少不必要的线程开销
服务熔断?服务降级?区别?熔断降级方案实现?Hystrix容错机制(熔断机制和服务降级)及其实现?
服务熔断(Circuit Breaker)是微服务架构中的容错机制,用于保护系统免受服务故障或异常的影响。当某个服务出现故障或异常时,服务熔断可以快速隔离该服务,确保系统稳定可用。
它通过监控服务的调用情况,当错误率或响应时间超过阈值时,触发熔断机制,后续请求将返回默认值或错误信息,避免资源浪费和系统崩溃。
服务熔断还支持自动恢复,重新尝试对故障服务的请求,确保服务恢复正常后继续使用。
服务降级(Fallback)是微服务架构中的容错机制,用于在系统资源紧张或服务故障时保证核心功能的可用性。
当系统出现异常情况时,服务降级会主动屏蔽一些非核心或可选的功能,而只提供最基本的功能,以确保系统的稳定运行。通过减少对资源的依赖,服务降级可以保证系统的可用性和性能。
它可以根据业务需求和系统状况来制定策略,例如替换耗时操作、返回默认响应、返回静态错误页面等。
服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)实现方式不太一样;服务降级具有代码侵入性(由控制器完成/或自动降级),熔断一般称为自我熔断。
框架 | 实现方案 | 特点 |
---|---|---|
Spring Cloud Netflix | Hystrix | 提供线程隔离、服务降级、请求缓存、请求合并等功能可与Spring Cloud其他组件无缝集成.官方已宣布停止维护,推荐使用Resilience4j代替 |
Spring Cloud | Resilience4j | 轻量级服务熔断库 提供类似于Hystrix的功能 具有更好的性能和更简洁的API 可与Spring Cloud其他组件无缝集成 |
Spring Cloud Alibaba | Sentinel | 阿里巴巴开源的流量控制和熔断降级组件提供实时监控、流量控制、熔断降级等功能与Spring Cloud Alibaba生态系统紧密集成 |
Dubbo | Dubbo自带熔断降级机制 | Dubbo框架本身提供的熔断降级机制可通过配置实现服务熔断和降级与Dubbo的RPC框架紧密集成 |
服务熔断:当服务的错误率或响应时间超过Hystrix预设的阈值时,熔断器将会打开,后续的请求将不再发送到实际的服务提供方,而是返回预设的默认值或错误信息。快速隔离故障服务,防止故障扩散,提高系统的稳定性和可用性。熔断方式有以下几种:
强制熔断:不再进行服务调用。比如服务提供方宕机
根据阈值熔断:当服务调用失败率达到一定阈值时,将后续请求快速失败。
半开状态熔断:在熔断器开启一段时间后,尝试发送一个测试请求到服务提供方,如果请求成功,则熔断器进入半开状态,否则继续保持开启状态。
降级:~
服务降级:当服务熔断打开时调用自定义降级方法或返回默认值保证系统继续正常运行。例如返回缓存数据、执行简化的逻辑或调用其他可靠的服务,以提供有限但可用的功能。
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
//服务降级
@Service
public class MyService {
@HystrixCommand(fallbackMethod = "fallbackMethod")
public String myServiceMethod() {}
public String fallbackMethod() {
// 降级方法的逻辑,当服务调用失败时会执行此方法,可以返回默认值或执行其他备用逻辑
}
}
请求缓存(Request Caching):缓存对同一请求的响应结果,当下次请求相同的数据时,直接从缓存中获取,避免重复的网络请求,提高系统的性能和响应速度
请求合并(Request Collapsing):将多个并发的请求合并为一个批量请求,减少网络开销和资源占用。高并发场景下有效地减少请求次数,提高系统的性能。
实时监控和度量(Real-time Monitoring and Metrics):对服务的执行情况进行监控和统计,包括错误率、响应时间、并发量等指标。通过监控数据,可以及时发现和解决服务故障或性能问题。
线程池隔离(Thread Pool Isolation):每个依赖服务的请求都放在独立的线程池中执行,避免因某个服务的故障导致整个系统的线程资源耗尽。提高系统的稳定性和可用性。
通过HystrixCircuitBreaker实现。有三种状态:CLOSED-关闭;OPEN-打开;HALF_OPEN-半开
OPEN表示熔断状态打开,命令执行时,直接调用回退逻辑。
红线:初始时处于CLOSED状态,链路处于健康状态。当周期(HystrixCommandProperties.default_metricsRollingStatisticalWindow = 10000 ms)内,总请求数超过一定量(HystrixCommandProperties.circuitBreakerRequestVolumeThreshold = 20) 。错误请求占总请求数超过一定比例(HystrixCommandProperties.circuitBreakerErrorThresholdPercentage = 50% ) 。断路器从CLOSED变成OPEN状态
绿线:断路器处于OPEN状态,命令执行时,若当前时间超过断路器开启时间一定时间(HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds = 5000ms),断路器变成HALF_OPEN状态,尝试调用正常逻辑,根据执行是否成功,打开或关闭熔断器【蓝线】。
服务限流?Sentinel怎么实现限流?限流算法?√流程控制规则?√怎么实现集群限流?
通过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统,达到限制速率则可以拒绝服务(定向到错误页或告知资源没有了)、排队或等待(比如秒杀、评论、下单)、降级(返回兜底数据或默认数据,如商品详情页库存默认有货)。
Sentinel通过动态管理限流规则,根据定义的规则对请求进行限流控制。
定义资源:可以是URL、方法等,用于标识需要进行限流的请求。
//原业务方法.
@SentinelResource(blockHandler = "blockHandlerForGetUser")
public User getUserById(String id) {
throw new RuntimeException("getUserById command failed");
}
// blockHandler 函数,原方法调用被限流/降级/系统保护时调用
public User blockHandlerForGetUser(String id, BlockException ex) {
return new User("admin");
}
配置限流规则:在Sentinel的配置文件中定义资源的限流规则。规则可以包括资源名称、限流阈值、限流模式(令牌桶或漏桶)等。
private static void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule1 = new FlowRule();
rule1.setResource(resource);
// Set max qps to 20
rule1.setCount(20);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
}
使用滑动窗口限流算法来实现限流。基于时间窗口的限流算法。将一段时间划分为多个时间窗口,并在每个时间窗口内统计请求的数量。通过动态地调整时间窗口的大小和滑动步长,可以更精确地控制请求的通过速率。
流量控制原理是监控应用流量的QPS或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性
1.QPS(Queries Per Second):当调用相关url对应的资源时,QPS达到单机阈值时限流
2.线程数:当调用相关url对应的资源时,线程数达到单机阈值时限流
设置限流模式
- 直连模式 默认的流控处理就是(直连-->快速失败);
- 关联模式 当关联的资源达到阈值,就限流自己。
- 链路模式 只针对从指定链路访问到本资源的请求做统计,判断是否超过阈值。直接拒绝直接拒绝(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。
利用Token Server和Token Client的机制来实现。Client向Token Server发送请求,Token Server根据配置的规则决定是否限流。
为什么要有网关?zuul原理? Spring Cloud Gateway?工作流程?为什么要用灰度发布?如何实现?
动态路由:反向代理
安全控制:统一认证(支持 HMAC, JWT, Basic, OAuth 2.0等常用协议)和鉴权(权限控制、IP 黑白名单)
协议转换
熔断、限流
灰度发布
api组合:可以调用一次网关实现调用两次微服务的功能
请求分析,记录日志
缓存
健康检查
可用性
高性能
API版本管理
作用:API 网关,路由,负载均衡等多种作用。
类似Nginx反向代理的功能
在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个 API网关根据请求的 url ,路由到相应的服务。当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用方通信进行权限控制,后将请求均衡分发给后台服务端。
Route(路由):定义了请求的匹配规则和转发目标。通过配置路由,可以将请求映射到后端的服务实例或URL上。路由规则可以根据请求的路径、方法、请求头等条件进行匹配,并指定转发的目标URI。
Predicate(断言):断言用于匹配请求的条件,如果请求满足断言的条件,则会应用所配置的过滤器。Spring Cloud Gateway提供了多种内置的断言,如Path(路径匹配)、Method(请求方法匹配)、Header(请求头匹配)等,同时也支持自定义断言。
Filter(过滤器):过滤器用于对请求进行处理和转换,可以修改请求、响应以及执行其他自定义逻辑。Spring Cloud Gateway提供了多个内置的过滤器,如请求转发、请求重试、请求限流等。同时也支持自定义过滤器,可以根据需求编写自己的过滤器逻辑。
Gateway Handler(网关处理器):网关处理器是Spring Cloud Gateway的核心组件,负责将请求转发到匹配的路由上。它根据路由配置和断言条件进行路由匹配,选择合适的路由进行请求转发。网关处理器还会依次应用配置的过滤器链,对请求进行处理和转换。
Gateway Filter Chain(网关过滤器链):网关过滤器链由一系列过滤器组成,按照配置的顺序依次执行。每个过滤器可以在请求前、请求后或请求发生错误时进行处理。过滤器链的执行过程可以修改请求、响应以及执行其他自定义逻辑。
SpringGateway执行流程
接受请求:接收客户端的 HTTP 请求。
寻找路由规则:根据配置的路由规则(如 URI、Header、Path 等),匹配到适合的路由。
WEB 过滤器链:触发全局过滤器和 Gateway Filter,进行一些通用的前置处理,例如日志记录、鉴权、限流等。
核心过滤器链执行:核心过滤器执行,比如 RouteToRequestUrlFilter(设置目标地址)、NettyRoutingFilter(将请求路由到目标服务)等。
请求转发到匹配的目标服务
响应回写
收到目标服务返回的响应后,执行后置过滤器处理(如修改响应数据、记录日志等),然后将响应回写给客户端。
Gray Release/金丝雀发布:在软件或服务发布过程中,将新版本的功能或服务以较小的比例引入到生产环境中,仅向部分用户或节点提供新功能的一种发布策略。
在传统的全量发布中,新版本的功能会一次性全部部署到所有的用户或节点上。如果新版本存在缺陷或问题,可能会对所有用户或节点产生严重的影响,导致系统崩溃或服务不可用。灰度发布采用较小的规模,并逐步将新版本的功能引入到生产环境中,仅向一小部分用户或节点提供新功能。通过持续监测和评估,可以在发现问题时及时回滚或修复。这种逐步引入新版本的方式可以降低风险,并提高系统的稳定性和可靠性。
根据用户划分:根据用户标识或用户组进行划分,在整个用户群体中只选择一小部分用户获得新功能。(常用)
根据地域划分:在不同地区或不同节点上进行划分,在其中的一小部分地区或节点进行新功能的发布。
根据流量划分:根据流量的百分比或请求次数进行划分,只将一部分请求流量引导到新功能上。
前端在灰度测试用户请求头打上标签,例如"grap-tag: true",而其他则为访问正式服务。
在负载均衡器Spring Cloud LoadBalancer中对请求头"grap-tag"进行判断,如果此标签不为空,并等于"true"时,表示访问灰度发布的服务,否则只访问正式的服务
在网关Spring Cloud Gateway中将Header标签"grap-tag: true"继续往下一个调用服务中传递
关键思路
注册中心区分正常服务和灰度服务;
负载均衡正确转发正常服务和灰度服务;
网关和 HTTP 工具传递灰度标签。
区分正式服务和灰度服务
在灰度服务既注册中心的MetaData(元数据)中标识为灰度服务,而元数据中没有标识(灰度服务)的则为正式服务,以Nacos为例
spring:
application:
name: canary-user-service
cloud:
nacos:
discovery:
username: nacos
password: nacos
server-addr: localhost:8848
namespace: public
register-enabled: true
metadata: { "grap-tag":"true" } # 标识自己为灰度服务
负载均衡Spring Cloud LoadBalancer 判断并调用灰度服务
//自定义负载均衡器,并使用轮询算法。如果Header中有灰度标签,则只查询灰度服务的节点实例,否则则查询出所有的正式节点实例(以供服务调用或服务转发)
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances,Request request) {
// 实例为空
if (instances.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("No servers available for service: " + this.serviceId);
}
return new EmptyResponse();
} else { // 服务不为空
RequestDataContext dataContext = (RequestDataContext) request.getContext();
HttpHeaders headers = dataContext.getClientRequest().getHeaders();
// 判断是否为灰度发布(请求)
if (headers.get(GlobalVariables.GRAY_KEY) != null &&
headers.get(GlobalVariables.GRAY_KEY).get(0).equals("true")) {
// 灰度发布请求,得到新服务实例列表
List<ServiceInstance> findInstances = instances.stream().
filter(s -> s.getMetadata().get(GlobalVariables.GRAY_KEY) != null &&
s.getMetadata().get(GlobalVariables.GRAY_KEY).equals("true"))
.toList();
if (findInstances.size() > 0) { // 存在灰度发布节点
instances = findInstances;
}
} else { // 查询非灰度发布节点
// 灰度发布测试请求,得到新服务实例列表
instances = instances.stream().
filter(s -> s.getMetadata().get(GlobalVariables.GRAY_KEY) == null ||
!s.getMetadata().get(GlobalVariables.GRAY_KEY).equals("true"))
.toList();
}
// 随机正数值 ++i( & 去负数)
int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;
// ++i 数值 % 实例数 取模 -> 轮询算法
int index = pos % instances.size();
// 得到服务实例方法
ServiceInstance instance = (ServiceInstance) instances.get(index);
return new DefaultResponse(instance);
}
}
网关传递灰度标识
- 要在网关 Spring Cloud Gateway 中传递灰度标识,只需要在 Gateway 的全局自定义过滤器中设置 Response 的 Header 即可,具体实现代码如下:package com.example.gateway.config;
import com.loadbalancer.canary.common.GlobalVariables;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class LoadBalancerFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 得到 request、response 对象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
if (request.getQueryParams().getFirst(GlobalVariables.GRAY_KEY) != null) {
// 设置金丝雀标识
response.getHeaders().set(GlobalVariables.GRAY_KEY,
"true");
}
// 此步骤正常,执行下一步
return chain.filter(exchange);
}
}
Openfeign 传递灰度标签HTTP
- 调用工具 Openfeign 传递灰度标签的实现代码如下:
import feign.RequestInterceptor;
import feign.RequestTemplate;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
@Component
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// 从 RequestContextHolder 中获取 HttpServletRequest
ServletRequestAttributes attributes = (ServletRequestAttributes)
RequestContextHolder.getRequestAttributes();
// 获取 RequestContextHolder 中的信息
Map<String, String> headers = getHeaders(attributes.getRequest());
// 放入 openfeign 的 RequestTemplate 中
for (Map.Entry<String, String> entry : headers.entrySet()) {
template.header(entry.getKey(), entry.getValue());
}
}
/**
* 获取原请求头
*/
private Map<String, String> getHeaders(HttpServletRequest request) {
Map<String, String> map = new LinkedHashMap<>();
Enumeration<String> enumeration = request.getHeaderNames();
if (enumeration != null) {
while (enumeration.hasMoreElements()) {
String key = enumeration.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
}
return map;
}
}
链路追踪?为什么要链路追踪?SkyWalking?Spring Cloud Sleuth原理
可以可视化地追踪请求从一个微服务到另一个微服务的调用情况。除了排查问题,链路追踪还可以帮助优化性能,可视化依赖关系、服务监控和告警
![sleuthprinciple.png](https://290ff162.telegraph-image-eg9.pages.dev/file/bc82ce3be04420eac161e.jpg)
Seata?支持哪些模式的分布式事务?实现原理?事务执行流程?全局事务ID和分支事务ID是怎么传递的?事务回滚是怎么实现的?
从业务无侵入的两阶段提交(全局事务)着手,在传统的两阶段上进行改进,把一个分布式事务理解成一个包含了若干分支事务的全局事务。而全局事务的职责是协调它管理的分支事务达成一致性,要么一起成功提交,要么一起失败回滚
AT(Atomikos)模式:默认。在业务代码中嵌入事务上下文,拦截并解析业务代码中的SQL语句,通过对数据库连接进行拦截和代理,实现事务的管理和协调。
TCC(Try-Confirm-Cancel)模式:基于补偿机制的分布式事务模式。业务逻辑需要实现Try、Confirm和Cancel三个阶段的操作。通过调用业务代码中的Try、Confirm和Cancel方法,并在每个阶段记录相关的操作日志
SAGA模式:基于事件驱动的分布式事务模式。每个服务都可以发布和订阅事件,通过事件的传递和处理来实现分布式事务的一致性。Seata提供了与SAGA模式兼容的Saga框架,用于管理和协调分布式事务的各个阶段。
XA模式:基于两阶段提交(Two-Phase Commit)协议的分布式事务模式。通过与数据库的XA事务协议进行交互,实现对分布式事务的管理和协调。需要数据库本身支持XA事务,并且需要配置相应的数据源
- 你们公司是如何处理分布式事务的?
- 我们某某特别严格的场景,用的是 TCC 来保证强一致性。
- 其他场基于阿里的 RocketMQ 来实现了分布式事务。如果是一般的分布式事务场景,订单插入之后要调用库存服务更新库存,库存数据没有资金那么的敏感,可以用可靠消息最终一致性方案。
- 基于seata的AT模式,有undolog
- 一阶段:业务数据和回滚日志记录UNDO LOG在同一个本地事务中提交,释放本地锁和连接资源
- 二阶段:提交异步化,非常快速地完成。回滚通过一阶段的回滚日志进行反向补偿。
主要包括三个核心组件
事务协调器TC(Transaction Coordinator):负责协调和管理分布式事务的整个过程。管理全局的分支事务的状态,用于全局性事务的提交和回滚。它接收事务的开始和结束请求,并根据事务的状态进行协调和处理。还负责记录和管理事务的全局事务 ID(Global Transaction ID)和分支事务 ID(Branch Transaction ID)。
事务管理器TM(Transaction Manager):用于开启、提交或回滚事务。负责全局事务的管理和控制。它协调各个分支事务的提交或回滚,并保证分布式事务的一致性和隔离性。还负责与事务协调器进行通信,并将事务的状态变更进行持久化。
资源管理器RM(Resource Manager):负责管理和控制各个参与者(Participant)的事务操作。它与事务管理器进行通信,并根据事务管理器的指令执行相应的事务操作,包括提交和回滚。用于分支事务上的资源管理,向TC注册分支事务,上报分支事务的状态,接收TC的命令来提交或者回滚分支事务。
基于两阶段提交(Two-Phase Commit)协议执行机制:
一阶段:预提交。事务协调器TC向各个资源管理器发送预提交请求,资源管理器RM执行相应的事务操作并返回执行结果。业务数据和回滚日志记录在同一个本地事务中提交,并释放本地锁和连接资源。
二阶段:真正提交。包括两个步骤:
提交异步化:事务协调器发出提交请求,各个资源管理器提交。
回滚反向补偿:如果在预提交阶段中有任何一个资源管理器返回失败结果,TC出回滚请求,各个RM回滚,利用一阶段的回滚日志进行反向补偿
事务发起方(Transaction Starter发起分布式事务的服务)发起全局事务:向Seata的事务协调器TC申请开启全局事务,生成全局事务ID(Global Transaction ID)。
事务协调器TC创建全局事务记录:并生成分支事务ID(Branch Transaction ID)。
分支事务注册:事务发起方将全局事务ID和分支事务ID发送给各个参与者(Participant),即资源管理器。参与者将分支事务ID注册到本地事务管理器,并将事务的执行结果反馈给事务协调器。
执行业务逻辑:在分布式事务的上下文中,各个参与者执行各自的本地事务,即执行业务逻辑和数据库操作。
预提交阶段:事务发起方向事务协调器发送预提交请求,事务协调器将预提交请求发送给各个参与者。
执行本地事务确认:参与者接收到预提交请求后,执行本地事务的确认操作,并将本地事务的执行结果反馈给事务协调器。
全局事务提交或回滚:事务协调器根据参与者反馈的结果进行判断,如果所有参与者的本地事务都执行成功,事务协调器发送真正的提交请求给参与者,参与者执行最终的提交操作;如果有任何一个参与者的本地事务执行失败,事务协调器发送回滚请求给参与者,参与者执行回滚操作。
完成全局事务:事务协调器TC接收到参与者的提交或回滚结果后,根据结果更新全局事务的状态,并通知事务发起方全局事务的最终结果。
![seata.png](https://b.bdstatic.com/comment/I4MgmLj55Sgosm2EdtLMDQe85d4b33946c0c9e11eb23fbf057d877.png)
通过上下文传递的方式进行传递。传递方式包括参数传递、线程上下文传递和消息中间件传递。可根据业务场景和技术选型进行选择和调整。
通过回滚日志。每个参与者在执行本地事务期间生成回滚日志,记录了对数据的修改操作,参与者根据回滚日志中的信息执行撤销操作,将数据恢复到事务开始前的状态。
可以将日志存储在不同的介质中。通过回滚日志的持久化和恢复,确保事务的一致性和恢复性
监控和告警?
Prometheus:开源的监控系统,具有灵活的数据模型和强大的查询语言,能够收集和存储时间序列数据。它可以通过HTTP协议定期拉取微服务的指标数据,并提供可扩展的存储和查询功能。
Grafana:开源的可视化仪表板工具,与 Prometheus 结合使用,创建实时和历史数据的仪表板。Grafana 提供了丰富的图表和可视化选项,可以帮助用户更好地理解和分析微服务的性能和状态。
日志收集?
ELK
Elasticsearch:分布式搜索和分析引擎,用于存储和索引大量的日志数据。它提供了快速的搜索和聚合功能,可以高效地处理大规模的日志数据。
Logstash:用于收集、过滤和转发日志数据的工具。它可以从各种来源(如文件、网络、消息队列等)收集日志数据,并对数据进行处理和转换,然后将其发送到Elasticsearch进行存储和索引。
Kibana:用于日志数据可视化和分析的工具。它提供了丰富的图表、仪表盘和搜索功能,可以帮助用户实时监控和分析日志数据,发现潜在的问题和趋势。
Elasticsearch提供数据存储和检索能力,Logstash负责将日志收集到ES,Kibana负责日志数据的可视化分析。
使用ELK进行微服务日志收集的一般流程如下:
在每个微服务中配置日志输出:将微服务的日志输出到标准输出(stdout)或日志文件。
使用Logstash收集日志:配置Logstash收集器,通过配置输入插件(如文件输入、网络输入等)监听微服务的日志输出,并进行过滤和处理。
将日志数据发送到Elasticsearch:配置Logstash的输出插件,将经过处理的日志数据发送到Elasticsearch进行存储和索引。
使用Kibana进行可视化和分析:通过Kibana连接到Elasticsearch,创建仪表盘、图表和搜索查询,实时监控和分析微服务的日志数据。
除了应用最广泛的ELK,还有一些其它的方案比如Fluentd、Graylog、Loki、Filebeat,一些云厂商也提供了付费方案,比如阿里云的sls。