跳至主要內容

springcloud

HeChuangJun约 16148 字大约 54 分钟

什么是微服务?

  • 微服务(Microservices)是一种软件架构风格,将一个大型应用程序划分为一组小型、自治且松耦合的服务。每个微服务负责执行特定的业务功能,并通过轻量级通信机制(如HTTP)相互协作。每个微服务可以独立开发、部署和扩展,使得应用程序更加灵活、可伸缩和可维护。
  • 在微服务的架构演进方向:单体式-->服务化SOA(Service-Oriented Architecture,面向服务的架构)-->微服务。
  • 微服务是一种特定的架构风格,而SOA是一种设计原则。微服务可以看作是对SOA思想的一种具体实践方式,但并不等同于SOA。
  • SOA关注于服务的重用性和组合性,但并没有具体规定服务的大小

微服务组件?

  • 注册中心:用于服务的注册与发现,管理微服务的地址信息。常见的实现包括:
    • Spring Cloud Netflix:Eureka、Consul
    • Spring Cloud Alibaba:Nacos
  • 配置中心:用于集中管理微服务的配置信息,可以动态修改配置而不需要重启服务。常见的实现包括:
    • Spring Cloud Netflix:Spring Cloud Config
    • Spring Cloud Alibaba:Nacos Config
  • 远程调用:用于在不同的微服务之间进行通信和协作。常见的实现保包括:
    • RESTful API:如RestTemplate、Feign
    • RPC(远程过程调用):如Dubbo、gRPC
  • API网关:作为微服务架构的入口,统一暴露服务,并提供路由、负载均衡、安全认证等功能。常见的实现包括:
    • Spring Cloud Netflix:Zuul、Gateway
    • Spring Cloud Alibaba:Gateway、Apisix等
  • 分布式事务:保证跨多个微服务的一致性和原子性操作。常见的实现包括:
    • Spring Cloud Alibaba:Seata
  • 熔断器:用于防止微服务之间的故障扩散,提高系统的容错能力。常见的实现包括:
    • Spring Cloud Netflix:Hystrix
    • Spring Cloud Alibaba:Sentinel、Resilience4j
  • 限流和降级:用于防止微服务过载,对请求进行限制和降级处理。常见的实现包括:
    • Spring Cloud Netflix:Hystrix
    • Spring Cloud Alibaba:Sentinel
  • 分布式追踪和监控:用于跟踪和监控微服务的请求流程和性能指标。常见的实现包括:
    • Spring Cloud Netflix:Spring Cloud Sleuth + Zipkin
    • Spring Cloud Alibaba:SkyWalking、Sentinel Dashboard

注册中心是用来干什么的?

  • 注册中心是用来管理和维护分布式系统中各个服务的地址和元数据的组件。它主要用于实现服务发现和服务注册功能。
    • 服务注册:各个服务在启动时向注册中心注册自己的网络地址、服务实例信息和其他相关元数据。这样,其他服务就可以通过注册中心获取到当前可用的服务列表。
    • 服务发现:客户端通过向注册中心查询特定服务的注册信息,获得可用的服务实例列表。这样客户端就可以根据需要选择合适的服务进行调用,实现了服务间的解耦。
    • 负载均衡:对同一服务的多个实例进行负载均衡,将请求分发到不同的实例上,提高整体的系统性能和可用性。
    • 故障恢复:监测和检测服务的状态,当服务实例发生故障或下线时,可以及时更新注册信息,从而保证服务能够正常工作。
    • 服务治理:进行服务的配置管理、动态扩缩容、服务路由、灰度发布等操作,实现对服务的动态管理和控制

说下Eureka、ZooKeeper、Nacos的区别?√

特性EurekaZooKeeperNacos
开发公司NetflixApache 基金会阿里巴巴
CAPAP(可用性和分区容忍性)CP(一致性和分区容忍性)既支持AP,也支持CP
功能服务注册与发现分布式协调、配置管理、分布式锁服务注册与发现、配置管理、服务管理
定位适用于构建基于 HTTP 的微服务架构通用的分布式协调服务框架适用于微服务和云原生应用
访问协议HTTPTCPHTTP/DNS
自我保护支持-支持
数据存储内嵌数据库、多个实例形成集群ACID 特性的分布式文件系统 ZAB 协议内嵌数据库、MySQL 等
健康检查Client BeatKeep AliveTCP/HTTP/MYSQL/Client Beat
特点简单易用、自我保护机制高性能、强一致性动态配置管理、流量管理、灰度发布等
可以看到Eureka和ZooKeeper的最大区别是一个支持AP,一个支持CP,Nacos既支持既支持AP,也支持CP。

Eureka实现原理了解吗?

  • 服务注册与发现: 当一个服务实例启动时,它会向Eureka Server发送注册请求,将自己的信息注册到注册中心。Eureka Server会将这些信息保存在内存中,并提供REST接口供其他服务查询。服务消费者可以通过查询服务实例列表来获取可用的服务提供者实例,从而实现服务的发现。
  • 服务健康检查: Eureka通过心跳机制来检测服务实例的健康状态。服务实例会定期向Eureka Server发送心跳,也就是续约,以表明自己的存活状态。如果Eureka Server在一定时间内没有收到某个服务实例的心跳,则会将其标记为不可用,并从服务列表中移除,下线实例。
  • 服务负载均衡: Eureka客户端在调用其他服务时,会从本地缓存中获取服务的注册信息。如果缓存中没有对应的信息,则会向Eureka Server发送查询请求。Eureka Server会返回一个可用的服务实例列表给客户端,客户端可以使用负载均衡算法选择其中一个进行调用。

Eureka Server怎么保证高可用?

  • 多实例部署: 通过将多个Eureka Server实例部署在不同的节点上,可以实现高可用性。当其中一个实例发生故障时,其他实例仍然可以提供服务,并保持注册信息的一致性。
  • 服务注册信息的复制: 当一个服务实例向Eureka Server注册时,每个Eureka Server实例都会复制其他实例的注册信息,以保持数据的一致性。当某个Eureka Server实例发生故障时,其他实例可以接管其工作,保证整个系统的正常运行。
  • 自我保护机制: Eureka还具有自我保护机制。当Eureka Server节点在一定时间内没有接收到心跳时,它会进入自我保护模式。在自我保护模式下,Eureka Server不再剔除注册表中的服务实例,以保护现有的注册信息。这样可以防止由于网络抖动或其他原因导致的误剔除,进一步提高系统的稳定性。

为什么微服务需要配置中心?

  • 微服务架构中的每个服务通常都需要一些配置信息,例如数据库连接地址、服务端口、日志级别等。这些配置可能因为不同环境、不同部署实例或者动态运行时需要进行调整和管理。
  • 微服务的实例一般非常多,如果每个实例都需要一个个地去做这些配置,那么运维成本将会非常大,这时候就需要一个集中化的配置中心,去管理这些配置。

Nacos配置中心的原理

  • 配置信息存储:Nacos默认使用内嵌数据库Derby来存储配置信息,还可以采用MySQL等关系型数据库。
  • 注册配置信息:服务启动时,Nacos Client会向Nacos Server注册自己的配置信息,这个注册过程就是把配置信息写入存储,并生成版本号。
  • 获取配置信息:服务运行期间,Nacos Client通过API从Nacos Server获取配置信息。Server根据键查找对应的配置信息,并返回给Client。
  • 监听配置变化:Nacos Client可以通过注册监听器的方式,实现对配置信息的监听。当配置信息发生变化时,Nacos Server会通知已注册的监听器,并触发相应的回调方法。

Nacos配置中心长轮询机制?

  • 一般来说客户端和服务端的交互分为两种:推(Push)和拉(Pull),Nacos在Pull的基础上,采用了长轮询来进行配置的动态刷新。
  • 客户端发起Pull请求,服务端检查配置是否有变更。如果没有变更,则设置一个定时任务,在一段时间后执行,并将当前的客户端连接加入到等待队列中。
  • 在等待期间,如果配置发生变更,服务端会立即返回结果给客户端,完成一次"推送"操作。
    如果在等待期间没有配置变更,等待时间达到预设的超时时间后,服务端会自动返回结果给客户端,即使配置没有变更。
  • 如果在等待期间,通过Nacos Dashboard或API对配置进行了修改,会触发一个事件机制,服务端会遍历等待队列,找到发生变更的配置项对应的客户端连接,并将变更的数据通过连接返回,完成一次"推送"操作。
  • 通过长轮询的方式,Nacos客户端能够实时感知配置的变化,并及时获取最新的配置信息。同时,这种方式也降低了服务端的压力,避免了大量的长连接占用内存资源。

能说下HTTP和RPC的区别吗?

  • HTTP(Hypertext Transfer Protocol)是一种应用层协议,主要强调的是网络通信;
  • RPC(Remote Procedure Call,远程过程调用)是一种用于分布式系统之间通信的协议,强调的是服务之间的远程调用。
  • 在微服务体系里,基于HTTP风格的远程调用通常使用框架如Feign来实现,基于RPC的远程调用通常使用框架如Dubbo来实现。
-HTTPRPC
定义HTTP(超文本传输协议)是一种用于传输超文本的协议。RPC(远程过程调用)是一种用于实现分布式系统中不同节点之间通信的协议。
通信方式基于请求-响应模型,客户端发送请求,服务器返回响应。基于方法调用模型,客户端调用远程方法并等待结果。
传输协议基于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的区别?

-FeignDubbo
定义Feign是一个声明式的Web服务客户端,用于简化HTTP API的调用。Dubbo是一个分布式服务框架,用于构建面向服务的微服务架构。
通信方式基于HTTP协议,使用RESTful风格的接口进行定义和调用。基于RPC协议,支持多种序列化协议如gRPC、Hessian等。
服务发现通常结合服务注册中心(如Eureka、Consul)进行服务发现和负载均衡。通过ZooKeeper、Nacos等进行服务注册和发现,并提供负载均衡功能。
服务治理不直接提供服务治理功能,需要结合其他组件或框架进行服务治理。提供服务注册与发现、负载均衡、容错机制、服务降级等服务治理功能。
跨语言性支持跨语言通信,可以使用HTTP作为通信协议实现不同语言之间的通信。支持跨语言通信,通过Dubbo的IDL生成不同语言的客户端和服务端代码。
生态系统集成了Spring Cloud生态系统,与Spring Boot无缝集成。拥有完整的生态系统,包括注册中心、配置中心、监控中心等组件。
适用场景适用于构建RESTful风格的微服务架构,特别适合基于HTTP的微服务调用。适用于构建面向服务的微服务架构,提供更全面的服务治理和容错机制。
  • Dubbo可以使用HTTP协议作为通信方式,而Feign也可以集成RPC协议进行远程调用。选择使用哪种远程调用方式取决于具体的业务需求和技术栈的选择

说一下Fegin?

  • Feign是一个声明式的Web服务客户端,它简化了使用基于HTTP的远程服务的开发。Feign使用RestTemplate实现Http调用,使用Ribbon实现负载均衡。
  • Feign的主要特点和功能包括:
    • 声明式API:Feign允许开发者使用简单的注解来定义和描述对远程服务的访问。通过使用注解,开发者可以轻松地指定URL、HTTP方法、请求参数、请求头等信息,使得远程调用变得非常直观和易于理解。
@FeignClient(name = "example", url = "https://api.example.com") 
 public interface ExampleService { 
     @GetMapping("/endpoint") 
     String getEndpointData(); 
 }
  • 集成负载均衡:Feign集成了Ribbon负载均衡器,可以自动实现客户端的负载均衡。它可以根据服务名和可用实例进行动态路由,并分发请求到不同的服务实例上,提高系统的可用性和可伸缩性。
  • 容错机制:Feign支持集成Hystrix容错框架,可以在调用远程服务时提供容错和断路器功能。当远程服务不可用或响应时间过长时,Feign可以快速失败并返回预设的响应结果,避免对整个系统造成级联故障。

为什么Feign第一次调用耗时很长?

  • 主要原因是由于Ribbon的懒加载机制,当第一次调用发生时,Feign会触发Ribbon的加载过程,包括从服务注册中心获取服务列表、建立连接池等操作,这个加载过程会增加首次调用的耗时。
ribbon: 
   eager-load: 
     enabled: true 
       clients: service-1
  • 解决方法:在应用启动时预热Feign客户端,自动触发一次无关紧要的调用,来提前加载Ribbon和其他相关组件。相当于提前进行了第一次调用。

Feign怎么实现认证传递?

  • 使用拦截器传递认证信息。可以通过实现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"; 
     } 
 }

Fegin怎么做负载均衡?Ribbon?

  • 在Feign中,负载均衡是通过集成Ribbon来实现的。Ribbon是Netflix开源的一个客户端负载均衡器.Ribbon通过从服务注册中心获取可用服务列表,并通过负载均衡算法选择合适的服务实例进行请求转发,实现客户端的负载均衡。

负载均衡算法?

1. springcloud组件有哪些? √

  • 服务发现与注册ZooKeeper、Eureka、consoul、Nacos
  • 服务间调用Feign
  • 断路器Hystrix、Sentinel
  • 网关Zuul、gatyway
  • 分布式配置consul
  • 负载均衡Ribbon、spring-cloud-loadbalancer

2. 微服务的优缺点?

  • 优点
    • 每一个服务足够内聚,代码容易理解
    • 开发效率提高,一个服务只做一件事
    • 微服务能够被小团队单独开发
    • 微服务是松耦合的,是有功能意义的服务
    • 可以用不同的语言开发,面向接口编程
    • 易于与第三方集成
    • 微服务只是业务逻辑的代码,不会和 HTML、CSS 或者其他界面组合
  • 缺点
    • 分布式系统的复杂性
    • 多服务运维难度,随着服务的增加,运维的压力也在增大
    • 系统部署依赖
    • 服务间通信成本
    • 数据一致性和事务管理
    • 系统集成测试
    • 性能监控
    • 团队沟通和协作成本

流行的微服务解决方案及区别

特点DubboSpring Cloud NetflixSpring Cloud Alibaba
开发语言JavaJavaJava
服务治理提供完整的服务治理功能提供部分服务治理功能提供完整的服务治理功能
服务注册与发现ZooKeeper/NacosEureka/ConsulNacos
负载均衡自带负载均衡策略RibbonRibbon\Dubbo负载均衡策略
服务调用RPC方式RestTemplate/FeignFeign/RestTemplate/Dubbo
熔断器SentinelHystrixSentinel/Resilience4j
配置中心ApolloSpring Cloud ConfigNacos Config
API网关Higress/APISIXZuul/GatewaySpring Cloud Gateway
分布式事务Seata不支持分布式事务Seata
限流和降级SentinelHystrixSentinel
分布式追踪和监控SkywalkingSpring Cloud Sleuth + ZipkinSkyWalking或Sentinel Dashboard
微服务网格Dubbo Mesh不支持微服务网格Service Mesh(Nacos+Dubbo Mesh)
社区活跃度相对较高目前较低相对较高
孵化和成熟度孵化较早,成熟度较高成熟度较高孵化较新,但迅速发展

3. 服务注册与发现

  • 基于spring-cloud-commons的discovery的DiscoveryClient接口,实现统一的客户端的注册发现。
  • spring-cloud-netflix-eureka-server和spring-cloud-netflix-eureka-client ,基于Eureka实现。
  • spring-cloud-alibaba-nacos-discovery,基于Nacos实现。
  • spring-cloud-zookeeper-discovery,基于Zookeeper实现。

3.1. 为什么要有服务注册与发现?

  • 屏蔽、解耦服务之间相互依赖的细节。服务之间的远程调用必须要知道IP、端口信息。调用方直接配置被调用方的IP、端口,这种调用直接依赖IP、端口的方式存在依赖,如被调用的IP、端口变化后,调用方也要同步修改。通过服务发现,将服务的IP、端口转化成服务名来调用。
  • 对微服务进行动态配置。微服务架构中,服务众多、服务之间的相互依赖错综复杂,无论是服务主动停止、意外挂掉,还是因为流量增加对服务实现扩容,这些服务或状态上的动态变化,都需要尽快的通知到被调用方,被调用方采取相应的策略。对于服务注册与发现要实时管理者服务的数据和状态,包括服务的注册上线、服务主动下线,异常服务的踢出。服务发现将服务IP、端口等细节通过一个服务名抽象给调用者,并动态管理者各个微服务的状态检测、状态更新,服务上线,下线等,这些都是微服务治理的基础,包括,负载均衡,链路跟踪。
  • 服务发现有两大模式:
    • 客户端发现模式:客户端决定相应服务实例的网络位置,并且对请求实现负载均衡。客户端查询服务注册表,后者是一个可用服务实例的数据库;然后使用负载均衡算法从中选择一个实例,并发出请求
    • 服务端发现模式:客户端通过负载均衡器向某个服务提出请求,负载均衡器查询服务注册表,并将请求转发到可用的服务实例。如同客户端发现,服务实例在服务注册表中注册或注销。

3.2. Eureka介绍

  • 由两个组件组成:Eureka服务端,服务注册中心,支持集群部署;Eureka 客户端,Java客户端,用来处理服务注册与发现。
  • 在应用启动时,Eureka客户端向服务端注册自己的服务信息,同时将服务端的服务信息缓存到本地。客户端会和服务端周期性的进行心跳交互,以更新服务租约和服务信息。

3.3. Eureka缓存机制?

eurekacache.png
eurekacache.png

3.4. Eureka自我保护模式?解决方法

  • 当Eureka Server节点在短时间内丢失了过多实例的连接时(比如网络故障或频繁的启动关闭客户端)就会进入自我保护模式,保护服务注册表中的信息,不再删除服务注册表中的数据(即不会注销任何微服务),当网络故障恢复后,就会自动退出自我保护模式
  • 解决方法
    • 等待Eureka Server自动恢复等待网络恢复(或者没有频繁的启动与关闭实例)
    • 重启Eureka Server生产环境建议对Eureka Server做负载均衡,这样在依次重启Eureka Server后,无效的实例会被清除。
    • 关闭Eureka自我保护模式。yml配置文件:
eureka:
  server:
    enable-self-preservation: false
    eviction-interval-timer-in-ms: 4000 # This is not required

3.5. eureka的注册中心怎么知道最近的服务,就近机房?

  • 通过属性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/

3.6. ZooKeeper,eureka,nacos原理?

  • 基于spring-cloud-commons的discovery的DiscoveryClient接口,实现统一的客户端的注册发现。
  • ZooKeeper(CP):leader+follower,leader写,同步到follower,follower读,保证顺序一致性,主动推送,leader崩溃的时候,为了保证数据一致性,尽量不要读到不一致的数据,此时要重新选举leader以及做数据同步,此时集群会短暂的不可用
  • Eureka(AP):peer-to-peer,各节点都能写也都能读,每个节点通过异步复同步给其他节点,所以可能数据不一致,任何一个节点宕机,其他节点正常工作,可用性高
  • Nacos(CP/AP):基于raft算法。还提供了配置管理、元数据管理和流量管理等功能,并且提供了一个可视化的控制台管理界面。

