自学内容网 自学内容网

探索 Sentinel 服务容错

Sentinel 是阿里巴巴开源的一款高可用防护组件,主要用于分布式系统中的流量控制、熔断降级和系统负载保护。它在 Java 微服务架构中扮演着重要的角色,帮助开发者确保系统的稳定性和可靠性。

以下是 Sentinel 的一些关键特性:

  1. 流量控制(Flow Control):通过对请求进行限流,防止系统被突发流量压垮。支持多种限流策略,如基于 QPS(每秒查询率)、并发线程数等。

  2. 熔断降级(Circuit Breaking):当某个资源的错误率或响应时间超过设定的阈值时,自动触发熔断,暂时阻止对该资源的访问,以避免故障蔓延。熔断后可以设置自动恢复机制。

  3. 系统负载保护(System Adaptive Protection):根据系统的整体负载情况(如 CPU 使用率、内存使用率等),动态调整限流策略,确保系统在高负载情况下仍能平稳运行。

  4. 实时监控(Real-time Monitoring):提供丰富的监控功能,可以实时查看系统的流量、响应时间、错误率等指标,帮助开发者及时发现和解决问题。

  5. 规则管理(Rule Management):支持通过控制台、API 等多种方式灵活配置和管理限流、熔断等规则,方便运维人员进行动态调整。

  6. 扩展性(Extensibility):提供了丰富的 SPI(Service Provider Interface),允许开发者根据自身需求进行扩展和定制。

Sentinel 通常与 Spring Cloud 和 Dubbo 等微服务框架集成使用,通过简单的配置即可实现对微服务的全面保护。在实际应用中,Sentinel 可以帮助开发者提高系统的健壮性,减少因流量激增或服务故障导致的系统崩溃风险。

参考 Sentinel-Wiki

微服务集成 Sentinel

        <!-- 容错组件sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

安装 Sentinel 控制台

下载 jar 包,解压,直接使用jar命令启动项目:

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar

yml 配置

在里面加入有关控制台的配置:

完整配置:

server:
  port: 8071
spring:
  application:
    name: service-admin
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql:///dgut?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=true
    type: com.alibaba.druid.pool.DruidDataSource
    username: root
    password: root
    druid: # 阿里连接池配置
      # 下面为连接池的补充设置,应用到上面所有数据源中
      # 初始化大小,最小,最大
      initial-size: 5
      min-idle: 5
      max-active: 20
      # 配置获取连接等待超时的时间
      max-wait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      time-between-eviction-runs-millis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      min-evictable-idle-time-millis: 300000
      validation-query: SELECT 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      # 打开PSCache,并且指定每个连接上PSCache的大小
      pool-prepared-statements: true
      #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
      max-pool-prepared-statement-per-connection-size: 20
      filters: stat,wall,log4j
      use-global-data-source-stat: true
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
      connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        port: 9999 # 跟控制台交流的端口,随意指定一个未使用的端口
        dashboard: localhost:8080 # 控制台服务端口

访问 Sentinel 控制台

访问 localhost:8080 进入控制台 ( 默认用户名密码是 sentinel/sentinel ):

测试

sentinel 默认懒加载为 false ,即需要访问一次服务后才能在控制台看到该服务,当值为 true 时,不需通过访问一次服务才能显示该服务。

spring.cloud.sentinel.eager = true

访问测试接口 localhost:8091/msg,在 sentinel 控制台可以看到服务:

对测试api新增流量控制,最多每秒3次请求:

测试效果:符合预期

 自定义异常返回

自定义异常返回用于区分异常原因是因为限流还是降级等,在config包下新建ExceptionHandlerPage类实现BlockExceptionHandler接口:

package com.yushanma.config;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//异常处理页面
@Component
public class ExceptionHandlerPage implements BlockExceptionHandler {
//BlockException 异常接口,包含Sentinel的五个异常
// FlowException 限流异常
// DegradeException 降级异常
// ParamFlowException 参数限流异常
// AuthorityException 授权异常
// SystemBlockException 系统负载异常
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
        httpServletResponse.setContentType("application/json;charset=utf-8");
        ResponseData data = null;
        if (e instanceof FlowException) {
            data = new ResponseData(-1, "接口被限流了...");
        } else if (e instanceof DegradeException) {
            data = new ResponseData(-2, "接口被降级了...");
        }
        httpServletResponse.getWriter().write(JSON.toJSONString(data));
    }
}

