Alibaba微服务组件Sentinel

Alibaba微服务组件Sentinel

分布式系统遇到的问题

Alibaba微服务组件Sentinel
1.sentinel服务雪崩
如果其中的下单服务不可用,就会出现线程池里所有线程都因等待响应而被阻塞,从而造成整个服务链路不可用,进而导致整个系统的服务雪崩,如图所示:
Alibaba微服务组件Sentinel
服务雪崩效应:因服务提供者的不可用导致服务调用者的不可用,并将不可用逐渐放大的过程,就叫服务雪崩效应。

导致服务不可用的原因:
Alibaba微服务组件Sentinel
在服务提供者不可用的时候,会出现大量重试的情况:用户重试、代码逻辑重试,这些重试最终导致:进一步加大请求流量。所以归根结底导致雪崩效应的最根本原因是:大量请求线程同步等待造成的资源耗尽。当服务调用者使用同步调用时,会产生大量的等待线程占用系统资源。一旦线程资源被耗尽,服务调用者提供的服务也将处于不可用状态,于是服务雪崩效应产生了。

2.解决方案
稳定性、恢复性

Reliability && Resilience

常见的容错机制:
1》超时机制
在不做任何处理的情况下,服务提供者不可用会导致消费者请求线程强制等待,而造成系统资源耗尽。加入超时机制,一旦超时,就释放资源。由于释放资源速度较快,一定程度上可以抑制资源耗尽的问题。

2》服务限流
Alibaba微服务组件Sentinel
3》隔离
原理:用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,则会进行降级处理,用户的请求不会被阻塞,至少可以看到一个执行结果(例如返回友好的提示信息),而不是无休止的等待或者看到系统崩溃。
隔离前:
Alibaba微服务组件Sentinel
Alibaba微服务组件Sentinel

隔离后:
Alibaba微服务组件Sentinel
b) 信号隔离:
信号隔离也可以用于限制并发访问,防止阻塞扩散,与线程隔离最大不同在于执行依赖代码的线程依然是请求线程(该线程需要通过信号申请,如果客户端是可信的且可以快速返回,可以使用信号隔离替换线程隔离,降低开销。信号量的大小可以动态调整,线程池大小不可以。)

4》服务熔断
远程服务不稳定或网络抖动时暂时关闭,就叫服务熔断。现实世界的断路器大家肯定都很了解,断路器实时监控电路的情况,如果发现电路电流异常,就会跳闸,从而防止电路被烧毁。
软件世界的断路器可以这样理解:实时监测应用,如果发现在一定时间内失败次数/失败率达到一定阈值,就"跳闸",断路器打开—此时,请求直接返回,而不去调用原本调用的逻辑。跳闸一段时间后(例如10秒),断路器会进入半开状态,这是一个瞬间态,此时允许一次请求调用该逻辑,如果成功,则断路器关闭,应用正常调用;如果调用依然不成功,断路器继续回到打开状态,过段时间再进入半开状态尝试—通过"跳闸",应用可以保护自己,而且避免浪费资源;而通过半开的设计,可实现应用的"自我修复"。
所以,同样的道理,当依赖的服务有大量超时时,在让新的请求去访问根本没有意义,只会无畏的消耗现有资源。比如我们设置了超时时间为1s,如果短时间内有大量请求在1s内都得不到响应,就意味着这个服务出现了异常,此时就没有必要再让其他的请求去访问这个依赖了,这个时候就应该使用断路器避免资源浪费。
Alibaba微服务组件Sentinel
5》服务降级
有服务熔断,必然要有服务降级。
所谓降级,就是当某个服务熔断之后,服务将不再被调用,此时客户端可以自己准备一个本地的fallback(回退)回调,返回一个缺省值。例如:(备用接口/缓存/mock数据)。这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强,当然这也要看适合的业务场景。

sentinel介绍

1.Sentinel是什么
Alibaba微服务组件Sentinel
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保证微服务的稳定性。
源码地址:https://github.com/alibaba/Sentinel
官方文档:https://github.com/alibaba/Sentinel/wiki

Sentinel具有以下特征:
1》丰富的应用场景:Sentinel承接了阿里巴巴近10年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用的应用等。
2》完备的实时监控:Sentinel同时提供实时监控功能。您可以在控制台中看到接入应用的单台机秒级数据,甚至500台以下规模的集群的汇总运行情况。
3》广泛的开源生态:Sentinel提供开箱即用的与其它开源框架/库的整合模块,例如与Spring Cloud、Dobbo、gRPC的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。
4》完善的SPI扩展点:Sentinel提供简单易用、完善的SPI扩展点。您可以通过实现扩展点,快速的定制逻辑。例如定制规则管理、适配数据源等。
Alibaba微服务组件Sentinel
2.Sentinel和Hystrix对比
Alibaba微服务组件Sentinel

sentinel流控规则初体验

官网地址:https://github.com/alibaba/Sentinel/wiki/

1.maven依赖

<?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.0.RELEASE</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <artifactId>sentinel-demo</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <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.1</version>
        </dependency>

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

2.controller代码

@RestController
@Slf4j
public class HelloController {

    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
     */
    @RequestMapping(value = "/hello")
    public String hello(){
        Entry entry = null;
        try {
            //sentinel针对资源进行限制的
            SphU.entry(RESOURCE_NAME);
            //被保护的业务逻辑
            String str = "hello world";
            log.info("======" + str + "======");
            return str;
        }catch (BlockException e1){
            //资源访问阻止,被限流或被降级
            //进行相应的处理操作
            log.info("block!");
            return "被流控了!";
        }catch (Exception ex){
            //若需要配置降级规则,需要通过这种方式记录业务异常
            Tracer.traceEntry(ex, entry);
        }finally {
            if(entry != null){
                entry.exit();
            }
        }
        return null;
    }

    /**
     * spring 的初始化方法
     */
    @PostConstruct  //init-method
    private static void initFlowRules(){

        //流控规则
        List<FlowRule> rules = new ArrayList<>();

        //流控
        FlowRule rule = new FlowRule();
        //设置受保护的资源
        rule.setResource(RESOURCE_NAME);
        //设置流控规则 QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //设置受保护的资源阈值
        //Set limit QPS to 20.
        rule.setCount(1);
        rules.add(rule);

        //通过@SentinelResource来定义资源并配置降级和流控的处理方法
        /*FlowRule rule2 = new FlowRule();
        //设置受保护的资源
        rule2.setResource(USER_RESOURCE_NAME);
        //设置流控规则QPS
        rule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //设置受保护的资源阈值
        //Set limit QPS to 20.
        rule2.setCount(1);

        rules.add(rule2);*/

        FlowRuleManager.loadRules(rules);

    }
}

3.测试
Alibaba微服务组件Sentinel

sentinel — @SentinelResource使用

1.maven依赖

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

2.controller代码

@RestController
@Slf4j
public class HelloController {

    private static final String RESOURCE_NAME = "hello";
    private static final String USER_RESOURCE_NAME = "user";
    private static final String DEGRADE_RESOURCE_NAME = "degrade";

    /**
     * spring 的初始化方法
     */
    @PostConstruct  //init-method
    private static void initFlowRules(){

        //流控规则
        List<FlowRule> rules = new ArrayList<>();

        //流控
        FlowRule rule = new FlowRule();
        //设置受保护的资源
        rule.setResource(RESOURCE_NAME);
        //设置流控规则 QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //设置受保护的资源阈值
        //Set limit QPS to 20.
        rule.setCount(1);
        rules.add(rule);

        //通过@SentinelResource来定义资源并配置降级和流控的处理方法
        FlowRule rule2 = new FlowRule();
        //设置受保护的资源
        rule2.setResource(USER_RESOURCE_NAME);
        //设置流控规则QPS
        rule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //设置受保护的资源阈值
        //Set limit QPS to 20.
        rule2.setCount(1);

        rules.add(rule2);

        FlowRuleManager.loadRules(rules);

    }

    /**
     * @SentinelResource 改善接口中资源定义和被流控降级后的处理方法
     * 怎么使用: 1.添加依赖 <artifactId>sentinel-annotation-aspectj</artifactId>
     *          2.配置bean---SentinelResourceAspect
     *          value  定义资源
     *          blockHandler  设置 流控降级后的处理方法(默认接口和该方法必须声明在同一个类)
     *          如果不想在同一个类中,指定 blockHandlerClass = ""  但是方法必须是static
     *          fallback 当接口出现了异常,就可以交给fallback指定的方法进行处理
     *          blockHandler 如果和 fallback同时指定了,则blockHandler优先级更高
     *          exceptionsToIgnore = "", 排除哪些异常不处理
     *
     * @param id
     * @return
     */
    @RequestMapping("/user")
    @SentinelResource(value = USER_RESOURCE_NAME
            , fallback = "fallbackHandleForGetUser"
            , blockHandler = "blockHandlerForGetUser")
    public User getUser(String id){
        int i = 1/0;
        return new User("mu");
    }

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

    /**
     * 注意:
     *  1. 一定要public
     *  2. 返回值一定要和源方法保证一致, 包含源方法的参数。
     *  3. 可以在参数最后添加BlockException  可以区分是什么规则的处理方法
     * @param id
     * @param ex
     * @return
     */
    public User blockHandlerForGetUser(String id, BlockException ex){
        ex.printStackTrace();
        return new User("流控了!!!");
    }

}

3.测试
Alibaba微服务组件Sentinel
Alibaba微服务组件Sentinel

sentinel降级规则初体验

1.controller代码

@RestController
@Slf4j
public class HelloController {

    private static final String RESOURCE_NAME = "hello";
    private static final String USER_RESOURCE_NAME = "user";
    private static final String DEGRADE_RESOURCE_NAME = "degrade";

    /**
     * 初始化
     */
    @PostConstruct
    public void initDegradeRule(){
        /**
         * 降级规则 异常
         */
        List<DegradeRule> degradeRules = 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);

        //统计时长,时间太短不好测 单位:ms
        degradeRule.setStatIntervalMs(60*1000);

        // 一分钟内: 执行了2次 出现了2次异常 就会触发熔断
        //熔断持续时长:单位 秒
        //一旦触发了熔断,再次请求对应的接口就会直接调用 降级方法。
        //10秒过了之后 --- 半开状态: 恢复接口请求调用,如果第一次请求就异常,再次熔断,不会根据设置的条件进行判断
        degradeRule.setTimeWindow(10);
        degradeRules.add(degradeRule);
        DegradeRuleManager.loadRules(degradeRules);

    }

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

        /**
         *  慢调用比例
         */
        /*TimeUnit.SECONDS.sleep(1);
        return new User("正常");*/
    }

    public User blockHandlerForFb(String id, BlockException ex){
        return new User("熔断降级!!");
    }

}

2.测试
Alibaba微服务组件Sentinel

上一篇:一次由查询转换引起的性能问题的分析


下一篇:什么是Tomcat响应静态资源?