4. 在 Spring Cloud 中的负载均衡

  • 基于spring-cloud-commons的loadbalancer的ServiceInstanceChooser接口实现统一的服务的选择。并且,负载均衡组件在选择需要调用的服务之后,还提供调用该服务的功能,具体方法见LoadBalancerClient接口的execute方法。
    • spring-cloud-netflix-ribbon,基于Ribbon实现。
    • spring-cloud-loadbalancer,提供简单的负载均衡功能。

4.1. 负载均衡的两种模式

  • 客户端模式:客户端节点有一份服务端清单,从Eureka服务注册中心获取。在Spring Cloud中使用@LoadBalanced注解开启客户端模式
  • 服务端模式(nginx):维护一个可用的服务端清单,然后通过心跳机制来删除故障的服务端节点以保证清单中都是可以正常访问的服务端节点。负载均衡服务器按照某种配置好的规则从可用服务端清单中选出一台服务器去处理客户端的请求。

4.2. 负载均衡作用

  • 优化资源使用,最大化吞吐量,最小化响应时间并避免任何单一资源的过载。使用多个组件进行负载平衡而不是单个组件可能会通过冗余来提高可靠性和可用性。负载平衡通常涉及专用软件或硬件,例如多层交换机或域名系统服务器进程。

4.3. ribbon介绍?核心组件?

  • 作用:主要提供客户端的软件负载均衡算法。
  • 核心组件
    • Server封装了服务实例的ip和端口之类
    • ServerList获取服务实例列表的
    • ServerListUpdater是用来更新服务注册表的数据
    • IRule负责负载均衡的算法的
    • IClientConfig获取到一些配置Ribbon的一些配置
    • ILoadBalancer主要是用来协调上面提到的各个核心组件的,使得他们能够协调工作

4.4. ribbon实现原理?

ribbonprinciple.png
ribbonprinciple.png

4.5. ribbon负载均衡算法?√

  • Round Robin轮询
  • Random 随机
  • AvailabilityFilteringRule会优先过滤掉由于多次访问故障而处于断路器跳闸状态被标记为circuit tripped的服务,还有并发的连接数(active connections 超过配置的阈值)超过临界值的服务,然后对剩余的服务列表按照轮训策略进行访问
  • WeightedResponseTimeRule根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的机率越大,刚启动时如果统计信息不足,则使用RoundRobinRule 策略,等统计信息足,会切换到WeightedResponseTimeRule
  • RetryRule先按照RoundRobinRule策略获取服务,如果获取服务失败则在制定时间内进行重试
  • BestAvailableRule会优先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,选择一个最小的并发请求的server.逐个考察Server,如果Server被tripped则忽略,在选择ActiveRequestsCount最小的server
  • ZoneAvoidanceRule复合判断server所在区域的性能和server的可用性选择服务器

4.6. Ribbon 缓存机制?

4.7. Ribbon 重试机制?

4.8. Ribbon 是怎么和 Eureka 整合的?

  • 首先,Ribbon 会从 Eureka Client 里获取到对应的服务列表。
  • 然后,Ribbon 使用负载均衡算法获得使用的服务。
  • 最后,Ribbon 调用对应的服务。

5. 声明式调用fegin?实现原理?

  • spring-cloud-openfeign ,基于 Feign 实现。
  • Feign 实现原理
    • 如果接口定义了@FeignClient注解,Feign会对这个接口创建一个动态代理。用那个接口本质就是会调用 Feign 创建的动态代理,
    • Feign的动态代理会根据接口上@RequestMapping 等注解,来动态构造出你要请求的服务的地址。
    • 最后针对这个地址,发起请求、解析响应。
    • 通过@EnableFeignClients和FeignClient注解 通过动态代理在本地实例化远程接口->封装Request对象并进行编码->使用restTemplate发起http请求并对获取结果进行解码。
      feginprinciple.png

5.1. Feign 和 Ribbon 的区别?

  • Ribbon和Feign都是使用于调用用其余服务的,不过方式不同。
    • 启动类用的注解不同。Ribbon使用的是@RibbonClient ;Feign 使用的是 @EnableFeignClients 。
    • 服务的指定位置不同。Ribbon是在@RibbonClient注解上设置;Feign则是在定义声明方法的接口中用@FeignClient注解上设置
  • 调使用方式不同
    • Ribbon需要构建Http请求,模拟Http请求而后用RestTemplate发送给其余服务,步骤相当繁琐。
    • Feign采使用接口的方式,将需要调使用的其余服务的方法定义成声明方法就可,不需要构建Http请求。不过要注意的是声明方法的注解、方法签名要和提供服务的方法完全一致。

5.2. Feign 是怎么和 Ribbon、Eureka 整合的?

feginribboneureka.png
feginribboneureka.png
  • 首先,用户调用 Feign 创建的动态代理。
  • 然后,Feign 调用 Ribbon 发起调用流程。
    • 首先,Ribbon 会从 Eureka Client 里获取到对应的服务列表。
    • 然后,Ribbon 使用负载均衡算法获得使用的服务。
    • ribbon调用对应的服务,调用 Feign ,而 Feign 调用 HTTP 库最终调用使用的服务。
    • 因为 Feign 和 Ribbon 都存在使用 HTTP 库调用指定的服务,两者集成后保留Feign的调用,而Ribbon只负责负载均衡功能

5.3. Feign 重试机制?

//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请求,而由于缓存了请求体,此时可能会影响服务器的资源。

6. 服务保障?

  • spring-cloud-netflix-hystrix基于 Hystrix 实现。
  • spring-cloud-alibaba-sentinel基于 Sentinel 实现。

什么是服务雪崩?

  • 在微服务中,假如一个或者多个服务出现故障,如果这时候,依赖的服务还在不断发起请求,或者重试,那么这些请求的压力会不断在下游堆积,导致下游服务的负载急剧增加。不断累计之下,可能会导致故障的进一步加剧,可能会导致级联式的失败,甚至导致整个系统崩溃,这就叫服务雪崩。
  • 防止方法
    • 服务高可用部署:确保各个服务都具备高可用性,通过冗余部署、故障转移等方式来减少单点故障的影响。
    • 限流和熔断:对服务之间的请求进行限流和熔断,以防止过多的请求涌入导致后端服务不可用。
    • 缓存和降级:合理使用缓存来减轻后端服务的负载压力,并在必要时进行服务降级,保证核心功能的可用性。

6.1. 为什么要使用服务保障?

  • 在微服务架构中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用(RPC)。为了保证其高可用,单个服务又必须集群部署。由于网络原因或者自身的原因,服务并不能保证服务的 100% 可用,如果单个服务出现问题,调用这个服务就会出现网络延迟,此时若有大量的网络涌入,会形成任务累积,导致服务瘫痪,甚至导致服务“雪崩”。为了解决这个问题,就出现断路器模型。

6.2. hystrix作用,原理√

  • 断路器,保护系统,控制故障范围。
  • 简介:Hystrix 是一个延迟和容错库,旨在隔离远程系统,服务和第三方库的访问点,当出现故障是不可避免的故障时,停止级联故障并在复杂的分布式系统中实现弹性。
  • 旨在通过添加延迟容错和容错逻辑来提高分布式系统的弹性,防止雪崩效应的发生。它通过隔离服务之间的依赖关系、限制并发线程池的大小、实现熔断机制等方式来实现容错。
  • 旨在隔离远程系统,服务和第三方库的访问点,当出现故障是不可避免的故障时,停止级联故障并在复杂的分布式系统中实现弹性。

6.3. Hystrix隔离策略

  • 线程池隔离(支持超时)。tomcat线程会将请求任务交给服务内部线程池的线程处理,线程完成后将调用结果返回给tomcat。从而实现资源隔离,服务内部的线程池的数量就决定了整个服务并发度;当请求的服务网络开销比较大的时候,或者是请求比较耗时的时候使用,因为,可以保证大量的容器(tomcat)线程可用,不会由于服务原因,一直处于阻塞或等待状态,快速失败返回
  • 信号量隔离。限制tomcat访问服务的线程数达到限流目的。请求缓存服务时使用,因为这类服务的返回通常会非常的快,不会占用容器线程太长时间,而且也减少了线程切换的一些开销,提高了缓存服务的效率
    hystrixprinciple.png

6.4. Hystrix 缓存机制?作用?

  • 减少重复的请求数。降低依赖服务的返回数据始终保持一致。
  • 在同一个用户请求的上下文中,相同依赖服务的返回数据始终保持一致。
  • 请求缓存在run()和construct()执行之前生效,所以可以有效减少不必要的线程开销

7. 什么是服务熔断?

  • 服务熔断是微服务架构中的容错机制,用于保护系统免受服务故障或异常的影响。当某个服务出现故障或异常时,服务熔断可以快速隔离该服务,确保系统稳定可用。
  • 它通过监控服务的调用情况,当错误率或响应时间超过阈值时,触发熔断机制,后续请求将返回默认值或错误信息,避免资源浪费和系统崩溃。
  • 服务熔断还支持自动恢复,重新尝试对故障服务的请求,确保服务恢复正常后继续使用。

什么是服务降级?

  • 服务降级是一种微服务架构中的容错机制,用于在系统资源紧张或服务故障时保证核心功能的可用性。
  • 当系统出现异常情况时,服务降级会主动屏蔽一些非核心或可选的功能,而只提供最基本的功能,以确保系统的稳定运行。通过减少对资源的依赖,服务降级可以保证系统的可用性和性能。
  • 它可以根据业务需求和系统状况来制定策略,例如替换耗时操作、返回默认响应、返回静态错误页面等。

熔断降级方案实现?

框架实现方案特点
Spring Cloud NetflixHystrix提供线程隔离、服务降级、请求缓存、请求合并等功能可与Spring Cloud其他组件无缝集成.官方已宣布停止维护,推荐使用Resilience4j代替
Spring CloudResilience4j轻量级服务熔断库 提供类似于Hystrix的功能 具有更好的性能和更简洁的API 可与Spring Cloud其他组件无缝集成
Spring Cloud AlibabaSentinel阿里巴巴开源的流量控制和熔断降级组件提供实时监控、流量控制、熔断降级等功能与Spring Cloud Alibaba生态系统紧密集成
DubboDubbo自带熔断降级机制Dubbo框架本身提供的熔断降级机制可通过配置实现服务熔断和降级与Dubbo的RPC框架紧密集成

Hystrix怎么实现服务容错?

  • 服务熔断(Circuit Breaker):Hystrix通过设置阈值来监控服务的错误率或响应时间。当错误率或响应时间超过预设的阈值时,熔断器将会打开,后续的请求将不再发送到实际的服务提供方,而是返回预设的默认值或错误信息。这样可以快速隔离故障服务,防止故障扩散,提高系统的稳定性和可用性。
  • 服务降级(Fallback):当服务熔断打开时,Hystrix可以提供一个备用的降级方法或返回默认值,以保证系统继续正常运行。开发者可以定义降级逻辑,例如返回缓存数据、执行简化的逻辑或调用其他可靠的服务,以提供有限但可用的功能。
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 
  
 /** 
 * 服务降级示例 
 **/ 
 @Service 
 public class MyService { 
  
     @HystrixCommand(fallbackMethod = "fallbackMethod") 
     public String myServiceMethod() { 
         // 实际的服务调用逻辑 
         // ... 
     } 
  
     public String fallbackMethod() { 
         // 降级方法的逻辑,当服务调用失败时会执行此方法 
         // 可以返回默认值或执行其他备用逻辑 
         // ... 
     } 
 }
  • 请求缓存(Request Caching):Hystrix可以缓存对同一请求的响应结果,当下次请求相同的数据时,直接从缓存中获取,避免重复的网络请求,提高系统的性能和响应速度。
  • 请求合并(Request Collapsing):Hystrix可以将多个并发的请求合并为一个批量请求,减少网络开销和资源占用。这对于一些高并发的场景可以有效地减少请求次数,提高系统的性能。
  • 实时监控和度量(Real-time Monitoring and Metrics):Hystrix提供了实时监控和度量功能,可以对服务的执行情况进行监控和统计,包括错误率、响应时间、并发量等指标。通过监控数据,可以及时发现和解决服务故障或性能问题。
  • 线程池隔离(Thread Pool Isolation):Hystrix将每个依赖服务的请求都放在独立的线程池中执行,避免因某个服务的故障导致整个系统的线程资源耗尽。通过线程池隔离,可以提高系统的稳定性和可用性。

