hystrix使用背景和場景
所有的系統(tǒng),特別是分布式系統(tǒng),都會遇到故障,如何構(gòu)建系統(tǒng)應(yīng)對這種故障呢?當(dāng)服務(wù)崩潰了,很容易檢查這個服務(wù)不在了,應(yīng)用程序很容易繞過他。但一個服務(wù)運(yùn)行很慢,檢查到這個服務(wù)性能不佳并繞過他非常的困難。性能不佳的服務(wù)很容易造成成資源的耗盡(線程池或者數(shù)據(jù)庫的連接),從而影響連鎖反應(yīng),就是傳說中的雪崩現(xiàn)象。因此如果沒有一個有效的保護(hù)措施,一個性能不佳的服務(wù)很容易拖垮整個應(yīng)用程序。
在遠(yuǎn)程服務(wù)性能不佳時候,客戶端的表現(xiàn)直接影響到問題向上游的傳播,在hystrix,一共有四種模式
- 客戶端負(fù)載均衡(client load balance)模式
- 熔斷器(circuit breaker)模式
- 后備(fallback)模式
- 艙壁(bulkhead)模式
- 客戶端負(fù)載均衡已經(jīng)在eureka中講到,這里就先不提了
- 熔斷器模式
當(dāng)遠(yuǎn)程服務(wù)被調(diào)用時,熔斷器會監(jiān)視這個調(diào)用,如果調(diào)用時間太長,熔斷器將會介入并終止調(diào)用;如果某一個遠(yuǎn)程調(diào)用的失敗次數(shù)足夠多,那么熔斷器也會快速失敗。 - 后備模式
后備模式是指當(dāng)遠(yuǎn)程調(diào)用失敗時,服務(wù)消費者將執(zhí)行替代方法,而不是生成一個異常。 - 艙壁模式
艙壁模式是建立在造船的概念基礎(chǔ)之上的,每個遠(yuǎn)程資源的調(diào)用都是隔離的并分配給線程池,如果一個遠(yuǎn)程服務(wù)的響應(yīng)時間很長,那么這種服務(wù)的線程池就會飽和并停止接受請求,然后對其他的遠(yuǎn)程服務(wù)調(diào)用不會影響,因為他們被分在不同的線程池中的。這就是艙壁模式。
這些在實際開發(fā)中都是非常重要的,也是在生成環(huán)境中很容易碰到的問題,也是很多故障直接原因。因此這幾個模式是經(jīng)驗之談。
簡單的使用hystrix
- 這里首先搭建一套服務(wù)注冊和發(fā)現(xiàn),具體可參考上篇文章。
- 添加maven依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
- 在spring boot啟動類上添加
@EnableCircuitBreaker,表示使用hystrix組件
/**
* 使用hystrix
* 使用注解@EnableCircuitBreaker表示將使用hystrix
*
* @author hui.wang
* @since 15 November 2018
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class ConsumerApplication {
@LoadBalanced
@Bean RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
- 簡單的使用hystrix
這里我在provider提供的接口模擬了業(yè)務(wù)超時
@RestController
public class ProviderController {
private final Logger LOGGER = Logger.getLogger(ProviderController.class);
/**
* 這里模擬業(yè)務(wù)操作
* 隨機(jī)生成一個數(shù)字,如果是偶數(shù),sleep 11s
*/
@RequestMapping(value = "/provider", method = RequestMethod.GET)
public String provider(@RequestParam String request) {
Random random = new Random();
int randomInt = random.nextInt();
if (randomInt % 2 == 0) {
sleep();
}
LOGGER.info("========================================");
LOGGER.info("provider service 被調(diào)用");
LOGGER.info("========================================");
return "provider, " + request;
}
private void sleep() {
try {
Thread.sleep(11000);
} catch (InterruptedException e) {
LOGGER.error(e);
}
}
}
接下來,我們再consumer端簡單使用hystrix的熔斷器模式,后壁模式,艙壁模式
/**
* 對hystrix簡單的使用
*
* @author hui.wang
* @since 15 November 2018
*/
@RestController
public class Controller {
private final Logger LOGGER = Logger.getLogger(Controller.class);
@Autowired
private RestTemplate restTemplate;
/**
* 簡單使用hystrix的熔斷器模式
* 使用注解@HystrixCommand,默認(rèn)超時時間為1000ms,超時將快速失敗
*/
@HystrixCommand
@RequestMapping("/consumer/v1")
public String consumerV1() {
return restTemplate.getForEntity("http://provider-server/provider?request={1}", String.class, "test").getBody();
}
/**
* 定制熔斷器的超時時間
* 通過commandProperties屬性定制熔斷器的行為
* 這里將熔斷器超時時間設(shè)為12000ms
*/
@HystrixCommand(
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "120000")
}
)
@RequestMapping("/consumer/v2")
public String consumerV2() {
return restTemplate.getForEntity("http://provider-server/provider?request={1}", String.class, "test").getBody();
}
/**
* 簡單使用hystrix的后備模式
* 在@HystrixCommand指定fallbackMethod值,就可以在調(diào)用失敗后,調(diào)用fallbackMethod指定的方法了
*/
@HystrixCommand(fallbackMethod = "fallback")
@RequestMapping("/consumer/v3")
public String consumerV3() {
return restTemplate.getForEntity("http://provider-server/provider?request={1}", String.class, "test").getBody();
}
private String fallback() {
return "invoke error";
}
/**
* 簡單使用hystrix的艙壁模式
* Hystrix使用threadPoolKey屬性值搭建一個線程池,
* 并使用threadPoolProperties中的屬性值對線程池配置
*/
@HystrixCommand(fallbackMethod = "fallback",
threadPoolKey = "consumer-v4",
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "30"),
@HystrixProperty(name = "maxQueueSize", value = "10")
}
)
@RequestMapping("/consumer/v4")
public String consumerV4() {
return restTemplate.getForEntity("http://provider-server/provider?request={1}", String.class, "test").getBody();
}
}
上面對這幾種模式進(jìn)行了簡單的使用,這里就不在說明
Hystrix的配置
-
execution.isolation.strategy表示隔離策略,隔離策略有兩種,一種為線程隔離一種為信號量隔離 -
execution.isolation.thread.timeoutInMilliseconds表示請求線程總超時時間 -
execution.timeout.enabled這個超時開關(guān)表示,當(dāng)超時后是否觸發(fā)fallback方法 -
execution.isolation.semaphore.maxConcurrentRequests當(dāng)隔離策略使用SEMAPHORE時,最大的并發(fā)請求量,如果請求超過這個最大值將拒絕后續(xù)的請求,默認(rèn)值為10. -
fallbackMethod表示當(dāng)觸發(fā)隔離條件的時候會調(diào)用fallback設(shè)置的降級方法,這里需要注意的是降級方法的入?yún)⒑头祷刂敌枰驮椒ㄒ恢?,否則在編譯的時候會報錯。 -
circuitBreaker.requestVolumeThreshold熔斷器在整個統(tǒng)計時間內(nèi)是否開啟的閥值,每個熔斷器默認(rèn)維護(hù)10個bucket,每秒一個bucket,每個bucket記錄成功,失敗,超時,拒絕的狀態(tài),該閾值默認(rèn)20次。也就是一個統(tǒng)計窗口時間內(nèi)(10秒鐘)至少請求20次,熔斷器才啟動。 -
circuitBreaker.sleepWindowInMilliseconds熔斷器默認(rèn)工作時間,默認(rèn)值為5秒.熔斷器中斷請求5秒后會進(jìn)入半打開狀態(tài),放部分流量過去重試,如果重試成功則會恢復(fù)正常請求。 -
circuitBreaker.errorThresholdPercentage熔斷器錯誤閾值,默認(rèn)為50%。當(dāng)在一個時間窗口內(nèi)出錯率超過50%后熔斷器自動啟動。 -
circuitBreaker.forceOpen熔斷器強(qiáng)制開關(guān),如果設(shè)置為true則表示強(qiáng)制打開熔斷器,所有請求都會拒絕,默認(rèn)為false。
ThreadPool Properties
-
coreSize線程池核心線程數(shù),默認(rèn)值為10,這里的含義和jdk的線程池一個意思。 -
maxQueueSize該參數(shù)表示配置線程值等待隊列長度,默認(rèn)值:-1,建議值:-1表示不等待直接拒絕,測試表明線程池使用直接決絕策略+ 合適大小的非回縮線程池效率最高.所以不建議修改此值。 當(dāng)使用非回縮線程池時,queueSizeRejectionThreshold,keepAliveTimeMinutes 參數(shù)無效。 -
queueSizeRejectionThreshold表示等待隊列超過閾值后開始拒絕線程請求,默認(rèn)值為5,如果maxQueueSize為1,則該屬性失效。