Sentinel提供了與Dubbo整合的模塊Sentinel Apache Dubbo Adapter,可以針對(duì)服務(wù)提供方和服務(wù)消費(fèi)方進(jìn)行流控,在使用的時(shí)候只需要添加下面依賴:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-apache-dubbo-adapter</artifactId>
<version>1.7.1</version>
</dependency>
添加好依賴后稿黍,Dubbo服務(wù)中的接口和方法就會(huì)成為Sentinel中的資源,只需要針對(duì)指定資源配置流控規(guī)則就可以實(shí)現(xiàn)Sentinel流控功能涯穷。
Sentinel Apache Dubbo Adapter實(shí)現(xiàn)限流的核心原理是基于Dubbo的SPI機(jī)制實(shí)現(xiàn)Filter擴(kuò)展八拱,Dubbo的Filter機(jī)制是專門為服務(wù)提供方和服務(wù)消費(fèi)方調(diào)用過(guò)程進(jìn)行攔截設(shè)計(jì)得到,每次執(zhí)行遠(yuǎn)程方法楔壤,該攔截都會(huì)被執(zhí)行鹤啡。
同時(shí),Sentinel Apache Dubbo Adapter還可以自定義開(kāi)啟或者關(guān)閉某個(gè)過(guò)濾器的功能挺邀,下面這段代碼表示關(guān)閉消費(fèi)端的過(guò)濾器揉忘。
@Bean
public ConsumerConfig consumerConfig() {
ConsumerConfig consumerConfig = new ConsumerConfig();
consumerConfig.setFilter("-sentinel.dubbo.filter");
return consumerConfig;
}
Dubbo服務(wù)接入Sentinel Dashboard
spring-cloud-starter-alibaba-sentinel目前無(wú)法支持Dubbo服務(wù)的限流跳座,所以針對(duì)Dubbo服務(wù)的限流只能使用sentinel-apache-dubbo-adapter。這個(gè)適配組件并沒(méi)有自動(dòng)接入sentinel dashboard泣矛,所以需要通過(guò)以下步驟來(lái)進(jìn)行接入疲眷。
- 引入sentinel-transport-simple-http依賴,這個(gè)依賴可以上報(bào)應(yīng)用相關(guān)信息到控制臺(tái)
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.7.1</version>
</dependency>
- 添加啟動(dòng)參數(shù)
-Djava.net.perferIPv4Stack=true -Dcsp.sentinel.api.port=8720 -Dcsp.sentinel.dashboard.server=localhost:7777 -Dproject.name=spring-cloud-sentinel-dubbo-provider
參數(shù)配置說(shuō)明:
-Djava.net.preferIPv4Stack:表示只支持IPv4您朽。
-Dcsp.sentinel.api.port:客戶端的port狂丝,用于上報(bào)應(yīng)用的信息。
-Dcsp.sentinel.dashboard.server:Sentinel Dashboard地址
-Dproject.name:應(yīng)用名稱哗总,會(huì)在Sentinel Dashboard右側(cè)展示几颜。
-
登陸Sentinel Dashboard之后,進(jìn)入"簇點(diǎn)鏈路"讯屈,就可以看到資源信息
這里的限流可以通過(guò)服務(wù)接口或者服務(wù)方法設(shè)置
服務(wù)接口:resourceName為接口的全限定名蛋哭,如com.xxx.sentinel.dubbo.IHelloService.
服務(wù)方法:resourceName為接口全限定名:方法名,如com.xxx.sentinel.dubbo.IHelloService:sayHello()涮母。
Dubbo服務(wù)限流規(guī)則配置
Dubbo的限流規(guī)則同樣可以通過(guò)以下方式來(lái)實(shí)現(xiàn)
- Sentinel Dashboard
- FlowRuleManager.loadRules(rules)谆趾。
在Sentinel Dashboard中配置流控規(guī)則,最終可以持久化到Nacos中叛本,然而規(guī)則的持久化機(jī)制在Spring Cloud Sentinel中是自動(dòng)實(shí)現(xiàn)的沪蓬。在Sentinel Apache Dubbo Adapter的組件中并沒(méi)有實(shí)現(xiàn)這個(gè)功能,需要我們自己按照下面步驟去實(shí)現(xiàn)規(guī)則的持久化来候。
- 添加sentinel-datasource-nacos的依賴
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.7.1</version>
</dependency>
- 通過(guò)Sentinel提供的InitFunc擴(kuò)展點(diǎn)跷叉,實(shí)現(xiàn)Nacos數(shù)據(jù)源的配置
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要實(shí)現(xiàn)自動(dòng)加載,需要在resource目錄下的META-INF/services中創(chuàng)建一個(gè)名稱為com.alibaba.csp.sentinel.init.InitFunc的文件营搅,文件內(nèi)容為com.sentinel.dubbo.NacosDataSourceInitFunc
- 訪問(wèn)Sentinel Dashboard云挟,在針對(duì)資源創(chuàng)建流控規(guī)則的時(shí)候,這個(gè)規(guī)則會(huì)同步保存到Nacos配置中心剧防。而當(dāng)Nacos配置中心發(fā)生變化的時(shí)候植锉,也會(huì)觸發(fā)事件機(jī)制通知Dubbo應(yīng)用重新加載流控規(guī)則。
Sentinel熱點(diǎn)限流
Sentinel提供了熱點(diǎn)參數(shù)限流的策略峭拘,它是一種特殊的限流俊庇,在普通限流的基礎(chǔ)上對(duì)同一個(gè)受保護(hù)的資源根據(jù)請(qǐng)求中的參數(shù)分別處理,該策略只對(duì)包含熱點(diǎn)參數(shù)的資源調(diào)用生效鸡挠。
熱點(diǎn)限流在下面的場(chǎng)景中使用較多:
- 服務(wù)網(wǎng)關(guān)層:例如防止網(wǎng)絡(luò)爬蟲和惡意攻擊辉饱,一種常用的方法就是限制爬蟲的IP地址,客戶端IP地址就是一種熱點(diǎn)參數(shù)拣展。
- 寫數(shù)據(jù)的服務(wù):例如業(yè)務(wù)系統(tǒng)提供寫數(shù)據(jù)的服務(wù)彭沼,數(shù)據(jù)會(huì)寫入數(shù)據(jù)庫(kù)之類得到存儲(chǔ)系統(tǒng)。存儲(chǔ)系統(tǒng)的底層會(huì)加鎖寫磁盤上的文件备埃,部分存儲(chǔ)系統(tǒng)會(huì)將某一類數(shù)據(jù)寫入同一個(gè)文件姓惑。如果底層寫同一個(gè)文件褐奴,會(huì)出現(xiàn)搶占鎖的情況,導(dǎo)致出現(xiàn)大量超時(shí)和失敗于毙。出現(xiàn)這種情況時(shí)一般有兩種解決辦法:修改存儲(chǔ)設(shè)計(jì)敦冬、對(duì)熱點(diǎn)參數(shù)限流。
Sentinel通過(guò)LRU策略結(jié)合滑動(dòng)窗口機(jī)制來(lái)實(shí)現(xiàn)熱點(diǎn)參數(shù)的設(shè)計(jì)唯沮,其中LRU策略可以統(tǒng)計(jì)單位時(shí)間內(nèi)最常訪問(wèn)的熱點(diǎn)數(shù)據(jù)脖旱,滑動(dòng)窗口機(jī)制可以協(xié)助統(tǒng)計(jì)每個(gè)參數(shù)的QPS。
熱點(diǎn)參數(shù)限流的使用
引入熱點(diǎn)參數(shù)限流依賴包sentinel-parameter-flow-control
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
<version>1.7.1</version>
</dependency>
接下來(lái)創(chuàng)建一個(gè)Rest接口介蛉,并定義限流點(diǎn)
@RestController
public 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();
}
}
}
}
針對(duì)不同的熱點(diǎn)參數(shù)萌庆,需要通過(guò)SphU.entry(resourceName, EntryType.IN, 1, id)方法設(shè)置。最后一個(gè)參數(shù)是數(shù)組币旧,有多個(gè)參數(shù)時(shí)就按照順序依次傳入践险。
同時(shí),針對(duì)hello資源設(shè)置熱點(diǎn)參數(shù)限流規(guī)則吹菱,通過(guò)ParamFlowRuleManager.loadRules()方法加載熱點(diǎn)參數(shù)規(guī)則
@PostConstruct
public 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熱點(diǎn)參數(shù)限流
如果通過(guò)@SentinelResource注解來(lái)定義資源的捏境,當(dāng)注解所配置的方法上有參數(shù)的時(shí)候,Sentinel會(huì)把這些參數(shù)傳入SphU.entry(res, args)毁葱,比如下面這段代碼,會(huì)把id這個(gè)參數(shù)作為熱點(diǎn)參數(shù)進(jìn)行限流贰剥。
@SentinelResource
@GetMapping("/hello")
public String sayHello(@PathParam("id") String id) {
return "hello";
}
默認(rèn)情況下倾剿,當(dāng)用戶訪問(wèn)這個(gè)接口時(shí)就會(huì)觸發(fā)熱點(diǎn)限流規(guī)則的驗(yàn)證。