【☁Spring Cloud】第二代Spring Cloud核心组件

第一代 Spring Cloud (主要是  Spring Cloud Netflix)很多组件已经进入停更维护模式。第二代Spring Cloud核心组件主要以Spring Cloud Alibaba为主,Spring Cloud Alibaba是由一些阿里巴巴 的开源组件和云产品组成的,2018年,Spring Cloud Alibaba 正式入住了 Spring Cloud 官方孵化器。

Nacos 服务注册和配置中心

Nacos (Dynamic Naming and Configuration Service)是阿里巴巴开源的一个针 对微服务架构中服务发现、配置管理和服务管理平台。

Nacos就是注册中心+配置中心的组合。(Nacos = Spring Cloud注册中心 + Spring Cloud配置中心)

Nacos功能特性:

  • 服务发现与健康检查
  • 动态配置管理
  • 动态DNS服务
  • 服务和元数据管理(管理平台的⻆度,nacos也有一个ui⻚面,可以看到注册的服务及其实例信息(元数据信息)等),动态的服务权重调整、动态服务优雅下线

Nacos架构图

  • OpenAPI:暴露标准Rest风格HTTP接口,简单易用,方便多语言集成

Nacos数据模型

Nacos 数据模型 Key 由三元组唯一确定, Namespace默认是空串,公共命名空间(public),分组默认是 DEFAULT_GROUP。

分为服务数据模型和配置数据模型。

Nacos单机搭建

下载地址:https://github.com/alibaba/Nacos/releases

根据需要选择win或者linux版本:

国内下载缓慢,我们可以下载源码打包。
git clone https://gitee.com/mirrors/Nacos.git , 然后切换到指定的release分支:git checkout 2.2.2 
进入源码目录,执行打包命令:mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
最终,会在nacos源码目录的distribution\target文件夹下生成nacos-server-2.2.2.zip
我们可以解压nacos-server-2.2.2.zip

进入cmd命令窗口,进入bin目录,执行单机启动命令: startup.cmd -m standalone

启动成功后,打开浏览器,输入:http://localhost:8848/nacos/index.html

在standalone模式下,Nacos默认使用derby数据库来存储配置信息。在Nacos_PATH/data目录,有一个derby-data的文件, Derby是Java编写的数据库,是Apache的开源项目,默认的所有配置就是存在derby这个数据库中。

Nacos代码演示

通用的父pom配置

<!-- 因为是总项目 所以用dependencyManagement来管理  因为其他的子项目就不会来管理版本了了 可以直接引用 -->
<dependencyManagement>
    <dependencies>
        <!-- spring cloud的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR12</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!-- spring cloud alibaba的依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <!-- <version>2.2.9.RELEASE</version> -->
            <version>2.2.1.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!-- spring boot的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.3.3.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--  数据库-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>
        <!--单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!-- lombok, idea需要安装插件Lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
    </dependencies>
</dependencyManagement>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.5</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>

作为注册中心

依赖配置:

<!--nacos client-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

在项目中新建bootstrap.yml配置文件,添加如下内容

server:
  port: 9000

spring:
  application:
    name: app-service
  cloud:
    nacos:
      # 【注册中心】相关配置
      discovery:
        # 命名空间(大分组)
        namespace: public # 自定义命名空间需要进入nacos控制台新建,若使用新建的命名空间,必须使用命名空间id
        # 分组名称,默认为【DEFAULT_GROUP】
        group: dev_group # 自定义group需要进入nacos控制台新建
        # 注册中心地址
        server-addr: localhost:8848

新版本的nacos,启动类上无需添加@EnableDiscoveryClient注解。

注册中心却不能通过group分组 ?

我们知道nacos默认分组是DEFAULT_GROUP,但是当我引入spring-cloud-starter-alibaba-nacos-discovery的版本是2.2.9.RELEASE时,发现在服务发现这块无法自定义分组,一旦自定义分组就注册不上,最后将spring-cloud-starter-alibaba-nacos-discovery的版本换成2.2.1.RELEASE时,却可以自定义分组,也注册上去,真是百思不得其解。

作为配置中心

nacos作为配置中心,主要分两部分:

  • 配置管理:使用Nacos页面管理配置文件。
  • 配置引用:在项目中引用想要的配置。

添加依赖:

<!--nacos config-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

配置管理

Nacos作为配置中心,必须先在Nacos管理页面新增一些配置。

在新增页面中填写相应信息:

  • Data ID:配置文件名。【如果在引用时,想要某个项目缺省式的引用到该配置文件,Data ID应该与服务的spring.application.name保持一致】
  • Group:配置分组,默认为DEFAULT_GROUP
  • 描述:描述
  • 配置格式:按需选择,现在一般都用yaml,即yml
  • 配置内容:配置内容

配置引用

项目中想用配置中心,还要写相应配置,告诉项目我要用哪个配置中心、哪个配置文件。

bootstrap.yml配置:

spring:
  application:
    name: app-service
  cloud:
    nacos:
      # 【注册中心】相关配置
      discovery:
        # 命名空间(大分组)
        namespace: public # 自定义命名空间需要进入nacos控制台新建,若使用新建的命名空间,必须使用命名空间id:5b51cad1-dce5-4c5d-ae7a-7d05e2ffc737
        # 分组名称,默认为【DEFAULT_GROUP】
        group: dev_group # 自定义group需要进入nacos控制台新建
        # 注册中心地址
        server-addr: localhost:8848
      # 【配置中心】相关配置
      config:
        # 命名空间(大分组)
        namespace: public
        # 配置中心地址
        server-addr: localhost:8848
        # 配置文件的data-id,默认使用服务名(spring.application.name)
        name: demo_dev.yaml
        # 配置文件类型:test/json/xml/yaml/properties
        file-extension: yaml
        # 分组名称,默认为【DEFAULT_GROUP】
        group: DEFAULT_GROUP
        # 自动刷新配置,默认为true
        refresh-enabled: true # 开启后, 会多出很多刷新的日志
        # 共享配置引用
#        shared-configs:
#          # 配置ID
#          - data-id: shared-datasource-dbcp2-pro.yaml
#            # 配置组名称,默认为【DEFAULT_GROUP】
#            group: SHARED_PRO
#            refresh: true
#          - data-id: shared-redis-pro.yaml
#            group: SHARED_PRO
#            refresh: true

Sentinel 分布式系统的流量防卫兵

Sentinel的特性

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场 景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、 集群流量控制、实时熔断下游不可用应用等。
  • 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到 接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo的整合。您只需要引入相应的依赖并进行简单的配 置即可快速地接入 Sentinel。
  • 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过 实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel 关键概念

  • 资源:它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或 由应用程序调用的其它应用提供的服务,甚至可以是一段代码。我们请求 的API接口就是资源
  • 规则:围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。

Sentinel安装

下载地址:https://github.com/alibaba/Sentinel/tags

以上这种方式,基本上想国内下载不太可能。

1)从gitee(码云)下载源码:git clone https://gitee.com/mirrors/Sentinel.git,然后切换下版本git checkout release-1.6,高版本对maven的要求比较高,本人使用的是maven3.2.5

2)通过maven打包命令mvn clean package -DskipTests对Sentinel源码进行打包。(记住下载的源码包不要放在中文路径下!!!否则会打包失败。)

打开cmd命令行,执行启动命令:java -jar sentinel-dashboard.jar  (默认端口是8080)

或者自定义端口:java -jar -Dserver.port=1234 sentinel-dashboard.jar

启动成功后,打开浏览器,访问:http://localhost:1234,默认账号和密码sentinel/sentinel

Sentinel代码演示

依赖配置

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

添加sentinel配置

spring:
  application:
    name: app-service
  cloud:
    sentinel:
      transport:
        dashboard: localhost:1234 # sentinel控制台地址
        #port: 8769 # 与 Sentinel 控制台做交互。比如 Sentinel 控制台添加了一个限流规则,会把规则数据 push 给这个 Http Server 接收 Http Server 再将规则注册到 Sentinel 中。
      eager: true # 取消控制台懒加载, 如果不设置,默认是false,需要发送请求才能监控得到

刷新sentinel控制台,如下图:

注意:

  • Sentinel的数据默认是存储在启动服务器的内存中,服务器重启也意味着配置、监控数据的丢失。
  • Sentinel的规则配置是以服务为维度的,如果服务重启了,所有规则也会丢失的。

流量监控规则 (FlowRule)

1)资源名

唯一名称,默认请求路径(如:http://localhost:8089/testA,就是/testA)

2)针对来源

Sentinel可以针对调用者进行限流,填写微服务名,指定对哪个微服务进行限流 ,默认default(不区分来源,全部限制)

3)阈值类型/单机阈值

  • QPS(每秒钟的请求数量)【挡在门外】:当调用该接口的QPS达到了阈值的时候,进行限流;
  • 线程数【关门打狗】:当调用该接口的线程数达到阈值时,进行限流

4)流控模式

  • 直接:接口达到限流条件时,直接限流
  • 关联:当关联的资源达到阈值时,就限流自己
  • 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就可以限流)[api级别的针对来源]

5)流控效果

  • 快速失败:直接失败
  • Warm Up:即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值
  • 排队等待

直接在sentinel控制台对相应的请求资源进行流量控制规则配置即可。 

熔断降级规则 (DegradeRule)

在微服务集群环境下服务见调用比较多,一旦调用链中某个被调用方服务不可用,那么会导致整个系统就会崩溃,这里可以使用Sentinel来做熔断保护。

1)RT(平均响应时间):当1s内持续进入5个请求,对应时刻的平均响应时间(秒级)均超过阀值(count,以ms为单位),那么在接下来的时间窗口(DegradeRule中的timeWindow,以s为单位),对这个方法的调用都会自动熔断(抛出DegradeException)。

2)异常比例(DEGRADE_GRADE_EXCEPTION_RATIO):当资源的每秒请求量>=5,并且每秒异常总数占通过量的比值超过阀值(Degrade中的count)之后,资源进入降级状态,即在接下来的时间窗口(DegradeRule中的timeWindow,以s为单位)之内,对这个方法的调用都会自动地返回。异常比率的阀值范围[0.0,1.0],代表0%~100%。

3)异常数(DEGRADE_GRADE_EXCEPTION_COUNT):当资源近1分钟的异常数目超过阀值之后会进行熔断,注意由于统计数据的时间窗口是分钟级别的,若timeWindow小于60s,则结束熔断状态后仍可能再进入熔断状态。

实现熔断降级有两种方式:

硬编码抛出异常的方式定义资源

@GetMapping("testSentinelA")
public Object testSentinelA() {
    Map result = new HashMap();
    //资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。得在Sentinel控制台中配置
    try(Entry entry= SphU.entry("t1")) {//抛出异常的方式定义限流资源名-t1
        // 被保护的业务逻辑 do something here...
        result = hystrixFeignClient.getInfo();
    }catch (BlockException ex){
        // 资源访问阻止,被限流或被降级
        // 在此处进行相应的处理操作
        System.out.println("资源被降级");
        result.put("resource", "t1");
        result.put("error", "资源被降级");
    }
    return result;
}

注解方式定义资源

/**
    * @SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:
    *
    * 1) value:资源名称,必需项(不能为空)
    * 2) entryType:entry 类型,可选项(默认为 EntryType.OUT)
    * 3) blockHandler / blockHandlerClass
    * blockHandler 对应处理 BlockException 的函数名称,可选项。
    * blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。
    * blockHandler 函数默认需要和原方法在同一个类中。
    * 若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
    *
    * 4) fallback / fallbackClass:
    * fallback 函数名称,可选项,用于在抛出异常时提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。
    * fallback 函数签名和位置要求:
    * - 返回值类型必须与原函数返回值类型一致;
    * - 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    * - fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
    *
    * 5) defaultFallback(since 1.6.0)
    * 默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。
    * 默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。
    * 若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。
    * defaultFallback 函数签名要求:
    * - 返回值类型必须与原函数返回值类型一致;
    * - 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    * - defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
    *
    * 6) exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
    */
@SentinelResource(value = "t2", blockHandler = "blockHandler")
@GetMapping("testSentinelB")
public Object testSentinelB() {
    Map result = hystrixFeignClient.getInfo();
    return result;
}

