vlambda博客
学习文章列表

Dubbo集成Sentinel实现限流

Sentinel提供了与Dubbo整合的模块Sentinel Apache Dubbo Adapter,可以针对服务提供方和服务消费方进行流控,在使用的时候只需要添加下面依赖:

<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-apache-dubbo-adapter</artifactId> <version>1.7.1</version></dependency>


添加好依赖后,Dubbo服务中的接口和方法就会成为Sentinel中的资源,只需要针对指定资源配置流控规则就可以实现Sentinel流控功能。


Sentinel Apache Dubbo Adapter实现限流的核心原理是基于Dubbo的SPI机制实现Filter扩展,Dubbo的Filter机制是专门为服务提供方和服务消费方调用过程进行拦截设计得到,每次执行远程方法,该拦截都会被执行。


同时,Sentinel Apache Dubbo Adapter还可以自定义开启或者关闭某个过滤器的功能,下面这段代码表示关闭消费端的过滤器。

@Beanpublic ConsumerConfig consumerConfig() { ConsumerConfig consumerConfig = new ConsumerConfig(); consumerConfig.setFilter("-sentinel.dubbo.filter"); return consumerConfig;}


Dubbo服务接入Sentinel Dashboard

spring-cloud-starter-alibaba-sentinel目前无法支持Dubbo服务的限流,所以针对Dubbo服务的限流只能使用sentinel-apache-dubbo-adapter。这个适配组件并没有自动接入sentinel dashboard,所以需要通过以下步骤来进行接入。

  • 引入sentinel-transport-simple-http依赖,这个依赖可以上报应用相关信息到控制台

<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-simple-http</artifactId> <version>1.7.1</version></dependency>
  • 添加启动参数

-Djava.net.perferIPv4Stack=true -Dcsp.sentinel.api.port=8720 -Dcsp.sentinel.dashboard.server=localhost:7777 -Dproject.name=spring-cloud-sentinel-dubbo-provider

参数配置说明:

 - -Djava.net.preferIPv4Stack:表示只支持IPv4。

 - -Dcsp.sentinel.api.port:客户端的port,用于上报应用的信息。

 - -Dproject.name:应用名称,会在Sentinel Dashboard右侧展示。


  • 登陆Sentinel Dashboard之后,进入"簇点链路",就可以看到资源信息

这里的限流可以通过服务接口或者服务方法设置


 - 服务接口:resourceName为接口的全限定名,如com.xxx.sentinel.dubbo.IHelloService.

 - 服务方法:resourceName为接口全限定名:方法名,如com.xxx.sentinel.dubbo.IHelloService:sayHello()。


Dubbo服务限流规则配置

Dubbo的限流规则同样可以通过以下方式来实现

  • Sentinel Dashboard

  • FlowRuleManager.loadRules(rules)


在Sentinel Dashboard中配置流控规则,最终可以持久化到Nacos中,然而规则的持久化机制在Spring Cloud Sentinel中是自动实现的。在Sentinel Apache Dubbo Adapter的组件中并没有实现这个功能,需要我们自己按照下面步骤去实现规则的持久化。

1. 添加sentinel-datasource-nacos的依赖

<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> <version>1.7.1</version></dependency>

2. 通过Sentinel提供的InitFunc扩展点,实现Nacos数据源的配置

public class NacosDataSourceInitFunc implements InitFunc{ private String serverAddr="localhost:8848"; private String groupId="DEFAULT_GROUP"; private String dataId="spring-cloud.sentinel-dubbo.provider-sentinel-flow";
@Override public void init() throws Exception { loadNacosData(); } private void loadNacosData(){ ReadableDataSource<String,List<FlowRule>> flowRuleDataSource= new NacosDataSource<>(serverAddr, groupId, dataId, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() { })); FlowRuleManager.register2Property(flowRuleDataSource.getProperty()); }}

NacosDataSourceInitFunc要实现自动加载,需要在resource目录下的META-INF/services中创建一个名称为com.alibaba.csp.sentinel.init.InitFunc的文件,文件内容为com.sentinel.dubbo.NacosDataSourceInitFunc


3. 访问Sentinel Dashboard,在针对资源创建流控规则的时候,这个规则会同步保存到Nacos配置中心。而当Nacos配置中心发生变化的时候,也会触发事件机制通知Dubbo应用重新加载流控规则。


Sentinel热点限流

Sentinel提供了热点参数限流的策略,它是一种特殊的限流,在普通限流的基础上对同一个受保护的资源根据请求中的参数分别处理,该策略只对包含热点参数的资源调用生效。


热点限流在下面的场景中使用较多:

2. 写数据的服务:例如业务系统提供写数据的服务,数据会写入数据库之类得到存储系统。存储系统的底层会加锁写磁盘上的文件,部分存储系统会将某一类数据写入同一个文件。如果底层写同一个文件,会出现抢占锁的情况,导致出现大量超时和失败。出现这种情况时一般有两种解决办法:修改存储设计、对热点参数限流。


Sentinel通过LRU策略结合滑动窗口机制来实现热点参数的设计,其中LRU策略可以统计单位时间内最常访问的热点数据,滑动窗口机制可以协助统计每个参数的QPS。


热点参数限流的使用

引入热点参数限流依赖包sentinel-parameter-flow-control

<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-parameter-flow-control</artifactId> <version>1.7.1</version></dependency>

接下来创建一个Rest接口,并定义限流点

@RestControllerpublic class ParamRuleController {  private String resourceName = "hello";  @GetMapping("/hello") public String sayHello(@PathParam("id") String id, @PathParam("name") String name) { Entry entry = null; try { entry = SphU.entry(resourceName, EntryType.IN, 1, id); return "success"; } catch (BlockException e) { e.printStackTrace(); return "block"; } finally { if (entry != null) { entry.exit(); } } }}

针对不同的热点参数,需要通过SphU.entry(resourceName, EntryType.IN, 1, id)方法设置。最后一个参数是数组,有多个参数时就按照顺序依次传入。


同时,针对hello资源设置热点参数限流规则,通过ParamFlowRuleManager.loadRules()方法加载热点参数规则

@PostConstructpublic void initParamRule() { ParamFlowRule rule = new ParamFlowRule(resourceName); rule.setParamIdx(0); rule.setGrade(RuleConstant.FLOW_GRADE_QPS); rule.setCount(1); ParamFlowRuleManager.loadRules(Collections.singletonList(rule));}


@SentinelResource热点参数限流

如果通过@SentinelResource注解来定义资源的,当注解所配置的方法上有参数的时候,Sentinel会把这些参数传入SphU.entry(res, args),比如下面这段代码,会把id这个参数作为热点参数进行限流。

@SentinelResource@GetMapping("/hello")public String sayHello(@PathParam("id") String id) { return "hello";}

默认情况下,当用户访问这个接口时就会触发热点限流规则的验证。