Sentinel系列之流控实践(集成SpringCloud)

代码层面的流控

1、创建SpringBoot项目

pom信息如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.2.5.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2.1.1.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

2、编写接口HelloController

@RestController
public class HelloController {

    @GetMapping("/say-hello")
    @SentinelResource(value = "sayHello",blockHandler = "helloBlockHandler")
    public String sayHello() {
        return "hello,sentinel";
    }

    public String helloBlockHandler(BlockException e){
        return "此路不通!";
    }

}

切记,helloBlockHandler与主方法要保持一致,入参可以多一个BlockException

3、编写规则并依赖

这里面有两个实现方式:1、使用配置类初始化规则    2、使用SPI机制实现拓展点规则

1、使用配置类初始化规则

定义一个配置类SentinelConfig,代码如下:

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

    @PostConstruct
    private void initRules() {
        List<FlowRule> rules=new ArrayList<FlowRule>();
        FlowRule rule = new FlowRule();
        rule.setResource("sayHello");//与@SentinelResource中的value保持一致
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(1);
        rule.setLimitApp("default");
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }

}

2、使用SPI机制实现拓展点规则

①实现InitFunc方法,代码如下:

public class FlowRuleInitFunc implements InitFunc {
    public void init() throws Exception {
        List<FlowRule> rules=new ArrayList<FlowRule>();
        FlowRule rule = new FlowRule();
        rule.setResource("sayHello");
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(1);
        rule.setLimitApp("default");
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
}

②在resources下创建META-INF/services/com.alibaba.csp.sentinel.init.InitFunc文件,然后把上面①中的代码所在类的路径添加进去即可

com.cb.springbootsentinel.func.FlowRuleInitFunc

4、启动验证

启动项目,访问:http://localhost:8080/say-hello

可以浏览器或者专门的API工具疯狂请求,就会发现刚开始是正常输出: hello,sentinel

由于限流,后面会出现:此路不通!


补充:使用Sentinel Dashboard控制台对接口直接进行流控

继续在上线的工程中编写,先去除所有的资源埋点(默认情况下,大多都是走控制台直接流控,有可视化页面,不用改代码,那为啥不用呢)

1、创建一个接口类DashController

代码如下:

@RestController
public class DashController {
    @GetMapping("/dash")
    public String dash(){
        return "dash";
    }
}

2、添加配置文件

#注册到dashboard服务名
spring.application.name=sentinel-server

#dashboard访问地址
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8080

3、进入控制台

找到sentinel-server服务,进入 簇点链路(这里会显示所有请求过的接口服务列表),如图

点击流控按钮,会有一个设置弹框,如下图:

为了方便显示效果,单机阈值填写1即可,其他都默认,然后点击新增,然后查看流控规则,会显示刚才我们新增的这条数据,如图:

4、启动服务

访问http://localhost:8080/dash

第一次请求会正常返回字符串:dash

如果频繁的刷新浏览器,最终会出现:Blocked by Sentinel (flow limiting)

就说明,控制台的流控操作到这里就完成且成功了


补充:自定义URL限流异常

类似于手机号白名单的操作,

默认情况下,URL层面的限流会直接返回:Blocked by Sentinel (flow limiting)

代码如下:

@Service
public class CustomUrlBlockHandler implements UrlBlockHandler {
    public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
        httpServletResponse.setHeader("Content-Type","application/json;charset=UTF-8");
        String msg="{\"code\":900,\"msg\":\"访问人数太多了,等会在请求!\"}";
        httpServletResponse.getWriter().write(msg);
    }
}

如果要跳转到中转页面,可以用下面的配置:

spring.sentinel.servlet.block-page.url=要跳转的url

然后重启服务,访问有流控的接口,会返回上述代码中自定义的json字符串的内容,如下:

 


补充:URL请求资源的清洗

也可以说是针对某些特殊接口的统一化

背景:流控,其实就是对http进行统计的,但是我们写接口,会遇到这种接口:/xxxx/xxx/{id},这种接口其实就一个,但是参数是可变的,所以也可以理解每次请求URL都是不一样的,这样的话,流控就没办法做统计了,看代码:

@RestController
public class CleanController {
    @GetMapping("/clean/{id}")
    public String dash(@PathVariable("id") Integer id) {
        return "clean" + id;
    }
}

这样的接口会造成两个问题:

①限流统计不准确,本来就是同一个接口,因为id的可变,导致每次统计的URL不一样

②导致资源数量过多,因为可变参数可能会很多,sentinel默认资源数是6000,如果id是>6000,如果正好也请求了>6000次,那多出来的资源将不会受到限流限制

所以针对这种接口,我们要做一个类似通配符的操作,让对应的这种接口都算在同一个接口下面:想上面的/clean/{id}这种接口全部规整到/clean/*下面,不管后面的参数怎么变,都算是/clean/*下面的请求,这样的话就可以统计了

@Service
public class CustomUrlCleaner implements UrlCleaner {
    public String clean(String url) {
        if (url == null || url.equals("")) {
            return url;
        } else if (url.startsWith("/clean/")) {
            return "/clean/*";
        }
        return url;
    }
}

到这里其实就可以了,访问一下http://localhost:8080/clean/1,再去观察一下Sentinel Dashboard控制台的簇点链路,会出现如图的接口:

你在请求http://localhost:8080/clean/2,也只是有这一个clean/*,然后针对这个接口做流控即可,效果就不展示了哈


下图是以上所有代码的实现的项目结构,只供参考:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值