1. 程式人生 > >(轉)Spring Cloud(二)

(轉)Spring Cloud(二)

(二期)23、微服務框架spring cloud(二)

【課程23】熔斷器-Hystrix.xmind0.1MB

【課程23】微服務...zuul.xmind0.2MB

熔斷器-Hystrix 雪崩效應

在微服務架構中通常會有多個服務層呼叫,基礎服務的故障可能會導致級聯故障,進而造成整個系統不可用的情況,這種現象被稱為服務雪崩效應。服務雪崩效應是一種因“服務提供者”的不可用導致“服務消費者”的不可用,並將不可用逐漸放大的過程。 

 

如下圖所示:A作為服務提供者,B為A的服務消費者,C和D是B的服務消費者。A不可用引起了B的不可用,並將不可用像滾雪球一樣放大到C和D時,雪崩效應就形成了。 

 

 

解決方向
  • 為網路其請求設定超時時間
  • 使用斷路器模式
  • 統計一段時間內的失敗次數來決定正常訪問還是直接返回
  • 斷路器處於半開狀態,應許一個請求進入,如果請求正常,就關閉斷路器。

在這種情況下就需要整個服務機構具有故障隔離的功能,避免某一個服務掛掉影響全域性。

在Spring Cloud 中Hystrix元件就扮演這個角色。 一個實現了超時機制和斷路器模式的工具類庫。

 

Hystrix會在某個服務連續呼叫N次不響應的情況下,立即通知呼叫端呼叫失敗,避免呼叫端持續等待而影響了整體服務。Hystrix間隔時間會再次檢查此服務,如果服務恢復將繼續提供服務。 

簡介

Hystrix是Netflix開源的一個延遲和容錯庫,用於隔離訪問遠端系統、服務或第三方庫,防止級聯失敗,從而提高系統的可用性與容錯性。

 

Hystrix主要通過以下幾點實現延遲和容錯:

  • 包裹請求:使用HystrixCommand(或HystrixObservableCommand)包裹對依賴的呼叫邏輯,每個命令在獨立執行緒中執行。這使用到了設計模式中的“命令模式”。
  • 跳閘機制:當某服務的錯誤率超過一定閾值時,Hystrix可以自動或者手動跳閘,停止請求該服務一段時間。
  • 資源隔離
    :Hystrix為每個依賴都維護了一個小型的執行緒池(或者訊號量)。如果該執行緒池已滿,發往該依賴的請求就被立即拒絕,而不是排隊等候,從而加速失敗判定。
  • 監控:Hystrix可以近乎實時地監控執行指標和配置的變化,例如成功、失敗、超時、以及被拒絕的請求等。
  • 回退機制:當請求失敗、超時、被拒絕,或當斷路器開啟時,執行回退邏輯。回退邏輯可由開發人員自行提供,例如返回一個預設值。
  • 自我修復:斷路器開啟一段時間後,會自動進入“半開”狀態。斷路器開啟、關閉、半開的邏輯轉換,上文已經詳細探討過,不再贅述。

服務降級

優先核心服務,非核心服務不可用或弱可用。

  • 通過HystrixCommand註解
  • fallbackMethod回退函式
Hystrix如何解決依賴隔離
  • 1:Hystrix使用命令模式HystrixCommand(Command)包裝依賴呼叫邏輯,每個命令在單獨執行緒中/訊號授權下執行。
  • 2:可配置依賴呼叫超時時間,超時時間一般設為比99.5%平均時間略高即可.當呼叫超時時,直接返回或執行fallback邏輯。
  • 3:為每個依賴提供一個小的執行緒池(或訊號),如果執行緒池已滿呼叫將被立即拒絕,預設不採用排隊.加速失敗判定時間。
  • 4:依賴呼叫結果分:成功,失敗(丟擲異常),超時,執行緒拒絕,短路。 請求失敗(異常,拒絕,超時,短路)時執行fallback(降級)邏輯。
  • 5:提供熔斷器元件,可以自動執行或手動呼叫,停止當前依賴一段時間(10秒),熔斷器預設錯誤率閾值為50%,超過將自動執行。
  • 6:提供近實時依賴的統計和監控
Hystrix整合步驟

第一步:匯入hystrix座標

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

第二步、啟動application添加註解@EnableHystrix啟動斷溶處理

@EnableHystrix
或者
@EnableCircuitBreaker

第三步、使用@HystrixCommand註解,在對應的介面上新增容災方法

@HystrixCommand(fallbackMethod = "getUsernameFallback")
@GetMapping("/username")
public Object getUsername() {
    return restTemplate.getForObject("http://EUREKA-CLIENT-2/username", String.class);
}
 
  
public Object getUsernameFallback() {
    return "fall back";
}

 

預設提示:@HystrixCommand可以和@DefaultProperties配合使用

 

@hystrixCommand註解引數詳解

Feign使用Hystrix回退機制

定義一個實現類,介面端使用@FeignClient的fallback指向要回退的類。

/**
 * Feign的fallback測試類
 * 使用@FeignClient的fallback屬性指定回退類
 */
@FeignClient(name = "microservice-provider-user", fallback = FeignClientFallback.class)
public interface UserFeignClient {
 
  
    @GetMapping(value = "/{id}")
    User findById(@PathVariable("id") Long id);
 
  
}
 
  
/**
 * 回退類FeignClientFallback需實現Feign Client介面
 * FeignClientFallback也可以是public class,沒有區別
 */
@Component
class FeignClientFallback implements UserFeignClient {
 
  
    @Override
    public User findById(Long id) {
        User user = new User();
        user.setId(-1L);
        user.setUsername("預設使用者");
        return user;
    }
}

同時在yml配置檔案中配置:

feign:
  hystrix:
    enabled: true
檢查回退原因

第一步、feign介面使用fallbackFactory屬性

@FeignClient(name = "eureka-client-2", fallbackFactory = Demo2ClientFallbackFactory.class)
public interface Demo2Client {
 
  
    @GetMapping("/username")
    public String getUsername();
 
  
}

第二步、定義fallbackFactory的實現類。重寫create方法,使用Throwable cause查看回退原因。

 
  
@Component
public class Demo2ClientFallbackFactory implements FallbackFactory<Demo2Client> {
 
  
    public Demo2Client create(Throwable cause) {
 
  
        return new Demo2Client() {
            @Override
            public String getUsername() {
                
                System.out.println(cause.toString());
                return "hello world fallbackfactory";
            }
        };
    }
}

在實際開發中,並不是所有的請求都要提前預備好服務降級問題,如果我就是要將服務呼叫失敗的資訊展示給使用者,那麼此時就沒有必要新增斷路器了。

 

Hystrix斷路器模式的狀態監控

關閉服務B端:

訪問 http://localhost:8010/actuator/health,結果如下,此時Hystrix的狀態依然是 UP,這是因為我們的失敗率還沒有達到閾值(預設是5秒返回20次失敗)

斷路器的狀態也會暴露在Actuator提供的 /actuator/health 埠中,這樣就可以直觀地瞭解斷路器的狀態。

<!--actuator用於應用監控管理-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

yml配置

management:
  endpoint:
    health:
      show-details: always

 

如果控制檯輸出:

#說明斷路器已經開啟
Hystrix circuit short-circuited and is OPEN
Hystrix的兩種隔離方式

https://www.cnblogs.com/java-synchronized/p/7927726.html

  • 執行緒隔離
  • 它將在單獨的執行緒上執行,併發請求受執行緒池中的執行緒數量的限制。
  • 把執行依賴程式碼的執行緒與請求執行緒(如:jetty執行緒)分離,請求執行緒可以自由控制離開的時間(非同步過程)。
  • 通過執行緒池大小可以控制併發量,當執行緒池飽和時可以提前拒絕服務,防止依賴問題擴散。
  • 線上建議執行緒池不要設定過大,否則大量堵塞執行緒有可能會拖慢伺服器。

  • 訊號量隔離
  • 它將在呼叫執行緒上執行,開銷相對較小,併發請求收到訊號量個數的限制。
  • 訊號隔離也可以用於限制併發訪問,防止阻塞擴散, 與執行緒隔離最大不同在於執行依賴程式碼的執行緒依然是請求執行緒(該執行緒需要通過訊號申請)
  • 當n個併發請求去呼叫一個目標服務介面時,都要獲取一個訊號量才能真正去呼叫目標服務介面,但訊號量有限,預設是10個,可以使用maxConcurrentRequests引數配置,如果併發請求數多於訊號量個數,就有執行緒需要進入佇列排隊,但排隊佇列也有上限,預設是 5,如果排隊佇列也滿,則必定有請求執行緒會走fallback流程,從而達到限流和防止雪崩的目的。

服務閘道器-Zuul

在微服務架構模式下,後端服務的例項數一般是動態的,對於客戶端而言很難發現動態改變的服務例項的訪問地址資訊。因此在基於微服務的專案中為了簡化前端的呼叫邏輯,通常會引入API Gateway作為輕量級閘道器,同時API Gateway中也會實現相關的認證邏輯從而簡化內部服務之間相互呼叫的複雜度。 

Spring Cloud體系中支援API Gateway落地的技術就是Zuul。Spring Cloud Zuul路由是微服務架構中不可或缺的一部分,提供動態路由,監控,彈性,安全等的邊緣服務。Zuul是Netflix出品的一個基於JVM路由和服務端的負載均衡器。 

 

它的具體作用就是服務轉發,接收並轉發所有內外部的客戶端呼叫。使用Zuul可以作為資源的統一訪問入口,同時也可以在閘道器做一些許可權校驗等類似的功能。 

整合步驟

第一步、

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

第二步、yml配置

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

第三步、配置路由規則,連結可以不用新增應用名稱。可以配置字首調整規則

# 路由規則配置
zuul:
  routes:
    api-a:
      path: /usernameByFeign
      serviceId: eureka-client-1

 

第三步、application添加註解@EnableZuulProxy

 

啟動專案之後可以看到在註冊中心已經Zuul的註冊了。

 

配置過濾器

第一步繼承閘道器路由。

  • PRE: 這種過濾器在請求被路由之前呼叫。我們可利用這種過濾器實現身份驗證、在叢集中選擇請求的微服務、記錄除錯資訊等。
  • ROUTING:這種過濾器將請求路由到微服務。這種過濾器用於構建傳送給微服務的請求,並使用Apache HttpClient或Netfilx Ribbon請求微服務。
  • POST:這種過濾器在路由到微服務以後執行。這種過濾器可用來為響應新增標準的HTTP Header、收集統計資訊和指標、將響應從微服務傳送給客戶端等。

ERROR:在其他階段發生錯誤時執行該過濾器。 

  • 除了預設的過濾器型別,Zuul還允許我們建立自定義的過濾器型別。例如,我們可以定製一種STATIC型別的過濾器,直接在Zuul中生成響應,而不將請求轉發到後端的微服務。
鑑權程式碼示例
 
  
public class PreLogFilter extends ZuulFilter {
 
  
    /**
     * pre請求之前
     * route用於將請求路由轉到微服務
     * post路由到微服務以後執行
     * error在其他階段發生錯誤的時候執行
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }
 
  
    /**
     * 執行順序
     * @return
     */
    @Override
    public int filterOrder() {
        return 0;
    }
 
  
    /**
     * true返回一個boolean判斷該過濾器是否要執行
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }
 
  
    /**
     * 過濾器執行具體內容
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        System.out.println("---》路由獲取所有引數值" + request.getParameterMap().toString());
        System.out.println(request.getHeader("cookie"));
        return null;
    }
}

 

@Bean
PreLogFilter preLogFilter() {
    return new PreLogFilter();
}
限流程式碼示例
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>27.0-jre</version>
</dependency>

 

import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
 
  
/**
 * 限流
 */
public class RateLimiterFilter extends ZuulFilter {
 
  
    //guava的令牌桶演算法
    private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);
 
  
    @Override
    public String filterType() {
        return PRE_TYPE;
    }
 
  
    @Override
    public int filterOrder() {
        return -5;
    }
 
  
    @Override
    public boolean shouldFilter() {
        return true;
    }
 
  
    @Override
    public Object run(){
 
  
        if(!RATE_LIMITER.tryAcquire()) {
            throw new RuntimeException("佔坑失敗~~");
        }
 
  
        System.out.println("-------->沒限流");
        return null;
    }
}

 

@Bean
RateLimiterFilter rateLimiterFilter() {
    return new RateLimiterFilter();
}
應用場景

前置過濾器

  • 限流
  • 鑑權
  • 引數校驗

後置過濾器

  • 統計
  • 日誌
高可用

多個zuul節點同時註冊到euraka上,節點名稱保持一致。與其他客戶端方法一致。這時候,有多個zuul對外暴露介面,外層就可以弄個nginx搞負載均衡。

Cloud-Admin專案

專案

文件中心