1. 程式人生 > >Spring Cloud Hystrix實現服務短路和服務降級

Spring Cloud Hystrix實現服務短路和服務降級

Spring Cloud Hystrix實現服務短路和服務降級

個人部落格
之前文章
Spring Cloud 服務註冊和發現
Spring Cloud 服務端高可用
Spring Cloud Ribbon實現負載均衡

背景

​ 在微服務架構中,我們將系統拆分成了一個個的服務單元,各單元應用間通過服務註冊與訂閱的方式互相依賴。由於每個單元都在不同的程序中執行,依賴通過遠端呼叫的方式執行,這樣就有可能因為網路原因或是依賴服務自身問題出現呼叫故障或延遲,而這些問題會直接導致呼叫方的對外服務也出現延遲,若此時呼叫方的請求不斷增加,最後就會出現因等待出現故障的依賴方響應而形成任務積壓

,執行緒資源無法釋放,最終導致自身服務的癱瘓,進一步甚至出現故障的蔓延最終導致整個系統的癱瘓。如果這樣的架構存在如此嚴重的隱患,那麼相較傳統架構就更加的不穩定。為了解決這樣的問題,因此產生了斷路器等一系列的服務保護機制。

斷路器和服務降級

斷路器(服務熔斷)

​ 很多朋友一開始可能會把斷路器和服務降級的概念搞混(表示一開始我也傻傻分不清),這兩個其實不是同一個東西來的,斷路器本身是一種開關裝置,用於在電路上保護線路過載,當線路中有電器發生短路時,斷路器能夠及時切斷故障電路、防止發生過載、發熱甚至起火等嚴重後果。在微服務架構中,斷路器的作用也是類似的,當某個服務單元發生故障(類似用電器發生短路)之後,通過斷路器的故障監控,向呼叫方返回一個錯誤響應,而不是長時間的等待。這樣就不會使得執行緒因服務呼叫故障服務被長時間佔用不釋放,避免了故障在分散式系統中的蔓延。

服務降級

​ 當某個服務出現故障了,在還沒修復之前,請求進來肯定都是出錯的,從系統整理負荷考慮,此時我們可以提供一種應急方案,主邏輯出錯,此時應急方案(次邏輯)就被當成主邏輯呼叫,保證系統的正常執行。等服務主邏輯被修復了再切換回主邏輯,切換這個過程,可以人工干預,也可以是通過自動策略實現。降級本身也是一種策略,不僅僅應用在微服務中,在分散式系統架構中,會針對很多場景提供降級方案。這樣能更好的保證系統的高可用性。

Spring Cloud斷路器和服務降級

​ 在Spring Cloud中,斷路器和服務降級是相輔相成的,通過Hystrix實現,當服務熔斷後,可直接呼叫回撥方法實現服務降級。同時Hystrix會從系統度量指標metrics中獲取服務的健康狀態,根據請求總數(QPS),錯誤百分比等資訊,來調整斷路器超時時間,某段時間內(休眠窗)使得原先需要2s(Hystrix預設超時時間)才能夠觸發熔斷的邏輯不再有超時時間限制,直接呼叫降級方法(次邏輯)。相當於在一段時間內,當次邏輯當成主邏輯執行,不再執行主邏輯。一段時間後斷路器將進入半開狀態,釋放一次請求到原來的主邏輯上,如果此次請求正常返回,那麼斷路器將繼續閉合,主邏輯恢復,如果這次請求依然有問題,斷路器繼續進入開啟狀態,休眠時間窗重新計時。

Hystrix 示例

​ 我們首先啟動3個工程,服務註冊中心、consumer服務、consumer2、這幾個工程都是基於前面章節的示例,不記得朋友可以去前面章節檢視。

​ 其中consumer是用來遠端掉用consumer2提供的服務,我們給他加上Hystrix功能

新增Hystrix依賴

       <dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
			<version>1.4.6.RELEASE</version>
		</dependency>

