斷路器:https://martinfowler.com/bliki/CircutiBreaker.htmlhtml
核心思想:java
在斷路器對象中封裝受保護的方法調用。web
該斷路器監控調用和斷路狀況spring
調用失敗觸發閾值後,後續調用直接由短路器返回錯誤,再也不執行實際調用。服務器
理解:網絡
客戶端經過circuit breaker調用服務提供者,正常的時候能夠調用。若是服務提供方出現了問題,發生了超時, 前幾回能夠超時處理, 到達一個閥值能夠經過斷路器進行處理, 就再也不向服務方發起請求。app
import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @Aspect @Component @Slf4j public class CircuitBreakerAspect { // 閥值 private static final Integer THRESHOLD = 3; //記錄失敗的次數 private Map<String, AtomicInteger> counter = new ConcurrentHashMap<>(); // 記錄被保護的次數 private Map<String, AtomicInteger> breakCounter = new ConcurrentHashMap<>(); /** * * @param pjp 程序鏈接點 * @return * @throws Throwable */ @Around("execution(* 攔截的區域") public Object doWithCircuitBreaker(ProceedingJoinPoint pjp) throws Throwable { // 獲取當前執行的方法 String signature = pjp.getSignature().toLongString(); log.info("Invoke {}", signature); Object retVal; try { if (counter.containsKey(signature)) { // 失敗次數達到預製,若是保護次數沒到,返回null if (counter.get(signature).get() > THRESHOLD && breakCounter.get(signature).get() < THRESHOLD) { log.warn("Circuit breaker return null, break {} times.", breakCounter.get(signature).incrementAndGet()); return null; } } else { counter.put(signature, new AtomicInteger(0)); breakCounter.put(signature, new AtomicInteger(0)); } retVal = pjp.proceed(); counter.get(signature).set(0); breakCounter.get(signature).set(0); } catch (Throwable t) { log.warn("Circuit breaker counter: {}, Throwable {}", counter.get(signature).incrementAndGet(), t.getMessage()); breakCounter.get(signature).set(0); throw t; } return retVal; } }
Hystrix [hɪst'rɪks],中文含義是豪豬,因其背上長滿棘刺,從而擁有了自我保護的能力。本文所說的Hystrix是Netflix開源的一款容錯框架,一樣具備自我保護能力。爲了實現容錯和自我保護,下面咱們看看Hystrix如何設計和實現的。框架
Hystrix設計目標:ide
實現了斷路服務器模式ui
在須要服務熔斷的方法上添加@HystrixCommand註解, fallbackMethod指定熔斷的地址,默認狀況下@HystrixCommand是在另一個線程執行的。能夠作一些超時的處理。
@HystrixProperty(name="excution.isolation.strategy",value="SEMAPHORE")設置爲信號
Hystrix配置項參考:
Spring cloud 支持
spring-cloud-starter-netfixi-hystrix
@EnableCircuitBreaker
Feign支持
feign.hystrix.enable=true
@FeignClient
fallback / fallbackFactory
簡單示例:
pom引入
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
application.properties
feign.client.config.default.connect-timeout=500
feign.client.config.default.read-timeout=500
#開啓feign支持 feign.hystrix.enabled=true
#cousul鏈接配置
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.prefer-ip-address=true
bootstarp.properties
spring.application.name=name-service
開啓註解:
@EnableDiscoveryClient // 註冊發現服務
@EnableFeignClients // feign的支持
@EnableCircuitBreaker // feignClient的演示
//Spring cloud 支持 @PostMapping("/order") @HystrixCommand(fallbackMethod = "fallbackCreateOrder") public CoffeeOrder createOrder() { /*業務代碼*/ return order; } public CoffeeOrder fallbackCreateOrder() { log.warn("Fallback to NULL order."); return null; }
//FeignClient 的支持 import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; @FeignClient(name = "waiter-service", contextId = "coffee", qualifier = "coffeeService", path="/coffee", fallback = FallbackCoffeeService.class) // 若是用了Fallback,不要在接口上加@RequestMapping,path能夠用在這裏 public interface TestService { @GetMapping("/{id}") Product getById(@PathVariable Long id); } /*實現TestService*/ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.util.Collections; import java.util.List; @Slf4j @Component public class FallbackTestService implements TestService{ @Override public Product getById(Long id) { /**發送了壟斷的邏輯代碼*/ return null; } } /**Controller調用*/ @GetMapping("testGetById") public String testGetById() { TestService.getById((long) 1); return ""; }