public Object blockHandler(BlockException e) {
    System.out.println("blockHandler-->发生BlockException异常了");
    Map<String, Object> result = new HashMap<>();
    result.put("resource", "t2");
    result.put("error", "资源被降级");
    return result;
}

 系统保护规则 (SystemRule)

当容量评估不到位,某个大流量接口限流配置不合理或者没有配置,导致系统崩溃,或者突然发现机器的load和CPU等开始飙升,但不能快速确认是什么原因造成等,这时候,需要一个全局的兜底防护方案,即使缺乏容量评估也能有一定的保护机制。这就是系统保护规则。

Sentinel系统自适应限流从整体维度对应入口流量进行控制,结合应用的Load、cup使用率、总体平均RT、入口QPS和并发线数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

1)load 自适应(仅对Linux/Unix系统有效)
当系统load1(1分钟平均负载)超过阈值,且并发线程数超过系统容量时触发。其中的load1,可以在Linux系统上通过命令 uptime 查看,这个命令返回3个值,分别为load1、load5、load15,表示系统1分钟的平均负载、5分钟的平均负载、15分钟的平均负载。

2)RT: 当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。

3)线程数: 当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。

4)入口 QPS: 当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

5)CPU 使用率(1.5.0+ 版本): 当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。

访问控制规则 (AuthorityRule)

很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过:

  • 若配置白名单,则只有请求来源位于白名单内时才可通过;
  • 若配置黑名单,则请求来源位于黑名单时不通过,其余的请求通过。

热点规则 (ParamFlowRule)

热点参数流控规则是一种更细粒度的流控规则, 它允许将规则具体到参数上。

参数索引代表第几个参数,从0开始,如下边的索引0代表name

public String message5(String name, Integer age);
  • 图中定义的规则表示:限制传入下标是0的参数,一秒只能请求一次;
  • 参数例外项可以定义不同类型的参数,不同的参数值给不同的阈值;

关键注解@SentinelResource。

流控自定义返回

/**
 * 流控自定义返回
 **/
@Component
public class SeckillSentinelConfig implements BlockExceptionHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws Exception {
        System.out.println("===>流控自定义返回");
        response.setContentType("application/json;charset=utf-8");
        Map<String, Object> res = new HashMap<>();
        if (ex instanceof FlowException) {
            res.put("msg", "流控规则被触发");
        } else if (ex instanceof DegradeException) {
            res.put("msg", "降级规则被触发");
        } else if (ex instanceof AuthorityException) {
            res.put("msg", "授权规则被触发");
        } else if (ex instanceof ParamFlowException) {
            res.put("msg", "热点规则被触发");
        } else if (ex instanceof SystemBlockException) {
            res.put("msg", "系统规则被触发");
        }
        response.setStatus(200);
        response.getWriter().write(JSON.toJSONString(res));
    }
}

Sentinel配置持久化到nacos

规则管理及推送

实现原理

1)控制台推送规则到Nacos/远程配置中心

2)Sentinel client 监听Nacos配置变化,更新本地缓存

下面使用Push模式:配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel

参考:在springCloud架构下实现sentinel持久化到nacos

gateway整合Sentinel

注:从 1.6.3 版本开始,控制台支持网关流控规则管理。该启动参数会将您的服务标记为 API Gateway,在接入控制台时您的服务会自动注册为网关类型,然后您即可在控制台配置网关规则和 API 分组。

需要修改gateway项目中的启动类:

@EnableDiscoveryClient
@SpringBootApplication
public class GatewayServer {
    public static void main(String[] args) {
        //只有添加这句,才能显示API管理等菜单,添加后还不生效的话,则重启Sentinel、清除浏览器缓存等
        System.setProperty("csp.sentinel.app.type", "1");
        SpringApplication.run(GatewayServer.class, args);
    }
}

最终Sentinel的监控页面如下:

依赖配置

<!--gateway网关, 切记不可再引入spring-boot-starter-web, 已引入了spring-boot-starter-webflux-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

<!--sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

<!--gateway整合sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

