使用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中各個參數的定義。
|
注解
|
描述
|
屬性
|
|
@CacheResult
|
該注解用來標記請求命令返回的結果應該被緩存,它必須與@HystrixCommand注解結合使用
|
cacheKeyMethod
|
|
@CacheRemove
|
該注解用來讓請求命令的緩存失效,失效的緩存根據定義Key決定
|
commandKey,
cacheKeyMethod
|
|
@CacheKey
|
該注解用來在請求命令的參數上標記,使其作為緩存的Key值,如果沒有標注則會使用所有參數。如果同事還是使用了@CacheResult和@CacheRemove注解的cacheKeyMethod方法指定緩存Key的生成,那么該注解將不會起作用
|
value
|
/** * 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 ""; }
- 減少重復的請求數,降低依賴服務的並發度;
- 在同一個用戶請求的上下文,想同依賴服務的返回數據始終保持一致。
- 請求緩存在run()和contruct()執行之前生效,所以可以有效減少不必要的線程開銷;
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; } }
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"; } }
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"; } }
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; } }
