Spring Cloud @HystrixCommand和@CacheResult注解使用,參數配置


  使用Spring Cloud時繞不開Hystrix,他幫助微服務實現斷路器功能。該框架的目標在於通過控制那些訪問遠程系統、服務和第三方庫的節點,從而對延遲和故障提供更強大的容錯能力。Hystrix具備服務降級,服務熔斷,線程和信號隔離,請求緩存,請求合並以及服務監控等強大功能。

  關於Hystrix的介紹請參見:http://www.sohu.com/a/131568369_494947,本文部分介紹引自此文,建議先閱讀此文了解Hystrix的使用場景和線程池概念。

  @HystrixCommand的基礎介紹請參見:http://blog.csdn.net/forezp/article/details/69934399,介紹的很詳細,我這里主要說一說這個注解的各個參數的使用。

 

  查看com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand的源代碼如下:

/**
 * Copyright 2012 Netflix, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.netflix.hystrix.contrib.javanica.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
 * This annotation used to specify some methods which should be processes as hystrix commands.
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface HystrixCommand {

    /**
     * The command group key is used for grouping together commands such as for reporting,
     * alerting, dashboards or team/library ownership.
     * <p/>
     * default => the runtime class name of annotated method
     *
     * @return group key
     */
    String groupKey() default "";

    /**
     * Hystrix command key.
     * <p/>
     * default => the name of annotated method. for example:
     * <code>
     *     ...
     *     @HystrixCommand
     *     public User getUserById(...)
     *     ...
     *     the command name will be: 'getUserById'
     * </code>
     *
     * @return command key
     */
    String commandKey() default "";

    /**
     * The thread-pool key is used to represent a
     * HystrixThreadPool for monitoring, metrics publishing, caching and other such uses.
     *
     * @return thread pool key
     */
    String threadPoolKey() default "";

    /**
     * Specifies a method to process fallback logic.
     * A fallback method should be defined in the same class where is HystrixCommand.
     * Also a fallback method should have same signature to a method which was invoked as hystrix command.
     * for example:
     * <code>
     *      @HystrixCommand(fallbackMethod = "getByIdFallback")
     *      public String getById(String id) {...}
     *
     *      private String getByIdFallback(String id) {...}
     * </code>
     * Also a fallback method can be annotated with {@link HystrixCommand}
     * <p/>
     * default => see {@link com.netflix.hystrix.contrib.javanica.command.GenericCommand#getFallback()}
     *
     * @return method name
     */
    String fallbackMethod() default "";

    /**
     * Specifies command properties.
     *
     * @return command properties
     */
    HystrixProperty[] commandProperties() default {};

    /**
     * Specifies thread pool properties.
     *
     * @return thread pool properties
     */
    HystrixProperty[] threadPoolProperties() default {};

    /**
     * Defines exceptions which should be ignored.
     * Optionally these can be wrapped in HystrixRuntimeException if raiseHystrixExceptions contains RUNTIME_EXCEPTION.
     *
     * @return exceptions to ignore
     */
    Class<? extends Throwable>[] ignoreExceptions() default {};

    /**
     * Specifies the mode that should be used to execute hystrix observable command.
     * For more information see {@link ObservableExecutionMode}.
     *
     * @return observable execution mode
     */
    ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;

    /**
     * When includes RUNTIME_EXCEPTION, any exceptions that are not ignored are wrapped in HystrixRuntimeException.
     *
     * @return exceptions to wrap
     */
    HystrixException[] raiseHystrixExceptions() default {};

    /**
     * Specifies default fallback method for the command. If both {@link #fallbackMethod} and {@link #defaultFallback}
     * methods are specified then specific one is used.
     * note: default fallback method cannot have parameters, return type should be compatible with command return type.
     *
     * @return the name of default fallback method
     */
    String defaultFallback() default "";
}

 

  讓我們來看一下這個注解的簡單應用:

package com.example.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@Service
public class ConsumerService {

    @Autowired
    private RestTemplate restTemplate;

    @HystrixCommand(commandKey = "testCommand", groupKey = "testGroup", threadPoolKey = "testThreadKey",
            fallbackMethod = "hiConsumerFallBack", ignoreExceptions = {NullPointerException.class},
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "30"),
                    @HystrixProperty(name = "maxQueueSize", value = "101"),
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"),
                    @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"),
                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1440")
            }
            )
    public String hiConsumer(String id) {
        
        //SERVICE_HI是服務端的spring.application.name,並且大寫,hi為服務端提供的接口
        return restTemplate.getForEntity("http://SERVICE_HI/hi", String.class).getBody();
    }
    
    public String hiConsumerFallBack(String id, Throwable e) {
        return "This is a error";
    }

}

  

  讓我們來逐個介紹下@HystrixCommand注解的各個參數:

  1:commandKey:配置全局唯一標識服務的名稱,比如,庫存系統有一個獲取庫存服務,那么就可以為這個服務起一個名字來唯一識別該服務,如果不配置,則默認是@HystrixCommand注解修飾的函數的函數名。

  2:groupKey:一個比較重要的注解,配置全局唯一標識服務分組的名稱,比如,庫存系統就是一個服務分組。通過設置分組,Hystrix會根據組來組織和統計命令的告、儀表盤等信息。Hystrix命令默認的線程划分也是根據命令組來實現。默認情況下,Hystrix會讓相同組名的命令使用同一個線程池,所以我們需要在創建Hystrix命令時為其指定命令組來實現默認的線程池划分。此外,Hystrix還提供了通過設置threadPoolKey來對線程池進行設置。建議最好設置該參數,使用threadPoolKey來控制線程池組。

  3:threadPoolKey:對線程池進行設定,細粒度的配置,相當於對單個服務的線程池信息進行設置,也可多個服務設置同一個threadPoolKey構成線程組。

  4:fallbackMethod:@HystrixCommand注解修飾的函數的回調函數,@HystrixCommand修飾的函數必須和這個回調函數定義在同一個類中,因為定義在了同一個類中,所以fackback method可以是public/private均可。

  5:commandProperties:配置該命令的一些參數,如executionIsolationStrategy配置執行隔離策略,默認是使用線程隔離,此處我們配置為THREAD,即線程池隔離。參見:com.netflix.hystrix.HystrixCommandProperties中各個參數的定義。

  6:threadPoolProperties:線程池相關參數設置,具體可以設置哪些參數請見:com.netflix.hystrix.HystrixThreadPoolProperties
  7:ignoreExceptions:調用服務時,除了HystrixBadRequestException之外,其他@HystrixCommand修飾的函數拋出的異常均會被Hystrix認為命令執行失敗而觸發服務降級的處理邏輯(調用fallbackMethod指定的回調函數),所以當需要在命令執行中拋出不觸發降級的異常時來使用它,通過這個參數指定,哪些異常拋出時不觸發降級(不去調用fallbackMethod),而是將異常向上拋出。
  8:observableExecutionMode:定義hystrix observable command的模式;
      9:raiseHystrixExceptions:任何不可忽略的異常都包含在HystrixRuntimeException中;
      10:defaultFallback:默認的回調函數,該函數的函數體不能有入參,返回值類型與@HystrixCommand修飾的函數體的返回值一致。如果指定了fallbackMethod,則fallbackMethod優先級更高。

請求緩存功能:
 
注解
描述
屬性
@CacheResult
該注解用來標記請求命令返回的結果應該被緩存,它必須與@HystrixCommand注解結合使用
cacheKeyMethod
@CacheRemove
該注解用來讓請求命令的緩存失效,失效的緩存根據定義Key決定
commandKey,
cacheKeyMethod
@CacheKey
該注解用來在請求命令的參數上標記,使其作為緩存的Key值,如果沒有標注則會使用所有參數。如果同事還是使用了@CacheResult和@CacheRemove注解的cacheKeyMethod方法指定緩存Key的生成,那么該注解將不會起作用
value
 
 
開啟請求緩存功能:
 
    開啟請求緩存功能需要使用注解@CacheResult,該注解源碼如下:
/**
* Copyright 2015 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.hystrix.contrib.javanica.cache.annotation;
 
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
/**
* Marks a methods that results should be cached for a Hystrix command.
* This annotation must be used in conjunction with {@link com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand} annotation.
*
* @author dmgcodevil
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheResult {
 
    /**
     * Method name to be used to get a key for request caching.
     * The command and cache key method should be placed in the same class and have same method signature except
     * cache key method return type, that should be <code>String</code>.
     * <p/>
     * cacheKeyMethod has higher priority than an arguments of a method, that means what actual arguments
     * of a method that annotated with {@link CacheResult} will not be used to generate cache key, instead specified
     * cacheKeyMethod fully assigns to itself responsibility for cache key generation.
     * By default this returns empty string which means "do not use cache method".
     *
     * @return method name or empty string
     */
    String cacheKeyMethod() default "";
}
 

 

 
從類簽名上看,該注解標記結果需要被緩存,並且這個注解需要結合@HystrixCommand注解一起使用。該注解有一個參數 cacheKeyMethod,用來指定獲取cacheKey的方法名。Hystrix會根據獲取到的cacheKey值來區分是否是重復請求,如果他們的cacheKey相同,那么該依賴服務只會在第一個請求到達時被真實的調用一次,另外一個請求則是直接從請求緩存中根據這個cacheKey來獲取到的結果。所以開啟請求緩存可以讓我們實現的Hystrix命令具備下面幾項好處:
  1. 減少重復的請求數,降低依賴服務的並發度;
  2. 在同一個用戶請求的上下文,想同依賴服務的返回數據始終保持一致。
  3. 請求緩存在run()和contruct()執行之前生效,所以可以有效減少不必要的線程開銷;    
 
使用@CacheResult開啟緩存代碼示例:
package com.example.demo;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
 
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import com.example.demo.entity.User;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
 
@Service
public class CacheResultDemo {
 
    @Autowired
    private RestTemplate restTemplate;
 
    @CacheResult(cacheKeyMethod = "getUserId")
    @HystrixCommand(fallbackMethod = "hiConsumerFallBack")
    public User hiConsumer(String id) {
        
        //SERVICE_HI是服務端的spring.application.name,並且大寫,hi為服務端提供的接口
        return restTemplate.getForEntity("http://SERVICE_HI/hi", User.class).getBody();
    }
    
    public String hiConsumerFallBack(String id, Throwable e) {
        return "This is a error";
    }
    
    public String getUserId(String id) {
        return id;
    }
 
}

 

 
使用CacheKey開啟緩存代碼示例:
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
importcom.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;
import com.example.demo.entity.User;
importcom.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
@Service
public class CacheKeyDemo {
      @Autowired
      private RestTemplate restTemplate;
      @HystrixCommand(fallbackMethod = "hiConsumerFallBack")
      public User hiConsumer(@CacheKey("id") String id) {
            
            //SERVICE_HI是服務端的spring.application.name,並且大寫,hi為服務端提供的接口
            return restTemplate.getForEntity("http://SERVICE_HI/hi", User.class).getBody();
      }
      
      public String hiConsumerFallBack(String id, Throwable e) {
            return "This is a error";
      }
}

 

 
其中@CacheKey除了可以指定方法參數為緩存key之外,還可以指定對象中的屬性作為緩存Key,如下:
package com.example.demo;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
 
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;
import com.example.demo.entity.User;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
 
@Service
public class CacheKeyDemo2 {
 
    @Autowired
    private RestTemplate restTemplate;
 
    @HystrixCommand(fallbackMethod = "hiConsumerFallBack")
    public User hiConsumer(@CacheKey("id") User user) {
        
        //SERVICE_HI是服務端的spring.application.name,並且大寫,hi為服務端提供的接口
        return restTemplate.getForEntity("http://SERVICE_HI/hi", User.class, user.getId()).getBody();
    }
    
    public String hiConsumerFallBack(String id, Throwable e) {
        return "This is a error";
    }
 
}
 

 

 
清理失效緩存功能:
    使用請求緩存時,如果只是杜操作,那么不需要考慮緩存中的內容是否正確的問題,但是如果請求命令中還有更新數據的寫操作,那么緩存中的數據就需要我們在進行寫操作時進行及時處理,以防止讀操作的請求命令獲取到了失效的數據。
    通過@CacheRemove注解來實現失效緩存清理功能:
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
importcom.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;
importcom.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;
import com.example.demo.entity.User;
importcom.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
@Service
public class CacheRemoveDemo {
      @Autowired
      private RestTemplate restTemplate;
      @CacheRemove(commandKey = "getUserId")
      @HystrixCommand(fallbackMethod = "hiConsumerFallBack")
      public void update(@CacheKey("id") User user) {
            
            //SERVICE_HI是服務端的spring.application.name,並且大寫,hi為服務端提供的接口
            restTemplate.postForObject("http://SERVICE_HI/hi", user, User.class);
            return;
      }
      
      public String hiConsumerFallBack(String id, Throwable e) {
            return "This is a error";
      }
      
      public String getUserId(String id) {
            return id;
      }
}

 


免責聲明!

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



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