Alibaba微服务组件 - Sentinel(三) Sentinel快速开始(API实现)+控制台访问

3. Sentinel快速开始(API实现)+控制台访问

文档地址:https://github.com/alibaba/Sentinel/wiki/如何使用
在官方文档中,定义的Sentinel进行资源保护的几个步骤:
1. 定义资源
2. 定义规则
3. 检验规则是否生效
sentinel可以自己作为一个组件在分布式项目中使用,我们先来实现sentinel-core使用

3.1 引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
        <relativePath/>
    </parent>


    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>sentinel-demo</artifactId>
    <version>1.0-SNAPSHOT</version>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--sentinel核心库-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>1.8.0</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.18</version>
        </dependency>

        <!--如果要使用@SentinelResource-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-annotation-aspectj</artifactId>
            <version>1.8.0</version>
        </dependency>

        <!--整合sentinel控制台-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-transport-simple-http</artifactId>
            <version>1.8.0</version>
        </dependency>

    </dependencies>


</project>

3.2 配置bean SentinelResourceAspect

(单独使用的话必须注入bean,这个切面没有定义@Component)

package com.xiexie.sentinel;

import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

/**
 * @Description
 * @Date 2022-04-13 9:11
 * @Author xie
 */
@SpringBootApplication
public class HelloApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
    }

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

3.3 编写测试逻辑

缺点:

  • 业务侵入性很强,需要在controller中写入非业务代码.
  • 配置不灵活 若需要添加新的受保护资源 需要手动添加 init方法来添加流控规则

演示流控规则

package com.xiexie.sentinel.controller;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
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.xiexie.sentinel.Entry.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @Description 演示流控规则(一般放在服务消费端)
 * @Date 2022-04-13 9:10
 * @Author xie
 */
@RestController
@Slf4j
public class FlowRuleController {

    // resource name 需要跟接口地址相同(通过接口地址定义资源名称)
    // 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
    private static final String RESOURCE_NAME = "hello";
    private static final String USER_RESOURCE_NAME = "user";
    private static final String DEGRADE_RESOURCE_NAME = "degrade";

    /**
     * sentinel进行流控(硬编码形式)
     * @return java.lang.String
     */
    @RequestMapping("/hello")
    public String hello() {

        Entry entry = null;
        // sentinel针对资源进行限制
        try {
            entry = SphU.entry(RESOURCE_NAME);
            // 被保护的业务逻辑
            String str = "hello sentinel";
            log.info("=====" + str + "=====");
            return str;
        } catch (BlockException e) {
            e.printStackTrace();
            // 资源访问阻止,被限流或者被降级
            // 进行相应的业务处理
            log.info("被限流了。。。");
            return "被限流了。。。";
        } catch (Exception e) {
            // 若需要配置降级规则,需要用这种方式记录业务异常
            Tracer.traceEntry(e, entry);
        } finally {
            if (Objects.nonNull(entry)) {
                entry.exit();
            }
        }
        return null;
    }


    /**
     * 定义流控规则
     *
     * @date 2022/4/13 9:34
     */
    @PostConstruct // 相当于bean的 init-method
    private void initFlowRules() {

        // 流控规则集合
        // 还有其他规则道理相同,比如:熔断降级规则 (DegradeRule)、系统保护规则 (SystemRule)、访问控制规则 (AuthorityRule)、热点规则 (ParamFlowRule)
        List<FlowRule> flowRuleList = new ArrayList();

        // 定义流控规则
        FlowRule flowRule1 = new FlowRule();
        // 设置受保护的资源
        flowRule1.setResource(RESOURCE_NAME);
        // 设置流控规则 限流阈值类型,QPS 模式(1)或并发线程数模式(0)
        flowRule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置保护限流阈值(整合起来就是1s 只能访问一次)
        flowRule1.setCount(1);
        flowRuleList.add(flowRule1);


        // 通过@SentinelResource来定义资源的流控和降级规则
        FlowRule flowRule2 = new FlowRule();
        // 设置受保护的资源
        flowRule2.setResource(USER_RESOURCE_NAME);
        // 设置流控规则 限流阈值类型,QPS 模式(1)或并发线程数模式(0)
        flowRule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置保护限流阈值(整合起来就是1s 只能访问一次)
        flowRule2.setCount(1);
        flowRuleList.add(flowRule2);


        // 加载配置好的规则
        FlowRuleManager.loadRules(flowRuleList);

    }

    /********************************* 使用@SentinelResource改善硬编码格式 ****************************/

    /**
     * @SentinelResource 改善接口中资源定义和被流控降级后的处理方法
     * 使用:
     * 1. 添加依赖:<artifactId>sentinel-annotation-aspectj</artifactId>
     * 2. 配置bean SentinelResourceAspect(单独使用的话必须注入bean,这个切面没有定义@Component)
     *    value:定义资源
     *    blockHandler:设置流控降级后的处理方法(默认该方法必须跟流控接口声明在同一个类中)
     *      如果不想在同一个类中可以用blockHandlerClass来指定,但是方法必须是public static修饰
     *    fallback:当接口出现了异常,就可以交给fallback执行的方法进行处理
     *      如果不想在同一个类中可以用fallbackClass来指定,但是方法必须是public static修饰
     *    优先级:blockHandler > fallback
     *    exceptionsToIgnore:要排除的异常
     *
     * @param id
     * @return com.xiexie.sentinel.Entry.User
     */
    @RequestMapping("/user")
    @SentinelResource(value = USER_RESOURCE_NAME
                    , blockHandler = "blockHandlerForGetUser"
                    , blockHandlerClass = {User.class}
                    , fallback = "fallbackForGetUser"
                    , exceptionsToIgnore = {ArithmeticException.class}
    )
    public User getUser(String id) {
        //int a = 1/0;
        return new User("xiexie");
    }

    /**
     * 注意:
     * 1. 一定要是public修饰
     * 2. 返回值一定要跟源方法一致,包含源方法参数(顺序也要一致)
     * 3. 可以在参数中加BlockException异常处理,可以区分是什么类型的规则,对症下药。
     * @param id
     * @param e
     * @return com.xiexie.sentinel.Entry.User
     */
    public User blockHandlerForGetUser(String id, BlockException e) {
        e.printStackTrace();
        return new User("被流控");
    }

    public User fallbackForGetUser(String id, Throwable throwable) {
        throwable.printStackTrace();
        return new User("异常处理");
    }


}

演示熔断降级规则

package com.xiexie.sentinel.controller;

import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.xiexie.sentinel.Entry.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description 演示熔断降级规则(一般放在服务提供端)
 * @Date 2022-04-13 11:21
 * @Author xie
 */
@RestController
public class DegradeRuleController {

    private static final String DEGRADE_RESOURCE_NAME = "degrade";


    @RequestMapping("/degrade")
    @SentinelResource(value = DEGRADE_RESOURCE_NAME, entryType = EntryType.IN, blockHandler = "blockHandlerForDegrade")
    public User degrade(String id) {
        // 异常数 比例
        throw new RuntimeException("异常了");
    }

    public User blockHandlerForDegrade(String id, BlockException e) {
        e.printStackTrace();
        return new User("触发熔断, 开始降级操作");
    }

    /**
     * 初始化熔断降级规则
     * @date 2022/4/13 11:22
     */
    @PostConstruct
    public void initDegradeRule() {

        // 降级规则异常
        List<DegradeRule> degradeRuleList = new ArrayList();

        DegradeRule degradeRule = new DegradeRule();
        degradeRule.setResource(DEGRADE_RESOURCE_NAME);
        // 熔断降级策略 支持慢调用比例/异常比例/异常数策略(默认值慢调用比例)
        degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        // 触发熔断异常数 2个
        degradeRule.setCount(2);
        // 触发熔断最小请求数 2次
        degradeRule.setMinRequestAmount(2);
        // 统计时长 (时间太短不好测试)1分钟 单位ms   默认1s
        degradeRule.setStatIntervalMs(60 * 1000);

        // --- 上述条件就是:一分钟内 请求了两次+  出现了两次异常 就会触发熔断

        // 熔断时长 单位s
        // 具体说明:一旦触发了熔断,再次请求接口就会直接调用降级方法,而不是接口本身
        //         10s过后进入半开状态,恢复接口本身调用,如果第一次请求就出现了异常,再次熔断,不会根据设置的条件进行判定
        //         然后继续重复上述操作。
        degradeRule.setTimeWindow(10);

        degradeRuleList.add(degradeRule);
        DegradeRuleManager.loadRules(degradeRuleList);
    }
}

User类

package com.xiexie.sentinel.Entry;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Description
 * @Date 2022-04-13 9:53
 * @Author xie
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private String name;

    /**
     * 注意:
     * 1. 一定要是public修饰
     * 2. 返回值一定要跟源方法一致,包含源方法参数(顺序也要一致)
     * 3. 可以在参数中加BlockException异常处理,可以区分是什么类型的规则,对症下药。
     * @param id
     * @param e
     * @return com.xiexie.sentinel.Entry.User
     */
    public static User blockHandlerForGetUser(String id, BlockException e) {
        e.printStackTrace();
        return new User("被流控");
    }

}

3.4 @SentinelResource注解实现

@SentinelResource 注解用来标识资源是否被限流、降级。
blockHandler:  定义当资源内部发生了BlockException应该进入的方法(捕获的是Sentinel定义的异常)
fallback:  定义的是资源内部发生了Throwable应该进入的方法
exceptionsToIgnore:配置fallback可以忽略的异常
源码入口:com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect

// 代码如上

3.5 规则的种类

  • 流量控制规则
  • 熔断降级规则
  • 系统保护规则
  • 访问控制规则
  • 热点规则

剩下规则演示请查看官方文档,基本是一样的操作https://github.com/alibaba/Sentinel/wiki/如何使用

3.6 如果需要控制台展示

控制台官方文档:https://github.com/alibaba/Sentinel/wiki/控制台

客户端需要引入 Transport 模块来与 Sentinel 控制台进行通信

<!--整合sentinel控制台-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
    <version>1.8.0</version>
</dependency>

并且设置JVM参数

‐Dcsp.sentinel.dashboard.server=youConsoleIp:port

3.7 启动 Sentinel 控制台

下载控制台 jar 包并在本地启动:详情可以参照官方文档https://github.com/alibaba/Sentinel/wiki/控制台#2-启动控制台

下载:
https://github.com/alibaba/Sentinel/releases
image

注意:启动 Sentinel 控制台需要 JDK 版本为 1.8 及以上版本。

# 启动控制台命令
java ‐jar sentinel‐dashboard‐1.8.0.jar

用户可以通过如下参数进行配置:
-Dsentinel.dashboard.auth.username=sentinel 用于指定控制台的登录用户名为 sentinel;
-Dsentinel.dashboard.auth.password=123456 用于指定控制台的登录密码为 123456;如果省略这两个参数,默认用户和密码均为sentinel;
-Dserver.servlet.session.timeout=7200 用于指定 Spring Boot 服务端 session 的过期时间,如 7200 表示 7200 秒;60m 表示 60 分钟,默认为 30 分钟;

java -Dserver.port=8858 -Dsentinel.dashboard.auth.username=xiexie -Dsentinel.dashboard.auth.password=123456 -jar sentinel-dashboard-1.8.0.jar

为了方便快捷启动可以在桌面创建.bat文件

pause
java -Dserver.port=8858 -Dsentinel.dashboard.auth.username=xiexie -Dsentinel.dashboard.auth.password=123456 -jar E:\xie-my-install\devolop\springcloudalibaba\version-2.2.5.RELEASE\sentinel-dashboard-1.8.0.jar

访问http://localhost:8080/#/login ,如果不配置直接启动的话默认用户名密码: sentinel/sentinel
image

Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包,所以要确保客户端有访问量;
意思就是需要先访问接口,信息才会通过API发送到控制台进行展示,不然刚开启是没有任何的数据在里面。
而且数据存放在内存中重启项目或者控制台都会丢失数据我们需要持久化在nacos或者其他的注册中心,或者自行扩展持久化
image

posted @ 2022-04-13 16:17  xiexie0812  阅读(256)  评论(0编辑  收藏  举报