6.5. Hystrix 熔断机制?实现?

  • Hystrix的熔断机制通过监控服务调用的状态来实现。当服务调用失败率达到一定阈值时,Hystrix会自动开启熔断器,将后续的请求快速失败,避免对服务的进一步压力。
  • Hystrix的熔断方式有以下几种:
    • 强制熔断:在某些情况下,比如服务提供方宕机等情况下,直接强制熔断,不再进行服务调用。
    • 根据阈值熔断:当服务调用失败率达到一定阈值时,自动开启熔断器,将后续请求快速失败。
    • 半开状态熔断:在熔断器开启一段时间后,尝试发送一个测试请求到服务提供方,如果请求成功,则熔断器进入半开状态,否则继续保持开启状态。
    • 降级:当服务调用失败或超时时,可以通过返回默认值或者调用备用服务的方式进行降级,保证系统的可用性。
  • Hystrix断路器通过HystrixCircuitBreaker实现。有三种状态:CLOSED-关闭;OPEN-打开;HALF_OPEN-半开
    • 断路器处于OPEN状态时,链路处于非健康状态,命令执行时,直接调用回退逻辑,跳过正常逻辑。
    • 红线:初始时断路器处于CLOSED状态,链路处于健康状态。当满足如下条件,断路器从CLOSED变成OPEN状态
      • 周期(可配,HystrixCommandProperties.default_metricsRollingStatisticalWindow = 10000 ms)内,总请求数超过一定量(可配,HystrixCommandProperties.circuitBreakerRequestVolumeThreshold = 20 ) 。错误请求占总请求数超过一定比例( 可配,HystrixCommandProperties.circuitBreakerErrorThresholdPercentage = 50% ) 。
    • 绿线:断路器处于OPEN状态,命令执行时,若当前时间超过断路器开启时间一定时间(HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds = 5000ms),断路器变成HALF_OPEN状态,尝试调用正常逻辑,根据执行是否成功,打开或关闭熔断器【蓝线】。
      hystrixcircuitbreakerstatuschange.png

6.6. 什么是 Hystrix服务降级?

  • 在 Hystrix 断路器熔断时,可以调用一个降级方法(需要配置和编码,也可以不写,也就是不会有服务降级的功能),返回相应的结果。

8. 服务熔断和服务降级的区别

  • 服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)实现方式不太一样;服务降级具有代码侵入性(由控制器完成/或自动降级),熔断一般称为自我熔断。

9. 服务限流

  • 限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务(定向到错误页或告知资源没有了)、排队或等待(比如秒杀、评论、下单)、降级(返回兜底数据或默认数据,如商品详情页库存默认有货)。

Sentinel怎么实现限流的?

  • 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); 
 }
  • 监控流量:Sentinel会监控每个资源的流量情况,包括请求的QPS(每秒请求数)、线程数、响应时间等。
  • 限流控制:当请求到达时,Sentinel会根据资源的限流规则判断是否需要进行限流控制。如果请求超过了限流阈值,则可以进行限制、拒绝或进行其他降级处理。

Sentinel采用的什么限流算法?

  • Sentinel使用滑动窗口限流算法来实现限流。滑动窗口限流算法是一种基于时间窗口的限流算法。它将一段时间划分为多个时间窗口,并在每个时间窗口内统计请求的数量。通过动态地调整时间窗口的大小和滑动步长,可以更精确地控制请求的通过速率。

6.7. Sentinel有哪些流程控制规则?√

  • 流量控制,其原理是监控应用流量的QPS或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性
  • 1.QPS(Queries Per Second):当调用相关url对应的资源时,QPS达到单机阈值时,就会限流。
  • 2.线程数:当调用相关url对应的资源时,线程数达到单机阈值时,就会限流。
  • 设置限流模式
    • 直连模式 sentinel默认的流控处理就是(直连-->快速失败);
    • 关联模式 当关联的资源达到阈值,就限流自己。
    • 链路模式 只针对从指定链路访问到本资源的请求做统计,判断是否超过阈值。直接拒绝直接拒绝(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。

Sentinel怎么实现集群限流?

Sentinel利用了Token Server和Token Client的机制来实现集群限流。开启集群限流后,Client向Token Server发送请求,Token Server根据配置的规则决定是否限流。

10. 网关服务

  • spring-cloud-netflix-zuul,基于Zuul1实现。Netflix最新开源的网关服务是Zuul2 ,基于响应式的网关服务。
  • spring-cloud-gateway ,基于Spring Webflux实现

10.1. 为什么要有网关?

  • 动态路由:反向代理

  • 安全控制:统一认证(支持 HMAC, JWT, Basic, OAuth 2.0等常用协议)和鉴权(权限控制、IP 黑白名单)

  • 协议转换

  • 熔断、限流

  • 灰度发布

  • api组合:可以调用一次网关实现调用两次微服务的功能

  • 请求分析,记录日志

  • 缓存

  • 健康检查

  • 可用性

  • 高性能

  • API版本管理

10.2. zuul原理?

  • 作用:API 网关,路由,负载均衡等多种作用。
  • 简介:类似Nginx反向代理的功能
  • 在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个 API网关根据请求的 url ,路由到相应的服务。当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用方通信进行权限控制,后将请求均衡分发给后台服务端。
    zuulprinciple.png

10.3. Spring Cloud Gateway

  • Route(路由):路由是Spring Cloud Gateway的基本构建块,它定义了请求的匹配规则和转发目标。通过配置路由,可以将请求映射到后端的服务实例或URL上。路由规则可以根据请求的路径、方法、请求头等条件进行匹配,并指定转发的目标URI。
  • Predicate(断言):断言用于匹配请求的条件,如果请求满足断言的条件,则会应用所配置的过滤器。Spring Cloud Gateway提供了多种内置的断言,如Path(路径匹配)、Method(请求方法匹配)、Header(请求头匹配)等,同时也支持自定义断言。
  • Filter(过滤器):过滤器用于对请求进行处理和转换,可以修改请求、响应以及执行其他自定义逻辑。Spring Cloud Gateway提供了多个内置的过滤器,如请求转发、请求重试、请求限流等。同时也支持自定义过滤器,可以根据需求编写自己的过滤器逻辑。

Spring Cloud Gateway的具体工作流程

springcloudgateway.png
springcloudgateway.png
  • Gateway Handler(网关处理器):网关处理器是Spring Cloud Gateway的核心组件,负责将请求转发到匹配的路由上。它根据路由配置和断言条件进行路由匹配,选择合适的路由进行请求转发。网关处理器还会依次应用配置的过滤器链,对请求进行处理和转换。
  • Gateway Filter Chain(网关过滤器链):网关过滤器链由一系列过滤器组成,按照配置的顺序依次执行。每个过滤器可以在请求前、请求后或请求发生错误时进行处理。过滤器链的执行过程可以修改请求、响应以及执行其他自定义逻辑。

11. 配置中心

  • spring-cloud-config基于Git、SVN作为存储。
  • spring-cloud-alibaba-nacos-config基于Nacos实现。
  • Apollo携程开源的配置中心。Spring Cloud最成熟的配置中心的选择。

11.1. Spring Cloud Config

  • 作用:配置管理
  • 简介:Spring Cloud Config 提供服务器端和客户端。服务器存储后端的默认实现使用 Git ,因此它轻松支持标签版本的配置环境,以及可以访问用于管理内容的各种工具。
  • 这个还是静态的,得配合 Spring Cloud Bus 实现动态的配置更新。

Apollo

12. 链路追踪

  • skywalking已经进入Apache监控链路和JVM等
  • spring-cloud-sleuth基于Zipkin实现

为什么要链路追踪

  • 链路追踪,可以可视化地追踪请求从一个微服务到另一个微服务的调用情况。除了排查问题,链路追踪黑还可以帮助优化性能,可视化依赖关系、服务监控和告警

SkyWalking

12.1. Spring Cloud Sleuth原理

sleuthprinciple.png
sleuthprinciple.png

13. springcloud整合图

  • Eureka + Ribbon + Feign + Hystrix + Zuul
    springcloud.png

如何实现灰度发布?

  • 灰度发布(Gray Release,也称为金丝雀发布)是指在软件或服务发布过程中,将新版本的功能或服务以较小的比例引入到生产环境中,仅向部分用户或节点提供新功能的一种发布策略。

为什么要使用灰度发布?

  • 在传统的全量发布中,新版本的功能会一次性全部部署到所有的用户或节点上。如果新版本存在缺陷或问题,可能会对所有用户或节点产生严重的影响,导致系统崩溃或服务不可用。灰度发布采用较小的规模,并逐步将新版本的功能引入到生产环境中,仅向一小部分用户或节点提供新功能。通过持续监测和评估,可以在发现问题时及时回滚或修复。这种逐步引入新版本的方式可以降低风险,并提高系统的稳定性和可靠性。

灰度实现方法?

  • 根据用户划分:根据用户标识或用户组进行划分,在整个用户群体中只选择一小部分用户获得新功能。(常用)
  • 根据地域划分:在不同地区或不同节点上进行划分,在其中的一小部分地区或节点进行新功能的发布。
  • 根据流量划分:根据流量的百分比或请求次数进行划分,只将一部分请求流量引导到新功能上。

Spring Cloud 全链路灰色发布具体实现

  • 前端程序在灰度测试的用户 Header 头中打上标签,例如在 Header 中添加“grap-tag: true”,其表示要进行灰常测试(访问灰度服务),而其他则为访问正式服务。
  • 在负载均衡器 Spring Cloud LoadBalancer 中,拿到 Header 中的“grap-tag”进行判断,如果此标签不为空,并等于“true”的话,表示要访问灰度发布的服务,否则只访问正式的服务。
  • 在网关 Spring Cloud Gateway 中,将 Header 标签“grap-tag: true”继续往下一个调用服务中传递。
  • 在后续的调用服务中,需要实现以下两个关键功能:
    • 在负载均衡器 Spring Cloud LoadBalancer 中,判断灰度发布标签,将请求分发到对应服务。
    • 将灰度发布标签(如果存在),继续传递给下一个调用的服务。
  • 经过第四步的反复传递之后,整个 Spring Cloud 全链路的灰度发布就完成了。
  • 关键思路
    • 1、注册中心区分正常服务和灰度服务;
    • 2、负载均衡正确转发正常服务和灰度服务;
    • 3、网关和 HTTP 工具传递灰度标签。

核心实现思路和代码

3.1 区分正式服务和灰度服务

  • 在灰度服务既注册中心的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" } # 标识自己为灰度服务

3.2 负载均衡调用灰度服务

// 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);
    }
}

3.3 网关传递灰度标识

  • 要在网关 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);
    }
}

3.4 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;
    }
}

Seata支持哪些模式的分布式事务?

  • Seata以下几种模式的分布式事务:
    • AT(Atomikos)模式:Seata默认支持的模式,最常用的。在AT模式下,Seata通过在业务代码中嵌入事务上下文,实现对分布式事务的管理。Seata会拦截并解析业务代码中的SQL语句,通过对数据库连接进行拦截和代理,实现事务的管理和协调。
      seataat1.png
      seataat2.png
    • TCC(Try-Confirm-Cancel)模式:TCC模式是一种基于补偿机制的分布式事务模式。在TCC模式中,业务逻辑需要实现Try、Confirm和Cancel三个阶段的操作。Seata通过调用业务代码中的Try、Confirm和Cancel方法,并在每个阶段记录相关的操作日志,来实现分布式事务的一致性。
      seatatcc.png
    • SAGA模式:SAGA模式是一种基于事件驱动的分布式事务模式。在SAGA模式中,每个服务都可以发布和订阅事件,通过事件的传递和处理来实现分布式事务的一致性。Seata提供了与SAGA模式兼容的Saga框架,用于管理和协调分布式事务的各个阶段。
      seatasaga.png
    • XA模式:XA模式是一种基于两阶段提交(Two-Phase Commit)协议的分布式事务模式。在XA模式中,Seata通过与数据库的XA事务协议进行交互,实现对分布式事务的管理和协调。XA模式需要数据库本身支持XA事务,并且需要在应用程序中配置相应的XA数据源。
      seataxa.png