博主這裡用的spring boot1.5.19,不加版本號,預設去找的Hystrix1.4.7RELEASE版本,pom會報錯提示找不到對應版本,跑到maven倉庫一看,最高才1.4.6.RELEASE,肯定找不到呀,所以這裡指定下版本就好了。

啟動類新增@EnableCircuitBreaker註解

@EnableCircuitBreaker
@SpringBootApplication
@EnableDiscoveryClient
public class EurekaClientApplication {

	public static void main(String[] args) {
//

		SpringApplication.run(EurekaClientApplication.class, args);
	}


	@Bean
	@LoadBalanced
	RestTemplate restTemplate(){
		return new RestTemplate();
	}

}

consumer的HelloService

@Service
public class HelloServer {

    @Autowired
    RestTemplate restTemplate;

    
    @HystrixCommand(fallbackMethod = "helloFallback")
    public String  hello(){
        return restTemplate.getForEntity("http://consumer2/hello",String.class).getBody();
    }

    public String helloFallback(){
        return "fallback  error";
    }
   
}

HelloService這裡主要是用來呼叫consumer2的hello服務,Hystrix是用於客戶端的,所以我們這裡開啟Hystrix,開啟只需要在呼叫方法上面加上@HystrixCommand註解就可以了,這裡我們在指定下fallbackMethod(用於降級的回撥方法)為helloFallback

consumer2的HelloController

@RestController
public class HelloController {

    private  final Logger logger  =  Logger.getLogger(getClass());

    @Autowired
    private DiscoveryClient client;

    @RequestMapping(value="/hello",method = RequestMethod.GET)
    public String index(){
        ServiceInstance instance = client.getLocalServiceInstance();
        logger.info("/hello host:"+instance.getHost()+"server_id"+instance.getServiceId()+"埠:"+instance.getPort());
        return "hello";
    }
}

HelloController就是consumer2提供的hello服務,這裡為了簡單測試,就沒加服務層了。

測試

我們啟動註冊中心、consumer、consumer2

服務列表

此時能夠看到consumer和consumer2例項都正常啟動了

訪問http://localhost:1112/hello

此時能夠看到服務能夠正常呼叫返回hello,接下來我們把consumer2停掉

不啟動consumer2服務測試

這裡我們測試下不啟動consumer2用來模擬服務出現故障的情況

服務列表

訪問http://localhost:1112/hello

此時能夠看到,consumer2出現故障時,能夠觸發服務熔斷並且呼叫我們指定的回撥方法,達到降級的目的。我們再次啟動consumer2,此時又能正常的呼叫,輸出hello了。

給consumer新增休眠,模擬網路阻塞

我們之前有提到,斷路器的預設短路時間為2秒,這裡我們給consumer2休眠3秒,看看能不能觸發服務熔斷

改造consumer2的HelloController類

@RestController
public class HelloController {

    private  final Logger logger  =  Logger.getLogger(getClass());

    @Autowired
    private DiscoveryClient client;

    @RequestMapping(value="/hello",method = RequestMethod.GET)
    public String index(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        ServiceInstance instance = client.getLocalServiceInstance();
        logger.info("/hello host:"+instance.getHost()+"server_id"+instance.getServiceId()+"埠:"+instance.getPort());
        return "hello";
    }

}

服務列表

這裡兩個例項都正常啟動了,上面的紅色警告是沒有關閉Eureka的保護機制提醒的,不影響呼叫。

訪問http://localhost:1112/hello

此時可以看到,即時服務正常啟動了,但是超過Hystrix的短路超時時間,也會觸發熔斷。Hystix的超時時間我們可以通過以下配置指定

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=60000

總結

通過這篇文章,我們瞭解了服務熔斷,服務降級。以及怎麼在Spring Cloud中通過Hystix實現斷路器和服務降級。

其實Hystix除了實現斷路器和服務降級,還可以用來快取請求(不能跨請求快取),請求合併等。。,以後有機會可以單獨介紹。

參考資料《Spring Cloud微服務實戰》