流量治理神器-Sentinel 究竟是怎么做到讓業(yè)務(wù)方接入簡(jiǎn)單?
點(diǎn)擊上方藍(lán)字“設(shè)為星標(biāo)”

基本使用
基本使用可以直接用SphU類對(duì)資源進(jìn)行保護(hù),使用方式如下:
?
public static void main(String[] args) {// 配置規(guī)則.initFlowRules();while (true) {// 1.5.0 版本開始可以直接利用 try-with-resources 特性try (Entry entry = SphU.entry("HelloWorld")) {// 被保護(hù)的邏輯System.out.println("hello world");} catch (BlockException ex) {// 處理被流控的邏輯System.out.println("blocked!");}}}
?
這種方式其實(shí)就違背了前面我們說的耦合問題,跟業(yè)務(wù)代碼耦合在了一起,擴(kuò)展性不好。
?

注解使用
可以在基本使用的基礎(chǔ)上優(yōu)化下,不在業(yè)務(wù)代碼中出現(xiàn)SphU相關(guān)的代碼,比如說定義一個(gè)注解在來實(shí)現(xiàn)這個(gè)功能。
?
使用方式如下:
?
@SentinelResource("HelloWorld")public void helloWorld() {// 資源中的邏輯System.out.println("hello world");}
?
通過注解和切面我們就可以將邏輯收攏,不會(huì)在散落在各個(gè)業(yè)務(wù)代碼中,就算有一天你的限流方式改成了其他框架,注解都不用變,直接將切面里面的邏輯更新即可。
?

適配Dubbo
當(dāng)我們需要對(duì)Dubbo的接口進(jìn)行限流時(shí),使用原生的代碼方式和注解方式都可以,但是這樣就需要我們?cè)诿總€(gè)調(diào)用的地方進(jìn)行改造,那么能不能做成自動(dòng)適配的方式,連注解都不用加呢?
?
在Sentinel中有一個(gè)sentinel-apache-dubbo-adapter的模塊就是專門用于適配dubbo的。原理就是通過Dubbo的Filter機(jī)制來實(shí)現(xiàn)通用的適配邏輯。
?
有consumer Filter和provider Filter, 業(yè)務(wù)方只需要依賴這個(gè)包,就自動(dòng)適配dubbo了,然后通過Sentinel的控制臺(tái)進(jìn)行配置,就可以達(dá)到限流和熔斷的效果了。
?

適配Feign

適配Feign
Dubbo都適配了,怎么能少的了Feign呢,其實(shí)原理都是一樣,也是通過Feign的Filter機(jī)制來適配。不過Feign的適配整合放在了Spring Cloud Alibaba中。
?
通過定義SentinelInvocationHandler,在invoke方法中適配Sentinel的邏輯。詳細(xì)代碼在com.alibaba.cloud.sentinel.feign.SentinelInvocationHandler中。
?

適配Zuul
對(duì)于Zuul的適配同樣有一個(gè)單獨(dú)的模塊sentinel-zuul-adapter。原理呢還是一樣,Zuul也有Filter, 既然是限流在Zuul中肯定是用pre filter。
?
實(shí)現(xiàn)類是com.alibaba.csp.sentinel.adapter.gateway.zuul.filters.SentinelZuulPreFilter,貼一小段代碼給大家看下:
?
public Object run() throws ZuulException {RequestContext ctx = RequestContext.getCurrentContext();String origin = this.parseOrigin(ctx.getRequest());String routeId = (String)ctx.get("proxy");DequeasyncEntries = new ArrayDeque(); String fallBackRoute = routeId;try {if (StringUtil.isNotBlank(routeId)) {ContextUtil.enter("sentinel_gateway_context$$route$$" + routeId, origin);this.doSentinelEntry(routeId, 0, ctx, asyncEntries);}Set<String> matchingApis = this.pickMatchingApiDefinitions(ctx);if (!matchingApis.isEmpty() && ContextUtil.getContext() == null) {ContextUtil.enter("zuul_default_context", origin);}Iterator var14 = matchingApis.iterator();while(var14.hasNext()) {String apiName = (String)var14.next();this.doSentinelEntry(apiName, 1, ctx, asyncEntries);}} catch (BlockException var12) {ZuulBlockFallbackProvider zuulBlockFallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(fallBackRoute);BlockResponse blockResponse = zuulBlockFallbackProvider.fallbackResponse(fallBackRoute, var12);ctx.setRouteHost((URL)null);ctx.set("serviceId", (Object)null);ctx.setResponseBody(blockResponse.toString());ctx.setResponseStatusCode(blockResponse.getCode());ctx.getResponse().setContentType("application/json; charset=utf-8");} finally {if (!asyncEntries.isEmpty()) {ctx.put("_sentinel_entries", asyncEntries);}}return null;}
?

總結(jié)

總結(jié)
本文只是為了讓大家了解,在開發(fā)一個(gè)底層框架的時(shí)候,需要考慮的問題。這個(gè)問題就是使用起來越簡(jiǎn)單越好,這才是好的框架該有的樣子。
?
當(dāng)然,Sentinel還適配了其他很多的框架,比如httpclient, Spring Cloud Gateway啊等,底層思想都是相同的,都是利用擴(kuò)展機(jī)制進(jìn)行統(tǒng)一處理。
?
大家好,我是從古代穿越過來的美男子:架構(gòu)擺渡人。我將把我的武功秘籍全部傳授與你們,覺得有用請(qǐng)分享給身邊的朋友。來個(gè)三連吧,感謝各位!另外我還在B站錄制了《真實(shí)訂單業(yè)務(wù),億級(jí)數(shù)據(jù)帶你實(shí)戰(zhàn)分庫分表》的實(shí)戰(zhàn)課程,記得去學(xué)習(xí)哦!
點(diǎn)擊閱讀原文直達(dá)主頁
