Skip to content

Spring Cloud常用组件表

服务名称组件名称
服务的注册和发现eureka、nacos、consul
服务的负载均衡ribbon
服务的互相调用openFeign、dubbo
服务的容错hystrix、sentinel
服务的网关gateway、zuul
服务配置统一管理config-server、nacos、apollo
服务消息总线bus
服务安全组件security、Oauth2.0
服务监控admin、jvm
链路追踪sleuth+zipkin

一、eureka 与 zookeeper 的区别

  • CAP原则
    • C :一致性、多个机器中的数据一致。
    • A:可用性,当多个节点中其中某个节点挂掉了,整个集群可以继续对外提供服务。
    • P:分区容错性,由于机房网络或者分区等原因会导致各个机器中的数据短暂不一致。

注册与发现框架中,因为物理规律的存在P是无法绕过的,所以P特性是一定存在的。因为,CAP原则无法全部满足,所以注册与发现框架所遵循 CP、AP两种原则,zookeeper 遵循CP数据一致性原则,eureka 遵循AP高可用原则。

二、ribbon 负载均衡

负载均衡的种类

ribbon - 负载均衡策略

三、openFeign

feign 的核心代码就是 使用 jdk 的 proxy 动态代理特性。

openFeign 启动执行流程

alt text

openFeign 接口调用内部执行流程

alt text

四、hystrix 断路器

为什么需要断路器:

在微服务架构中,我们将项目根据业务功能拆分成一个个独立的服务,服务之间可以相互调用。根据业务需求的不同,每一个独立的服务我们可能部署多个。在项目的使用中可能因为网络、程序异常、硬件主机环境等等原因可能会导致部分服务离线,或者在出现高并发时,会导致服务统一宕机,即雪崩。为了兼容解决这种种情况,所以出现了断路器模型。

断路器机制:

断路器拥有三种状态:开启、关闭、半开。当Hystrix Command请求后端服务失败数量超过一 定比例(默认50%),断路器会切换到开路状态(Open)。这时所有请求会直接失败而不会发送到后端服务。断路器保持在开路状态一段时间后(默认5秒),自动切换到半开路状态(HALF-OPEN)。这时会判断下一次请求的返回情况,如果请求成功,断路器切回闭路状态 (CLOSED),否则重新切换到开路状态(OPEN)。

Fallback:

Fallback即降级操作,当一个请求失败时通过后备方法返回适合业务请求的返回值,一般是通用默认值。

资源隔离:

  • hystrix 中资源隔离方式主要是 线程池隔离技术 和 信号量隔离技术

为什么要使用资源隔离

  • 避免因对某一个依赖服务的调用接口延迟或失败,导致服务所有的线程资源全部消耗在这个服务的接口调用上,从而导致后续服务请求崩溃。

线程池隔离 和 信号量隔离的区别

  • 线程池隔离:主要关注的是线程资源的隔离。通过给每个任务分配独立的线程,使任务之间相互隔离,避免彼此干扰。
  • 信号量隔离:主要关注的是对共享资源的访问控制。信号量实际上是一种计数器,用于控制对共享资源的并发访问数量。通过控制信号量的许可数,限制对共享资源的并发访问量。

适用场景

  • 线程池隔离适用于将任务彼此隔离开,每个任务都具有独立的执行环境的情况。例如,需要独立处理一些耗时的任务或需要保证任务间不受影响的情况,适于执行并发任务。基本适于99%的业务场景。
  • 信号量隔离适用于需要控制对共享资源的并发访问量控制的情况。例如,限制同时访问某个文件的线程数量。

Hystrix 断路器注解: 参考文章

  • @HystrixCommand:
    • 此注解表示此方法是 hystrix 方法,其中 fallbackMethod 定义回退方法的名称,可以为当前方法 Hystrix 默认命令属性。
    • 也就是使用注解的形式配置 Hystrix,简化开发代码编写,更专注于功能的实现。
    • HystrixCommandAspect 通过 AOP 拦截所有的 @HystrixCommand 注解的方法,从而使得 @HystrixCommand 能够集成到 Spring boot 中。
  • @EnableCircuitBreaker
    • 等同于@EnableHystrix,启动断路器,开启Hystrix。EnableCircuitBreakerImportSelector是SpringFactoryImportSelector子类。此类在初始化后,会执行selectImports(AnnotationMetadata metadata)的方法。此方法会根据注解启动的注解(这里指@EnableCircuitBreaker)从spring.factories文件中获取其配置需要初始化@Configuration类(这里是org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration),从而最终初始化HystrixCommandAspect 类,从而实现拦截HystrixCommand的功能。
  • @DefaultProperties
    • @DefaultProperties是类(类型)级别的注释,允许为当前类指定Hystrix默认命令属性,如groupKey,threadPoolKey,commandProperties,threadPoolProperties,ignoreExceptions和raiseHystrixExceptions。使用此注解指定的属性,会覆盖在配置文件中定义的默认配置,也将在类中使用@HystrixCommand注解的方法中公用,除非某个方法明确使用相应的@HystrixCommand参数来指定这些属性。

