Spring-cloudマイクロサービスアーキテクチャ構築03-Hystrixの理解と構成使用

15969 ワード

1.hystrixの概要
分散型のサービスシステムでは、サービスダウンタイムが発生することはよくあります.hystrixが提供するクライアントのフレックスモード設計は、クライアントを迅速に失敗させ、リモートリソースを保護し、サービス消費の「上流」伝播を防止することができます.
Hystrixライブラリは高度に構成可能であり、開発者が定義した遮断器モードとハッチ壁モードを使用する動作を厳格に制御することができる.開発者は、Hystrix遮断器の構成を変更することで、Hystrixがリモートコールをタイムアウトするまで待つ時間を制御できます.開発者は、Hystrix遮断器がいつブレーキを切るか、Hystrixが遮断器をリセットしようとするかを制御することもできます.
Hystrixを使用すると、開発者はまた、リモート・サービス・コールごとに個別のスレッド・グループを定義し、各スレッド・グループに対応するスレッド数を構成することで、ハッチング・ウォールを微調整することもできます.これにより、開発者は、一部のリモート・リソース・コールが要求量が高いため、リモート・サービス・コールを微調整できます.
クライアントフレックスモード?次の4つの点があります.
  • クライアント負荷等化モードはribbonモジュールによって提供される.
  • 遮断器モード(circuit breaker);
  • クイック失敗モード(fallback);
  • 室壁モード(bulkhead);
  • 次に、Feign-service(Feign結合Hystrix)モジュールとDemo-dervice(前編の基礎サービスモジュール)によってhystixコンポーネントの機能テストと理解を行う.

  • 2.hystrix-serviceモジュール快速構築
    注意:本プロジェクトはideaツールを採用して構築する
  • idea自身のspring initializrを使用してプロジェクトの初期化を行い、プロジェクト名は:feign-serviceで、その主なテストはfeignの遠隔呼び出しに基づいて、restTemplateのテストもあります;
  • 初期化完了項目後pomファイルインポート
  • 
    
    
        org.springframework.cloud
        spring-cloud-starter-netflix-eureka-client
    
    
    
        org.springframework.cloud
        spring-cloud-starter-openfeign
    
    
        org.springframework.cloud
        spring-cloud-starter-netflix-hystrix
    
    
  • アプリケーションを修正する.ymlファイル、
  • の構成を追加
    management:
      endpoints:
        web:
          exposure:
            include: "*"  #           ,       hystrix.stream  
      endpoint:
        health:
          show-details: ALWAYS
    # feign  
    feign:
      compression:
        request:
          enabled:  true  #        
          mime-types: text/xml;application/xml;application/json #          
          min-request-size: 2048  #         ,       
        response:
        #        
          enabled:  true
      hystrix:
        #  feign   hystrix  ,     feign   hystrix  
    
  • bootstrapを修正する.ymlファイル、eureka-configをリンクし、
  • の構成を追加します.
    #            。
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8761/eureka/
      instance:
        hostname: localhost
        preferIpAddress: true
    
  • サービス開始クラスの最終変更:
  • @ServletComponentScan
    @EnableFeignClients
    @SpringCloudApplication
    public class FeignServiceApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(FeignServiceApplication.class, args);
        }
    
        /**
         *   feign        
         * Logger.Level       :
         * NONE,       (  )。
         * BASIC,         URL             。
         * HEADERS,               。
         * FULL,            ,      。
         */
        @Bean
        Logger.Level feignLoggerLevel(){
            return Logger.Level.HEADERS;
        }
    
        /**
         *   restTemplate    
         */
        @Bean
        @LoadBalanced
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    
    }
    

    最後に、リモートコールクライアントインタフェースを追加すると、次のようになります.
    /**
     * feign            demo-service   
     * name           
     * fallback        
     */
    @FeignClient(value = "demo-service",
    //        configuration = DisableHystrixConfiguration.class, //        
            fallback = DemoServiceHystrix.class)
    public interface DemoClient {
    
        @GetMapping(value = "/test/hello")
        ResultInfo hello();
    
        @GetMapping(value = "/test/{id}",consumes = "application/json")
        ResultInfo getTest(@PathVariable("id") Integer id);
    
        @PostMapping(value = "/test/add")
        ResultInfo addTest(Test test);
    
        @PutMapping(value = "/test/update")
        ResultInfo updateTest(Test test);
    
        @GetMapping(value = "/test/collapse/{id}")
        Test collapse(@PathVariable("id") Integer id);
    
        @GetMapping(value = "/test/collapse/findAll")
        List collapseFindAll(@RequestParam(value = "ids") List ids);
    }
    
    /**
     * restTemplate    
    */
    @Component
    public class RestClient {
    
        @Autowired
        RestTemplate restTemplate;
    
        @HystrixCommand
        public ResultInfo getTest(Integer id){
            log.info(">>>>>>>>>   restTemplate      >>>>>>>>>>>>");
            ResponseEntity restExchange =
                    restTemplate.exchange(
                            "http://demo-service/test/{id}",
                            HttpMethod.GET,
                            null, ResultInfo.class, id);
            return restExchange.getBody();
        }
    }
    
  • テストインタフェース
  • を追加
    @Log4j2
    @RestController
    @RequestMapping("/test")
    public class FeignController {
    
        @Autowired
        private DemoClient demoClient;
    
        @Autowired
        private RestClient restClient;
    
        @HystrixCommand
        @GetMapping("/feign/{id}")
        public ResultInfo testFeign(@PathVariable("id") Integer id){
            log.info("  feign          。。。");
            ResultInfo test = demoClient.getTest(id);
            log.info("          : " + test);
            /**
             * hystrix          1 
             *        fallbackMethod       
             */
            //log.info("    :" + randomlyRunLong() + "  ");
            return test;
        }
    
        @HystrixCommand
        @GetMapping("/rest/{id}")
        public ResultInfo testRest(@PathVariable("id") Integer id){
            log.info("  restTemplate          。。。");
            return restClient.getTest(id);
        }
    
  • はこの構成でhystrixを完了し、テスト使用を開始することができ、テストリモートコールのサービスインスタンスはdemo-serviceであり、postmanまたは他のツールを通じて構成が完璧であることを発見することができ、hystrixの具体的な構成と使用について説明します.

  • 3.hystrix返品メカニズム
  • ロールバックメカニズムは、バックアップメカニズムとも呼ばれ、サービスコールが到達できない場合、またはサービスコールがタイムアウトに失敗した場合のバックアップ操作です.fallback定義には2つの方法があります:
  • feignの@FeignClientでfallbackプロパティを定義し、c次clientインタフェースを実装するクラスを定義します.
  • @HystrixCommand(fallbackMethod="buildFallbackMethod")方式;
  • サービスコール遅延のメカニズムを使用して、
  • を処理します.
    @HystrixCommand(
                //      feign               
                fallbackMethod = "buildFallbacktestFeign",
        )
        @GetMapping("/feign/{id}")
        public ResultInfo testFeign(@PathVariable("id") Integer id){
            ...     
            /**
             * hystrix          1 
             *        fallbackMethod       
             */
            log.info("    :" + randomlyRunLong() + "  ");
            return test;
        }
            ...     
        /**
         * testFeign     
         * @return
         */
        private ResultInfo buildFallbacktestFeign(Integer id){
            return ResultUtil.success("testFeign       ,   : " + id );
        }
        //         
        private Integer randomlyRunLong(){
            Random rand = new Random();
            int randomNum = rand.nextInt(3) + 1;
            if (randomNum==3) {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return randomNum;
        }
    
  • 上記のようにtestfeignインタフェースを呼び出すと、乱数が3のときにスレッドのスリープが発生し、hystrixのデフォルトの呼び出しタイムアウト時間を超え、インタフェースはバックグラウンドメソッドbuildFallbacktestFeignの戻り値を返します.

  • 注意:ロールバックメソッドでリモートインタフェースの呼び出しを行う場合も、@HystrixCommandを使用してラップする必要があります.そうしないと、問題が発生して大きな損をします.
    4.hystrixスレッドプールの分離とパラメータの微調整
  • スレッドプール分離は、グローバルに設定してもよいし@HystrixCommandに以下のパラメータを格納して構成してもよい.動的構成が必要な場合はaopで構成してもよい.構成パラメータは以下の通りである:
  • .
    //                   
    threadPoolKey = "test",
    threadPoolProperties = {
            @HystrixProperty(name = "coreSize",value="30"),
            @HystrixProperty(name="maxQueueSize", value="10")},
    //                         
    commandProperties={
            //   hystrix          ,       
    //  @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="4000"),
            //                   ,   10             
            @HystrixProperty(name="circuitBreaker.requestVolumeThreshold", value="10"),
            //     requestVolumeThreshold     ,          
            @HystrixProperty(name="circuitBreaker.errorThresholdPercentage", value="75"),
            //              ,            
            @HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds", value="7000"),
            //           10000ms
            @HystrixProperty(name="metrics.rollingStats.timeInMilliseconds", value="15000"),
            // timeInMilliseconds    ,      ,cpu       
            @HystrixProperty(name="metrics.rollingStats.numBuckets", value="5")}
    
  • は、メソッドに追加する他、クラス上でクラスのグローバル制御
  • を行うこともできる.
    //        
    @DefaultProperties(
        commandProperties={
                //                   ,   10             
                @HystrixProperty(name="circuitBreaker.requestVolumeThreshold", value="10"),
                //     requestVolumeThreshold     ,          
                @HystrixProperty(name="circuitBreaker.errorThresholdPercentage", value="75")}
    )
    

    5.hystrixキャッシュ構成
    Hystrixリクエストキャッシュは、一度だけ書き込むと結果が変わらないのではなく、リクエストがControllerに到達するたびにHystrixRequestContextを初期化する必要があります.これまでのキャッシュは存在しませんでした.同じリクエストで結果が同じであることを保証し、同じリクエストでの最初のアクセス後に結果をキャッシュします.キャッシュのライフサイクルは1回のみ要求されます!redisを使用してurlキャッシュを行うモードとは異なります.テストコードは次のとおりです.
    @RestController
    @RequestMapping("/cache")
    public class CacheTestController {
        @Autowired
        private CacheService cacheService;
        @GetMapping("/{id}")
        public ResultInfo testCache(@PathVariable("id") Integer id){
            //       
            log.info("     : "+cacheService.testCache(id));
            //               
            log.info("     : "+cacheService.testCache(id));
            //     
            cacheService.updateCache(new Test(id,"wangwu","121"));
            //               ,         
            log.info("     : "+cacheService.testCache(id));
            //               
            log.info("     : "+cacheService.testCache(id));
            return ResultUtil.success("cache     !!!");
        }
    }
    
    @Service
    public class CacheService {
        @Autowired
        private DemoClient demoClient;
        
        /**
         * commandKey       
         * groupKey   
         * threadPoolKey      
         *
         * CacheResult         
         *  cacheKeyMethod       key            @CacheKey()    
         *
         * CacheKey()       key ,     
         * CacheKey("id") Integer id     ,  CacheKey()        key  
         *  java.beans.IntrospectionException: Method not found: isId
         *
         *              :
         *   java.lang.IllegalStateException: Request caching is not available. Maybe you need to initialize the HystrixRequestContext?
         *
         *   :                   ,        Controller   ,      
         *      HystrixRequestContext     ,            ,            
         *          ,                    ,             !
         *         redis  url       。
         *   ,          HystrixRequestContext   。
         */
        @CacheResult(cacheKeyMethod = "getCacheKey")
        @HystrixCommand(commandKey = "testCache", groupKey = "CacheTestGroup", threadPoolKey = "CacheTestThreadPool")
        public ResultInfo testCache(Integer id){
            log.info("test cache       。。。");
            return demoClient.getTest(id);
        }
        
        /**
         *           :
         * 1、                        ,                   ,
         *       fallbackMethod     ;
         * 2、           String  ,      :
         *    com.netflix.hystrix.contrib.javanica.exception.HystrixCachingException:
         *            return type of cacheKey method must be String.
         */
        private String getCacheKey(Integer id){
            log.info("      key  。。。");
            return String.valueOf(id);
        }
    
        @CacheRemove(commandKey = "testCache")
        @HystrixCommand(commandKey = "updateCache", groupKey = "CacheTestGroup", threadPoolKey = "CacheTestThreadPool")
        public ResultInfo updateCache(@CacheKey("id") Test test){
            log.info("update cache       。。。");
            return demoClient.updateTest(test);
        }
    }
    
  • postmanによるテストコールhttp://localhost:8090/cache/1;demo-serviceサービスは2回応答しており、最初のキャッシュchenが成功し、update後にキャッシュが削除されたことを示しています.テストの過程で出会った問題はすでに注釈されており、読者は自分でテストすることができる.

  • 6.hystrix異常放出処理
  • @HystrixCommandのignoreExceptions属性は無視する異常をHystrixBadRequestExceptionとしてパッケージし、コールバックを実行しない.
  • @RestController
    @RequestMapping("/exception")
    public class ExceptionTestController {
    
        @Autowired
        private DemoClient demoClient;
    
        /**
         * ignoreExceptions     RuntimeException  
         *    HystrixBadRequestException,       .
         */
        @HystrixCommand(ignoreExceptions = {RuntimeException.class},
                        fallbackMethod = "buildFallbackTestException")
        @GetMapping("/{id}")
        public ResultInfo testException(@PathVariable("id") Integer id){
            log.info("test exception           。。。");
            if (id == 1){
                throw new RuntimeException("        ");
            }
            return demoClient.getTest(id);
        }
    
        /**
         * testFeign     
         * @return
         */
        private ResultInfo buildFallbackTestException(Integer id){
            return ResultUtil.success("testException       ,   : " + id );
        }
    }
    
  • インタフェースコールhttp://localhost:8090/exception/1サービスは異常を放出し、インタフェースは異常情報を受信する.ignoreExceptions={RuntimeException.class}を削除してインタフェースを再度呼び出すと、buildFallbackTestExceptionロールバックメソッドが実行されていることがわかります.

  • 8.hystrix要求連結
    注意:マージメソッド自体を要求するときに高遅延のコマンドは、一般的に遅延の低いサービスに対して遅延時間の合理化と遅延タイムウィンドウ内の同時量を考慮する必要があります.
  • 統合を要求するテスト
  • @RestController
    @RequestMapping("/collapse")
    public class CollapseController {
    
        @Autowired
        private CollapseService collapseService;
    
        @GetMapping("/{id}")
        public ResultInfo testRest(@PathVariable("id") Integer id){
            log.info("   Collapse         ,    : " + LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
            Test test = collapseService.testRest(id);
            log.info("   Collapse         ,    : " + LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
            /**
             *       :
             *          : 2018-10-18T10:40:12.374
             *          : 2018-10-18T10:40:13.952
             *        :
             *          : 2018-10-18T10:43:41.472
             *          : 2018-10-18T10:43:41.494
             */
            return ResultUtil.success(test);
        }
    }
    
    @Service
    public class CollapseService {
    
        @Autowired
        private DemoClient demoClient;
    
        @HystrixCollapser(
                //        batch  
                batchMethod = "findAll",
                collapserProperties = {
                        //          100ms ,                    
                        @HystrixProperty(name = "timerDelayInMilliseconds", value = "1000")
                })
        public Test testRest(Integer id){
            Test test = demoClient.collapse(id);
            return test;
        }
    
        // batch method must be annotated with HystrixCommand annotation
        @HystrixCommand
        private List findAll(List ids){
            log.info("   findAll        Collapse     。。。");
    
            return demoClient.collapseFindAll(ids);
        }
    }
    

    -呼び出しインタフェースhttp://localhost:8090/collapse/1jmeterなどの同時テストツールを使用してテストできます.上記のコメント情報の結果を返すと、これらのパラメータを設定するには多方面のテストが必要であることを示します.
    9.Hystrix ThreadLocalコンテキストの伝達
    具体的には、以下の参考ブログを参考にしてもいいし、githubコードをダウンロードしてテストしてもいいです.
    注意:ThreadLocalコンテキストの転送を構成した後、hystrixのcacheテストを振り返ってテストしたところ、キャッシュをクリーンアップする機能が失効したことがわかりました.アイデアのあるブロガーがアドバイスをしてくれることを望んでいます.ありがとうございます.
    本文githubコードアドレス:私のgithub:spring-cloud基礎モジュール構築---ご指摘を歓迎します
    参考博文:SpringCloud(8)Hystrix要求キャッシュのHystrix使用によるThreadLocalコンテキストの伝達