<!--nacos注册中心的依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

application.yml配置

server:
  port: 8666
spring:
  application:
    name: gateway-server
  cloud:
    nacos:
      # 【注册中心】相关配置
      discovery:
        # 命名空间(大分组)
        namespace: public # 自定义命名空间需要进入nacos控制台新建,若使用新建的命名空间,必须使用命名空间id
        # 分组名称,默认为【DEFAULT_GROUP】
        group: DEFAULT_GROUP # 自定义group需要进入nacos控制台新建
        # 注册中心地址
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:1234 # sentinel控制台地址
        #port: 8769 # 与 Sentinel 控制台做交互。比如 Sentinel 控制台添加了一个限流规则,会把规则数据 push 给这个 Http Server 接收 Http Server 再将规则注册到 Sentinel 中。
      eager: true # 取消控制台懒加载, 如果不设置,默认是false,需要发送请求才能监控得到
    gateway:
      locator:
        enabled: true #表明gateway开启服务注册和发现的功能
      routes:  # 路由
        - id: app-consumer-service #路由ID,没有固定要求,但是要保证唯一,建议配合服务名
          uri: lb://app-consumer-service  # 匹配提供服务的路由地址
          predicates: # 断言
            - Path=/demo/** # 断言,路径相匹配进行路由

异常处理

当触发限流后页面显示的是Blocked by Sentinel: ParamFlowException

这个原理是DefaultBlockRequestHandler;实现了BlockRequesthandler接口。

为了展示更加友好的限流提示, Sentinel支持自定义异常处理。

可以在GatewayCallbackManager注册回调进行定制:

  • setBlockHandler :注册函数用于实现自定义的逻辑处理被限流的请求,对应接口为 BlockRequestHandler 。默认实现为 DefaultBlockRequestHandler ,当被限流时会返回类似于下面的错误信息:Blocked by Sentinel: ParamFlowException 。

yml配置

spring:
  cloud:
    sentinel:
      scg:
        fallback:
          mode: response
          response-body: "{'code':'10099', 'message': '服务器忙,请稍后再试!'}"

注入bean

//注入Bean:自定义异常
@PostConstruct
public void initBlockHandler() {
    //com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler
    BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
        @Override
        public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
            Map<String, String> res = new HashMap<>();
            res.put("code", "429");
            if (throwable instanceof DegradeException) {
                res.put("msg", "降级规则被触发");
            } else if (throwable instanceof ParamFlowException) {
                res.put("msg", "热点规则被触发"); //热点规则比普通的流控规则更细粒度
            } else if (throwable instanceof SystemBlockException) {
                res.put("msg", "系统规则被触发");
            }
            return ServerResponse.status(HttpStatus.OK)
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(res));
        }
    };
    //com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager
    GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}

监控页面操作

1)将所有应用启动后,访问网关:http://localhost:8666/demo/testOpenFeignGet

2)在【Sentinel控制台-请求链路】菜单中,可以看到我们的请求,然后可以设置相关的流控和降级规则。

整合常见的坑

版本问题

问题版本:

<!-- spring-boot -->
<version>2.3.3.RELEASE</version>
<!-- Spring Cloud Hoxton.SR12 依赖 -->
<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
<!-- spring cloud alibaba 依赖 -->
<spring-cloud-alibaba.version>2.3.3.RELEASE</spring-cloud-alibaba.version>

修改后的版本:

<!-- spring-boot -->
<version>2.2.5.RELEASE</version>
<!-- Spring Cloud Hoxton.SR8 依赖 -->
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
<!-- spring cloud alibaba 依赖 -->
<spring-cloud-alibaba.version>2.2.4.RELEASE</spring-cloud-alibaba.version>

网上有人说:SpringBoot版本大于 2.2.0 以及小于 2.3.1才可以,超过这个版本Sentinel网关限流将不生效。

服务调用Dubbo

Dubbo代码演示

公共api模块:dubbo-api

在父项目中创建一个公共api模块。pom.xml指定<packaging>jar</packaging>

UserInfoService接口定义:

public interface UserInfoService {

    UserInfo getById(Long id);

    void saveUser(UserInfo userInfo);

    List<UserInfo> findList();
}

UserInfo实体定义:

public class UserInfo implements Serializable {
    private static final long serialVersionUID = 3532540898610423113L;

    private Long id;
    private String username;
    private String age;
    //省略get/set方法   
}

服务提供者:app-provider

依赖配置

<!--nacos注册中心的依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<!-- dubbo依赖 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>

<!--公共api, 服务提供者和服务消费者都需要引入-->
<dependency>
    <groupId>com.harvey</groupId>
    <artifactId>dubbo-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

服务实现类UserInfoServiceImpl

@Service //这里是org.apache.dubbo.config.annotation.Service
public class UserInfoServiceImpl implements UserInfoService {

    static Map<String, UserInfo> userInfoMap = new HashMap<>();

    static {
        for (long i = 10000; i < 10010; i++) {
            UserInfo userInfo = new UserInfo();
            userInfo.setId(i);
            userInfo.setUsername("user-" + i);
            userInfo.setAge(String.valueOf(i));
            userInfoMap.put(String.valueOf(i), userInfo);
        }
    }

    @Override
    public UserInfo getById(Long id) {
        return userInfoMap.get(String.valueOf(id));
    }

    @Override
    public void saveUser(UserInfo userInfo) {
        userInfoMap.put(String.valueOf(userInfo.getId()), userInfo);
    }

    @Override
    public List<UserInfo> findList() {
        return new ArrayList<>(userInfoMap.values());
    }
}

application.yml配置

spring:
  application:
    name: app-provider # 不加会提示无法处理${spring.application.name}的错误

dubbo:
  application:
    name: app-provider
    id: app-provider # 必须指定, 否则报异常:contains illegal character, only digit, letter, '-', '_' or '.' is legal.
  protocol:
    name: dubbo #Dubbo服务暴露的协议配置
    port: -1 #协议端口(-1 表示自增端口,从 20880 开始)
  registry:
    address: nacos://localhost:8848  #注册中心, 这里使用nacos
  scan:
    base-packages: com.harvey.provider.dubbo.service  #指定Dubbo服务实现类的扫描基准包

注:启动类上加不加@EnableDubbo注解都没有关系

服务消费者:app-consumer

依赖配置

<!--web 应用, 因为该应用有对外的restful接口,所以需要声明为web应用-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!--nacos注册中心的依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<!-- dubbo依赖 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>

<!--公共api, 服务提供者和服务消费者都需要引入-->
<dependency>
    <groupId>com.harvey</groupId>
    <artifactId>dubbo-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

服务调用UserController

@RequestMapping("/user")
@RestController
public class UserController {

    @Reference //org.apache.dubbo.config.annotation.Reference;
    private UserInfoService userInfoService;

    @GetMapping("getById")
    public UserInfo getById(Long id) {
        return userInfoService.getById(id);
    }

    @PostMapping("saveUser")
    public boolean saveUser(@RequestBody UserInfo userInfo) {
        userInfoService.saveUser(userInfo);
        return true;
    }

    @GetMapping("findList")
    public List<UserInfo> findList() {
        return userInfoService.findList();
    }
}

application.yml配置

server:
  port: 9999

spring:
  application:
    name: app-consumer

dubbo:
  application:
    name: app-consumer
    id: app-consumer # 必须指定, 否则报异常:contains illegal character, only digit, letter, '-', '_' or '.' is legal.
  protocol:
    name: dubbo #Dubbo服务暴露的协议配置
    port: -1 #协议端口(-1 表示自增端口,从 20880 开始)
  registry:
    address: nacos://localhost:8848  #注册中心, 这里使用nacos
  cloud:
    # 表示要订阅服务的服务名,可以配置'*'
    # 代表订阅所有服务,不推荐使用。若需订阅多应用,使用 "," 分割
    subscribed-services: app-provider
  consumer:
    check: false #关闭订阅服务是否启动的检查【检查时,没有服务提供者会报错】

启动服务提供者和服务提供者,就可以调用了。

Seata分布式事务

 

posted @ 2023-04-16 09:33  残城碎梦  阅读(242)  评论(0编辑  收藏  举报