网站首页
Java
站长
开源
框架
理论
JS
Linux
DB
服务器
NET
生活
软件
PHP
其他
您的位置:首页 > 开源 > Sentinel入门流控编码方式
Sentinel入门流控编码方式
2023-2-23    851    0

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应过载保护、热点流量防护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
sentinel.jpg


Sentinel 分为两个部分:
核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
Sentinel 社区正在将流量治理相关标准抽出到 OpenSergo 标准中,Sentinel 作为流量治理标准实现。有关 Sentinel 流控降级与容错 spec 的最新进展,请参考 opensergo-specification,也欢迎社区一起来完善标准与实现。
Sentinel 社区官方网站:https://sentinelguard.io/ 


入门步骤,首先引入POM依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.8</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>Sentinel</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Sentinel</name>
    <description>Sentinel</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!-- ______________________________________________ Spring -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- ______________________________________________ Ali sentinel -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>1.8.5</version>
        </dependency>
        <!-- 连接控制台,启动后手动触发一次才能在控制台看到 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-transport-simple-http</artifactId>
            <version>1.8.5</version>
        </dependency>
        <!-- 使用注解配置时 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-annotation-aspectj</artifactId>
            <version>1.8.5</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-parameter-flow-control</artifactId>
            <version>1.8.5</version>
        </dependency>
        <!-- ______________________________________________ tools -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.0.M1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>


编码流控规则,这里采用手动编码,这里没有接入控制台

package com.example.sentinel.config;

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 org.springframework.context.annotation.Configuration;

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

@Configuration
public class InitSRule {
    /**
     * Sentinel log base directory is: C:\Users\cuisu\logs\csp\
     * p 代表通过的请求。
     * block 代表被阻止的请求。
     * s 代表成功执行完成的请求个数。
     * e 代表用户自定义的异常。
     * rt 代表平均响应时长。
     */
    @PostConstruct
    public void initFlowRule() {
        //1.创建存放限流规则的集合
        List<FlowRule> rules = new ArrayList<>();//2.创建限流规则
        FlowRule rule = new FlowRule();
        //定义资源,表示sentinel会对哪个资源生效
        rule.setResource("getHello");
        //定义限流规则类型为QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //定义了QPS每秒能通过的请求个数
        rule.setCount(1);
        //3.将限流规则存放到集合中
        rules.add(rule);
        //4.加载限流规则
        FlowRuleManager.loadRules(rules);
    }
}


Sentinel的流控编码方式有如下几种

1) 抛出异常的方式

/**
 * SphU 包含了 try-catch 风格的 API。
 * 用这种方式,当资源发生了限流之后会抛出 BlockException。这个时候可以捕捉异常,进行限流之后的逻辑处理。
 */
@RequestMapping("/getHello1")
public String getHello1() {
    Entry entry = null;
    try {
        entry = SphU.entry("getHello");
        return "hello,now is :" + DateUtil.now();
    } catch (BlockException blockException) {
        return "系统繁忙";
    } catch (Exception e) {
        return "系统异常";
    } finally {
        // 务必保证 exit,务必保证每个 entry 与 exit 配对
        if (entry != null) {
            entry.exit();
        }
    }
}

2) 返回布尔值的方式

/**
 * 抛出异常的方式是当被限流时以抛出异常的形式感知,我们通过捕获异常进行限流的处理,这种方式跟上面不同的在于不抛出异常,而是返回一个布尔值
 * 我们通过判断布尔值来进行限流逻辑的处理。这样我们就可以很容易写出if-else结构的代码。
 */
@RequestMapping("/getHello2")
public String getHello2() {
    if (SphO.entry("getHello")) {
        try {
            return "hello,now is :" + DateUtil.now();
        } catch (Exception e) {
            e.printStackTrace();
            return "系统异常";
        } finally {
            SphO.exit(); //关闭资源
        }
    } else {
        return "系统繁忙";
    }
}

3) 注解的方式

刚才的POM中已经引入了注解的配置,这里再编写代码使生效

package com.example.sentinel.config;
import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * 用于配置Sentinel注解生效
 */
@Configuration
public class SAspectConfig {
    @Bean
    public SentinelResourceAspect sentinelResourceAspect() {
        return new SentinelResourceAspect();
    }
}

然后使用注解编写代码

/**
 * 采用注解方式
 * value是资源名称,是必填项
 * blockHandler填限流处理的方法名称
 * fallback 接口出现异常时,Sentinel提供熔断降级的功能
 */
@RequestMapping("/getHello3")
@SentinelResource(value = "getHello", blockHandler = "helloBlock", fallback = "helloFallBack")
public String getHello3() {
    return "hello,now is :" + DateUtil.now();
}
public String helloBlock(BlockException e) {
    return "系统繁忙";
}
/**
 * 注意是异常时才会熔断
 */
public String helloFallBack(Throwable e) {
    return "系统异常,请稍后重试";
}

4) 熔断降级(看3注解中fallback)
5) 异步调用

在启动类增加注解启用异步支持

package com.example.sentinel;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync // 异步调用
public class SentinelApplication {
    public static void main(String[] args) {
        SpringApplication.run(SentinelApplication.class, args);
    }
}

编写异步调用的实现代码

package com.example.sentinel.service;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
 * 异步调用接口
 */
@Service
public class AsyncService {
    @Async
    public void hello(){
        System.out.println("异步开始");
        try {
            Thread.sleep(5000);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("异步结束");
    }
}

编写Controller代码

/**
 * 异步调用链路的统计
 */
@Autowired
private AsyncService asyncService;
@RequestMapping("/getHello4")
public String getHello4() {
    AsyncEntry asyncEntry = null;
    try {
        asyncEntry = SphU.asyncEntry("getHello");
        asyncService.hello();
        return "hello,now is :" + DateUtil.now();
    } catch (BlockException blockException) {
        return "系统繁忙";
    } catch (Exception e) {
        e.printStackTrace();
        return "系统异常,请稍后重试";
    } finally {
        if (asyncEntry != null) {
            asyncEntry.exit();
        }
    }
}


因此这里设置了QPS为1,浏览器打开地址刷新时,连续刷新,就会提示流控。

上一篇: Sentinel入门Controller自动定义为资源
下一篇: 使用Bitmaps位图实现Redis签到
发表评论:
您的网名:
个人主页:
编辑内容: