Live2D

SpringCloud微服务框架二

Springcloud微服务框架一


Springcloud微服务框架三



22.GateWay的概述

1.GateWay是什么?

SpringCloud Gateway作为Spring Cloud 生态系统中的网关,目标是替代Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 1.x非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。

Spring Cloud Gateway的目标提供统一的路由方式且基于 Filter链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。

2.作用
  • 方向代理
  • 鉴权
  • 流量控制
  • 熔断
  • 日志监控
3.微服务架构中网关的位置

img

GateWay工作流程
三大核心概念

1.Route(路由)-路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如断言为true则匹配该路由

2.Predicate(断言) - 参考的是Java8的java.util.function.Predicate,开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由;

3.Filter(过滤) - 指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。

img

web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。

predicate就是我们的匹配条件;而fliter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了

核心逻辑:路由转发 + 执行过滤器链

23.GateWay的使用

1.新建Gateway9527工程
1.pom依赖如下
 <dependencies>
        <!--gateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--一般基础配置类-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.0</version>
        </dependency>
    </dependencies>
2.yml文件内容如下
server:
  port: 9527

spring:
  application:
    name: Gateway9527
  cloud:
    gateway:
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://CLOUD-PROVIDER-SERVICE #匹配后提供服务的路由地址
          predicates:
            - Path=/provider/payment/get/**         # 断言,路径相匹配的进行路由

#        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8001         #匹配后提供服务的路由地址
#          #uri: lb://cloud-payment-service #匹配后提供服务的路由地址
#          predicates:
#            - Path=/payment/lb/**         # 断言,路径相匹配的进行路由

eureka:
  instance:
    hostname: cloud-gateway-service
  client: #服务提供者provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka
3.主启动类
package com.wl.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * @Autor: wl
 * @Date: 2021-11-09     13:34
 * @Version 1.0
 * @Describe
 */
@SpringBootApplication
@EnableEurekaClient
public class Gateway9527Main {
    public static void main(String[] args) {
        SpringApplication.run(Gateway9527Main.class,args);
    }
}
4.测试

启动eureka7001工程,Payment8001工程,启动9527网关

在添加网关后,访问网关的ip地址可以获取到8001工程的结果

image

5.GateWay配置路由的另一种方式

配置一个config类

package com.wl.springcloud.config;

import cn.hutool.core.util.IdUtil;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Autor: wl
 * @Date: 2021-11-09     15:52
 * @Version 1.0
 * @Describe
 */
@Configuration
public class GateWayConfig {

    @Bean
    public RouteLocator  customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route(IdUtil.simpleUUID(),
                r->r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
        return routes.build();
    }
    @Bean
    public RouteLocator  customRouteLocator2(RouteLocatorBuilder routeLocatorBuilder){
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route(IdUtil.simpleUUID(),
                r->r.path("/guoji").uri("lb://CLOUD-PROVIDER-SERVICE/provider/payment/get/**")).build();
        return routes.build();
    }
}
结论

通过访问Gateway可以起到保护端口的作用,由GateWay进行微服务的调用,使用 uri: lb://CLOUD-PROVIDER-SERVICE配置动态路由,调用微服务,实现负载均衡。

24.GateWay常用的Predicate

官网地址

1.The After Route Predicate Factory

spring:
  cloud:
    gateway:
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务
          uri: lb://CLOUD-PROVIDER-SERVICE #匹配后提供服务的路由地址
          predicates:
             - After=2021-11-09T21:33:34.510+08:00[Asia/Shanghai] #表示在这个时间以后才可以访问

2.The Before Route Predicate Factory

spring:
  cloud:
    gateway:
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务
          uri: lb://CLOUD-PROVIDER-SERVICE #匹配后提供服务的路由地址
          predicates:
             - Before =2021-11-09T21:33:34.510+08:00[Asia/Shanghai] #表示在这个时间之前才可以访问

3.The Between Route Predicate Factory

spring:
  cloud:
    gateway:
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务
          uri: lb://CLOUD-PROVIDER-SERVICE #匹配后提供服务的路由地址
		- Path=/provider/payment/get/** 
          predicates:
             - Between  =2021-10-09T21:33:34.510+08:00[Asia/Shanghai],2021-11-09T21:33:34.510+08:00[Asia/Shanghai] #表示在这个时间区间中才可以访问

4.The Cookie Route Predicate Factory

spring:
  cloud:
    gateway:
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务
          uri: lb://CLOUD-PROVIDER-SERVICE #匹配后提供服务的路由地址
		- Path=/provider/payment/get/** 
          predicates:
             - Cookie=username,wl #这里是携带cookie的值是否为wl,是才可以访问。 前面为cookie名称后面为正则表达式

使用curl 请求 : curl http://localhost:9527/provider/payment/get/1 --cookie "username=wl"

请求结果
image

5.The Header Route Predicate Factory

spring:
  cloud:
    gateway:
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务
          uri: lb://CLOUD-PROVIDER-SERVICE #匹配后提供服务的路由地址
		- Path=/provider/payment/get/** 
          predicates:
             - Header=X-Request-Id, \d+ #如果请求的标头名称X-Request-Id与\d+正则表达式匹配(即,它的值为一位或多位数字),则此路由匹配。前面为请求头携带的名称后面为正则表达式


使用curl 请求 : curl http://localhost:9527/provider/payment/get/1 -H "X-Request-Id:123"

请求结果

image

6.The Host Route Predicate Factory

主机路由谓词工厂采用一个参数:主机名模式列表。该模式是一个 Ant 风格的模式,以.作为分隔符。此谓词匹配Host与模式匹配的标头

演示省略....

7.The Method Route Predicate Factory

spring:
  cloud:
    gateway:
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务
          uri: lb://CLOUD-PROVIDER-SERVICE #匹配后提供服务的路由地址
		- Path=/provider/payment/get/** 
          predicates:
             - Method=GET,POST #如果请求的标头名称X-Request-Id与\d+正则表达式匹配(即,它的值为一位或多位数字),则此路由匹配。前面为请求头携带的名称后面为正则表达式

8.The Path Route Predicate Factory
9.The Query Route Predicate Factory
10.The RemoteAddr Route Predicate Factory
11.The weight Route Predicate Factory

其他的使用见官网

25.GateWay的Filter

路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。Spring Cloud Gateway内置了多种路由过滤器,他们都由GatewayFilter的工厂类来产生。

官方文档

这里主要演示自定义Filter

package com.wl.springcloud.config;

import lombok.extern.slf4j.Slf4j;
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.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Date;

/**
 * @Autor: wl
 * @Date: 2021-11-09     22:43
 * @Version 1.0
 * @Describe
 */
@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        log.info("***********come in MyLogGateWayFilter:  "+new Date());
        String username=exchange.getRequest().getQueryParams().getFirst("username");
        if (username==null){
            log.info("************用户名为null,非法用户,┭┮﹏┭┮");
            exchange.getResponse().setStatusCode(HttpStatus.BAD_GATEWAY);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

加上了自定义过滤器,我每次访问的时候参数中必须带入用户名,否则就会请求失败。
image

26.Config分布式配置中心

1.分布式系统的缺陷

image

2.Config分布式配置中心是什么

img

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

3.Config配置总控中心搭建

首先在自己的git上面创建一个仓库添加以下文件内容

1.config-dev.yml

config:
  info: "master branch,springcloud-config/config-dev.yml version=51"

2.config-prod.yml

config:
  info: "master branch,springcloud-config/config-prod.yml version=51"

3.config-test.yml

config:
  info: "master branch,springcloud-config/config-test.yml version=51" 
1.新建工程cloud-config-center-3344
<dependencies>
    <!--添加消息总线RabbitMQ支持-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-bus-amqp</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
2.主启动类
package com.wl.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

/**
 * @Autor: wl
 * @Date: 2021-11-10     9:15
 * @Version 1.0
 * @Describe
 */
@SpringBootApplication
@EnableConfigServer // 开启注册中心
public class CenterConfigMain3344 {
     public static void main(String[] args) {
             SpringApplication.run(CenterConfigMain3344.class,args);
         }
}

3.yml文件
server:
  port: 3344
spring:
  application:
    name: cloud-config-center
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/wuyi1998/springconfig
          search-paths: springconfig
      label: master

eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka

windows下修改hosts文件,增加映射

127.0.0.1 config-3344.com

image

配置读取规则

官网文档

/{label}/{application}-{profile}.yml(推荐)

成功实现了用SpringCloud Config通过GitHub获取配置信息
4.Config客户端配置与测试
1.新建工程cloud-config-client-3355
 <dependencies>
        <!--添加消息总线RabbitMQ支持-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
2.bootstrap.yml

applicaiton.yml是用户级的资源配置项

bootstrap.yml是系统级的,优先级更加高

Spring Cloud会创建一个Bootstrap Context,作为Spring应用的Application Context的父上下文。

初始化的时候,BootstrapContext负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的Environment。

Bootstrap属性有高优先级,默认情况下,它们不会被本地配置覆盖。Bootstrap context和Application Context有着不同的约定,所以新增了一个bootstrap.yml文件,保证Bootstrap Context和Application Context配置的分离。

要将Client模块下的application.yml文件改为bootstrap.yml,这是很关键的,因为bootstrap.yml是比application.yml先加载的。bootstrap.yml优先级高于application.yml。

server:
  port: 3355

spring:
  application:
    name: config-client
  cloud:
    #Config客户端配置
    config:
      label: master #分支名称
      name: config #配置文件名称
      profile: dev #读取后缀名称   上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/master/config-dev.yml
      uri: http://localhost:3344 #配置中心地址k


#服务注册到eureka地址
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka

# 暴露监控端点
management:
  endpoints:
    web:
      exposure:
        include: "*"


3.主启动类
package com.wl.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * @Autor: wl
 * @Date: 2021-11-10     10:29
 * @Version 1.0
 * @Describe
 */
@SpringBootApplication
@EnableEurekaClient
public class ConfigClientMain3355 {
     public static void main(String[] args) {
             SpringApplication.run(ConfigClientMain3355.class,args);
         }
}

4.配置类
package com.wl.springcloud.config;

import com.wl.springcloud.ConfigClientMain3355;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Autor: wl
 * @Date: 2021-11-10     10:35
 * @Version 1.0
 * @Describe
 */
@RestController
@Slf4j
@RefreshScope
public class ConfigClient3355 {
   private static final Logger logger = LoggerFactory.getLogger(ConfigClientMain3355.class);

   @Value("${config.info}")
   private String configInfo;

   @GetMapping("/configInfo")
   public String getConfigInfo()
   {
      return configInfo;
   }




}

发起get请求: http://localhost:3355/configInfo

发现可以拿到配置中心的数据

image

5.分布式配置的动态刷新问题

当在git远程仓库上修改配置的话,只有配置中心工程3344可以及时获取到修改后数据,而配置中心客户端3355无法获取修改后的数据,除非重启工程。

6.Config动态刷新之手动版

修改3355模块

pom中引入actuator监控

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

修改YML,添加暴露监控端口配置:

# 暴露监控端点
management:
  endpoints:
    web:
      exposure:
        include: "*"

在controller层添加@RefreshScope注解

import org.springframework.cloud.context.config.annotation.RefreshScope;
...

@RestController
@RefreshScope//<-----
public class ConfigClientController
{
...
}

然后在发一个Post请求刷新

curl -X POST "http://localhost:3355/actuator/refresh"

想想还有什么问题?

  • 假如有多个微服务客户端3355/3366/3377
  • 每个微服务都要执行—次post请求,手动刷新?
  • 可否广播,一次通知,处处生效?
  • 我们想大范围的自动刷新,求方法

27.Bus消息总线

1.是什么?

Spring Cloud Bus 配合Spring Cloud Config 使用可以实现配置的动态刷新。

image

Spring Cloud Bus是用来将分布式系统的节点与轻量级消息系统链接起来的框架,它整合了Java的事件处理机制和消息中间件的功能。Spring Clud Bus目前支持RabbitMQ和Kafka。

2.能干什么?

Spring Cloud Bus能管理和传播分布式系统间的消息,就像一个分布式执行器,可用于广播状态更改、事件推送等,也可以当作微服务间的通信通道。

image

3.配置 rabbitmq环境

此处省略步骤。。。。。。。。

30.cloudAlibaba

1.cloudAlibaba是什么?

Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。

依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。

诞生:2018.10.31,Spring Cloud Alibaba 正式入驻了Spring Cloud官方孵化器,并在Maven 中央库发布了第一个版本。

官方网站

2.SpringcloudAlibaba的优点:

  • 服务限流降级:默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
  • 服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
  • 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
  • 消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
  • 分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。
  • 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
  • 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
  • 阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

31.Nacos

1.Naocos的由来

前四个字母分别为Naming和Configuration的前两个字母,最后的s为Service。

2.为什么使用Nacos

Nacos是注册中心+配置中心的组合 -> Nacos = Eureka+Config+Bus

  • 替代Eureka做服务注册中心
  • 替代Config做服务配置中心
3.注册中心的对比
服务注册与发现框架 CAP模型 控制台管理 社区活跃度
Eureka AP 支持 低(2.x版本闭源)
Zookeeper CP 不支持
consul CP 支持
Nacos AP 支持
4.Nacos安装

1.确保JDK8和Maven环境

2.官方网站下载Nacos

3.在bin目录下,双击运行startup.cmd启动Nacos

4.访问 http://localhost:8848/nacos,默认账号密码都是nacos

5.Nacos作为注册中心使用-提供者
1.新建工程cloudAlibaba-provider-Payment9001
 <dependencies>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--日常通用jar包配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
2.主启动类
package com.wl.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @Autor: wl
 * @Date: 2021-11-11     13:26
 * @Version 1.0
 * @Describe
 */
@SpringBootApplication
@EnableDiscoveryClient
public class Payment9001 {
     public static void main(String[] args) {
             SpringApplication.run(Payment9001.class,args);
         }

}

3.yml文件
server:
  port: 9001
spring:
  application:
    name: nacos-provdier-Payment
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848 # 将服务注册到Nacos的地址
# 暴露监控端点
management:
  endpoints:
    web:
      exposure:
        include: '*'
4.业务层
package com.wl.springcloud.controller;

import com.wl.springcloud.ConfigClientMain3355;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Autor: wl
 * @Date: 2021-11-10     10:35
 * @Version 1.0
 * @Describe
 */
@RestController
@Slf4j
@RefreshScope
public class ConfigClientController3355 {
   private static final Logger logger = LoggerFactory.getLogger(ConfigClientMain3355.class);

   @Value("${config.info}")
   private String configInfo;

   @GetMapping("/configInfo")
   public String getConfigInfo()
   {
      return configInfo;
   }




}

打开nacos的图形化用户界面,可以看到此工程被注册到nacos注册中心上了

image

此时访问地址: http://localhost:9001/payment/nacos/1 可以访问ok

再复制此工程 修改端口号为9002,用于集群,测试负载均衡

6.Nacos作为注册中心使用-消费者
1.新建工程 cloudAlibaba-consumer-Order6001
<dependencies>
    <!--SpringCloud ailibaba nacos -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!-- SpringBoot整合Web组件 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--日常通用jar包配置-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
2.主启动类
package com.wl.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @Autor: wl
 * @Date: 2021-11-11     14:23
 * @Version 1.0
 * @Describe
 */
@SpringBootApplication
@EnableDiscoveryClient
public class Order6001 {
    public static void main(String[] args) {
        SpringApplication.run(Order6001. class,args);
    }
}
3.yml内容
server:
  port: 6001
spring:
  application:
    name: nacos-consumer-Order
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
management:
  endpoints:
    web:
      exposure:
        include: '*'
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
  nacos-user-service: http://nacos-provdier-Payment
4.配置RestTemplate远程调用
package com.wl.springcloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * @Autor: wl
 * @Date: 2021-11-11     14:48
 * @Version 1.0
 * @Describe
 */
@Configuration
public class ApplicationContextConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return  new RestTemplate();
    }
}
5.业务类
package com.wl.springcloud.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;


/**
 * @Autor: wl
 * @Date: 2021-11-11     14:30
 * @Version 1.0
 * @Describe
 */
@RestController
public class Oder6001Controller {


    @Value("${service-url.nacos-user-service}")
    private String serverUrl;

    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/consumer/nacos/{id}")
    public String getServerId(@PathVariable("id") Integer id){
        return restTemplate.getForObject(serverUrl+"/payment/nacos/"+id,String.class);
    }


}

访问地址:http://localhost:6001/consumer/nacos/1 发现会以轮询的模式进行负载均衡

image

image

7.Nacos作为服务配置中心
1.新建工程cloudAlibaba-config-nacos-client3377
<dependencies>
    <!--nacos-config-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    <!--nacos-discovery-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--web + actuator-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--一般基础配置-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
2.主启动类
package com.wl.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @Autor: wl
 * @Date: 2021-11-11     15:20
 * @Version 1.0
 * @Describe
 */
@SpringBootApplication
@EnableDiscoveryClient
public class NacosClient3377Main {
    public static void main(String[] args) {
            SpringApplication.run(NacosClient3377Main.class,args);
        }
}
3.properties配置
server.port=3377
spring.application.name=nacos-config-client
# 开发环境
spring.profiles.active=dev
# 注册中心地址
spring.cloud.nacos.discovery.server-addr=localhost:8848
# 配置中心地址
spring.cloud.nacos.config.server-addr=localhost:8848
# 文件的后缀格式
spring.cloud.nacos.config.file-extension=properties
4.业务类
package com.wl.springcloud.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RefreshScope //支持Nacos的动态刷新功能。
public class ConfigClientController
{
    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/config/info")
    public String getConfigInfo() {
        return configInfo;
    }
}
5.在Nacos中添加配置信息

Nacos中的dataid的组成格式及与SpringBoot配置文件中的匹配规则

官网文档

在 Nacos Spring Cloud中,dataId的完整格式如下

${prefix}-${spring-profile.active}.${file-extension}

prefix默认为spring.application.name的值,也可以通过配置项spring.cloud.nacos.config.prefix来配置。
spring.profile.active即为当前环境对应的 profile,详情可以参考 Spring Boot文档。注意:当spring.profile.active为空时,对应的连接符 - 也将不存在,datald 的拼接格式变成${prefix}.${file-extension}
file-exetension为配置内容的数据格式,可以通过配置项spring .cloud.nacos.config.file-extension来配置。目前只支持properties和yaml类型。
通过Spring Cloud 原生注解@RefreshScope实现配置自动更新。

最后的公式是:

${spring.application.name)}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

在nacos中新增配置,如下图

image

对应的公式是

image

Nacos自带动态刷新,解决了springConfig需要手动刷新的痛点!

Nacos YYDS!

8.Nacos命名空间、DataId和分组之间的关系

实际开发中,通常一个系统会有三种环境

  • dev开发环境

  • test测试环境

  • prod生产环境

如何保证指定环境启动时服务能正确读取到Nacos上相应环境的配置文件呢?

只需要将yml文件中的开发环境改成需要的环境

spring.profiles.active=test

然后在nacos上新建一个

image

访问 http://localhost:3377/config/info

image

32.Nacos集群的搭建

1.Linux上数据库的配置
找到sql脚本,然后在服务器上运行

image

复制里面的sql脚本去服务器上的数据库运行

2.application.properties文件的修改
加上以下内容
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://wl:3306/nacos_config?characterEncoding=utf8&serverTimezone=Asia/Shanghai
db.user=root
db.password=123456
3.Linux服务器上nacos的集群配置cluster.conf
输入命令查看主机ip
hostname -i
打开cluster.conf,并在里面加入以下内容
172.26.37.8:3333
172.26.37.8:4444
172.26.37.8:5555
4.编辑Nacos的启动脚本startup.sh,使它能够接受不同的启动端口
找到bin下面的startup.sh启动文件,进行修改

image

平时单机版的启动,都是./startup.sh即可

但是,集群启动,我们希望可以类似其它软件的shell命令,传递不同的端口号启动不同的nacos实例。
命令: ./startup.sh -p 3333表示启动端口号为3333的nacos服务器实例,和上一步的cluster.conf配置的一致。

修改内容:

img

img

执行方式 - startup.sh - p 端口号

image

这里讲一个坑,nacos集群默认运行内存是1g,我们需要修改内存,服务器内存大的可以忽略,打开startup.sh文件,修改里面的内容

image

5.Nginx的配置,作为负载均衡器
修改nginx的配置文件 - nginx.conf

image

6.启动三个nacos注册中心

启动3个nacos注册中心

  • startup.sh - p 3333
  • startup.sh - p 4444
  • startup.sh - p 5555

启动nginx

7.测试

省略步骤。。。

33.Sentinel是什么

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel 具有以下特征:

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

  • 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。

  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。

  • 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel 的主要特性:

img

Hystrix与Sentinel比较:

  • Hystrix
    1. 需要我们程序员自己手工搭建监控平台
    2. 没有一套web界面可以给我们进行更加细粒度化得配置流控、速率控制、服务熔断、服务降级
  • Sentinel
    1. 单独一个组件,可以独立出来。
    2. 直接界面化的细粒度统一配置。

下载地址

下载jar包到本地 运行,默认端口号为8080,登录账号密码都为sentinel

34.Sentinel的初始化监控

1.新建工程Module cloudalibaba-sentinel-service8401
pom内容
<dependencies>
    <!--SpringCloud ailibaba nacos -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
    <!--SpringCloud ailibaba sentinel -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <!--openfeign-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <!-- SpringBoot整合Web组件+actuator -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--日常通用jar包配置-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>4.6.3</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

</dependencies>
启动类
package com.wl.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @Autor: wl
 * @Date: 2021-11-18     10:15
 * @Version 1.0
 * @Describe
 */
@SpringBootApplication
@EnableDiscoveryClient
public class SentinelMain8401 {
    public static void main(String[] args) {
            SpringApplication.run(SentinelMain8401.class,args);
        }
}
配置文件内容
server:
  port: 8401
spring:
  application:
    name: cloud-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848 # nacos注册中心地址
    sentinel:
     transport:
       dashboard: 127.0.0.1:8080 #sentinel注册中心地址
       port: 8719 #给nacos发送心跳的端口号
       clientIp: 127.0.0.1 #监控的ip

#监控暴露
management:
  endpoints:
    web:
      exposure:
        include: '*'

feign:
  sentinel:
    enabled: true # 激活Sentinel对Feign的支持
业务类
package com.wl.springcloud.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
@Slf4j
public class FlowLimitController {
    @GetMapping("/testA")
    public String testA()
    {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "------testA";
    }

    @GetMapping("/testB")
    public String testB()
    {
        log.info(Thread.currentThread().getName()+"\t"+"...testB");
        return "------testB";
    }

    /**
     * 服务降级-秒级响应时间、异常数、异常比例
     * @return
     */
    @GetMapping("/testFallBack")
    public String testFallBack(){
        // 秒级平均响应时间测试
//        try {
//            TimeUnit.SECONDS.sleep(2);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        //异常测试
        int age=10/0;

        return "testFallBack";
    }

    /**
     * @SentinelResource(value = "testFallBack",blockHandler = "deal_testFallBack")
     *      value里可以写任何东西,但是必须是唯一标识
     *      blockHandler里面的代表的是兜底的方法
     * @return
     */
    @GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
    public  String testHotKey(@RequestParam(value = "p1",required = false)String p1 ,@RequestParam(value = "p2",required = false)String p2){
        return "testHotKey";
    }

    public String deal_testHotKey(String p1, String p2, BlockException exception){
        return "/(ㄒoㄒ)/~~";
    }
}

35.Sentinel的流控规则简介

基本介绍

img

进一步解释说明:

  • 资源名:唯一名称,默认请求路径。

  • 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)。

  • 阈值类型/单机阈值:

    • QPS(每秒钟的请求数量)︰当调用该API的QPS达到阈值的时候,进行限流。
    • 线程数:当调用该API的线程数达到阈值的时候,进行限流。
  • 是否集群:不需要集群。

  • 流控模式:

    • 直接:API达到限流条件时,直接限流。
    • 关联:当关联的资源达到阈值时,就限流自己。
    • 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【API级别的针对来源】
  • 流控效果:

    • 快速失败:直接失败,抛异常。
    • Warm up:根据Code Factor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值。
    • 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效。

36.Sentinel流控的使用

访问sentinel的图形化用户界面,首次访问可能是以下页面

img

因为sentinel采用的是懒加载的方式,所以需要线把接口访问一遍,访问 http://localhost:8401/testB 和http://localhost:8401/testA,出现图片下的内容

image

1.QPS-直接-快速失败

点击流控按钮,选择以下配置

image

这里就是说,如果在一秒钟之内请求数大于阈值,则会报默认错误 Blocked by Sentinel (flow limiting)

2.线程数-直接-快速失败

image

image

当请求 http://localhost:8401/testA 的时候,连续点击两次请求可以看到报错Blocked by Sentinel (flow limiting)

3.QPS-关联-快速失败
  • 当自己关联的资源达到阈值时,就限流自己
  • 当与A关联的资源B达到阀值后,就限流A自己(B惹事,A挂了)

image

Postman模拟并发密集访问testB

在集合中添加请求

image

添加集合测试

image

点击Run test

发现B超过了阈值,A也报错了

4.Warm-up 预热

image

默认coldFactor为3,即请求QPS 从 threshold / 3开始,经预热时长逐渐升至设定的QPS阈值

threshold也就是自己设置的阈值

应用场景

如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,可慢慢的把流量放进来,慢慢的把阀值增长到设置的阀值

5.排队等待

匀速排队,让请求以均匀的速度通过,阀值类型必须设成QPS,否则无效。

设置:/testA每秒1次请求,超过的话就排队等待,等待的超时时间为20000毫秒。

img

img

37.Sentinel降级简介

1.熔断降级概述

除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。

现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。

RT(平均响应时间,秒级)
  • 平均响应时间 超出阈值 且 在时间窗口内通过的请求>=5,两个条件同时满足后触发降级。

  • 窗口期过后关闭断路器。

  • RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)。

异常比列(秒级)
  • QPS >= 5且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级 。
异常数(分钟级)
  • 异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
Sentinei的断路器是没有类似Hystrix半开状态的。(Sentinei 1.8.0 已有半开状态) 半开的状态系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用。
2.Sentinel服务降级-RT

官方文档

简介

平均响应时间(DEGRADE_GRADE_RT):当1s内持续进入5个请求,对应时刻的平均响应时间(秒级)均超过阈值( count,以ms为单位),那么在接下的时间窗口(DegradeRule中的timeWindow,以s为单位)之内,对这个方法的调用都会自动地熔断(抛出DegradeException )。注意Sentinel 默认统计的RT上限是4900 ms,超出此阈值的都会算作4900ms,若需要变更此上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx来配置。

