当前位置: 首页 > news >正文

Sentinel源码—1.使用演示和简介二

大纲

1.Sentinel流量治理框架简介

2.Sentinel源码编译及Demo演示

3.Dashboard功能介绍

4.流控规则使用演示

5.熔断规则使用演示

6.热点规则使用演示

7.授权规则使用演示

8.系统规则使用演示

9.集群流控使用演示

5.熔断规则使用演示

(1)案例说明熔断和降级

(2)Sentinel Dashboard中熔断规则的配置项

(3)熔断策略之慢调用比例

(4)熔断策略之异常比例和异常数

(1)案例说明熔断和降级

一.熔断

假设电商平台有多个库存服务实例,用于处理商品库存查询。有一个库存服务实例可能由于网络问题等原因而变得不稳定,为了避免该库存服务实例异常而影响整个服务,可以对该实例实施熔断。

在这种情况下,可以设定一个阈值。如果在一段时间内发现对该库存服务实例的请求有50%+是失败的,那么会触发熔断器,在一段时间内停止对该库存服务实例进行调用。这样这个异常的库存服务实例便不会影响到商品的库存查询业务了。

二.降级

假设电商平台有个商品推荐功能,它会根据用户浏览记录推荐相关商品。在大促期间,平台可能会遭受巨大流量冲击,导致服务响应时间变慢。为了避免影响核心功能,可以在高峰期间对商品推荐功能进行降级。这意味着系统只会提供基本的商品推荐,而不会使用复杂的推荐算法。这样虽然会降低用户体验,但可以确保平台的核心功能仍然可用。

通过熔断和降级机制,系统可以在节点不稳定或高负载情况下保持稳定。从而避免系统崩溃或性能下降,这是分布式系统中流量治理的重要实践。

熔断和降级往往都是结合一起使用的。针对单节点进行熔断的时候,适合单独使用熔断规则。针对所有节点进行熔断的时候,适合熔断 + 降级一起使用。

假设整个库存查询服务都出了问题,各个服务实例都触发了熔断。这时就可以考虑结合降级策略,确保系统依然能够提供基本的功能。

比如在库存查询的情况下,可选择的降级方案有:

方案一:降级到可靠介质

可以将查询库存的请求切换到查询备份数据库或其他可靠介质,这样虽然可能会降低性能,但仍然可以为用户提供基本的服务。

方案二:降级到限流方法

可以实施限流,即控制并发请求数,以避免过多的请求加重故障状况。例如实现一个每秒只允许处理一定数量的库存查询请求的方法,从而保护系统免受过载的影响。

(2)Sentinel Dashboard中熔断规则的配置项

资源名:就是通过SphU.entry("xxx")或@SentinelResource("xxx")定义的名称。

熔断策略:比如慢调用比例、异常比例、异常数。

最大RT:RT就是Response Time,即响应时间。当熔断策略为慢调用比例时会出现此选项,其他两种策略不会显示此选项,也就是当请求时间超出多少RT后会进行熔断。

比例阈值:当熔断策略配置慢调用比例时,该值为慢调用占所有请求的比例上限。当熔断策略配置异常比例时,该值为请求异常所占比例的上限。取值范围:0.0 ~ 1.0,小数其实就是百分比。比如配置0.1,则为10%,最大为1.0,也就是100%。

异常数:当熔断策略选择异常数时才会出现异常数选项,含义就是请求的异常数量。值得注意的是:Sentinel中异常降级的统计是仅针对业务异常,而Sentinel本身的异常也就是BlockException是不生效的。比如触发流控报异常了,那么是不会统计到异常数当中。

熔断时长:当达到熔断阈值后,会进入熔断状态。超出配置的熔断时长后会恢复到Half Open状态。也就是说当超出熔断时长后不会立即恢复,而是看新进入的请求是否正常。如果还是不正常,则继续熔断,反之恢复。

最小请求数:请求数目大于该值时才会根据配置的熔断策略进行降级。比如配置该值为10 ,但是请求一共才3个。那么即使比例阈值设置的100% ,熔断策略也不会生效的,因为没达到最小请求数。

统计时长:就是统计慢调用比例、异常比例、异常数时的时长。

(3)熔断策略之慢调用比例

慢调用比例,就是一个用于度量系统中慢速或延迟调用所占比例的指标。即在一定时间窗口内,慢速调用的数量与总调用数量的比例。

例如在过去的10秒内(统计时长),系统总共处理了100个调用,其中有10个调用的响应时间超过了1000毫秒(最大RT),那么慢调用比例就是10%。

下面的配置要达到的效果是:当资源testSlowCall在10秒(统计时长)内请求数达10个(最小请求数)以上,且响应时长超过1秒(最大RT)的请求数量大于1(10 * 0.1)个时进行熔断,熔断5秒后资源testSlowCall会变成Half Open状态。即5秒后的第一个请求如果没有问题则恢复正常,否则继续熔断。

同时针对上述配置会新增一个接口如下:即提供一个RT为3s的接口。接下来就可以实现慢调用比例的效果了,只需要在10s内(统计时长)请求testSlowCall资源10次(最小请求数)即可。由于第10次请求时发现RT已超1s,此时就会触发熔断并熔断时长为5s。这次请求被熔断后就不会进入主方法了,会直接返回默认的异常页。

@GetMapping("testSlowCall")
@SentinelResource(value = "testSlowCall")
public String testSlowCall() throws InterruptedException {
    //业务逻辑处理
    TimeUnit.SECONDS.sleep(3000);
    return "ok";
}

当然,被熔断后也可以通过@SentinelResource指定执行自定义的方法。其中@SentinelResource注解的fallback属性,就能指定执行自定义方法。但该属性有很多限制,比如其方法必须和接口在同一个类中等。

@GetMapping("testSlowCall")
@SentinelResource(value = "testSlowCall", fallback = "testSlowCallFallback")
public String testSlowCall() throws InterruptedException {
    // select db
    TimeUnit.SECONDS.sleep(3);
    return "ok";
}

public String testSlowCallFallback() {
    //降级
    return "fallBack";
}

(4)熔断策略之异常比例和异常数

这里的异常指业务异常,不包括Sentinel的流控异常BlockedException。

一.异常比例

下面为了测试异常比例的熔断策略创建一个接口,这个接口的资源名为testErrorRate,降级方法为testErrorRateFallback。

@GetMapping("testErrorRate")
@SentinelResource(value = "testErrorRate", fallback = "testErrorRateFallback")
public String testErrorRate(Integer id) {
    if (null == id) {
        throw new NullPointerException("id is null");
    }
    return "ok";
}

public String testErrorRateFallback(Integer id) {
    //降级
    return "fallBack";
}

这个接口的熔断规则如下:当10秒(统计时长)内达到10个请求(最小请求数)以上,而且请求异常比例超过20%时,就会触发熔断并熔断时长为5秒。熔断5秒后资源testErrorRate会变成Half Open状态。即5秒后的第一个请求如果没有问题则恢复正常,否则就继续熔断。

二.异常数

异常数就是在单位时间内超出错误数则触发熔断,和异常比例唯一不同的就是异常比例是计算百分比,而异常数是直接计算错误数量。

(5)总结

一.熔断策略总结

策略一:慢调用比例

衡量在一定时间内,响应时间超过阈值的调用占总调用数量的比例。比如在一分钟内,有10%的调用响应时间超过1秒。

策略二:异常比例

衡量在一定时间内,发生异常的调用占总调用数量的比例。比如在一小时内,有5%的调用发生异常。

策略三:异常数

计算在一定时间内发生的异常调用的绝对数量。例如,过去一天中,发生了100次调用异常。

二.实际应用场景总结

一个在线支付系统有一个接口用于处理支付请求:

场景一:慢调用比例

过去10分钟内有20%的支付请求的响应时间超过了3秒,超过预设的阈值。

场景二:异常比例

在过去一小时内,有2%的支付请求发生了异常,可能是由于网络问题或支付平台故障引起的。

场景三:异常数

昨天共有30次支付请求发生了异常,可能是由于无效的订单号或者连接超时导致的。

6.热点规则使用演示

(1)参数限流的意思

(2)参数限流的实战

(1)参数限流的意思

传统的流量控制,一般是通过资源维度来限制某接口或方法的调用频率。但有时需要更细粒度地控制不同参数条件下的访问速率,即参数限流。

参数限流允许根据不同的参数条件设置不同的流量控制规则,这种方式非常适合处理特定条件下的请求,能更加精细地管理流量。

场景一:假设有一个在线电影订票系统,某个接口允许用户查询电影的放映时间。但只希望每个用户每10秒只能查询接口1次,以避免过多的查询请求。这时如果直接将接口的QPS限制为5是不能满足要求的。因为需求是每个用户每5分钟只能查询1次,而不是每秒一共只能查询5次。

因此可以使用参数限流设置一个规则,根据用户ID来限制每个用户的查询频率。将限流的维度从资源维度细化到参数维度,从而实现每个用户每10秒只能查询接口1次。

场景二:一个SAAS服务/中台服务,希望能根据不同的商家/业务方来做限流规则。比如规模大的商家/业务方,允许调用的QPS是1000,而小的只能是50。

(2)参数限流的实战

下面实现每个用户每10秒只能查询一次电影票信息,首先在pom.xml添加依赖:

<!-- 可以使用热点参数限流功能 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-parameter-flow-control</artifactId>
    <version>1.8.6</version>
</dependency>

然后新增一个接口:

@RestController
@RequestMapping("/hotKey")
public class TestHotKeyController {
    @GetMapping("/testMovieTicket")
    @SentinelResource(value = "testMovieTicket", fallback = "testMovieTicketFallback")
    public String testMovieTicket(String userId, Integer movieId) {
        return "testMovieTicketSuccess";
    }

    public String testMovieTicketFallback(String userId, Integer movieId) {
        return "testMovieTicketFail";
    }
}

接着针对该接口配置如下规则:相同参数值(第一个参数)每隔10秒只能请求一次。

新增热点规则之后,就可以对热点规则进行编辑。在编辑框里,提供了高级选项进行进一步的选择。

假如希望影院工作人员可以每秒查询10次,老板可以每秒查询100次,而购票者则只能每10秒查询一次。其中工作人员的userId值为100和200,老板的userId值为9999。那么可以如下配置:注意限流阈值是以秒为单位的,需要乘以统计窗口时长10。

(3)总结

参数限流允许基于不同参数条件来限制不同请求的访问速率。在实际应用中,参数限流可以适用于各种场景。例如秒杀系统、订票系统、广告点击等。通过合理设置参数限流规则,可以有效地保护系统免受过多请求的影响。

7.授权规则使用演示

(1)授权规则的核心概念

(2)授权规则的实战

(1)授权规则的核心概念

有时只允许特定用户或IP地址才能访问系统,这时可以使用Sentinel的授权规则(黑白名单)功能。授权规则功能有两个核心概念:黑名单和白名单。

一.黑名单

一种限制性授权规则,用于限制某些用户、操作或资源的访问权限。

二.白名单

一种授予性授权规则,允许特定用户、操作或资源访问受限制的功能。

授权规则适用的场景:

场景一:用户身份认证

当用户登录系统时,系统会验证其身份,确保用户是合法用户。例如,只有登录的管理员才能发布新的电影信息。

场景二:角色和权限分配

系统会为不同角色的用户分配不同的权限。例如,管理员可管理电影信息,编辑可编辑但不能发布,访客只能浏览。

场景三:操作访问控制

某些操作可能只能被特定角色的用户执行。例如,只有管理员可以删除电影信息。

场景四:数据保护

系统需要保护敏感数据,确保只有授权的用户可以访问。例如,用户只能访问自己的个人信息。

场景五:安全访问

限制只有特定用户或特定IP地址可以访问系统,防止恶意用户或恶意IP地址对系统进行攻击或滥用资源。

(2)授权规则的实战

首先需要定义授权规则,Sentinel授权规则配置类是AuthorityRule。因此自己定义一个类,初始化Sentinel授权规则配置,代码如下:

这里没有借助Sentinel Dashboard来配置规则,而是通过代码方式实现。配置规则并不一定要借助Sentinel Dashboard,通过代码也可以完成。如下代码就设置了一个白名单策略,且白名单的值只能是user1或user2。这意味着只有user1和user2这两个用户才能访问"authority-demo"资源。

@Component
public class SentinelAuthorityRule {
    @PostConstruct
    public void init() {
        doInit();
    }
    
    private void doInit() {
        //定义资源名称
        String resource = "authority-demo";
        //定义授权规则,此类为 Sentinel 内部类,并非自己定义的
        AuthorityRule rule = new AuthorityRule();
        rule.setResource(resource);
        //白名单
        rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
        //限制user1/user2能访问
        rule.setLimitApp("user1,user2");
        //注册授权规则
        AuthorityRuleManager.loadRules(Collections.singletonList(rule));
    }
}

当然,对应于Sentinel Dashboard中进行授权规则配置如下:

接着添加接口应用授权规则:

@RestController
@RequestMapping("/sentinelAuthority")
public class SentinelAuthorityController {
    //资源名称
    private static final String RESOURCE_NAME = "authority-demo";

    //userId此处当参数传递仅仅是为了模拟,实际生产环境中还需符合自身公司规范,比如放到header里,从header获取等
    @GetMapping("/demo")
    public String demo(String userId) {
        //进行授权规则验证
        ContextUtil.enter(RESOURCE_NAME, userId);
        Entry entry = null;
        try {
            entry = SphU.entry(RESOURCE_NAME);
            //执行业务逻辑
            return "ok";
        } catch (BlockException ex) {
            //授权验证未通过
            //处理授权验证失败的逻辑
            return "fail";
        } finally {
            if (entry != null) {
                entry.exit();
            }
        }
    }
}

最后启动服务进行验证便可发现:

http://localhost:19966/sentinelAuthority/demo?userId=user1返回ok;
http://localhost:19966/sentinelAuthority/demo?userId=user2返回ok;
http://localhost:19966/sentinelAuthority/demo?userId=user3返回fail;

8.系统规则使用演示

(1)什么是系统规则

(2)系统规则实战

(1)什么是系统规则

系统规则是针对整个系统进行流量控制的,是操作系统级别或服务器级别的,不是应用级别或资源级别的。一台服务器可以部署很多应用(资源),虽然可为每个资源设置流控规则,但是服务器也可能被压爆。如果因为一个服务导致服务器垮了,那么也会对其他服务产生影响。所以,服务器本身也需要可靠性,也需要做一些流控规则配置。比如入口QPS阈值指的是当前服务器上所有接口的入口流量。

它提供多种阈值类型,当触发这些阈值时,系统便会拒绝新的流量请求。

一.Load(负载)阈值

Load阈值可以限制系统的负载。当系统负载过高时,限制新的请求进入系统,以避免系统崩溃。

应用场景: 电商平台在大促活动期间,大量用户涌入平台使得系统资源紧张。此时可以使用设置Load阈值,当系统负载过高时,限制新请求进入系统。

二.RT(平均响应时间)阈值

RT阈值可限制系统的平均响应时间,即请求从接收到响应的平均耗时,RT高可能表示系统负载或性能存在问题。

应用场景:在一个即时消息应用中,确保用户能及时收到消息非常重要。此时可以设置RT阈值,限制消息接口的平均响应时间,保障用户体验。

三.线程数阈值

线程数阈值可限制系统的并发线程数,避免过多线程竞争资源影响性能。

应用场景:在一个高并发的在线游戏中,每个用户都可能占用一个独立线程。此时可以设置线程数阈值,限制并发线程数,避免过多的线程占用资源。

四.入口QPS(每秒查询数)阈值

入口QPS阈值用于限制所有接口的入口流量,防止短时间内大量请求涌入。

应用场景: 在一个热门的抢购活动中,用户可能频繁刷新页面以获取商品信息。可以使用入口QPS阈值,限制商品详情接口的访问频率。

五.CPU使用率阈值

CPU使用率阈值用于限制系统的CPU使用率,避免CPU负载过高。

应用场景:在一个图像渲染应用中,每个任务需要大量计算资源。可以设置CPU使用率规则,限制渲染任务的CPU使用率。

(2)系统规则实战

如下是官网的Demo:通过系统规则管理类SystemRuleManager加载initSystemRule()的阈值。一旦超出initSystemRule()中配置的阈值,新进来的请求将被直接拒绝。

package com.alibaba.csp.sentinel.demo.system;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import com.alibaba.csp.sentinel.util.TimeUtil;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;

public class SystemGuardDemo {
    private static AtomicInteger pass = new AtomicInteger();
    private static AtomicInteger block = new AtomicInteger();
    private static AtomicInteger total = new AtomicInteger();
    
    private static volatile boolean stop = false;
    private static final int threadCount = 100;
    private static int seconds = 60 + 40;
    
    public static void main(String[] args) throws Exception {
        //打印测试结果
        tick();
        //初始化系统规则配置
        initSystemRule();
        
        //启动多线程进行测试
        for (int i = 0; i < threadCount; i++) {
            Thread entryThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        Entry entry = null;
                        try {
                            entry = SphU.entry("methodA", EntryType.IN);
                            pass.incrementAndGet();
                            try {
                                TimeUnit.MILLISECONDS.sleep(20);
                            } catch (InterruptedException e) {
                                
                            }
                        } catch (BlockException e1) {
                            block.incrementAndGet();
                            try {
                                TimeUnit.MILLISECONDS.sleep(20);
                            } catch (InterruptedException e) {
                                
                            }
                        } catch (Exception e2) {
                            
                        } finally {
                            total.incrementAndGet();
                            if (entry != null) {
                                entry.exit();
                            }
                        }
                    }
                }
            });
            entryThread.setName("working-thread");
            entryThread.start();
        }
    }
    
    //初始化系统规则配置
    private static void initSystemRule() {
        List<SystemRule> rules = new ArrayList<SystemRule>();
        SystemRule rule = new SystemRule();
        //max load is 3,系统最高负载为3.0
        rule.setHighestSystemLoad(3.0);
        //max cpu usage is 60%,系统CPU最大使用率为60%
        rule.setHighestCpuUsage(0.6);
        //max avg rt of all request is 10 ms,系统最大平均响应时间为10毫秒
        rule.setAvgRt(10);
        //max total qps is 20,系统最大QPS为20
        rule.setQps(20);
        //max parallel working thread is 10,系统最大并行线程数为10
        rule.setMaxThread(10);
        
        rules.add(rule);
        //通过系统规则配置管理类SystemRuleManager将规则配置注册进去,一旦超出上述阈值,新进来的请求将被直接拒绝
        SystemRuleManager.loadRules(Collections.singletonList(rule));
    }
    
    private static void tick() {
        Thread timer = new Thread(new TimerTask());
        timer.setName("sentinel-timer-task");
        timer.start();
    }
    
    static class TimerTask implements Runnable {
        @Override
        public void run() {
            System.out.println("begin to statistic!!!");
            long oldTotal = 0;
            long oldPass = 0;
            long oldBlock = 0;
            while (!stop) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    
                }
                long globalTotal = total.get();
                long oneSecondTotal = globalTotal - oldTotal;
                oldTotal = globalTotal;
                
                long globalPass = pass.get();
                long oneSecondPass = globalPass - oldPass;
                oldPass = globalPass;
                
                long globalBlock = block.get();
                long oneSecondBlock = globalBlock - oldBlock;
                oldBlock = globalBlock;
                System.out.println(seconds + ", " + TimeUtil.currentTimeMillis() + ", total:" + oneSecondTotal + ", pass:" + oneSecondPass + ", block:" + oneSecondBlock);
                if (seconds-- <= 0) {
                    stop = true;
                }
            }
            System.exit(0);
        }
    }
}

9.集群流控使用演示

(1)部署集群环境

(2)集群流控实战

(1)部署集群环境

Sentinel源码中就有一个实现了集群流控的Demo。

因此为了演示集群效果,可以直接启动:

com.alibaba.csp.sentinel.demo.cluster.app.ClusterDemoApplication;

接下来会启动三次此项目,分别指定不同的端口。启动之前,需要在IDEA中分别添加如下JVM参数。

第一次启动项目时添加的参数:

 -Dcsp.sentinel.log.use.pid=true -Dproject.name=sentinle.cluster.demo.embedded 
 -Dserver.port=8081 -Dcsp.sentinel.dashboard.server=localhost:8080 
 -Dcsp.sentinel.api.port=8881

对应的截图如下:

第二次启动项目时添加的参数:

 -Dcsp.sentinel.log.use.pid=true -Dproject.name=sentinle.cluster.demo.embedded 
 -Dserver.port=8082 -Dcsp.sentinel.dashboard.server=localhost:8080 
 -Dcsp.sentinel.api.port=8882

对应的截图如下:

第三次启动项目时添加的参数:

 -Dcsp.sentinel.log.use.pid=true -Dproject.name=sentinle.cluster.demo.embedded 
 -Dserver.port=8083 -Dcsp.sentinel.dashboard.server=localhost:8080 
 -Dcsp.sentinel.api.port=8883

对应的截图如下:

分别启动三个ClusterDemoApplication项目,然后再分别访问如下地址:

http://localhost:8081/hello/sentinel
http://localhost:8082/hello/sentinel
http://localhost:8083/hello/sentinel

接着打开Sentinel Dashboard机器列表,便会发现注册进来了三个服务。

(2)集群流控实战

首先,需要新增Token Server和Token Client。可以随意选择一个服务作为Token Server,另外两个作为Token Client。

接着,新建一个集群规则:集群QPS阈值为1。这也就意味着三台服务加起来的QPS为1,即整个集群内1s只能访问一次。配置如下图所示:

至此,就完成了集群限流的配置。

相关文章:

  • 【算法学习笔记】37:扩展中国剩余定理(EXCRT)求解任意线性同余方程组
  • 【微服务管理】注册中心:分布式系统的基石
  • python每日一练
  • 【模块化拆解与多视角信息3】教育背景:学历通胀时代的生存法则
  • JMeter使用
  • css解决边框四个角有颜色
  • 关于数据清洗和数据处理实践学习笔记
  • 任意文件读取 + java逆向 -- File_download sqctf WP
  • 【中级软件设计师】前趋图 (附软考真题)
  • HJ16 购物单
  • 【Linux生成SSH秘钥实现远程连接】Linux生成SSH秘钥对与修改服务配置文件实现无密码远程连接
  • PyCharm 开发工具 修改背景颜色
  • VMware vCenter Server 安全漏洞升级方案一则
  • 基于 SSM 高校二手交易平台
  • 如何在 Java 中对 PDF 文件进行数字签名(教程)
  • 打造现代数据基础架构:MinIO对象存储完全指南
  • 如何快速部署基于Docker 的 OBDIAG 开发环境
  • 初识大模型
  • OpenAI 焕新力作:ChatGPT 开启“记忆长廊”,对话皆成专属印记
  • 自然语言处理spaCy
  • 西湖大学本科新增临床医学专业,今年本科招生专业增至8个
  • 国际金价冲上3500美元,本月已涨超12%!分析人士提醒:警惕短期多头获利了结
  • 中远海运:坚决反对美方对中国海事物流及造船业301调查的歧视性决定
  • “30小时不够”,泽连斯基建议延长停火至30天
  • 平安银行一季度净赚超140亿元降5.6%,营收降13.1%
  • 美接连派轰炸机、无人机前往日本,驻日美军正升级空中力量