此篇笔记包括的技术:Consul,OpenFeign,LoadBancer,Resilience4J,Micrometer+ZipKin,Gateway。

=======注意,这儿有用!!!!!=======

笔记事例代码已经同步到:gitee.com/yanghaokun0…

=======注意,这儿有用!!!!!=======

Consul服务发现与注册中心

官网:www.consul.io/

Consul软件装置

Docker方法:

docker run -d --name consul -p 8500:8500 consul agent -dev -server -bootstrap -client 0.0.0.0 -ui

装置验证:

拜访:http://你的装置地址:8500

原生SpringCloud组件运用学习笔记V1.0

装置成功。以上装置没有解决数据耐久化,数据都存储在内存中。

客户端注册到Consul中

导入坐标:

<!--consul服务发现注册-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!--web健康检测-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

yaml装备:

# consul服务地址
consul:
  host: cloud.demo
  port: 8500
# consul注册发现装备
spring:
  cloud:
    consul:
      host: ${consul.host}
      port: ${consul.port}
      discovery:
        # 注册到consul的服务称号
        service-name: ${spring.application.name}
        prefer-agent-address: true
        # 心跳检查
        heartbeat:
          # 敞开
          enabled: true
        # 在注册时运用ip地址而不是主机名
        prefer-ip-address: true

发动类敞开服务注册注解:

@EnableDiscoveryClient

发动微服务组件项目,观察Consul操控台:

原生SpringCloud组件运用学习笔记V1.0

Consul分布式装备中心(浅用)

Consul装备云yaml文件内容

原生SpringCloud组件运用学习笔记V1.0

原生SpringCloud组件运用学习笔记V1.0

原生SpringCloud组件运用学习笔记V1.0

解说:在Consul服务中装备了微服务 cloud-product 开发环境(dev)的云装备信息。

客户端读取云装备内容

导入云装备坐标:

<!--consul云装备读取-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

创立 bootstrap.yaml 体系等级项目发动文件

内容如下:

---
spring:
  application:
    name: cloud-product
  profiles:
    active: dev
---
# consul服务地址
consul:
  host: cloud.demo
  port: 8500
---
# consul注册发现装备
spring:
  cloud:
    consul:
      host: ${consul.host}
      port: ${consul.port}
      discovery:
        # 注册到consul的服务称号
        service-name: ${spring.application.name}
        prefer-agent-address: true
        # 心跳检查
        heartbeat:
          # 敞开
          enabled: true
        # 在注册时运用ip地址而不是主机名
        prefer-ip-address: true
---
spring:
  cloud:
    consul:
      # 装备中心
      config:
        # 装备文件称号已 - 进行分割
        profile-separator: '-'
        # 装备文件类型
        format: yaml
        watch:
          # 监听更新时刻距离 默许 55秒 测试 设置为 1秒
          wait-time: 1

装备解说:项目一发动就去注册到Consul服务中,并且读取cloud-product-dev.yaml 文件的内容到本地。

原生SpringCloud组件运用学习笔记V1.0

Spring中读取装备文件内容完成动态改写的两种方法:

  • @Value()注解 + @RefreshScope注解 完成动态改写。

  • @Component注解 + @ConfigurationProperties()注解,完成动态改写,比如如下:

@ConfigurationProperties(prefix = "dateformat")
@Component
@Data
public class DateFormat {
    private String dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
    private String dateFormat = "yyyy-MM-dd";
}

创立一个拜访接口进行测试,代码如下:

@RestController("/yun")
public class YunConfigController {
    @Resource
    private DateFormat dateFormat;
    @GetMapping("/getInfo")
    public String getInfo(){
        return dateFormat.getDateTimeFormat();
    }
}

拜访:

原生SpringCloud组件运用学习笔记V1.0

更新Consul上的云装备文件内容:

原生SpringCloud组件运用学习笔记V1.0

观察idea操控台打印:

原生SpringCloud组件运用学习笔记V1.0

再次拜访测试接口:

原生SpringCloud组件运用学习笔记V1.0

已完成云装备更新影响程序结果的作用。

OpenFeign长途调用接口

维护base-openfeign-api项目接口

根据以往项目开发经验,将OpenFeign维护的接口,单独创立出一个工程项目进行维护,过程如下:

创立一个base-openfeign-api的项目。

原生SpringCloud组件运用学习笔记V1.0

导入openfeign坐标,以及基础公共实体坐标。

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

创立维护某个微服务的openfeign接口:

@FeignClient("cloud-product")
public interface ProductApi {
    /**
     * 根据产品id查询产品信息
     */
    @GetMapping("/product/getProductById/{id}")
    public ResponseResult<ProductVO> getProductById(@PathVariable("id") Integer id);
}

接口上核心注解:@FeignClient() ,标明次api接口面向注册中心哪个服务。

运用base-openfeign-api项目进行长途拜访

导入base-openfeign-api坐标到运用工程中:

<!--openfeign-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--长途调用服务接口项目-->
<dependency>
    <groupId>com.haokun</groupId>
    <artifactId>base-openfeign-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

发动类上激活你需求的api接口所在包:

@EnableFeignClients(basePackages = {"com.haokun.api.product"})

业务中运用:

@Resource
private ProductApi productApi;
// 调用接口
ResponseResult<ProductVO> productRes = productApi.getProductById(order.getProductId());

原生SpringCloud组件运用学习笔记V1.0

原生SpringCloud组件运用学习笔记V1.0

上述过程已完成,在微服务一个体系内,能够运用openfeign的方法进行长途调用。

复制一份产品微服务程序,形成集群服务,运用openfeign长途调用产品服务,测试 LoadBancer 负载均衡:

原生SpringCloud组件运用学习笔记V1.0

发动:

原生SpringCloud组件运用学习笔记V1.0

LoadBancer 默许完成的是轮训算法进行拜访:

原生SpringCloud组件运用学习笔记V1.0

经过上述测试,已经完成openfeign + LoadBancer + consul 服务的注册发现,负载均衡,长途拜访功能。

原生SpringCloud组件运用学习笔记V1.0

LoadBancer的依赖由consul带入。

经过上面的过程已经完成了事例的第一个版别,下面逐渐参加微服务组件,代码地址如下: gitee.com/yanghaokun0…

openfeign的高档特性

调用时刻超时机制

yaml装备内容:

# openfeign超不时刻装备
spring:
  cloud:
    openfeign:
      client:
        config:
          # 默许调用全部接口的时刻为1.5秒
          default:
            connect-timeout: 1500
            read-timeout: 1500
          # 指定微服务组件的超不时刻
          cloud-product:
            connect-timeout: 3000
            read-timeout: 3000

超时会报错抛出反常,内容如下:

原生SpringCloud组件运用学习笔记V1.0

重试机制:

/**
 * openfeign装备
 */
@SpringBootConfiguration
public class FeignConfig {
    /**
     * 重试装备
     *
     * @return
     */
    @Bean
    public Retryer myRetryer() {
        // openfeign 默许装备是不走重试战略的
        // return Retryer.NEVER_RETRY;
        // 最大恳求次数为3 (1+2),初始距离时刻为100ms,重试间最大距离时刻为1s
        return new Retryer.Default(100, 1, 3);
    }
}

能够完成在恳求超不时,从头发送两次恳求。

恳求日志

原生SpringCloud组件运用学习笔记V1.0

观察恳求日志:

原生SpringCloud组件运用学习笔记V1.0

恳求连接池

openfeign 默许运用的是 java.base java.net.HttpURLConnection,没有连接池,替换为 httpClient 连接池,为openfeign来供给连接供给。

引进连接池坐标:

<!--恳求连接池-->
<!-- httpclient5-->
<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.3</version>
</dependency>
<!-- feign-hc5-->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-hc5</artifactId>
    <version>13.1</version>
</dependency>

yaml装备内容:

# 装备恳求连接池
spring:
  cloud:
    openfeign:
      httpclient:
        hc5:
          enabled: true

恳求观察:

原生SpringCloud组件运用学习笔记V1.0

敞开恳求呼应紧缩

yaml装备:

# 敞开恳求呼应紧缩形式
spring:
  cloud:
    openfeign:
      # 紧缩
      compression:
        # 恳求
        request:
          enabled: true
          # 最小紧缩要求
          min-request-size: 2048
          # 紧缩数据类型
          mime-types: text/xml, application/xml, application/json
        # 呼应
        response:
          enabled: true

原生SpringCloud组件运用学习笔记V1.0

Resilience4j熔断降级,限流,阻隔

我对熔断降级,限流,阻隔的理解:熔断就是在恳求拜访一个服务时,触发了设置的阈值,然后开端熔断,不再恳求,并且快速回来数据,称为降级处理,阻隔和限流很相似,是设置拜访一个服务的恳求数量保持在多少,多的恳求会进行降级处理。

  • 设定一个需求:在拜访一个恳求时,恳求数量6个,出现了3次反常信息,错误率达到50%,进行熔断降级处理,并回来体系繁忙请稍后再试的数据内容。

  • 设定第二个需求:拜访一个恳求,这个恳求1秒只能并发5个,多的恳求会降级处理,并回来当时人数较多,请稍后再试的数据。

  • 设定第三个需求:拜访一个恳求,这个恳求1秒只能经过10个,多的恳求会降级处理,并回来当时人数较多,请稍后再试的数据。

针对resilience4j熔断的方法两种:超时,反常,我将运用openfeign来操控超时,若超时抛出反常,让resilience4j来计算,达到阈值时熔断。

熔断降级

导入坐标:

<!--熔断降级resilience4j-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<!--由于断路维护等需求AOP完成,所以必须导入AOP包-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

yaml装备内容:

# 熔断降级前提装备
spring:
  cloud:
    openfeign:
      # 断路器
      circuitbreaker:
        enabled: true
        group:
          enabled: true

坑:

# 延伸TimeLimiter默许的恳求超不时长 它 1秒的
resilience4j:
  timelimiter:
    configs:
      default:
        timeout-duration: 10s

主要内容:

# 装备断路器
resilience4j:
  circuitbreaker:
    configs:
      # 默许断路器装备
      default:
        # 断路器方法 计数的方法
        sliding-window-type: count_based
        # 计算数量周期
        sliding-window-size: 6
        # 最小计算数量
        minimum-number-of-calls: 6
        # 阈值 百分比
        failure-rate-threshold: 50
        #熔断时长 秒
        wait-duration-in-open-state: 10s
        # 需求敞开半开状况来测试
        automatic-transition-from-open-to-half-open-enabled: true
        # 半开测试拜访恳求数量
        permitted-number-of-calls-in-half-open-state: 2
        # 计数的反常
        record-exceptions:
          - java.lang.Exception
    instances:
      cloud-product:
        base-config: default

Java代码侵略加强:

原生SpringCloud组件运用学习笔记V1.0

@CircuitBreaker(name = "cloud-product", fallbackMethod = "nextOrderFallbackMethod")
@Override
public Boolean nextOrder(Order order) {
}
public Boolean nextOrderFallbackMethod(Order order,Throwable throwable){
    if (true){
        throw new ServiceException("体系繁忙请稍后再试");
    }
    return false;
}

降级的方法参数有要求,如上:需求与装备了断路器的方法参数共同时,后面还需求参加Throwable参数类型。

开端压测:

原生SpringCloud组件运用学习笔记V1.0

其他恳求在熔断的时刻内将直接降级处理:

原生SpringCloud组件运用学习笔记V1.0

原生SpringCloud组件运用学习笔记V1.0

阻隔

限制对下流的并发数量。

导入坐标:

<!--对下流拜访的拜访阻隔-->
<!--resilience4j-bulkhead-->
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-bulkhead</artifactId>
</dependency>

yaml装备:

# 对下流拜访的拜访阻隔
resilience4j:
  bulkhead:
    configs:
      default:
        # 最大答应数量
        max-concurrent-calls: 5
        # 时刻周期
        max-wait-duration: 1s
    instances:
      cloud-product:
        base-config: default

Java侵略增强:

原生SpringCloud组件运用学习笔记V1.0

@Bulkhead(name = "cloud-product",fallbackMethod = "nextOrderBulkheadFallbackMethod",type = Bulkhead.Type.SEMAPHORE)

降级方法:

原生SpringCloud组件运用学习笔记V1.0

开端压测:

答应的范围内成功:

原生SpringCloud组件运用学习笔记V1.0

失败:

原生SpringCloud组件运用学习笔记V1.0

原生SpringCloud组件运用学习笔记V1.0

限流

导入坐标:

<!--限流-->
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-ratelimiter</artifactId>
</dependency>

yaml装备内容:

# 限流
resilience4j:
  ratelimiter:
    configs:
      # 默许每秒只能经过1个恳求
      default:
        limit-for-period: 10
        limit-refresh-period: 1s
        timeout-duration: 1
    instances:
      cloud-product:
        base-config: default

Java代码侵略增强:

原生SpringCloud组件运用学习笔记V1.0

压测触发,降级处理:

原生SpringCloud组件运用学习笔记V1.0

总结:resilience4j不好用,装备繁琐,代码侵略,难维护!

分布式链路追寻

micrometer(收集数据) + zipkin(数据展示)

建立 zipkin

Docker的方法建立:

docker run -d --name zipkin -p 9411:9411 openzipkin/zipkin

浏览器拜访:你的服务地址:9411

原生SpringCloud组件运用学习笔记V1.0

引进micrometer

父工程版别操控内容:

<properties>
    <!--链路追寻版别操控-->
    <micrometer-tracing.version>1.2.0</micrometer-tracing.version>
    <micrometer-observation.version>1.12.0</micrometer-observation.version>
    <feign-micrometer.version>12.5</feign-micrometer.version>
    <zipkin-reporter-brave.version>2.17.0</zipkin-reporter-brave.version>
</properties>

<dependencyManagement>
    <dependencies>
        <!--链路追寻版别操控-->
        <!--micrometer-tracing-bom导入链路追寻版别中心  1-->
        <dependency>
                <groupId>io.micrometer</groupId>
                <artifactId>micrometer-tracing-bom</artifactId>
                <version>${micrometer-tracing.version}</version>
                <type>pom</type>
                <scope>import</scope>
        </dependency>
        <!--micrometer-tracing目标追寻  2-->
        <dependency>
                <groupId>io.micrometer</groupId>
                <artifactId>micrometer-tracing</artifactId>
                <version>${micrometer-tracing.version}</version>
        </dependency>
        <!--micrometer-tracing-bridge-brave适配zipkin的桥接包 3-->
        <dependency>
                <groupId>io.micrometer</groupId>
                <artifactId>micrometer-tracing-bridge-brave</artifactId>
                <version>${micrometer-tracing.version}</version>
        </dependency>
        <!--micrometer-observation 4-->
        <dependency>
                <groupId>io.micrometer</groupId>
                <artifactId>micrometer-observation</artifactId>
                <version>${micrometer-observation.version}</version>
        </dependency>
        <!--feign-micrometer 5-->
        <dependency>
                <groupId>io.github.openfeign</groupId>
                <artifactId>feign-micrometer</artifactId>
                <version>${feign-micrometer.version}</version>
        </dependency>
        <!--zipkin-reporter-brave 6-->
        <dependency>
                <groupId>io.zipkin.reporter2</groupId>
                <artifactId>zipkin-reporter-brave</artifactId>
                <version>${zipkin-reporter-brave.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

微服务项目工程引进坐标:

<!--链路追寻-->
<!--micrometer-tracing目标追寻  1-->
<dependency>
	<groupId>io.micrometer</groupId>
	<artifactId>micrometer-tracing</artifactId>
</dependency>
<!--micrometer-tracing-bridge-brave适配zipkin的桥接包 2-->
<dependency>
	<groupId>io.micrometer</groupId>
	<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<!--micrometer-observation 3-->
<dependency>
	<groupId>io.micrometer</groupId>
	<artifactId>micrometer-observation</artifactId>
</dependency>
<!--feign-micrometer 4-->
<dependency>
	<groupId>io.github.openfeign</groupId>
	<artifactId>feign-micrometer</artifactId>
</dependency>
<!--zipkin-reporter-brave 5-->
<dependency>
	<groupId>io.zipkin.reporter2</groupId>
	<artifactId>zipkin-reporter-brave</artifactId>
</dependency>

yaml装备内容:

# 链路追寻zipkin装备
management:
  zipkin:
    tracing:
      # zipkin服务端地址
      endpoint: http://cloud.demo:9411/api/v2/spans
  tracing:
    sampling:
      # 默许0.1 10次恳求收集一次,值越大收集越及时
      probability: 1.0

发送恳求,观察zipkin操控台

原生SpringCloud组件运用学习笔记V1.0

能够发现第一次恳求拜访很慢。

原生SpringCloud组件运用学习笔记V1.0

点击一个恳求详细内容:SHOW按钮点击

原生SpringCloud组件运用学习笔记V1.0

发送一个会报反常的恳求:

原生SpringCloud组件运用学习笔记V1.0

原生SpringCloud组件运用学习笔记V1.0

Gateway网关

网关HelloWorld

创立网关微服务独立项目,引进坐标:

<!--gateway-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

需求引进所运用的服务注册中心,并注册进入。

yaml装备一个路由:

# 装备网关路由
spring:
  cloud:
    gateway:
      routes:
          # 仅有id
        - id: cloud-order
          # 动态拜访服务地址
          uri: lb://cloud-order
          # 断语
          predicates:
            - Path=/order/**

网关三大组件:路由:routes,断语:predicates,过滤器:filters

经过网关的服务地址+端口号进行拜访,有效的屏蔽了真正微服务服务的地址。

网关动态获取服务地址

uri: lb://cloud-order

这儿的lb,就是:LoadBancer,从服务注册中心拉去下来,负载均衡。

断语predicates

断语官网介绍 docs.spring.io/spring-clou…

原生SpringCloud组件运用学习笔记V1.0

项目发动也加载了这些断语。

原生SpringCloud组件运用学习笔记V1.0

最常用的就是Path断语了。

测试一下头信息需求带着的内容:需求恳求带着头信息authentication,值为存数字才能够断语成功

predicates:
  - Header=authentication, d+

原生SpringCloud组件运用学习笔记V1.0

测试不带着authentication头信息:

原生SpringCloud组件运用学习笔记V1.0

带着:

原生SpringCloud组件运用学习笔记V1.0

自定义断语 与 自定义过滤器相似。

过滤器

对已断语成功的恳求,锦上添花:

参考官网地址: docs.spring.io/spring-clou…

运用内置过滤器工厂往恳求头中增加信息,证明恳求是经过了网关,若客户端直接绕开网关,则恳求失败:

spring:
  cloud:
    gateway:
      routes:
          # 仅有id
        - id: cloud-order
          # 动态拜访服务地址
          uri: lb://cloud-order
          # 断语
          predicates:
            - Path=/order/**
            # - Header=authentication, d+
          filters:
            - AddRequestHeader=me, yang

原生SpringCloud组件运用学习笔记V1.0

自定义全局过滤器

往恳求头中增加客户端恳求的IP地址。

@Component
public class ClientIpFilter implements GlobalFilter, Ordered {
    private static final String CLIENT_IP_HEADER = "X-Client-IP";
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取客户端IP
        String clientIp = exchange.getRequest().getRemoteAddress().getHostString();
        // ReadOnlyHttpHeaders 报错
        // exchange.getRequest().getHeaders().add(CLIENT_IP_HEADER, clientIp);
        ServerHttpRequest request = exchange.getRequest().mutate().header(CLIENT_IP_HEADER, clientIp).build();
        return chain.filter(exchange.mutate().request(request).build());
    }
    /**
     * 值越小,优先级越高
     */
    @Override
    public int getOrder() {
        return -1;
    }
}

网关发送到微服务时:

原生SpringCloud组件运用学习笔记V1.0

计算接口调用时刻

@Component
@Slf4j
public class GlobalTimeTestFilter implements GlobalFilter, Ordered {
    //开端拜访时刻
    private static final String BEGIN_VISIT_TIME = "begin_visit_time";
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //先记录下拜访接口的开端时刻
        exchange.getAttributes().put(BEGIN_VISIT_TIME, System.currentTimeMillis());
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            Long beginVisitTime = exchange.getAttribute(BEGIN_VISIT_TIME);
            if (beginVisitTime != null) {
                log.info("拜访接口主机: " + exchange.getRequest().getURI().getHost());
                log.info("拜访接口端口: " + exchange.getRequest().getURI().getPort());
                log.info("拜访接口URL: " + exchange.getRequest().getURI().getPath());
                log.info("拜访接口URL参数: " + exchange.getRequest().getURI().getRawQuery());
                log.info("拜访接口时长: " + (System.currentTimeMillis() - beginVisitTime) + "ms");
                log.info("分割线: ###################################################");
                System.out.println();
            }
        }));
    }
    @Override
    public int getOrder() {
        return -2;
    }
}

原生SpringCloud组件运用学习笔记V1.0

自定义条件过滤器

恳求头中带着指定id

@Component
@Slf4j
public class CheckGatewayFilterFactory extends AbstractGatewayFilterFactory<CheckGatewayFilterFactory.Config> {
    public CheckGatewayFilterFactory() {
        super(CheckGatewayFilterFactory.Config.class);
    }
    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                log.info("经过了校验条件过滤器");
                List<String> ids = List.of("2670", "2690");
                String id = exchange.getRequest().getHeaders().getFirst("id");
                if (Objects.isNull(id) || id.isEmpty() || !ids.contains(id)) {
                    exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
                    return exchange.getResponse().setComplete();
                }
                return chain.filter(exchange);
            }
        };
    }
    @Override
    public List<String> shortcutFieldOrder() {
        return List.of("id");
    }
    @Data
    public static class Config {
        private String id;
    }
}

yaml装备它:

# 装备网关路由
spring:
  cloud:
    gateway:
      routes:
          # 仅有id
        - id: cloud-order
          # 动态拜访服务地址
          uri: lb://cloud-order
          # 断语
          predicates:
            - Path=/order/**
            # - Header=authentication, d+
          filters:
            - AddRequestHeader=me, yang
            - Check

恳求拜访:

失败:

原生SpringCloud组件运用学习笔记V1.0

成功:

原生SpringCloud组件运用学习笔记V1.0

以上的笔记就结束了对原生SpringCloud的一些组件学习与练习。代码已标签: gitee.com/yanghaokun0…