3、spring cloud hystrix

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)模式
  1. 客戶端負(fù)載均衡已經(jīng)在eureka中講到,這里就先不提了
  2. 熔斷器模式
    當(dāng)遠(yuǎn)程服務(wù)被調(diào)用時,熔斷器會監(jiān)視這個調(diào)用,如果調(diào)用時間太長,熔斷器將會介入并終止調(diào)用;如果某一個遠(yuǎn)程調(diào)用的失敗次數(shù)足夠多,那么熔斷器也會快速失敗。
  3. 后備模式
    后備模式是指當(dāng)遠(yuǎn)程調(diào)用失敗時,服務(wù)消費者將執(zhí)行替代方法,而不是生成一個異常。
  4. 艙壁模式
    艙壁模式是建立在造船的概念基礎(chǔ)之上的,每個遠(yuǎn)程資源的調(diào)用都是隔離的并分配給線程池,如果一個遠(yuǎn)程服務(wù)的響應(yīng)時間很長,那么這種服務(wù)的線程池就會飽和并停止接受請求,然后對其他的遠(yuǎn)程服務(wù)調(diào)用不會影響,因為他們被分在不同的線程池中的。這就是艙壁模式。

這些在實際開發(fā)中都是非常重要的,也是在生成環(huán)境中很容易碰到的問題,也是很多故障直接原因。因此這幾個模式是經(jīng)驗之談。

簡單的使用hystrix

  1. 這里首先搭建一套服務(wù)注冊和發(fā)現(xiàn),具體可參考上篇文章。
  2. 添加maven依賴
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>
  1. 在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);
    }
}
  1. 簡單的使用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,則該屬性失效。

源碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容