五、Spring Clund Sleuth 链路追踪

在maven中加入zipkin的依赖,zipkin中包含了sleuth 所以不需要单独引入sleuth

xml
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>

通过docker-compose启动zipkin服务:

yml
version: '3'
services:
  storage:
    image: openzipkin/zipkin-elasticsearch6
    container_name: elasticsearch
  zipkin:
    image: openzipkin/zipkin
    container_name: zipkin
    environment:
      - STORAGE_TYPE=elasticsearch
      # Point the zipkin at the storage backend
      - ES_HOSTS=elasticsearch
      # Uncomment to see requests to and from elasticsearch
      # - ES_HTTP_LOGGING=BODY
    ports:
      # Port used for the Zipkin UI and HTTP Api
      - 9411:9411
      # Uncomment if you set SCRIBE_ENABLED=true
      # - 9410:9410
    depends_on:
      - storage

  dependencies:
    image: openzipkin/zipkin-dependencies
    container_name: dependencies
    entrypoint: crond -f
    environment:
      - STORAGE_TYPE=elasticsearch
      - ES_HOSTS=elasticsearch
      # Uncomment to see dependency processing logs
      # - ZIPKIN_LOG_LEVEL=DEBUG
      # Uncomment to adjust memory used by the dependencies job
      # - JAVA_OPTS=-verbose:gc -Xms1G -Xmx1G
    depends_on:
      - storage

在需要链路追踪的服务的application.yml配置文件中加入如下配置:

base-url 为 zipkin服务的连接地址

yml
spring:
  zipkin:
    base-url: http://127.0.0.1:9411/
  sleuth:
    sampler:
      probability: 1 #配置采样, 默认采集比例为: 0.1 即 10% 所设置的值介于 0 与 1 之间
      rate: 10 # 为了使用 速率限制采集器, 选择每秒间

zipkin 服务如下所示:

alt text

六、Admin 服务

新增子项目 Admin,POM.xml文件引入 spring-boot-admin 包

xml
<dependencies>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
        </dependency>
    </dependencies>

并且在Application文件中启动admin服务

java
package com.jyc.admin;

import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
@EnableAdminServer
public class AdminServiceApplication {

  public static void main(String[] args) {
    SpringApplication.run(AdminServiceApplication.class, args);
  }

}

在监控项目的application.yml配置监控指标:

yml
management:
  endpoints:
    web:
      exposure:
        include: '*' # 监控所有指标,包括 内存、CPU、网络等

项目启动如下图:

alt text

七、Gateway 网关 官方文档

Gateway断言规则:

alt text

动态路由配置以及断言配置:

alt text

自定义全局过滤器:

java
package com.jyc.gateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class GlobalFilter implements org.springframework.cloud.gateway.filter.GlobalFilter, Ordered {
  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

    //过滤通过
    chain.filter(exchange);
    return null;
  }

  /**
   * 过滤执行排序,返回的数字越小,越先执行
   * @return
   */
  @Override
  public int getOrder() {
    return 0;
  }
}

八、Gateway 结合 Redis 实现请求量限流

项目POM引入包:

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

项目配置(主要是 routes 相关配置):

yml
server:
  port: 8888
spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      enabled: true
      discovery:
        locator:
          enabled: true # 开启动态路由
          lower-case-service-id: true # 开启服务名称小写
      routes:
        - id: login-service-route # 路由 id,保持唯一即可
          uri: lb://user-service # uri 统一资源定位符  url 统一资源标识符
          predicates: # 断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上
            - Path=/user/getUserInfoByUserId #匹配规则
          filters:
            - name: RequestRateLimiter # 过滤器名称
              args: # 过滤器参数
                key-resolver: '#{@ipKeyResolver}' # 通过 spel 表达式取 ioc 容器中的 bean的值
                redis-rate-limiter.replenishRate: 1 # 生成令牌的速度
                redis-rate-limiter.burstCapacity: 3 # 桶容量
  main:
    web-application-type: reactive
  redis:
    host: localhost
    port: 14379
    password: 0dnyhuNoC8anRCCM
eureka:
  client:
    service-url:
      defaultZone: http://jyc-master:8802/eureka
    registry-fetch-interval-seconds: 3
  instance:
    hostname: ${HOSTNAME:jyc-master}
    instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}

创建自定义的 keyResolver :

java
package com.jyc.gateway.config;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.core.publisher.Mono;

/**
 * 限流
 */
@Configuration
public class RequestLimitConfig {

  @Bean
  @Primary  //主候选的
  public KeyResolver ipKeyResolver(){
    return exchange -> Mono.just(exchange.getRequest().getHeaders().getHost().getHostString());
  }

  @Bean
  public KeyResolver apiKeyResolver(){
    return exchange -> Mono.just(exchange.getRequest().getPath().value());
  }
}

当我们快速请求速度超过配置设定的 值时,请求被拒绝:

alt text

正常的请求速度,正常返回:

alt text