SpringCloud(三)Hystrix解決雪崩問題-服務降級

雪崩問題

微服務中,服務間調用關係錯綜複雜,一個請求,可能需要調用多個微服務接口才能實現,會形成非常複雜的調用鏈路:
在這裏插入圖片描述
如圖,一次業務請求,需要調用A、P、 H、I 四個服務,這四個服務又可能調用其它服務。如果此時,某個服務出現問題。例如微服務 I 發生異常,請求阻塞,用戶不會得到響應,則tomcat的這個線程不會釋放,於是越來越多的用戶請求到來,越來越多的線程會阻塞:
服務器支持的線程和併發數有限,請求一直阻塞, 會導致服務器資源耗盡,從而導致所有其它服務都不可用,形成雪崩效應。
在這裏插入圖片描述
這就好比,一個汽車生產線,生產不同的汽車,需要使用不同的零件,如果某個零件因爲種種原因無法使用,那麼就會造成整臺車無法裝配,陷入等待零件的狀態,直到零件到位,才能繼續組裝。此時如果有很多 個車型都需要這個零件,那麼整個工廠都將陷入等待的狀態,導致所有生產都陷入癱疾。一個零件的波及範圍不斷擴大。

Hystix解決雪崩問題

Hystix解決雪崩問題的手段有兩個

  • 線程隔離
  • 服務熔斷
    在這裏插入圖片描述
  • Hystrix爲每個依賴服務調用分配一個小的線程池, 如果線程池已滿調用將被立即拒絕,默認不採用排隊加速失敗判定時間。
    用戶的請求將不再直接訪問服務,而是通過線程池中的空閒線程來訪問服務,如果線程池已滿,或者請求超時,則會進行降級處理,什麼是服務降級?
  • 服務降級:優先保證核心服務,而非核心服務不可用或弱可用。
    用戶的請求故障時,不會被阻塞,更不會無休止的等待或者看到系統崩潰,至少可以看到一個執行結果(例如返回友好的提示信息:服務器正忙等)。
    服務降級雖然會導致請求失敗,但是不會導致阻塞,而且最多會影響這個依賴服務對應的線程池中的資源,對其它服務沒有響應。
  • 觸發Hystix服務降級的情況:
    線程池已滿
    請求超時

開啓Hystrix

首先在做降級處理之前,應該明確是給那個服務降級,是服務的提供方還是消費方?一般情況是服務的消費方去調用服務的提供方的,服務的提供方出現問題,纔會導致服務的消費方出現問題。所以應該是對服務的消費方進行服務的降級處理,例如在調用一個服務時,出現了問題,消費方可以快速的給用戶處理響應或一個友好提示。
服務的消費方進行降級處理,不會讓服務提供方的的錯誤影響自己錯誤,這樣自己就不會出現錯誤了,當然用戶在調用服務的消費方時就不會出現錯誤。

環境準備

<!--hystrix-->
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

啓動類中,開啓@EnableCircuitBreaker功能

//@EnableCircuitBreaker
//@EnableDiscoveryClient
//@SpringBootApplication

@SpringCloudApplication
public class ConsumerApplication {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){return new RestTemplate();}

    public static void main(String[] args) {
       SpringApplication.run(ConsumerApplication.class);
    }
}

@SpringCloudApplication註解源碼

@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public @interface SpringCloudApplication {
}

controller改寫

編寫降級邏輯當目標服務的調用出現故障,我們希望快速失敗,給用戶一個友好提示。 因此需要提前編寫好失敗時的降級處理邏輯,要使用@HystixCommond來完成:要注意,因爲熔斷的降級邏輯方法必須跟正常邏輯方法保證:相同的參數列表和返回值聲的。返回User對象沒有太大意義,一 般會返回友好提示。所以我們把findById的方法改造爲返回String, 反正也是Json數據。這樣失敗邏輯中返回一個錯誤說明會比較方便。
@HystrixCommand(fallbackMethod = "findByIdFallback")用來聲明一一個降級邏輯的方法當user-service正常提供服務時,訪問與以前一致。但是當我們將user-service停機時,會發現頁面返回了降級處理

@RestController
@RequestMapping("/consumer")
@DefaultProperties(defaultFallback = "findByIdFallback")
public class ConsumerController {
    @Autowired
    private RestTemplate restTemplate;
    
    @GetMapping("{id")
    //失敗容錯指令,開啓服務降級線程隔離的處理,要求這兩個方法返回值和參數列表必須相同,因爲服務降級的處理邏輯必須和原因的處理保持一致
    //@HystrixCommand(fallbackMethod = "findByIdFallback")
    //不同接口不同方法的超時等待時長應該是不一樣的,@HystrixCommand可以設置,配置在類上,則是此類的所有方法生效,要想全局起作用,那就在application.yml中配置
    @HystrixCommand(fallbackMethod = "findByIdFallback",
    commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
    })
    public String findById(@PathVariable("id")Integer id){
        String url="http://user-service/user/findById/"+id;
        String user = restTemplate.getForObject(url, String.class);
        return user;
    }

    public String findByIdFallback(Integer id){
        return "不好意思!服務器正忙!!!!";
    }

    //默認fallback方法,不需要再有參數了
    public String findByIdFallback(){
        return "不好意思!服務器正忙!!!!";
    }
}

application.yaml配置失敗容錯指令

# 配置全局等待超時時長爲3秒,默認是1秒
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000
# 只針對某個服務或方法進行配置等待超時時長,這樣就不用在方法中或類中配置
    user-service:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000

明確:線程隔離是在內部實現,內部已經利用線程池進行了處理,我們只需要去配置他的超時時長,並且開啓他的功能即可。

測試

服務提供方user-service線程休眠,模擬線程出現異常。

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public User findById(Integer id){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }//模擬等待時長2秒
        return userMapper.selectByPrimaryKey(id);
    }

    public List<User> findAll(){ return userMapper.selectAll();  }
}

在這裏插入圖片描述
啓動服務,一切正常。
在這裏插入圖片描述
顯示一個友好的提示信息。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章