3.sentinel-服务容错

jmeter-接口测试

下载

1

jmeter.bat 启动文件
jmeter.properties 配置文件

修改配置文件改为中文

2

接口测试

3
4
5

6

修改tomcat最大线程数

server:
  port: 8091
  tomcat:
    max-threads: 10

启动测试

7
8

Sentinel服务熔断

依赖注入

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

监控平台jar下载

在这里插入图片描述

启动命令

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

访问链接 http://localhost:8080/

账号密码默认是sentinel

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

修改配置

spring:
  cloud:
   sentinel:
      transport:
        port: 9999  #与控制台交流的端口,随意指定一个未使用的端口即可
        dashboard: localhost:8080 #指定控制台服务的地址

由于sentinel控制台是软加载,需要访问接口后才会有数据展示

在这里插入图片描述

控制台实现接口流控

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

sentinel规则

流控规则

1
2

3

直接流控模式

  • 直接流控模式是最简单的模式,当指定的接口达到限流条件时开启限流

关联流控模式

  • 关联流控模式指的是,当指定接口关联的接口达到限流条件时,开启对指定接口开启限流。
  • 流控模式中的关联,可以使得其它的请求服务达到阈值,本服务进行流控。

链路

4

第一种方式
<spring-cloud-alibaba.version>2.1.1.RELEASE</spring-cloud-alibaba.version>
@Override
//定义资源,value指定资源名称
@SentinelResource("message")
public String message() {
    return "message";
}
@GetMapping("/message1")
public String message1() {
    return orderService.message();
}

@GetMapping("/message2")
public String message2() {
    return orderService.message();
}

修改配置 添加filter.enabled=false

spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        port: 9999  #与控制台交流的端口,随意指定一个未使用的端口即可
        dashboard: localhost:8080 #指定控制台服务的地址
      filter:
        enabled: false

添加配置文件

@Configuration
public class FilterContextConfig {
    @Bean
    public FilterRegistrationBean sentinelFilterRegistration(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new CommonFilter());
        registrationBean.addUrlPatterns("/*");
        //入口资源关闭聚合
        registrationBean.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY,"false");
        registrationBean.setName("sentinelFilter");
        registrationBean.setOrder(1);
        return registrationBean;
    }
}
第二种方式
<spring-cloud-alibaba.version>2.1.3.RELEASE</spring-cloud-alibaba.version>

修改配置

server:
  port: 8091
spring:
  application:
    name: service-order
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        port: 9999  #与控制台交流的端口,随意指定一个未使用的端口即可
        dashboard: localhost:8080 #指定控制台服务的地址
      web-context-unify: false

5
6

流控效果

7

降级规则

8
9
10

热点规则

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

@GetMapping("/message3")
//必须使用该注解,否则热点规则不起作用
@SentinelResource("message3")
public String message3(String name, Integer age) {
    return "message3" + name + age;
}

限制第二个参数age,当age为15时提升阈值为1000

11
12
13

授权规则

14

@Component
public class RequestOriginParserDefinition implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        String serviceName = httpServletRequest.getParameter("serviceName");
        return serviceName;
    }
}

15
16

17

系统规则

18

自定义异常返回

@Component
public class ExceptionHandlerPage implements UrlBlockHandler {
    @Override
    public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
        httpServletResponse.setContentType("application/json;charset=utf-8");
        ResponseData responseData = null;
        if (e instanceof FlowException) {
            responseData = new ResponseData(-1, "接口被限流了");
        } else if (e instanceof DegradeException) {
            responseData = new ResponseData(-2, "接口被降级了");
        } else if (e instanceof ParamFlowException) {
            responseData = new ResponseData(-3, "接口被参数限流");
        } else if (e instanceof AuthorityException) {
            responseData = new ResponseData(-4, "接口授权异常");
        } else if (e instanceof SystemBlockException) {
            responseData = new ResponseData(-5, "系统负载异常");
        } else {
            responseData = new ResponseData(-6, e.toString());
        }
        httpServletResponse.getWriter().write(JSON.toJSONString(responseData));
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
class ResponseData {
    private int code;
    private String message;
}

@SentinelResource

使用方法

/**
 * value            定义一个资源
 * blockHandler     定义当资源内部发生了BlockException应该进入的方法
 * fallback         定义当资源内部发生了Throwable应该进入的方法
 */
@SentinelResource(
        value = "message2",
        blockHandler = "blockHandler",
        fallback = "fallback"
)
public String message2(String message) {
    return message;
}


/**
 * 当前方法的返回值和参数要和原方法一致
 * 允许在参数列表最后加入一个BlockException参数,用来接收原方法中发生的异常
 */
public String blockHandler(String message, BlockException e){
    return "blockException";
}

public String fallback(String message, Throwable e){
    return "throwable";
}

使用类

@SentinelResource(
        value = "message2",
        blockHandlerClass = OrderServiceImplBlockHandler.class,
        blockHandler = "blockHandler",
        fallbackClass = OrderServiceImplFallback.class,
        fallback = "fallback"
)
public String message2(String message) {
    return message;
}
public class OrderServiceImplBlockHandler {
    public static String blockHandler(String message, BlockException e){
        return "blockException";
    }
}
public class OrderServiceImplFallback {
    public static String fallback(String message, Throwable e){
        return "throwable";
    }
}

规则持久化

在这里插入图片描述

DataSource扩展常见的实现方式有:

  • 拉模式:客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是RDBMS、文件,甚至是VCS等。这样做的方式是简单,缺点是无法及时获取变更。
  • 推模式:规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper
    等配置中心。这种方式有更好的实时性和一致性保证。

Sentinel目前支持以下数据源扩展:

  • Pull-based: 文件、Consul
  • Push-based: ZooKeeper, Redis, Nacos, Apollo, etcd

Pull 模式

原理:

  • 扩展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件
    等。
  • pull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。使用时需要在客户端注册数据源:将对应的读数据源注册至对应的
    RuleManager,将写数据源注册至 transport 的 WritableDataSourceRegistry 中。
    在这里插入图片描述

导入依赖

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-extension</artifactId>
</dependency>

添加文件

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 java.io.File;
import java.io.IOException;
import java.util.List;

/**
 * FileDataSourceInit for : 自定义Sentinel存储文件数据源加载类
 */
public class FileDataSourceInit implements InitFunc {
    @Override
    public void init() throws Exception {
        // TIPS: 如果你对这个路径不喜欢,可修改为你喜欢的路径
        String ruleDir = System.getProperty("user.home") + "/sentinel/rules";
        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 hotParamFlowRulePath = ruleDir + "/param-flow-rule.json";

        this.mkdirIfNotExits(ruleDir);
        this.createFileIfNotExits(flowRulePath);
        this.createFileIfNotExits(degradeRulePath);
        this.createFileIfNotExits(systemRulePath);
        this.createFileIfNotExits(authorityRulePath);
        this.createFileIfNotExits(hotParamFlowRulePath);
        // 流控规则
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
                flowRulePath,
                flowRuleListParser
        );
        // 将可读数据源注册至FlowRuleManager
        // 这样当规则文件发生变化时,就会更新规则到内存
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());
        WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
                flowRulePath,
                this::encodeJson
        );
        // 将可写数据源注册至transport模块的WritableDataSourceRegistry中
        // 这样收到控制台推送的规则时,Sentinel会先更新到内存,然后将规则写入到文件中
        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<>(
                flowRulePath,
                authorityRuleListParser
        );
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
                authorityRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);

        // 热点参数规则
        ReadableDataSource<String, List<ParamFlowRule>> hotParamFlowRuleRDS = new FileRefreshableDataSource<>(
                hotParamFlowRulePath,
                hotParamFlowRuleListParser
        );
        ParamFlowRuleManager.register2Property(hotParamFlowRuleRDS.getProperty());
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
                hotParamFlowRulePath,
                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>> hotParamFlowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<ParamFlowRule>>() {
            }
    );

    /**
     * 创建目录
     *
     * @param filePath
     */
    private void mkdirIfNotExits(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    /**
     * 创建文件
     *
     * @param filePath
     * @throws IOException
     */
    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);
    }
}

resource 目录下创建 resources/META-INF/services 目录并创建文件com.alibaba.csp.sentinel.init.InitFunc ,内容为:

pre.cg.config.FileDataSourceInit

Pull 优缺点

  • 优点
    • 简单,无任何依赖
    • 没有额外依赖
  • 缺点
    • 不保证一致性(规则是使用FileRefreshableDataSource定时更新,会有延迟)
    • 实时性不保证(规则是使用FileRefreshableDataSource定时更新)
    • 拉取过于频繁也可能会有性能问题
    • 由于文件存储于本地,容易丢失

Feign整合Sentinel

修改配置文件

#开启feign对sentinel支持    
feign:
  sentinel:
    enabled: true

建立容错类

容错类,需要实现Fegin所在的接口,并实现接口的所有方法,一旦Fegin远程调用出现问题,就会进入当前类中同名方法,执行容错逻辑

@Service
public class ProductServiceFallback implements ProductFeignClient {

    @Override
    public Product findByPid(Long pid) {
        Product product = new Product();
        product.setId(250L)
                .setName("fallback");
        return product;
    }
}

修改@FeignClient注解,添加容错类

@FeignClient(value = "service-product",qualifier = "service-product",fallback = ProductServiceFallback.class)
public interface ProductFeignClient {

    @GetMapping("/product/{pid}")
    @LoadBalanced
    Product findByPid(@PathVariable("pid") Long pid);
}

容错类并获取报错信息(两种方法只能使用一个)

@FeignClient(value = "service-product",qualifier = "service-product"
        ,fallbackFactory = ProductServiceFallbackFactory.class)
public interface ProductFeignClient {

    @GetMapping("/product/{pid}")
    @LoadBalanced
    Product findByPid(@PathVariable("pid") Long pid);
}
@Component
public class ProductServiceFallbackFactory implements FallbackFactory<ProductFeignClient> {
    @Override
    public ProductFeignClient create(Throwable throwable) {
        return new ProductFeignClient() {
            @Override
            public Product findByPid(Long pid) {
                Product product = new Product();
                product.setId(250L)
                        .setName("fallback"+throwable.toString());
                return product;
            }
        };
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值