注意:Sentinel 1.7.0才有平均响应时间DEGRADE_GRADE_RT),Sentinel 1.8.0的没有这项,取而代之的是慢调用比例 (SLOW_REQUEST_RATIO)。

慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。link

Sentinel的流程图:

img

配置

image

我们在testA中让线程睡一秒钟,这样的话,平均达不到响应时间200毫秒,看看会发生什么

image

jemter压力测试(sentinel要求在一秒钟之内必须达到五个请求及以上,才会开启服务降级)

image

结论

按照上述配置,永远一秒钟打进来10个线程(大于5个了)调用testD,我们希望200毫秒处理完本次任务,如果超过200毫秒还没处理完,在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了后续我停止jmeter,没有这么大的访问量了,断路器关闭(保险丝恢复),微服务恢复OK。

3.Sentinel服务降级-异常比例
配置

image

修改业务类中的代码(出现空指针异常)

image

结论

第一次访问绝对报错,因为除数不能为零,我们看到error窗口,但是达到5次报错后,进入熔断后降级。

4.Sentinel服务降级-异常数

image

38.Sentinel热点key

基本介绍

image

官方文档

何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。imgSentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。

承上启下复习start

兜底方法,分为系统默认和客户自定义,两种

之前的case,限流出问题后,都是用sentinel系统默认的提示: Blocked by Sentinel (flow limiting)

我们能不能自定?类似hystrix,某个方法出问题了,就找对应的兜底降级方法?

    /**
     * @SentinelResource(value = "testFallBack",blockHandler = "deal_testFallBack")
     *      value里可以写任何东西,但是必须是唯一标识
     *      blockHandler里面的代表的是兜底的方法
     * @return
     */
    @GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
    public  String testHotKey(@RequestParam(value = "p1",required = false)String p1 ,@RequestParam(value = "p2",required = false)String p2){
        return "testHotKey";
    }

    public String deal_testHotKey(String p1, String p2, BlockException exception){
        return "/(ㄒoㄒ)/~~";
    }
配置

image

参数例外项

  • 普通 - 超过1秒钟一个后,达到阈值1后马上被限流
  • 我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样
  • 特例 - 假如当p1的值等于5时,它的阈值可以达到200

image

上面兜底方案面临的问题

  1. 系统默认的,没有体现我们自己的业务要求。
  2. 依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。
  3. 每个业务方法都添加—个兜底的,那代码膨胀加剧。
  4. 全局统—的处理方法没有体现。
客户自定义限流处理逻辑

自定义限流处理类 - 创建CustomerBlockHandler类用于自定义限流处理逻辑

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;

public class CustomerBlockHandler {
    public static CommonResult handlerException(BlockException exception) {
        return new CommonResult(4444,"按客戶自定义,global handlerException----1");
    }
    
    public static CommonResult handlerException2(BlockException exception) {
        return new CommonResult(4444,"按客戶自定义,global handlerException----2");
    }
}

RateLimitController
@RestController
public class RateLimitController {
	...

    @GetMapping("/rateLimit/customerBlockHandler")
    @SentinelResource(value = "customerBlockHandler",
            blockHandlerClass = CustomerBlockHandler.class,//<-------- 自定义限流处理类
            blockHandler = "handlerException2")//<-----------
    public CommonResult customerBlockHandler()
    {
        return new CommonResult(200,"按客戶自定义",new Payment(2020L,"serial003"));
    }
}

Sentinel控制台的配置

image

@SentinelResource 注解

​ 注意:注解方式埋点不支持 private 方法。

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:

  • value:资源名称,必需项(不能为空)

  • entryType:entry 类型,可选项(默认为 EntryType.OUT)

  • blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

  • fallback /fallbackClass:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:

  • 返回值类型必须与原函数返回值类型一致;
    方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

  • defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
    返回值类型必须与原函数返回值类型一致;
    方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

  • exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。




Springcloud微服务框架三
posted @ 2021-11-09 21:07  没有梦想的java菜鸟  阅读(205)  评论(0编辑  收藏  举报