了解Seata的实现原理吗?

  • Seata的实现原理主要包括三个核心组件:事务协调器(Transaction Coordinator)、事务管理器(Transaction Manager)和资源管理器(Resource Manager)。
    • 事务协调器(Transaction Coordinator):事务协调器负责协调和管理分布式事务的整个过程。它接收事务的开始和结束请求,并根据事务的状态进行协调和处理。事务协调器还负责记录和管理事务的全局事务 ID(Global Transaction ID)和分支事务 ID(Branch Transaction ID)。
    • 事务管理器(Transaction Manager):事务管理器负责全局事务的管理和控制。它协调各个分支事务的提交或回滚,并保证分布式事务的一致性和隔离性。事务管理器还负责与事务协调器进行通信,并将事务的状态变更进行持久化。
    • 资源管理器(Resource Manager):资源管理器负责管理和控制各个参与者(Participant)的事务操作。它与事务管理器进行通信,并根据事务管理器的指令执行相应的事务操作,包括提交和回滚。
      seatamodel.png
  • Seata的实现原理基于两阶段提交(Two-Phase Commit)协议,具体的机制如下:
    • 一阶段:在事务提交的过程中,首先进行预提交阶段。事务协调器向各个资源管理器发送预提交请求,资源管理器执行相应的事务操作并返回执行结果。在此阶段,业务数据和回滚日志记录在同一个本地事务中提交,并释放本地锁和连接资源。
    • 二阶段:在预提交阶段成功后,进入真正的提交阶段。此阶段主要包括提交异步化和回滚反向补偿两个步骤:
    • 提交异步化:事务协调器发出真正的提交请求,各个资源管理器执行最终的提交操作。这个阶段的操作是非常快速的,以确保事务的提交效率。
    • 回滚反向补偿:如果在预提交阶段中有任何一个资源管理器返回失败结果,事务协调器发出回滚请求,各个资源管理器执行回滚操作,利用一阶段的回滚日志进行反向补偿。

Seata的事务执行流程是什么样的?

  • 事务发起方(Transaction Starter)发起全局事务:事务发起方是指发起分布式事务的应用程序或服务。它向Seata的事务协调器发送全局事务的开始请求,生成全局事务ID(Global Transaction ID)。
  • 事务协调器创建全局事务记录:事务协调器接收到全局事务的开始请求后,会为该事务创建相应的全局事务记录,并生成分支事务ID(Branch Transaction ID)。
  • 分支事务注册:事务发起方将全局事务ID和分支事务ID发送给各个参与者(Participant),即资源管理器。参与者将分支事务ID注册到本地事务管理器,并将事务的执行结果反馈给事务协调器。
  • 执行业务逻辑:在分布式事务的上下文中,各个参与者执行各自的本地事务,即执行业务逻辑和数据库操作。
  • 预提交阶段:事务发起方向事务协调器发送预提交请求,事务协调器将预提交请求发送给各个参与者。
  • 执行本地事务确认:参与者接收到预提交请求后,执行本地事务的确认操作,并将本地事务的执行结果反馈给事务协调器。
  • 全局事务提交或回滚:事务协调器根据参与者反馈的结果进行判断,如果所有参与者的本地事务都执行成功,事务协调器发送真正的提交请求给参与者,参与者执行最终的提交操作;如果有任何一个参与者的本地事务执行失败,事务协调器发送回滚请求给参与者,参与者执行回滚操作。
  • 完成全局事务:事务协调器接收到参与者的提交或回滚结果后,根据结果更新全局事务的状态,并通知事务发起方全局事务的最终结果。

全局事务ID和分支事务ID是怎么传递的?

  • 全局事务ID和分支事务ID在分布式事务中通过上下文传递的方式进行传递。常见的传递方式包括参数传递、线程上下文传递和消息中间件传递。具体的传递方式可以根据业务场景和技术选型进行选择和调整。

Seata的事务回滚是怎么实现的?

  • Seata的事务回滚是通过回滚日志实现的。每个参与者在执行本地事务期间生成回滚日志,记录了对数据的修改操作。
  • 当需要回滚事务时,事务协调器向参与者发送回滚请求,参与者根据回滚日志中的信息执行撤销操作,将数据恢复到事务开始前的状态。
  • 回滚日志的管理和存储是Seata的核心机制,可以选择将日志存储在不同的介质中。通过回滚日志的持久化和恢复,Seata确保了事务的一致性和恢复性。

服务监控

32.你们的服务怎么做监控和告警?

  • Prometheus:Prometheus 是一个开源的监控系统,具有灵活的数据模型和强大的查询语言,能够收集和存储时间序列数据。它可以通过HTTP协议定期拉取微服务的指标数据,并提供可扩展的存储和查询功能。
  • Grafana:Grafana 是一个开源的可视化仪表板工具,与 Prometheus 结合使用,创建实时和历史数据的仪表板。Grafana 提供了丰富的图表和可视化选项,可以帮助用户更好地理解和分析微服务的性能和状态。

33.你们的服务怎么做日志收集?

  • ELK:
    • Elasticsearch:Elasticsearch是一个分布式搜索和分析引擎,用于存储和索引大量的日志数据。它提供了快速的搜索和聚合功能,可以高效地处理大规模的日志数据。
    • Logstash:Logstash是一个用于收集、过滤和转发日志数据的工具。它可以从各种来源(如文件、网络、消息队列等)收集日志数据,并对数据进行处理和转换,然后将其发送到Elasticsearch进行存储和索引。
    • Kibana:Kibana是一个用于日志数据可视化和分析的工具。它提供了丰富的图表、仪表盘和搜索功能,可以帮助用户实时监控和分析日志数据,发现潜在的问题和趋势。
  • 这三者里Elasticsearch提供数据存储和检索能力,Logstash负责将日志收集到ES,Kibana负责日志数据的可视化分析。
  • 使用ELK进行微服务日志收集的一般流程如下:
elk.png
elk.png
  • 在每个微服务中配置日志输出:将微服务的日志输出到标准输出(stdout)或日志文件。
  • 使用Logstash收集日志:配置Logstash收集器,通过配置输入插件(如文件输入、网络输入等)监听微服务的日志输出,并进行过滤和处理。
  • 将日志数据发送到Elasticsearch:配置Logstash的输出插件,将经过处理的日志数据发送到Elasticsearch进行存储和索引。
  • 使用Kibana进行可视化和分析:通过Kibana连接到Elasticsearch,创建仪表盘、图表和搜索查询,实时监控和分析微服务的日志数据。
  • 除了应用最广泛的ELK,还有一些其它的方案比如Fluentd、Graylog、Loki、Filebeat,一些云厂商也提供了付费方案,比如阿里云的sls。