@Data
@AllArgsConstructor//全参构造
@NoArgsConstructor//无参构造
class ResponseData {
private int code;
    private String message;
}

新增流控和降级规则,测试:

 

Sentinel规则持久化

在 config 包下新建 FilePersistence 类实现 InitFunc 接口:

package com.yushanma.config;

import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.*;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
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.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.springframework.beans.factory.annotation.Value;

import java.io.File;
import java.io.IOException;
import java.util.List;

public class FilePersistence implements InitFunc {
@Value("spring.application:name")
private String appcationName;

    @Override
    public void init() throws Exception {
        String ruleDir = System.getProperty("user.home") + "/sentinelrules/"+appcationName;
        String flowRulePath = ruleDir + "/flow-rule.json";
        String degradeRulePath = ruleDir + "/degrade-rule.json";
        String systemRulePath = ruleDir + "/system-rule.json";
        String authorityRulePath = ruleDir + "/authority-rule.json";
        String paramFlowRulePath = ruleDir + "/param-flow-rule.json";
        this.mkdirIfNotExits(ruleDir);
        this.createFileIfNotExits(flowRulePath);
        this.createFileIfNotExits(degradeRulePath);
        this.createFileIfNotExits(systemRulePath);
        this.createFileIfNotExits(authorityRulePath);
        this.createFileIfNotExits(paramFlowRulePath);
// 流控规则
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new
                FileRefreshableDataSource<>(
                flowRulePath,
                flowRuleListParser
        );
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());
        WritableDataSource<List<FlowRule>> flowRuleWDS = new
                FileWritableDataSource<>(
                flowRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
// 降级规则
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new
                FileRefreshableDataSource<>(
                degradeRulePath,
                degradeRuleListParser
        );
        DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
        WritableDataSource<List<DegradeRule>> degradeRuleWDS = new
                FileWritableDataSource<>(
                degradeRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
// 系统规则
        ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new
                FileRefreshableDataSource<>(
                systemRulePath,
                systemRuleListParser
        );
        SystemRuleManager.register2Property(systemRuleRDS.getProperty());
        WritableDataSource<List<SystemRule>> systemRuleWDS = new
                FileWritableDataSource<>(
                systemRulePath,
                this::encodeJson);
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
// 授权规则
        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new
                FileRefreshableDataSource<>(
                authorityRulePath,
                authorityRuleListParser
        );
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new
                FileWritableDataSource<>(
                authorityRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
// 热点参数规则
        ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new
                FileRefreshableDataSource<>(
                paramFlowRulePath,
                paramFlowRuleListParser
        );
        ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new
                FileWritableDataSource<>(
                paramFlowRulePath,
                this::encodeJson
        );
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
    }
private Converter<String, List<FlowRule>> flowRuleListParser = source ->
            JSON.parseObject(
                    source,
                    new TypeReference<List<FlowRule>>() {
                    }
            );
    private Converter<String, List<DegradeRule>> degradeRuleListParser = source
            -> JSON.parseObject(
            source,
            new TypeReference<List<DegradeRule>>() {
            }
    );
    private Converter<String, List<SystemRule>> systemRuleListParser = source ->
            JSON.parseObject(
                    source,new TypeReference<List<SystemRule>>() {
                    }
            );
    private Converter<String, List<AuthorityRule>> authorityRuleListParser =
            source -> JSON.parseObject(
                    source,
                    new TypeReference<List<AuthorityRule>>() {
                    }
            );
    private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser =
            source -> JSON.parseObject(
                    source,
                    new TypeReference<List<ParamFlowRule>>() {
                    }
            );
    private void mkdirIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }
private void createFileIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.createNewFile();
        }
    }
private <T> String encodeJson(T t) {
return JSON.toJSONString(t);
    }

}

在 resources 下创建配置目录 META-INF/services ,然后添加文件com.alibaba.csp.sentinel.init.InitFunc,在该文件中添加配置类的全路径:

com.yushanma.config.FilePersistence

测试:配置好流控后重新开启服务,规则依然生效


原文地址:https://blog.csdn.net/weixin_47560078/article/details/118727349

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!