6-SpringクラウドのHystrixフォールトトレランス処理(前編)
1 はじめに
1.1 雪崩について
1.1.1 壊滅的な雪崩とは何ですか?
- 呼び出しチェーン内のサービス障害により一連のサービスが不可能になる、つまりリンク全体がアクセスできなくなるため、マイクロサービスは互いに呼び出します。
1.1.2 サービスアバランシェの原因
- サービスプロバイダーが利用できません。
例: ハードウェア障害、プログラムのバグ、キャッシュの故障、過剰な同時リクエスト (ダブルイレブンなど) など。
で、キャッシュの内訳通常、キャッシュ アプリケーションが再起動されたとき、すべてのキャッシュがクリアされたとき、および短期間に多数のキャッシュが期限切れになったときに発生します。キャッシュ ミスが多数発生すると、リクエストがバックエンドに直接アクセスするため、サービス プロバイダーが過負荷になり、サービスが利用できなくなります。 - サービスの呼び出し元が利用できません。
例: 同期リクエストのブロックによって引き起こされるリソースの枯渇。 - トラフィックを増やして再試行してください。のように:
- ユーザーの再試行:
サービス プロバイダーが利用できなくなった後、ユーザーはインターフェイスでの長時間の待機に耐えられず、ページを更新し続け、さらにはフォームを送信します。 - コード再試行ロジック:
サービスの呼び出し側に多数のサービス例外が存在した後の再試行ロジック。
- ユーザーの再試行:
1.1.3 壊滅的な雪崩の影響を防ぐ方法
- サービスのダウングレード
- サービスサーキットブレーカー
- キャッシュをリクエストする
1.2 前回の紹介
- この記事は前回の記事の続きであるため、一部のクラスタやサービスについては紹介していません。たとえば、Eureka クラスタは前回の記事をベースにまだ起動しています。前回の記事の紹介は次のとおりです。
2. Hystrixフォールトトレランス処理
2.1 プロジェクトの構築 (Ribbon は Hystrix を統合)
2.1.1 プロジェクトの構造
- プロジェクト全体を連続的に見せ、階層を明確にするために、このプロジェクトは新しいモジュールを直接作成していますが、このプロジェクトは、単にリボンに Hystrix を追加しただけの、dog-consumer-80 のアップグレード版であることがわかります。
- プロジェクトの構造は次のとおりです。
2.1.2 pom ファイル
-
次のように:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.liu.susu</groupId> <artifactId>dog-cloud-parent</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>dog-consumer-ribbon-hystrix-80</artifactId> <packaging>jar</packaging> <name>dog-consumer-ribbon-hystrix-80</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>com.liu.susu</groupId> <artifactId>dog-po</artifactId> <version>${project.version}</version> </dependency> <!--版本同${spring-boot.version}--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--引入ribbon相关依赖,ribbon是客户端的负载均衡,ribbon需要和eureka整合--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <!--引入Hystrix相关依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
2.1.3 ymlファイル
-
次のように:
server: port: 80 spring: application: name: dog-consumer-ribbon-hystrix eureka: client: # 客户端注册进eureka服务列表内 register-with-eureka: false # false表示不向注册中心注册自己 service-url: defaultZone: http://IP1:2886/eureka/,http://IP2:2886/eureka,http://IP3:2886/eureka/
2.1.4 構成クラス
- 次のように:
2.1.5 スタートアップクラス
- 次のように:
2.1.6 コントローラー
-
次のように:
package com.liu.susu.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; /** * @Description * @Author susu */ @RestController public class DogConsumerController { private static final String REST_URL_PREFIX = "http://DOG-PROVIDER"; @Autowired private RestTemplate restTemplate; @RequestMapping(value = "/consumer/dog/hello") public String hello(){ System.out.println("=============================="); String url = REST_URL_PREFIX + "/dog/hello"; return restTemplate.getForObject(url, String.class); } /** * http://localhost:80/consumer/dog/getDogByNum/1 * http://localhost/consumer/dog/getDogByNum/1 */ @RequestMapping("/consumer/dog/getDogByNum/{dogNum}") public Object getDogByNum(@PathVariable("dogNum") Long dogNum){ String url = REST_URL_PREFIX + "/dog/getDogByNum/" + dogNum; return restTemplate.getForObject(url, Object.class); } }
2.1.7 サービスが利用可能であることの確認を開始する
- Eureka クラスターといくつかのサービス プロバイダーは停止していないため、次のように確認してください。
- 新しく作成したサービス コンシューマを起動し、次のようにアクセスします。
- 問題がないことを確認した後、サービスの低下と融合のシミュレーションを続けて続行できます。
2.1.8 デモリモートサービスは利用できません
2.1.8.1 簡単なデモンストレーション
-
ここで、これらのサービス プロバイダーをすべて停止します (最初にサービス プロバイダーが停止されたとき、サービス プロバイダーはまだ登録センターにありました。設定されたハートビート時間を確認してください)。
-
次に、再度アクセスして効果 (プロセスの変更) を確認します。
- まず
Connection refused (Connection refused)
(これはサービスが利用できないことを意味します)
- しかし、
No instances available
サービスを停止しているため、この状況は正常です。
- まず
2.1.8.2 リモートサービスの正常と異常
- リモートサービスは正常です
- 返された結果があります
- 例外が渡される
- リモートサービスエラー
- リモート サービスが利用できない (アクセスが拒否されました)
- リモートサービス応答タイムアウト
2.2 サービスのダウングレード
2.2.1 簡単に言えば、サービスのダウングレード
-
サービスの低下とは、サービスに障害が発生したり異常な場合に、コア サービスの正常な動作を確保するために、重要でないサービスを一時的にシャットダウンしたり、単純なキャッシュ データを返したりすることを指します。サービスの低下により、サービスの応答時間とエラー率が効果的に削減され、システムの可用性が向上します。
つまり、サービスの低下は、システム リソース全体の合理的な割り当てです。コア サービスと非コア サービスを区別します。あるサービスのアクセス遅延時間や異常等を予測し、回避する方法を提供する。これは、全体的な負荷を考慮した、グローバルな考慮事項です。
2.2.2 サービス低下のシミュレーション
- 次に、タイムアウトをシミュレートします。
- サービスがダウングレードされていない場合、意図的にタイムアウトを設計すると、タイムアウトがどれだけ長くても、SpringCloud はリモート呼び出しを待機します。。自分で試してみることもできますが、ここでは説明しません。
- サービスのタイムアウトとダウングレード処理をシミュレートするためのコア コードは次のとおりです。
-
スタートアップクラス
@EnableHystrix //开启Hystrix容错处理能力
-
コントローラー内のメソッド
@HystrixCommand(fallbackMethod = "errorHello")
/** * @EnableHystrix 启动类上注解,开启Hystrix容错处理能力 * @HystrixCommand 代表当前方法是一个需要做容错处理的方法 * @EnableHystrix 结合 @HystrixCommand,默认地配置了一个远程服务超时配置,默认设置超时是1秒 */ @RequestMapping(value = "/consumer/dog/hello") @HystrixCommand(fallbackMethod = "errorHello") public String hello(){ System.out.println("=============================="); //默认情况下,SpringCloud远程调用时,不管多久都会等 try { Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException(e); } String url = REST_URL_PREFIX + "/dog/hello"; return restTemplate.getForObject(url, String.class); } /** * 降级方法 * 1、返回类型要和对应的服务方法的返回类型一致 * 2、参数和对应的服务方法要一致 * 3、返回的内容:远程服务访问错误时(比如超时),返回的拖底数据 */ public String errorHello(){ return "服务器忙,请稍后再试!"; }
-
2.2.3 効果を確認する
-
次のように:
2.3 サービスサーキットブレーカー
2.3.1 簡単に言えば、サービスサーキットブレーカー
- サービスの融合とは、サービスに障害や異常が発生した場合に、サービスへの接続を切断してサービスがリクエストを受け付けないようにすることで、障害の拡大を防ぎ、サービスの可用性を迅速に回復することです。サービス融合は通常、特定の戦略に基づいてサービスの可用性を検出します。サービスの応答時間またはエラー率が特定のしきい値を超えると、融合メカニズムがトリガーされ、サービスへの接続が切断され、その後接続が切断されます。サービスが正常になるまで定期的に再試行されます。
- つまり、サービスサーキットブレーカーは通常、サービス (ダウンストリーム サービス) の障害サービスの低下は一般に全体の負荷から考慮されます。
2.3.2 サービスサーキットブレーカー 1 のシミュレート (サービスの停止)
2.3.2.1 ダウングレードコード
-
次のように:
@RequestMapping("/consumer/dog/getDogByNum/{dogNum}") @HystrixCommand(fallbackMethod = "errorGetDogByNum",commandProperties = { //是否开启熔断机制 @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ENABLED,value = "true"), //一个时间窗内,发生远程访问错误的次数阈值,达到开启熔断 @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD,value = "3"), //一个时间窗内,发生远程访问错误的百分比,达到则开启熔断 @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE,value = "20"), //开启熔断后,多少毫秒内,不发起远程服务访问 @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS,value = "3000") }) public Object getDogByNum(@PathVariable("dogNum") Long dogNum){ System.out.println("本地测试熔断...."); String url = REST_URL_PREFIX + "/dog/getDogByNum/" + dogNum; return restTemplate.getForObject(url, Object.class); } public Object errorGetDogByNum(Long dogNum){ System.out.println("进入熔断,dogNum是:===>"+dogNum); return "熔断——服务器忙,请稍后再试!"; }
2.3.2.2 効果を確認する
-
次のようにサービス プロバイダーを停止します。
2.3.3 シミュレーションサービスサーキットブレーカー 1 (サーバーシミュレーション例外)
2.3.3.1 サービスプロバイダーの変更
-
コードを次のように変更します
System.out.println("进入服务提供者,模拟异常...."); System.out.println(2 / dogNum);
-
サービスを開始します。便宜上、ローカルで直接開始します。
2.3.3.2 効果を確認する
2.3.3.2.1 サーキットブレーカー処理をコメントアウトする
- 500 回の頭痛、原因がまだわかりません。次のようにサービス プロバイダーに行ってログを開く必要があります。
2.3.3.2.2 ヒューズ処理の追加
- サービスプロバイダーのログは次のとおりです。
- 消費者にサービスを提供する
- ページ、あなたが顧客の場合、はるかに快適に見えます
2.4 ファジー使用 (サーキットブレーカー? ダウングレード?)
- ファジーコンセプト: サービスサーキットブレーカーは、サービス低下の拡張バージョンです。したがって、消費者側でエラーが発生した場合は、サービスサーキットブレーカーを使用することもでき、その効果は上記と同様です。
- コンシューマ側のサービスサーキットブレーカーのエラーは次のとおりです。
2.4.1 コード (サーバー コードは通常、コンシューマ コードは例外をシミュレートします)
-
次のように:
@RequestMapping("/consumer/dog/getDogByNum/{dogNum}") @HystrixCommand(fallbackMethod = "errorGetDogByNum",commandProperties = { //是否开启熔断机制 @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ENABLED,value = "true"), //一个时间窗内,发生远程访问错误的次数阈值,达到开启熔断 @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD,value = "3"), //一个时间窗内,发生远程访问错误的百分比,达到则开启熔断 @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE,value = "20"), //开启熔断后,多少毫秒内,不发起远程服务访问 @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS,value = "3000") }) public Object getDogByNum(@PathVariable("dogNum") Long dogNum){ System.out.println("本地测试熔断...."); System.out.println(2 / dogNum); String url = REST_URL_PREFIX + "/dog/getDogByNum/" + dogNum; return restTemplate.getForObject(url, Object.class); } public Object errorGetDogByNum(Long dogNum){ System.out.println("进入熔断,dogNum是:===>"+dogNum); return "熔断——服务器忙,请稍后再试!"; }
2.4.2 効果
2.4.2.1 ヒューズがオンになっていない場合の影響
- 次のように:
2.4.2.2 ヒューズをオンにした後の影響
-
まず、次のように、エラーが発生しない Dog_num をテストします。
-
再度テストすると次のようにエラーが発生します。
-
次のように、再度クリックしてもエラーは発生しないはずです。
3. Feign は Hystrix フォールト トレランス処理を統合します
- Dog-API プロジェクトでコードを直接変更しやすくするには、次の 2 つの方法があります。
- まずは元のコードを見てください
- まずは元のコードを見てください
3.1 コアコード
3.1.1 実装クラスとアノテーション
3.1.1.1 方法 1
- 以下のように実装クラスを追加します。
- 注釈を次のように変更します
@FeignClient(value = "DOG-PROVIDER",fallback = DogClientApiImpl.class) //方式1
3.1.1.2 方法 2
-
次のように実装クラスを追加します。
package com.liu.susu.api; import com.liu.susu.pojo.Dog; import feign.hystrix.FallbackFactory; import org.springframework.stereotype.Component; import java.util.List; /** * @Description * @Author susu */ @Component public class DogClientApiFallbackFactory implements FallbackFactory<DogClientApi> { @Override public DogClientApi create(Throwable throwable) { return new DogClientApi() { @Override public String hello() { System.out.println("进入 DogClientApiFallbackFactory 服务降级--->hello"); return "DogClientApiFallbackFactory 服务降级处理,请稍后再试"; } @Override public Object getDogByNum(Long dogNum) { return null; } @Override public List<Dog> getAllDog() { return null; } }; } }
-
注釈を次のように変更します。
@FeignClient(value = "DOG-PROVIDER",fallbackFactory = DogClientApiFallbackFactory.class) //方式2
3.1.2 コンシューマ yml
- コンシューマーの yml ファイルを構成します (忘れないでください!!!)
feign: hystrix: enabled: true
3.1.3 効果を確認する
- サービスを開始して効果を確認する
- それからサービスプロバイダーを切断して効果を確認してください
. まあ、それだけです. 他の方法でも同じ効果があります. 興味がある場合は、自分で試してみてください.