项目优化之流量控制控件sentinel+Nacos

Sentinel与Hystrix区别

在这里插入图片描述
● 隔离策略:
信号量隔离,一种轻量级的隔离方式,通过控制并发请求的数量来保护系统资源的,适用于对性能要求较高的场景。
线程池隔离,通过为每个依赖服务分配独立的线程池来实现隔离,虽然提供了更好的隔离性,但会带来额外的线程切换开销。
● 熔断降级策略:
慢调用比例或异常比例的熔断策略,可以根据请求的响应时间或异常比例来决定是否触发熔断,灵活性较高。
失败比率的熔断策略。当请求的失败率达到一定阈值时,触发熔断机制。
两种策略相比较,前者是预防服务异常,后者是当服务产生异常的时候,才触发处理。
● 实时指标实现:
采用的是滑动窗口来实现,可以提供更精确的实时数据,帮助系统更好地做出流量控制和熔断决策。
hystrix,基于RxJava的滑动窗口来统计实现,方式相对复杂;
● 配置规则:
支持多种数据源,比如文件、Nacos、Zookeeper等动态配置形式;两者相比,sentinel灵活性和实时性更高。
● 扩展性:
sentinel,提供多个扩展点,供用户自定义;
hystrix,通过插件提供扩展,扩展性相对较弱;
● 基于注解的支持:
都支持注解,但hystrix功能有限;
● 限流:
Sentinel,支持基于 QPS 的限流(每秒查询率)、基于调用关系的限流(如根据调用链路进行限流)、支持慢启动(主要用于系统初始化或者流量突然增加的场景,过程中系统允许通过的流量会按照一定的斜率逐渐上升)和匀速排队模式(较为严格的流量控制方式,它主要用于处理突发流量,以固定的速率对请求进行处理),适用于不同的流量控制场景。
Hystrix, 限流功能有限,主要依赖线程池的大小来控制并发请求。
两种对比,Sentinel的限流方式更多;
● 流量整形:
Sentinel,支持流量整形,可以通过匀速排队模式来控制请求的通过速率,避免突发流量对系统的冲击。
hystrix,不支持流量整形。
● 系统的自我保护:
Sentinel,支持系统自适应保护,可以根据系统的实时负载和资源使用情况动态调整流量控制策略。
hystrix,不支持系统的自我保护。
● 控制台:
Sentinel,提供了开箱即用的控制台,支持规则配置、秒级监控、机器发现等功能,便于运维和监控。
hystrix,控制台功能相对简单;
● 常见框架的适配:
Sentine,支持多种框架,如 Servlet、Spring Cloud、Dubbo、gRPC 等,适配性较好。
hystrix,主要支持 Servlet 和 Spring Cloud Netflix,适配性相对较弱。

Sentinel概述

初识Sentinel

🍎 事前准备

雪崩问题:
	在微服务的调用链中,如果某个节点出现问题,会导致整个微服务链出现问题;
解决方式:
	1 超时处理,超过固定的时间,直接返回错误,不做无休止等待;
	2 舱壁模式,限定每个业务能使用的线程数,避免消耗整个tomcat资源,即线程隔离;
	3 熔断降级,断路器,统计业务异常比例,超过阈值,直接拦截该请求;
	4 流量控制,限制业务的qps,避免服务因流量的突增而故障; 预防故障出现;
是什么?
	Sentinel 是阿里中间件团队研发的面向分布式服务架构的轻量级、高可用流量控制组件,于今年2018年正式开源。
	主要以流量为切入点,从流量控制、熔断降级、系统负载保护、实时监控和控制台 等多个维度来帮助用户提升服务的稳定性。

sentinel 服务启动

参考链接:https://developer.aliyun.com/article/1476696

具体过程:
下载sentinel-dashboard-1.8.3.jar
所属文件夹打开cmd 执行java -jar sentinel-dashboard-1.8.3.jar 
	出现 Started DashboardApplication in 5.224 seconds (JVM running for 5.819) 启动成功;
访问:localhost:8080
	可以得到sentinel控制台界面,账号密码均是sentinel;

🍍 问题1:启动失败 java -jar

问题描述:
java.lang.IllegalStateException: Cannot load configuration class:
com.alibaba.csp.sentinel.dashboard.DashboardApplication
Caused by: java.lang.ExceptionInInitializerError: null
Caused by: org.springframework.cglib.core.CodeGenerationException: java.lang.reflect.InaccessibleObjectException-->Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @7bf58d89
解决:
将环境变量的path 将C:\Program Files\Common Files\Oracle\Java\javapath; 该地址去掉了;
参考博客是:https://blog.csdn.net/m0_37450089/article/details/121503987

🍍 问题2 修改sentinel基础信息

修改sentinel服务相关信息,基于java -jar运行命令进行修改
服务端口
-Dserver.port=8090
控制台账号
-Dserver.dashboard.auth.username=xxx
控制台密码
-Dserver.dashboard.auth.password=xxx
实现修改是:
java  + 上述内容 + -jar sentinel-dashboard-1.8.x.jar

其余配置信息,sentinel官方网站--wiki--控制台

sentinel整合springboot

🍇 引入依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2023.0.1.2</version>
</dependency>
🍇 application配置文件
spring:
# sentinel配置
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080  # Sentinel 控制台地址
        port: 8719                 # Sentinel 连接端口
  application:
    name: applicationName # 根据运行项目自定义名字
 
🍇 访问端点,触发监控
	访问服务的接口;

Sentinel总述

Sentinel是阿里开源的项目,提供了流量控制、热点流控、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。
Sentinel 的使用可以分为两个部分:
控制台(Dashboard):控制台主要负责管理和推送各种规则、集群限流分配管理、机器发现等。
核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 7 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。


Nacos概述

概念

定义:
	Nacos /:kəʊs/Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的配置管理、服务发现和服务治理的综合解决方案。
特性:
	服务发现与服务健康检查;动态配置管理;动态DNS管理;服务与元数据管理;

🍇 基础配置信息

🍅 定义:
配置 = 项目启动所需要的信息;
配置中心 = 管理所有微服务所需的配置信息;

🍅 思考:
配置中心-微服务之间配置信息如何获取?
	工作人员发布配置信息到配置中心;
	微服务通过网络协议获取到所需配置信息;
	如果配置信息发生更新,会给各个微服务发送更新通知;
	微服务得到更新通知后,会获取最新配置信息;

市面主流配置中心的对比

spring cloud config = 是spring cloud官方推出的配置中心;
apollo = 携程开源的配置中心;
Nacos = 阿里开源的配置中心;

在这里插入图片描述
在这里插入图片描述

nacos配置

代码实现

🍋 发布配置信息
在这里插入图片描述
在这里插入图片描述
🍋 通过java程序获取配置

配置nacos客户端api依赖
具体代码:
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.StringReader;
import java.util.Map;
import java.util.Properties;

@Slf4j
@Configuration
@ConfigurationProperties(prefix = "nacos")
public class NacosConfig {

    private String serverAddr ="127.0.0.1:8848"; //nacos 地址
    private String dataId ="nacos_simple_demo.yaml"; //Data Id
    private String group ="DEFAULT_GROUP"; //Group

    @Autowired
    private ConfigurableEnvironment environment;

    @PostConstruct
    public void init() throws NacosException, IOException {
        Properties properties = new Properties();
        properties.put("serverAddr", serverAddr);
        ConfigService configService = NacosFactory.createConfigService(properties);
        String content = configService.getConfig(dataId, group, 5000);

// 解析配置内容为 Properties
        Properties configProperties = new Properties();
        try {
            configProperties.load(new StringReader(content));
        } catch (IOException e) {
            log.error("Failed to load Nacos config", e);
            throw new RuntimeException(e);
        }
// 将配置注入到 Spring 环境中
        MapPropertySource propertySource = new MapPropertySource("nacosConfig", (Map) configProperties);
        environment.getPropertySources().addLast(propertySource);

        log.info("Nacos Config Loaded: " + content);
    }
}

配置文件

# 该内容写在bootstrap中
spring:
  cloud:
    nacos:
      config:
        # Nacos 服务器地址
        server-addr: localhost:8848
        # 若使用了命名空间,填写命名空间 ID
        namespace: cce62d68-51e5-4716-9968-a52bca7f8359
        name: nacos_simple_demo.yaml
        group: DEFAULT_GROUP
        file-extension: properties # 确保格式匹配
  application:
    # 应用名称
    name: test1Project
  main:
    allow-bean-definition-overriding: true
 # 解决:The bean 'nacosRefreshProperties', defined in class path resource [com/alibaba/cloud/nacos/NacosConfigAutoConfiguration.class], could not be registered. 

🍎 结论
# application.yml配置文件
Data ID 默认是 ${prefix}-${spring.profiles.active}.${file-extension}。
prefix = spring.application.name的值;
file-extension = spring.cloud.nacos.config.file-extension的值,只支持properties和yaml,默认是properties;
如果spring.profiles.active不存在,则会变成${prefix}.${file-extension} ;
data-id 这个key,也可以是name

多配置文件

🍇 配置形式1——nacos多个配置文件配置

spring:
  cloud:
    nacos:
      config:
        # Nacos 服务器地址
        server-addr: 127.0.0.1:8848
        group: dev
        namespace: xxxx
        shared-configs: # 共享配置
        // shared-configs必须这样写;
          - data-id: my-application.properties
            group: dev
            refresh: true
            file-extension: properties
        extension-configs: # 扩展配置
        // extension-configs 必须这样写;
          - data-id: flow_rules.json
            group: dev
            refresh: true
            file-extension: json
  application:
    # 应用名称
    name: my-application

🍇 配置形式2——sentinel多个流控文件配置

spring:
  cloud:
    nacos:
      config:
        # Nacos 服务器地址
        server-addr: 127.0.0.1:8848
        # Nacos 配置的 Group
        group: prod
        # 若使用了命名空间,填写命名空间 ID
        namespace: xxx
    sentinel:
      datasource:
        flow-rules: # 自定义数据源名称
          nacos:
            server-addr: localhost:8848
            dataId: sentinel-rules
            groupId: prod
            namespace: xxx
            rule-type: flow
        param-rules: # 热点限流
          nacos:
            server-addr: localhost:8848
            dataId: sentinel-paramRules
            groupId: prod
            namespace: xxx
            rule-type: param_flow
  application:
    # 应用名称
    name: application

服务发现

配置

// 通常在application.yml中
spring:
  application:
    name: application # 服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848  # Nacos 地址
        service: application  # 注册到 Nacos 的服务名
        namespace: xxx

启动

🍋 单机部署:

🍇 nacos下载;
	从git下载源码,然后mvn编译;
	下载xx.zip包=编译后的压缩包,直接解压运行;
🍇 进入nacos所在文件夹的bin目录下;D:\soft\nacos\nacos-server-2.5.0\nacos\bin
🍇 运行startup.cmd -m standalone 实现单机部署,默认端口是8848
🍇 访问nacos服务:http://localhost:8848/nacos 默认账密为nacos-nacos

🍇 测试nacos启动无误
	通过curl命令行工具,用HTTP协议测试;
	windows系统通过where curl得到所在目录,在c:/Windwos/System32 
		版本信息:
			curl 8.10.1 (Windows) libcurl/8.10.1 Schannel zlib/1.3 WinIDN
			Release-Date: 2024-09-18
	发布配置:
		curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=HelloWorld"
		执行后返回true,说明实现;
	获取配置:
		curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"
		执行后命令行输出配置信息:
			配置存在,则为 HelloWorld
			配置不存在,则为 config data not exist
🍇 服务关闭
	关闭当前cmd窗口

🍋 修改数据存储方式

单机部署,通常采用嵌入式数据库存储(Derby),不方便数据备份与查询,故此修改为mysql数据库;
数据库变更从Derby到mysql:
	下载mysql数据库,5.6+8以下;
	创建nacos_config数据库,执行数据库创建sql文件;
		现有博客都是1.x.x版本:(执行nacos-mysql.sql文件);本地测试采用2.5.0版本(执行mysql-schema.sql文件);
	配置application.properties文件;在目录:D:\soft\nacos\nacos-server-2.5.0\nacos\conf
		具体配置信息:
			# spring.datasource.platform=mysql
			# spring.sql.init.platform=mysql
			### Count of DB:
			# db.num=1
			### Connect URL of DB:
			# db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
			# db.user.0=nacos
			# db.password.0=nacos

🍋 思考:

Nacos Server 的配置数据是存在哪里呢?
	D:\soft\nacos\nacos-server-2.5.0\nacos\data\derby-data\文件夹中;
Nacos Server的数据源是Derby还是MySQL由运行模式决定;
	standalone=Derby;cluster=MySQL;
安装curl?
	参考博客:https://blog.csdn.net/g310773517/article/details/143906337

🍋 集群部署:

Nacos Server需要配置MySQL数据库;
需要采用Nginx转发到多个节点,最前面挂域名;

使用

流控

问题

🍐nacos配置不起效问题

描述:
	springboot项目中configurationProperties配置的nacos如下:
	private String serverAddr ="127.0.0.1:8848"; //nacos 地址
    private String dataId ="nacos_simple_demo.yaml"; //Data Id
    private String group ="DEFAULT_GROUP"; //Group

	但项目运行显示的nacos配置如下:
	springboot的启动日志是[fixed-localhost_8848] [add-listener] ok, tenant=, dataId=test1Project.properties, group=DEFAULT_GROUP, cnt=1
	两者不符合;
分析:
	如果项目中引入了 spring-cloud-starter-alibaba-nacos-config,它会自动从 bootstrap.yml 或环境变量中读取 dataId。
	如果 bootstrap.yml 中定义了 spring.cloud.nacos.config.name 或 spring.application.name,这些值会被用作默认的 dataId。
解决:
	将项目中的spring.application.name注释掉;

🍐 nacosRefreshProperties could not be registered

// 在application.yml文件配置如下内容即可解决;但本质问题仍旧没有解决;
spring:
  # 后面的bean会覆盖前面相同名称的bean
  main:
    allow-bean-definition-overriding: true

限流规则+控制台

控制台涉及到:实时监控、簇点链路、流控规则、熔断规则、热点规则、系统规则、授权规则、集群流控、机器列表等;

实时监控

簇点链路

簇点链路:
	项目内的调用链路,即访问controller--service--mapper,这就是调用链路;
	而sentinel监控不会监控所有,而只会监控springMVC的每一个端点(Endpoint),即controller方法;
	请求链路的根是:所有springmvc项目的根是 sentinel-spring-web-context
	而每一个controller都在该根下;

流控规则

熔断规则

热点规则限流

概述

基础参数限流

🍇依赖导入
🍇 基础sentinel配置
上述两个过程均采用sentinel整合springboot的操作实现

🍇sentinel配置

import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowItem;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


@Configuration // 配置类
public class SentinelConfig {

    private static String LimitApikey = "a";
    private static String LimitSource = "hello";

    @PostConstruct
    public void initFlowRules() {
        initFlowRulesApikey();
    }
	// 测试sentinel启动正常
    public void initFlowRulesTest(){
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        rule.setResource("hello"); // 资源名称
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // 等级
        rule.setCount(10); // 阈值 限制 QPS 为 1
        rule.setLimitApp("default"); // 限制请求来源 表示这个规则对所有来源的请求都有效。
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
        System.out.println("initFlowRulesTest end");
    }

	// 测试根据请求参数可以直接限制流控
    public void initFlowRulesApikey(){
        List<ParamFlowRule> rules = new ArrayList<>();
        ParamFlowRule a = new ParamFlowRule(SentinelConfig.LimitSource)
                .setCount(Double.MAX_VALUE) // 允许访问的数量
                .setParamIdx(0) // 假设apikey是URL参数列表中的第一个参数
                .setGrade(RuleConstant.FLOW_GRADE_QPS)
                .setDurationInSec(1) // 持续时间,单位为秒
                .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT) // 即当流量达到阈值时,直接拒绝多余的请求。
                .setParamFlowItemList(
                        Collections.singletonList(new ParamFlowItem()
                                .setObject("a")
                                .setCount(0))
                );
        rules.add(a);
        ParamFlowRuleManager.loadRules(rules);
    }
}
// 备注:
// rule.setCount,是针对资源 "SentinelConfig.LimitSource" 的所有参数值的默认限流阈值设置。也就是说,当一个参数值没有在 ParamFlowItem 里被单独配置限流规则时,就会使用这个默认值。

🍇实现方法代码

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.unicom.opn.api.core.dto.base.BaseParams;
import com.unicom.opn.api.core.dto.base.ResultDto;
import com.unicom.opn.api.core.enums.ResultCodeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;

