SpringCloud(6)---熔斷降級理解、Hystrix實戰


SpringCloud(6)---熔斷降級理解、Hystrix實戰

 

一、概念

   1、為什么需要熔斷降級

1)需求背景

   它是系統負載過高突發流量或者網絡等各種異常情況介紹,常用的解決方案。

   在一個分布式系統里,一個服務依賴多個服務,可能存在某個服務調用失敗,比如超時、異常等,如何能夠保證在一個依賴出問題的情況下,不會導致整體服務失敗。

   比如:某微服務業務邏輯復雜,在高負載情況下出現超時情況。

  內部條件:程序bug導致死循環、存在慢查詢、程序邏輯不對導致耗盡內存

  外部條件:黑客攻擊、促銷、第三方系統響應緩慢。

(2)解決思路

   解決接口級故障的核心思想是優先保障核心業務和優先保障絕大部分用戶。比如登錄功能很重要,當訪問量過高時,停掉注冊功能,為登錄騰出資源。

(3)解決策略

  熔斷,降級,限流,排隊。

    2、什么是熔斷

     一般是某個服務故障或者是異常引起的,類似現實世界中的‘保險絲’,當某個異常條件被觸發,直接熔斷整個服務,而不是一直等到此服務超時,為了防止防止整個系統的故障,

而采用了一些保護措施。過載保護。比如A服務的X功能依賴B服務的某個接口,當B服務接口響應很慢時,A服務X功能的響應也會被拖慢,進一步導致了A服務的線程都卡在了X功能

上,A服務的其它功能也會卡主或拖慢。此時就需要熔斷機制,即A服務不在請求B這個接口,而可以直接進行降級處理。

   3、什么是降級

      服務器當壓力劇增的時候,根據當前業務情況及流量,對一些服務和頁面進行有策略的降級。以此緩解服務器資源的的壓力,以保證核心業務的正常運行,同時也保持了客戶和

大部分客戶的得到正確的相應。

自動降級:超時、失敗次數、故障、限流

 (1)配置好超時時間(異步機制探測回復情況);

 (2)不穩的的api調用次數達到一定數量進行降級(異步機制探測回復情況);

 (3)調用的遠程服務出現故障(dns、http服務錯誤狀態碼、網絡故障、Rpc服務異常),直接進行降級。

人工降級:秒殺、雙十一大促降級非重要的服務。

  4、熔斷和降級異同

相同點

   1)從可用性和可靠性觸發,為了防止系統崩潰

   2)最終讓用戶體驗到的是某些功能暫時不能用

不同點:

    1)服務熔斷一般是下游服務故障導致的,而服務降級一般是從整體系統負荷考慮,由調用方控制

   2)觸發原因不同,上面顏色字體已解釋

   5、熔斷到降級的流程講解

Hystrix提供了如下的幾個關鍵參數,來對一個熔斷器進行配置:

circuitBreaker.requestVolumeThreshold    //滑動窗口的大小,默認為20 
circuitBreaker.sleepWindowInMilliseconds //過多長時間,熔斷器再次檢測是否開啟,默認為5000,即5s鍾 
circuitBreaker.errorThresholdPercentage  //錯誤率,默認50%

3個參數放在一起,所表達的意思就是:

    每當20個請求中,有50%失敗時,熔斷器就會打開,此時再調用此服務,將會直接返回失敗,不再調遠程服務。直到5s鍾之后,重新檢測該觸發條件,判斷是否把熔斷器關閉,或者繼續打開。

這里面有個很關鍵點,達到熔斷之后,那么后面它就直接不去調該微服務。那么既然不去調該微服務或者調的時候出現異常,出現這種情況首先不可能直接把錯誤信息傳給用戶,所以針對熔斷

我們可以考慮采取降級策略。所謂降級,就是當某個服務熔斷之后,服務器將不再被調用,此時客戶端可以自己准備一個本地的fallback回調,返回一個缺省值。 

這樣做,雖然服務水平下降,但好歹可用,比直接掛掉要強,當然這也要看適合的業務場景。

 

二、Hystrix實戰

 

      使用到的組件包括:Eureka、Feign包括以下三個項目:

 

    (1)Eureka-server:   7001    注冊中心

 

    (2)product-server :8001   商品微服務

 

    (3)order-server :   9001   訂單微服務

 

注冊中心、商品微服務、在之前博客都已搭建,這里就不重復寫。這里只寫order-server微服務。具體可以看上篇博客:SpringCloud(5)---Feign服務調用

    1、pom.xml

        <!--hystrix依賴,主要是用  @HystrixCommand -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

   2、application.yml

server:
  port: 9001

#指定注冊中心地址
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:7001/eureka/

#服務的名稱
spring:
  application:
    name: order-service
    
#開啟feign支持hystrix  (注意,一定要開啟,舊版本默認支持,新版本默認關閉)
# #修改調用超時時間(默認是1秒就算超時)
feign:
  hystrix:
    enabled: true
  client:
    config:
      default:
        connectTimeout: 2000 readTimeout: 2000

 3、SpringBoot啟動類

@SpringBootApplication
@EnableFeignClients
//添加熔斷降級注解
@EnableCircuitBreaker
public class OrderApplication {

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

}

   4、ProductClient

/**
 * 商品服務客戶端
 * name = "product-service"是你調用服務端名稱
 * fallback = ProductClientFallback.class,后面是你自定義的降級處理類,降級類一定要實現ProductClient
 */
@FeignClient(name = "product-service",fallback = ProductClientFallback.class)
public interface ProductClient {

    //這樣組合就相當於http://product-service/api/v1/product/find
    @GetMapping("/api/v1/product/find")
    String findById(@RequestParam(value = "id") int id);

}

    5、ProductClientFallback降級處理類

/**
 * 針對商品服務,錯降級處理
 */
@Component
public class ProductClientFallback implements ProductClient {

    @Override
    public String findById(int id) {

        System.out.println("ProductClientFallback中的降級方法");

        //這對gai該接口進行一些邏輯降級處理........
        return null;
    }
}

     6、OrderController類

    注意:fallbackMethod = "saveOrderFail"中的saveOrderFail方法中的參數類型,個數,順序要和save一模一樣,否則會報找不到saveOrderFail方法。

@RestController
@RequestMapping("api/v1/order")
public class OrderController {

    @Autowired
    private ProductOrderService productOrderService;

    @RequestMapping("save")
    //當調用微服務出現異常會降級到saveOrderFail方法中
    @HystrixCommand(fallbackMethod = "saveOrderFail")
    public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id") int productId){

        return productOrderService.save(userId, productId);
    }

    //注意,方法簽名一定要要和api方法一致
    private Object saveOrderFail(int userId, int productId){

        System.out.println("controller中的降級方法");

        Map<String, Object> msg = new HashMap<>();
        msg.put("code", -1);
        msg.put("msg", "搶購人數太多,您被擠出來了,稍等重試");
        return msg;
    }
}

      7、測試

(1)正常情況

先將訂單服務(order)和商品服務(product)同時啟動,如圖:

訂單服務調用商品服務正常

(2)異常情況

此刻我將商品微服務停掉:只啟動訂單微服務,這時去調用商品服務當然會出現超時異常情況。

在調接口,發現已經成功到降級方法里

在看controller中的降級方法和ProductClientFallback降級方法的實現先后順序,它們的順序是不固定的,有可能controller中降級方法先執行,也可能ProductClientFallback降級方法先執行。

具體要看哪個線程先獲得cpu執行權。

 

三、結合redis模擬熔斷降級服務異常報警通知實戰

主要是完善完善服務熔斷處理,報警機制完善結合redis進行模擬短信通知用戶下單失敗。

    1、pom.xml

    <!--springboot整合redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

    2、application.yml

#服務的名稱 #redis
spring:
  application:
    name: order-service
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    timeout: 2000

   3、OrderController類

主要看降級方法的不同

@RestController
@RequestMapping("api/v1/order")
public class OrderController {

    @Autowired
    private ProductOrderService productOrderService;

    //添加bean
    @Autowired
    private StringRedisTemplate redisTemplate;

    @RequestMapping("save")
    //當調用微服務出現異常會降級到saveOrderFail方法中
    @HystrixCommand(fallbackMethod = "saveOrderFail")
    public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id") int productId,HttpServletRequest request){

        return productOrderService.save(userId, productId);
    }

    //注意,方法簽名一定要要和api方法一致
    private Object saveOrderFail(int userId, int productId, HttpServletRequest request){
        
        //監控報警
        String saveOrderKye = "save-order";
        //有數據代表20秒內已經發過
        String sendValue = redisTemplate.opsForValue().get(saveOrderKye);
        final String ip = request.getRemoteAddr();

        //新啟動一個線程進行業務邏輯處理
        new Thread( ()->{
            if (StringUtils.isBlank(sendValue)) {
                System.out.println("緊急短信,用戶下單失敗,請離開查找原因,ip地址是="+ip);
                //發送一個http請求,調用短信服務 TODO
                redisTemplate.opsForValue().set(saveOrderKye, "save-order-fail", 20, TimeUnit.SECONDS);

            }else{
                System.out.println("已經發送過短信,20秒內不重復發送");
            }
        }).start();

        Map<String, Object> msg = new HashMap<>();
        msg.put("code", -1);
        msg.put("msg", "搶購人數太多,您被擠出來了,稍等重試");
        return msg;
    }

}

     4、測試

    當20秒內連續發請求會提醒已發短信。

 官方文檔:https://github.com/Netflix/Hystrix/wiki/Configuration#execution.isolation.strategy

 

 我只是偶爾安靜下來,對過去的種種思忖一番。那些曾經的舊時光里即便有過天真愚鈍,也不值得譴責。畢竟,往后的日子,還很長。不斷鼓勵自己,

 天一亮,又是嶄新的起點,又是未知的征程(上校8)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM