关于springcloud:Sentinel流量防卫兵

37次阅读

共计 6571 个字符,预计需要花费 17 分钟才能阅读完成。

前言

在咱们平时工作中,总会有这样的事件产生:服务无奈接受过多的申请而被打挂。

个别咱们能够从两个方面解决:

  1. 减少节点,程度扩大(钱总是万能的)
  2. 对申请量过高的接口进行限流(没钱也不是不能够)

突发状况下咱们会先用第一种计划,而后再过渡到第二种。毕竟:穷就一个字

随着这样的事件产生多了,零碎就会能够预计的朝这样的方向演变:

  • 单个接口的限流 -> 多个接口的限流

    沉睡能力:限流能够配置,想要对哪个接口进行限流,就改下配置,立刻失效。

  • 单个零碎须要限流 -> 多个零碎须要限流

    沉睡能力:限流性能组件化,后续还有零碎须要限流性能,引入依赖即可,不须要反复开发。

  • 等等

通过这样的推论:每个零碎都会产生高并发 -> 每个零碎都会朝这个方向演变 -> 总有演变了很久的零碎 -> 网上是否曾经存在这样的轮子?

别说,真的有!明天咱们要意识的配角 Sentinel 就是这样的 又大又圆 的轮子~

介绍

随着微服务的风行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量管制组件,次要以流量为切入点,从流量管制、熔断降级、零碎自适应爱护等多个维度来帮忙您保障微服务的稳定性。

官网地址:https://sentinelguard.io/zh-cn/

话不多说,先来个案例感触感触

案例

需要:要求每秒钟通过的 qps 限定在 20

注:案例中所有统计相干的代码只是为了更加直观的体现 sentinel 的作用

引入依赖:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.8.2</version>
</dependency>

1. 定义流控规定

private void initFlowRules() {
  // 定义流控规定
  FlowRule rule = new FlowRule();
  // 资源名与须要限流的资源名雷同
  rule.setResource("HelloWorld");
  // 设置限流形式为 QPS
  rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
  // 设置 QPS 为 20
  rule.setCount(20);
  // 加载规定
  FlowRuleManager.loadRules(Collections.singletonList(rule));
}

2. 模仿申请

// 记录申请总数
private static final AtomicInteger TOTAL = new AtomicInteger();
// 记录申请通过数
private static final AtomicInteger PASS = new AtomicInteger();
// 记录申请回绝数
private static final AtomicInteger BLOCK = new AtomicInteger();

private void request() {for (int i = 0; i < 30; i++) {new Thread(() -> {while (true){
        // 记录总 qps
        TOTAL.incrementAndGet();
        // 进行限流
        try (Entry entry = SphU.entry("HelloWorld")) {
          // 记录通过数
          PASS.incrementAndGet();} catch (BlockException e) {
          // 记录回绝数
          BLOCK.incrementAndGet();}
        // 模仿业务期待 0 -50 毫秒
        try {TimeUnit.MILLISECONDS.sleep(new Random().nextInt(50));
        } catch (InterruptedException ignored) {}}
    }).start();}
}

3. 统计

public void count() {new Thread(() -> {
    int oldTotal = 0, oldPass = 0, oldBlock = 0;
    while (true){
      // 计算以后 qps
      int total = TOTAL.get();
      int secondsTotal = total - oldTotal;
      oldTotal = total;

      // 计算每秒通过数
      int pass = PASS.get();
      int secondsPass = pass - oldPass;
      oldPass = pass;

      // 计算每秒回绝数
      int block = BLOCK.get();
      int secondsBlock = block - oldBlock;
      oldBlock = block;

      log.info("以后 qps:{}, pass: {}, block:{}", secondsTotal, secondsPass, secondsBlock);
      try {
        // 进展一秒
        TimeUnit.SECONDS.sleep(1);
      } catch (InterruptedException ignored) {}}
  }).start();}

4. 测试

@Test
public void testBlock() throws IOException {
  // 初始化规定
  this.initFlowRules();
  // 模仿高并发拜访
  this.request();
  // 统计 qps
  this.count();
  // 避免程序终止
  System.in.read();}

5. 测试后果

总体来说,测试后果合乎预期

思考

以上案例是最简略的入门案例,也是 Sentinel 的外围所在。

其中要害的代码便是:SphU.entry(“HelloWorld”)

如果还想在其余业务代码中减少限流,则须要做出如下批改并减少流控规定

try (Entry entry = SphU.entry("资源名")) {// 业务代码} catch (BlockException e) {// 依据异样进行解决}

然而很显著,这是一个通用代码块,惟一的变量就是 ” 资源名 ”,咱们很容易就想到通过切面的形式进行优化

如果是你,你会想要怎么革新它呢?

咱们先来看看 Sentinel 的切面应用形式吧

整合 SpringBoot

1. 引入注解依赖

<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-annotation-aspectj</artifactId>
  <version>1.8.2</version>
</dependency>

2. 编写 Controller&Servcie 的 Demo

@RestController
@RequestMapping("/foo")
public class FooController {

    @Autowired
    private FooService fooService;

    @GetMapping
    public String hello(String name) {return fooService.hello(name);
    }
}
public interface FooService {String hello(String name);
}
@Service
public class FooServiceImpl implements FooService {

    @Override
    public String hello(String name){return "hello" + name;}
}

3. 开启切面

@Configuration
public class SentinelAspectConfiguration {

    @Bean
    public SentinelResourceAspect sentinelResourceAspect() {return new SentinelResourceAspect();
    }
}

4. 减少注解与限流解决

@SentinelResource(value = "hello", blockHandler = "exceptionHandler")
@Override
public String hello(String name){return "hello" + name;}
public String exceptionHandler(String name, BlockException ex) {return "被限流了";}

blockHandler: 限流对应的解决办法,办法参数和返回值与业务办法雷同,对应着入门案例中的 catch 逻辑

对于 SentinelResource 注解的更多信息:https://github.com/alibaba/Se…

5. 配置流控规定启动

@SpringBootApplication
public class SentinelDemoApplication {public static void main(String[] args) {
          // 初始化流控规定
        initFlowRules();
        SpringApplication.run(SentinelDemoApplication.class, args);
    }

    private static void initFlowRules(){
        // 定义流控规定
        FlowRule rule = new FlowRule();
        // 资源名注解中的雷同
        rule.setResource("hello");
        // 设置限流形式为 QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置 QPS 为 2
        rule.setCount(2);
        // 加载规定
        FlowRuleManager.loadRules(Collections.singletonList(rule));
    }
}

6. 测试

curl ‘http://localhost:8080/foo?name= 张三 ’

7. 后果

在页面上多刷新几次就将呈现咱们在 exceptionHanlder 中返回的 ” 被限流了 ” 提醒语

再次思考

整合是整合了,不晓得大家有没有像我一样:有股吃了苍蝇个别的好受感

一个注解就要配一个限流规定,反正我算是吐了。

无关限流异样解决的逻辑能够应用公共的,大家能够查看下面贴出的官网文档链接

那么咱们应该怎么样能力让本人心田畅通呢?

咱们认真品一下加载规定的逻辑,如果咱们把这个步骤写成一个接口?

哦豁,那我这个规定岂不是想加就加,想改就改?

这里我就不演示了,因为 Sentinel 曾经把这件事件做了,并且还很贴心的做了一个控制台~

整合 Sentinel 控制台

1. 装置 Sentinel 控制台

下载 jar 包

下载地址:https://github.com/alibaba/Se…

启动

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

默认账号密码为:sentinel sentinel

如果想要批改默认的账号密码,可减少参数

-Dsentinel.dashboard.auth.username=sentinel

-Dsentinel.dashboard.auth.password=123456

2. 增加依赖

<!-- 通信依赖 -->
<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-transport-simple-http</artifactId>
  <version>1.8.2</version>
</dependency>
<!-- webmvc 适配 -->
<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-spring-webmvc-adapter</artifactId>
  <version>1.8.2</version>
</dependency>

3. 编写测试接口

@GetMapping("/test")
public String test() {return "ok";}

4. 配置对立异样解决

@Slf4j
public class MyBlockExceptionHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setStatus(HttpServletResponse.SC_OK);
        try (PrintWriter out = response.getWriter()) {out.write(new ObjectMapper().writeValueAsString("{\"message\":\" 被限流了 \"}"));
            out.flush();}
        catch (IOException ignored) {}}
}

将处理器退出到拦截器中

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // Add Sentinel interceptor
        addSpringMvcInterceptor(registry);
    }

    private void addSpringMvcInterceptor(InterceptorRegistry registry) {SentinelWebMvcConfig config = new SentinelWebMvcConfig();

        config.setBlockExceptionHandler(new MyBlockExceptionHandler());
        // 辨别申请形式
        config.setHttpMethodSpecify(true);

        registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**");
    }
}

6. 配置控制台地址

在 resources 下新建 sentinel.properties 配置文件

# 利用名称
project.name=sentinel-demo
#sentinel 控制台地址
csp.sentinel.dashboard.server=localhost:8081

5. 测试

第一次申请接口用于触发控制台初始化

curl ‘http://localhost:8080/foo/test’

关上控制台并登录

在簇点链路栏中能够看到呈现了方才拜访的资源地址

点击 + 流控 按钮

一个 qps 阈值为 2 的规定

再次测试,多刷几次:curl ‘http://localhost:8080/foo/test’

配置未然失效

眼尖的小伙伴曾经发现了:右边的菜单栏好多规定能够配置,咱们下次再聊吧~

问题

的确,在退出控制台之后解决了之前的问题,然而又产生了新的问题,不晓得小伙伴有没有发现?

之前咱们的流控规定是写在代码里的,服务进行重启都会从新加载到内存中。

当初咱们把规定配置在 sentinel 控制台,由控制台推送到服务中。

留神:咱们启动 sentinel 时并没有配置过数据库,所以如果服务重启了,配置会隐没吗?

答案是会的,那么又怎么能力解决这个问题呢?

因为太久没更新过了,还没复原状态,明天的内容也挺多了,下次吧~

小结

明天介绍了 Sentinel 这个弱小的流量防护工具——尽管只是初窥门径,但不障碍大家感触到他的弱小之处。

咱们从一个最根本的案例登程,通过对上一个案例的思考,引出下一个案例的解决方案,循序渐进。

同时在最初,我还留下了一点点问题供大家思考,大家也能够上官网进行寻找解决方案。

最初,心愿大家有所播种~

下期:Sentinel 控制台 & 整合 SpringCloud

想要理解更多精彩内容,欢送关注公众号:程序员阿鉴

集体博客空间:https://zijiancode.cn

正文完
 0