@Slf4j
@Service
public class TestHandler {
    @SentinelResource(value = "hello",blockHandler = "handleBlockForHello")
    // 设置资源
    public ResultDto hello(String apikey) {
        return ResultDto.result(ResultCodeEnum.SUCCESS,"Hello,Sentinel!");
    }
    public ResultDto handleBlockForHello(String apikey, BlockException e) {
        log.error("sentinel block",e);
        return ResultDto.result(ResultCodeEnum.ApikeyError);
    }
}

🍇调用接口

import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.unicom.opn.api.core.annotation.RequestJson;
import com.unicom.opn.api.core.controller.core.TestHandler;
import com.unicom.opn.api.core.dto.base.BaseParams;
import com.unicom.opn.api.core.dto.base.ResultDto;
import com.unicom.opn.api.core.dto.req.PrefetchReq;
import com.unicom.opn.api.core.dto.req.PrefetchWechatReq;
import com.unicom.opn.api.core.enums.BusinessEnum;
import com.unicom.opn.api.core.enums.ResultCodeEnum;
import com.unicom.opn.api.core.trace.ServiceId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.alibaba.csp.sentinel.Entry;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

@RestController
public class TestController {
    @Autowired
    private TestHandler testHandler;
    @RequestMapping("/hello")
    @ServiceId(BusinessEnum.TEST_XHJ)
    public ResultDto hello(@RequestParam("apikey") String apikey){
        return  testHandler.hello(apikey);
    }
}

🍎总结问题1

问题描述:
	上述相同代码,只更换依赖就不能正常拦截请求了;(暂时未探索出来是什么问题)
可以正常使用的是:
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2023.0.1.2</version>
</dependency>

不可以正常使用的:
<dependency>
	<groupId>com.alibaba.csp</groupId>
	<artifactId>sentinel-core</artifactId>
	<version>1.8.6</version>
</dependency>
<dependency>
	<groupId>com.alibaba.csp</groupId>
	<artifactId>sentinel-parameter-flow-control</artifactId>
	<version>1.8.6</version>
</dependency>

🍎总结问题2:

情景1:
通过postman发起的请求:http://127.0.0.1:8080/hello?apikey=b
多次请求,返回结果有时成功,有时失败;
已知拦截规则是apikey=a拦截;
出现频次,请求5次就会出现异常拦截的情况;
解决:
修改ParamFlowRule的setDurationInSec,设置持续时间这个属性,将原来的1s改成10s 可以解决该问题,但具体该问题的上线是多少次请求以及什么频次内会出现上述问题 还未进行测试;

情景2:
修改ParamFlowRule的setDurationInSec,设置持续时间这个属性,将原来的1s改成10s
测试模拟 1s 访问1500次 即循环1500次 每次间隔0.66ms
解决:
已完成测试,没有出现异常拦截的情况;

对象参数限流——新增设限参数

🍇 sentinel版本
本地运行sentinel为1.8.3版本;
idea中使用spring-cloud-starter-alibaba-sentinel该依赖;

🍇 依赖导入以及application配置都保持和基础参数限流一致;

🍇 xxxController方法参数输入为对象
@RequestMapping("/hello1")
    @ServiceId(BusinessEnum.TEST_XHJ)
    public ResultDto hello1(TestObject testObject){
        return  testHandler.hello1(testObject,testObject.getApikey());
    } // 需要限制的参数 testObject.getApikey()

🍇 修改xxxhandler方法参数,将需要限制的对象属性作为参数输入
@SentinelResource(value = "test1",blockHandler = "handleBlockForHello1")
public ResultDto hello1(TestObject testObject,String apikey) {
    return ResultDto.result(ResultCodeEnum.SUCCESS,"Hello,Sentinel!");
}

public ResultDto handleBlockForHello1(TestObject testObject,String apikey,BlockException e) {
    log.error("sentinel block1 :",e);
    return ResultDto.result(ResultCodeEnum.ApikeyError);
}

🍇 sentinelConfig配置
public void limitApikey(){
        List<ParamFlowRule> rules = new ArrayList<>();
        ParamFlowRule a = new ParamFlowRule("test1")
                .setCount(Double.MAX_VALUE) // 允许访问的数量
                .setParamIdx(1)
                .setGrade(RuleConstant.FLOW_GRADE_QPS)
                .setDurationInSec(10) // 持续时间,单位为秒
                .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT) //即当流量达到阈值时,直接拒绝多余的请求。
                .setParamFlowItemList(
                        Collections.singletonList(new ParamFlowItem()
                                .setObject("a")
                                .setCount(0))
                );
        rules.add(a);
        ParamFlowRuleManager.loadRules(rules);
    }
问题1:
假设对象的参数apikey不存在时,这个会报什么错误?
可以给对象参数的属性添加注解@NotBlack(message = "")
这样就会被代码直接拦截;

对象参数限流——修改toString

参考内容:
https://blog.csdn.net/qq_28618027/article/details/131476599
https://cloud.tencent.com/developer/article/1754662
该方式最终没有实现。
博客跟学:
https://cloud.tencent.com/developer/article/1754662

本地版本是:Maven:com.alibaba.csp:sentinel-parameter-flow-control:1.8.6
checkFlow方法 = com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowSlot#checkFlow
passCheck方法 = com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowChecker#passCheck
该版本的代码中,passCheck代码没有针对 博客中的“根据索引获取方法参数的值” 以及 “针对参数为对象的情况,通过实现ParamFlowArgument重写paramFlowKey”
passClusterCheck = QPS限流并集群流控
passLocalCheck = 非集群流控

🍎 报错情形1:

依赖配置文件:
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-parameter-flow-control</artifactId>
    <version>1.8.6</version>
</dependency>
    <!-- 添加 Sentinel 核心依赖 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.8.6</version> <!-- 可以根据需要选择合适的版本 -->
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-webmvc-adapter</artifactId>
    <version>1.8.6</version>
</dependency>

将上述配置文件的版本号改成1.8.3 会出现如下问题:
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sentinelConfig': Invocation of init method failed; 
nested exception is java.lang.NoClassDefFoundError: com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRule    
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sentinelConfig': Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRule   Caused by: java.lang.NoClassDefFoundError: com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRule   Caused by: java.lang.ClassNotFoundException: com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule
1.8.6版本也出现了上述问题;
已知 本地运行的sentinel版本是1.8.3

解决:
修改后的配置文件:
<dependency>
   <groupId>com.alibaba.cloud</groupId>
   <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
   <version>2023.0.1.2</version>
</dependency>
<dependency>
   <groupId>com.alibaba.csp</groupId>
   <artifactId>sentinel-parameter-flow-control</artifactId>
   <version>1.8.6</version>
</dependency>
<!--              添加 Sentinel 核心依赖 -->
<dependency>
   <groupId>com.alibaba.csp</groupId>
   <artifactId>sentinel-core</artifactId>
   <version>1.8.6</version> <!-- 可以根据需要选择合适的版本 -->
</dependency>

🍎 报错内容2:

在testObject对象中,实现ParamFlowArgument接口,重写paramFlowKey方法;
报错:
org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'testController': Lookup method resolution failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class 
Caused by: java.lang.IllegalStateException: Failed to introspect Class [com.unicom.opn.api.core.controller.TestController] 
Caused by: java.lang.NoClassDefFoundError: com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowArgument
说的是ParamFlowArgument没有找到,是不是因为运行的sentinel是1.8.3 而使用的是1.8.6 导致的问题? 不是该问题

该问题暂时没有想法去修改;

根据sentinel官网文档的说明,发现paramFlowRule的paramFlowItemList只支持基本数据类型和字符串类型,是不支持对象数据类型的。
在这里插入图片描述

集群流控

概述

🍇流量控制(flow control):
	其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
	原理:
    	SentinelBucket(桶)为单位记录一个时间窗口内的请求总数、异常总数、总耗时等指标数据。
    	一个Bucket可以是记录一秒内的数据,也可以是10毫秒内的数据,称这个时间窗口为Bucket的统计单位,由使用者自定义。
    	单机模式下:如果设置某接口的 QPS 阈值为 100,每个实例会独立统计自己的 QPS,当某个实例的 QPS 超过 100 时,该实例会触发限流。
各实例之间互不影响 ,因为它们的统计桶是隔离的。
		集群模式下:所有实例的流量统计会通过一个中心化存储(如 Redis)共享同一个统计桶 。例如:如果设置某接口的全局 QPS 阈值为 100,所有实例的请求会汇总到中心化的统计桶中,总 QPS 超过 100 时,所有实例都会触发限流。
🍇集群流控:
	用来统计数据的称为 Sentinel 的 token server,其他的实例作为 Sentinel 的 token client 会向 token server 去请求 token,如果能获取到 token,则说明当前的 qps 还未达到总的阈值,否则就说明已经达到集群的总阈值,当前实例需要被 block;
	两种身份:
		token client:集群流控客户端,用于向所属 token server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。
		token server:即集群流控服务端,处理来自 token client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。
🍇实现方案:
	支持限流规则和热点规则两种规则,并支持两种形式的阈值计算方式:
	集群总体模式:即限制整个集群内的某个资源的总体 qps 不超过此阈值。
	单机均摊模式:单机均摊模式下配置的阈值等同于单机能够承受的限额,token server 会根据连接数来计算总的阈值(比如独立模式下有 3 个 client 连接到了 token server,然后配的单机均摊阈值为 10,则计算出的集群总量就为 30),按照计算出的总的阈值来进行限制。这种方式根据当前的连接数实时计算总的阈值,对于机器经常进行变更的环境非常适合。

🍇 token server部署方式:
	独立部署:单独启动一个token server来接收所有token client的请求;
		存在问题 = 当token server服务瘫痪,则其余的token client会自动退化成本地流控的模式;
	嵌入式部署:作为内置的token server和服务在同一进程中启动。集群中的各个实例都是对等的,token server和token client可以随时变换身份。
		Sentinel 为我们提供了一个 api 来进行 token server 与 token client 的切换:
		http://<ip>:<port>/setClusterMode?mode=<xxx> 
		其中mode值包括:0=client;1=server;-1=关闭;
🍇 结合nacos实现部署:
	原因:采用sentinel控制台配置token server和token client以及流控规则,会存在流控规则不能持久化、server和client配置丢失的问题;

独立部署

嵌入式部署

server相关依赖;

client相关依赖;


sentinel+Nacos流控

项目配置

🍉 springboot项目配置

spring:
	cloud:
	  sentinel:
	    transport:
	      port: 8719
	      dashboard: localhost:8080
	    datasource:
	      ds:
	        nacos:
	          server-addr: localhost:8848
	          data-id: xxx_xxx_sentinel  # 这里不能使用-如果需要划分得采用_
	          group-id: DEFAULT_GROUP
	          refresh-enabled: true
	          rule-type: flow
	          data-type: json
	application:
  		name: opn-api

🍇 其中rule-type的值:
flow=流量控制规则 param-flow=热点参数限流规则;degrade=降级规则;system=系统规则;authority=授权规则;
🍇 其中datasource下一级ds需要根据不同的xx规则,进行再区分;
datasource:
	flow:
		nacos:
	param-flow:
		nacos:

🍎 注意:
sentinel按照上述配置,数据库的配置规则改成nacos规则,则代码中编写的sentinel规则就会失效,且在sentinel控制台不显示;
对于接口类型的资源,在配置规则的时候,需要写成全url,比如“/hello” 不能是“hello”

🍉 依赖配置

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2023.0.1.2</version>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.8.8</version>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-parameter-flow-control</artifactId>
    <version>1.8.8</version>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-api-gateway-adapter-common</artifactId>
    <version>1.8.8</version>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    <version>2023.0.1.2</version> 
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <version>1.8.8</version> 
</dependency>

🍇 项目1可以运行但项目2不能运行

🍟报错信息1Exception in thread "main" java.lang.IllegalArgumentException: Cannot instantiate interface org.springframework.context.ApplicationContextInitializer : com.alibaba.cloud.sentinel.custom.context.SentinelApplicationContextInitializer
Caused by: java.lang.UnsupportedClassVersionError: com/alibaba/cloud/sentinel/custom/context/SentinelApplicationContextInitializer has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 52.0

将spring-cloud-starter-alibaba-nacos-config 和 spring-cloud-starter-alibaba-sentinel版本号
从2023.0.1.2 改成 2.2.6.RELEASE 又出现下列报错;

🍟 报错信息2The bean 'nacosRefreshProperties', defined in class path resource [com/alibaba/cloud/nacos/NacosConfigAutoConfiguration.class], could not be registered. A bean with that name has already been defined in URL [jar:file:/D:/soft/maven/responsitory/com/alibaba/cloud/spring-cloud-starter-alibaba-nacos-config/2.2.6.RELEASE/spring-cloud-starter-alibaba-nacos-config-2.2.6.RELEASE.jar!/com/alibaba/cloud/nacos/refresh/NacosRefreshProperties.class] and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
改成2021.1

可运行配置:

<dependencies>
     <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>com.dianping.cat</groupId>
         <artifactId>cat-client</artifactId>
         <version>3.1.0</version>
     </dependency>
     <dependency>
         <groupId>com.alibaba.cloud</groupId>
         <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
         <version>2021.1</version>
     </dependency>
     <dependency>
         <groupId>com.alibaba.csp</groupId>
         <artifactId>sentinel-spring-webmvc-adapter</artifactId>
         <version>1.8.3</version> <!-- 确保与 spring-cloud-starter-alibaba-sentinel 兼容 -->
     </dependency>
     <dependency>
         <groupId>com.alibaba.csp</groupId>
         <artifactId>sentinel-core</artifactId>
         <version>1.8.0</version>
     </dependency>
     <dependency>
         <groupId>com.alibaba.csp</groupId>
         <artifactId>sentinel-parameter-flow-control</artifactId>
         <version>1.8.0</version>
     </dependency>
     <dependency>
         <groupId>com.alibaba.csp</groupId>
         <artifactId>sentinel-api-gateway-adapter-common</artifactId>
         <version>1.8.0</version>
     </dependency>
     <dependency>
         <groupId>com.alibaba.csp</groupId>
         <artifactId>sentinel-transport-simple-http</artifactId>
         <version>1.8.0</version>
     </dependency> 
     <!--     Sentinel+Nacos配置   -->
     <dependency>
         <groupId>com.alibaba.cloud</groupId>
         <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
         <version>2021.1</version>
     </dependency>
     <dependency>
         <groupId>com.alibaba.csp</groupId>
         <artifactId>sentinel-datasource-nacos</artifactId>
         <version>1.8.0</version>
     </dependency>
     <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <scope>test</scope>
     </dependency>
 </dependencies>

项目运行

🍇 初始化一个springboot基础项目;
	参考博客:
		https://blog.csdn.net/yuran06/article/details/122012790
		https://blog.csdn.net/qq_33210338/article/details/118961132
	nacos和sentinel规则互相同步的实现:https://blog.csdn.net/zhuocailing3390/article/details/138139180
🍇 springboot项目配置sentinel+nacos  在application.yml中
	在前文中描述得到。

🍇 sentinel运行
	cmd到sentinel所在目录;
	运行命令:java -jar sentinel-dashboard-x.x.x.jar
	或者:Java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-x.x.x.jar

🍇 nacos运行
参考:https://blog.csdn.net/crazymakercircle/article/details/143141533
定义:是Naming and configuration service的简写,是阿里巴巴开发的一个开源分布式服务发现和配置管理平台;
	nacos下载:https://github.com/alibaba/nacos/releases
	解压后,到目录D:\soft\nacos\nacos-server-2.5.0\nacos\bin中
	在cmd中,输入startup.cmd -m standalone
	访问nacos控制台:http://localhost:8848/nacos

🍇 运行springboot项目即可

实战案例

以请求路径限流

🍇 测试controller类
@RestController
public class TestController {
    @GetMapping("/hello")
    @SentinelResource("helloResource")
    // @SentinelResource 注解将 /hello 接口标记为一个受保护的资源,资源名为 helloResource。
    public String hello(){
        return "Hello,Sentinel!";
    }
}

🍇 Sentinel 配置类
@Configuration
public class SentinelConfig {
    @PostConstruct
    public void initFlowRules() {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        rule.setResource("helloResource");
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(1); // 限制 QPS 为 1
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
}

请求路径限流-修改返回结果

🍇 新增类实现implements BlockExceptionHandler该接口;

@Component
public class CustomBlockExceptionHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws IOException {
        // 设置响应状态码为 200
        response.setStatus(HttpServletResponse.SC_OK);
        // 设置响应内容类型为 JSON
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");

        ResponseDto result = new ResponseDto(ResultCodeEnum.SeqOverError);
        JSONObject jsonData = new JSONObject(result);
        // 将 JSON 数据写入响应
        try (PrintWriter out = response.getWriter()) {
            out.print(jsonData);
            out.flush();
        }
    }
}

🍇 修改依赖包版本号
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.8.6</version>
</dependency>

最后

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值