Sentinel 是阿里中間件團隊開源的,面向分佈式服務架構的輕量級高可用流量控制組件,主要以流量爲切入點,從流量控制、熔斷降級、系統負載保護等多個維度來幫助用戶保護服務的穩定性。這裏你們可能會問:Sentinel 和以前經常使用的熔斷降級庫 Netflix Hystrix 有什麼異同呢?Sentinel官網有一個對比和Hystrix遷移到sentinel的文章,這裏摘抄一個總結的表格,具體的對比能夠點此 連接 查看。 java
功能對比
從對比的表格能夠明顯看到,Sentinel比Hystrix在功能性上還要強大一些。git
Sentinel 功能主要體如今三個方面github
對於系統來講,任意時間到來的請求每每是隨機不可控的,而系統的處理能力是有限的。咱們須要根據系統的處理能力對流量進行控制。 web
控制角度以下:spring
當檢測到調用鏈路中某個資源出現不穩定的表現,例如請求響應時間長或異常比例升高的時候,則對這個資源的調用進行限制,讓請求快速失敗,避免影響到其它的資源而致使級聯故障。手段以下json
Sentinel 同時提供系統維度的自適應保護能力。防止雪崩,是系統防禦中重要的一環。當系統負載較高的時候,若是還持續讓請求進入,可能會致使系統崩潰,沒法響應。在集羣環境下,網絡負載均衡會把本應這臺機器承載的流量轉發到其它的機器上去。若是 這個時候其它的機器也處在一個邊緣狀態的時候,這個增長的流量就會致使這臺機器也崩潰,最後致使整個集羣不可用。api
針對這個狀況,Sentinel 提供了對應的保護機制,讓系統的入口流量和系統的負載達到一個平衡,保證系統在能力範圍以內處理最多的請求。markdown
這裏我使用sentinel 是基於gradle配置,兼容spring clould alibaba,因此添加以下依賴網絡
compile'com.alibaba.cloud:spring-cloud-starter-alibaba-sentinel:2.1.0.RELEASE' compile group: 'com.alibaba.csp', name: 'sentinel-transport-simple-http', version: '1.6.3'
Sentinel 提供了 @SentinelResource 註解用於定義資源,並提供了 AspectJ 的擴展用於自動定義資源、處理 BlockException等,固然也支持使用aop的方式,這裏演示使用aop的方式,添加以下配置類架構
@Configuration public class SentinelAspectConfiguration { @Bean public SentinelResourceAspect sentinelResourceAspect() { return new SentinelResourceAspect();
}
}
@SentinelResource
用於定義資源,並提供可選的異常處理和 fallback 配置項 。@SentinelResource 註解包含如下屬性
服務具體實現類
@Service @Slf4j public class HelloProviderServiceImpl implements HelloProviderService { @Autowired private ConfigurableEnvironment configurableEnvironment; // 對應的 `handleException` 函數須要位於 `ExceptionUtil` 類中,而且必須爲 static 函數 @Override @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = { ExceptionUtil.class}) public void test() { log.info("Test"); } @Override @SentinelResource(value = "sayHi", blockHandler = "exceptionHandler", fallback = "helloFallback") public String sayHi(long time) { if (time < 0) { throw new IllegalArgumentException("invalid arg"); } try { Thread.sleep(time); } catch (InterruptedException e) { throw new IllegalArgumentException("inter arg"); } return String.format("Hello time %d", time); }
// 這裏俗稱資源埋點,在設置限流策略的時候會根據此埋點來控制 @Override @SentinelResource(value = "helloAnother", defaultFallback = "defaultFallback", exceptionsToIgnore = {IllegalStateException.class}) public String helloAnother(String name) { if (name == null || "bad".equals(name)) { throw new IllegalArgumentException("oops"); } if ("foo".equals(name)) { throw new IllegalStateException("oops"); } return "Hello, " + name; } // Fallback 函數,函數簽名與原函數一致或加一個 Throwable 類型的參數. public String helloFallback(long s, Throwable ex) { log.error("fallbackHandler:" + s); return "Oops fallbackHandler, error occurred at " + s; } //默認的 fallback 函數名稱 public String defaultFallback() { log.info("Go to default fallback"); return "default_fallback"; } // Block 異常處理函數,參數最後多一個 BlockException,其他與原函數一致. public String exceptionHandler(long s, BlockException ex) { // Do some log here. return "Oops,exceptionHandler, error occurred at " + s; } }
服務接口
public interface HelloProviderService { public String sayHi(long t) throws InterruptedException; String helloAnother(String name); void test(); }
ExceptionUtil類
@Slf4j public final class ExceptionUtil { public static void handleException(BlockException ex) { log.info("Oops: " + ex.getClass().getCanonicalName()); } }
controller 類
@RestController @Slf4j public class HelloProviderController { @Autowired HelloProviderServiceImpl helloServiceProviderService; @GetMapping("/sayHi") public String sayHi(@RequestParam(required = false) Long time) throws Exception { if (time == null) { time = 300L; } helloServiceProviderService.test();
return helloServiceProviderService.sayHi(time); } @GetMapping("baz/{name}") public String apiBaz(@PathVariable("name") String name) { return helloServiceProviderService.helloAnother(name); } }
一個輕量級的開源控制檯,它提供機器發現以及健康狀況管理、監控(單機和集羣),規則管理和推送的功能。主要能夠經過該控制檯對服務端設置的資源埋點進行動態的限流配置推送,這樣能夠靈活的設置限流策略而不用在代碼裏寫死
平均響應時間 (DEGRADE_GRADE_RT):當 1s 內持續進入 5 個請求,對應時刻的平均響應時間(秒級)均超過閾值(count,以 ms 爲單位),那麼在接下的時間窗口(DegradeRule 中的 timeWindow,以 s 爲單位)以內,對這個方法的調用都會自動地熔斷(拋出 DegradeException)。注意 Sentinel 默認統計的 RT 上限是 4900 ms,超出此閾值的都會算做 4900 ms,若須要變動此上限能夠經過啓動配置項 -Dcsp.sentinel.statistic.max.rt=xxx 來配置。
異常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):當資源的每秒請求量 >= 5,而且每秒異常總數佔經過量的比值超過閾值(DegradeRule 中的 count)以後,資源進入降級狀態,即在接下的時間窗口(DegradeRule 中的 timeWindow,以 s 爲單位)以內,對這個方法的調用都會自動地返回。異常比率的閾值範圍是 [0.0, 1.0],表明 0% - 100%。
異常數 (DEGRADE_GRADE_EXCEPTION_COUNT):當資源近 1 分鐘的異常數目超過閾值以後會進行熔斷。注意因爲統計時間窗口是分鐘級別的,若 timeWindow 小於 60s,則結束熔斷狀態後仍可能再進入熔斷狀態。
能夠啓用Sentinel 控制檯,在控制檯上直接配置熔斷降級規則。
Feign是Netflix公司開源的輕量級的一種負載均衡的HTTP客戶端,,使用Feign調用API就像調用本地方法同樣,從避免了 調用目標微服務時,須要不斷的解析/封裝json 數據的繁瑣。 Spring Cloud引入Feign而且集成了Ribbon實現客戶端負載均衡調用。 通俗一點講:能夠像調用本地方法同樣的調用遠程服務的方法。
固然其中也有很多坑等踩。
Sentinel 適配了 Fegin組件。若是想使用,除了引入 spring-cloud-starter-alibaba-sentinel
的依賴外還須要 2 個步驟:
配置文件打開 Sentinel 對 Feign 的支持:feign.sentinel.enabled=true
openfeign starter
依賴使 sentinel starter
中的自動化配置類生效:compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-openfeign', version: '2.1.3.RELEASE'
添加接口 EchoService類,該接口經過@FeignClient(name = "service-provider")註解來綁定該接口對應service01服務
@FeignClient(name = "nacos-provider-sentianel1", fallback = EchoServiceFallback.class, configuration = FeignConfiguration.class) public interface EchoService { @GetMapping(value = "/sayHi") String sayHi(@RequestParam(value = "time", required = false) Long time); @RequestMapping("/api/{name}") String apiBaz(@PathVariable("name") String name); }
其中 @FeignClient 中name 中的值做爲 提供服務提供方的名稱,該接口中配置當前服務須要調用nacos-provider-sentianel1服務提供的接口。nacos-provider-sentianel1註冊到註冊服務上,我這裏使用的是Nacos.
服務配置以下
nacos-provider-sentianel1 中的controller是這個樣子的,這裏能夠看到 和EchoService中的方法簽名都是一致的
@RestController
public class HelloProviderController2 { @GetMapping("/echo") public String helloConsumer(@RequestParam(required = false) Long time) { return "echo"; } @GetMapping("/api/{name}") public String apiBaz(@PathVariable("name") String name) { return "another provider " + name; } }
添加 EchoServiceFallback,這裏是fegin的Fallback機制,主要用來作容錯處理。由於
在網絡請求時,可能會出現異常請求,若是還想再異常狀況下使系統可用,那麼就須要容錯處理。
@Component。 public class EchoServiceFallback implements EchoService { @Override public String sayHi(Long time) { return "sayHi fallback"; } @Override public String apiBaz(String name) { return "apiBaz fallback"; } }
添加FeignConfiguration
@Configuration
public class FeignConfiguration { @Bean public EchoServiceFallback echoServiceFallback() { return new EchoServiceFallback(); } }
在上文HelloProviderServiceImpl的基礎上添加EchoService調用
@Service @Slf4j public class HelloProviderServiceImpl implements HelloProviderService { @Autowired private ConfigurableEnvironment configurableEnvironment; @Autowired EchoService echoService; // 對應的 `handleException` 函數須要位於 `ExceptionUtil` 類中,而且必須爲 static 函數 @Override @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = { ExceptionUtil.class}) public void test() { log.info("Test"); } @Override @SentinelResource(value = "sayHi", blockHandler = "exceptionHandler", fallback = "helloFallback") public String sayHi(long time) { if (time < 0) { throw new IllegalArgumentException("invalid arg"); } try { Thread.sleep(time); } catch (InterruptedException e) { throw new IllegalArgumentException("inter arg"); } return String.format("Hello time %d", time); } @Override @SentinelResource(value = "helloAnother", defaultFallback = "defaultFallback", exceptionsToIgnore = {IllegalStateException.class}) public String helloAnother(String name) { if (name == null || "bad".equals(name)) { throw new IllegalArgumentException("oops"); } if ("foo".equals(name)) { throw new IllegalStateException("oops"); } return "Hello, " + name; } // Fallback 函數,函數簽名與原函數一致或加一個 Throwable 類型的參數. public String helloFallback(long s, Throwable ex) { log.error("fallbackHandler:" + s); return "Oops fallbackHandler, error occurred at " + s; } //默認的 fallback 函數名稱 public String defaultFallback() { log.info("Go to default fallback"); return echoService.apiBaz("bad"); //return "default_fallback"; } // Block 異常處理函數,參數最後多一個 BlockException,其他與原函數一致. public String exceptionHandler(long s, BlockException ex) { // Do some log here. return "Oops,exceptionHandler, error occurred at " + s; } }
這裏咱們在defaultFallback中使用 echoService.apiBaz("bad") 來調用nacos-provider-sentianel1 的apiBaz方法
在sentinel控制檯中配置helloAnother的降級規則,當觸發降級後,將會調用acos-provider-sentianel1服務的apiBaz方法,返回結果。
使用sentinel控制系統流量,當系統流超出當前服務的接受範圍的時候,能夠經過Feign 調用降級服務,這樣就可構成一個最基礎的熔斷降級模塊,固然Feign中還集成了Ribbon,能夠經過配置實現客戶端負載均衡調用。