线程池隔离 |
信号量隔离 |
|
线程 |
与调用线程非相同线程 |
与调用线程相同(Tomcat线程) |
开销 |
排队、调度、上下文切换 |
无线程切换,开销低 |
异步 |
支持 |
不支持 |
并发支持 |
支持(量大线程池大小) |
支持(量大信号量上限) |
当请求的服务网络开销比较大,或者是请求比较耗时,我们最好使用线程隔离策略,这样的策略,可以保证大量的容器(Tomcat)线程可用,不会因服务原因,一直处于阻塞或等待状态,快速失败返回。
-
线程池大小 = 服务TP99响应时长(单位秒) * 每秒请求量 + 冗余缓冲值
-
超时时间(单位毫秒) = 1000(毫秒) / 每秒请求量
-
缓存注解未生效,此时Hystrix注解切面的执行是在最外层,由于Hystrix内部执行是通过ProceedingJoinPoint.getTarget()获取目标对象,使用反射调用的方式直接执行到目标对象方法上,从而造成中间其它注解逻辑丢失。可通过指定注解执行顺序@Order解决保证Hystrix注解执行在最里层。
-
因缓存异常造成该查询方法被熔断,如果Hystrix注解切面的执行是在最外层,此时Hystrix熔断管理的方法逻辑除了第三方服务远程调用,也包括了缓存调用逻辑。如果缓存调用出现异常就会算作整个方法异常,从而引起整个方法被熔断。
@HystrixCommand(fallbackMethod="queryUserByIdFallback")
public User queryUserById(String userId) {
if(StringUtils.isEmpty(userId)) {
throw new BizException("参数不合法");
}
Result<User> result;
try {
result = userFacade.queryById(userId);
} catch(Exception e) {
log.error("query user error. id={}", id, e);
}
if(result != null && result.isSuccess()) {
return result.getData();
}
return null;
}
-
参数校验不通过时的异常处理,非法参数校验等非系统调用的异常失败不应该影响熔断逻辑,不应该算作失败统计范围内。对此优化建议是将参数校验放到远程调用封装方法的外面,或者封装成HystrixBadRequestException进行抛出。因为在Hystrix内部逻辑中HystrixBadRequestException异常已默认为不算作失败统计范围内。
-
try-catch远程调用的异常处理,对远程服务的直接调用进行try-catch会把异常直接“吞掉”,会直接造成Hystrix获取不到网络异常等服务不可用异常。建议在catch日志记录处理后将异常再throw出来。
-
fallback方法访问级别、参数等要与对应依赖服务一致,对于需要获取触发fallback的异常实例,可以通过fallback方法增加Throwable类型参数(加到最后一个参数)即可。
-
fallback 方法中执行的逻辑尽量轻量,如用本地缓存或静态默认值,避免远程调用。
-
如果fallback方法里有远程调用,建议也使用Hystrix包装起来,且保证与主命令线程池的隔离。
-
对于写操作的远程调用不建议使用fallback降级,写服务的调用失败可以直接抛出给方法调用侧进行业务判断。
-
通过系统参数hystrix.plugin.HystrixDynamicProperties.implementation
-
基于Java SPI机制
-
Archaius动态属性扩展实现类(默认)
-
Hystrix内置基于System.getProperty的HystrixDynamicProperties实现
public interface HystrixDynamicProperties {
/**
* Requests a property that may or may not actually exist.
* @param name property name, never <code>null</code>
* @param fallback default value, maybe <code>null</code>
* @return never <code>null</code>
*/
public HystrixDynamicProperty<String> getString(String name, String fallback);
/**
* Requests a property that may or may not actually exist.
* @param name property name, never <code>null</code>
* @param fallback default value, maybe <code>null</code>
* @return never <code>null</code>
*/
public HystrixDynamicProperty<Integer> getInteger(String name, Integer fallback);
/**
* Requests a property that may or may not actually exist.
* @param name property name, never <code>null</code>
* @param fallback default value, maybe <code>null</code>
* @return never <code>null</code>
*/
public HystrixDynamicProperty<Long> getLong(String name, Long fallback);
/**
* Requests a property that may or may not actually exist.
* @param name property name
* @param fallback default value
* @return never <code>null</code>
*/
public HystrixDynamicProperty<Boolean> getBoolean(String name, Boolean fallback);
}
public interface HystrixDynamicProperty<T> extends HystrixProperty<T>{
public String getName();
/**
* Register a callback to be run if the property is updated.
* @param callback callback.
*/
public void addCallback(Runnable callback);
}
private abstract static class CustomDynamicProperty<T> implements HystrixDynamicProperty<T>, PropertyObserver {
protected final String name;
protected final T defaultValue;
protected List<Runnable> callbacks;
protected CustomDynamicProperty(String propName, T defaultValue) {
this.name = propName;
this.defaultValue = defaultValue;
PropertyObserverManager.add(this);
}
@Override
public String getName() {
return name;
}
@Override
public void addCallback(Runnable callback) {
if (callbacks == null)
callbacks = new ArrayList<>(1);
this.callbacks.add(callback);
}
@Override
public String keyName() {
return name;
}
@Override
public void update(PropertyItem item) {
if(getName().equals(item.getName())) {
for(Runnable r : callbacks) {
r.run();
}
}
}
}
private static class StringDynamicProperty extends CustomDynamicProperty<String> {
protected StringDynamicProperty(String propName, String defaultValue) {
super(propName, defaultValue);
}
@Override
public String get() {
return ConfigManager.getString(name, defaultValue);
}
}
private static class IntegerDynamicProperty extends CustomDynamicProperty<Integer> {
protected IntegerDynamicProperty(String propName, Integer defaultValue) {
super(propName, defaultValue);
}
@Override
public Integer get() {
String configValue = ConfigManager.get(name);
if(StringUtils.isNotEmpty(configValue)) {
return Integer.valueOf(configValue);
}
return defaultValue;
}
}
private static class LongDynamicProperty extends CustomDynamicProperty<Long> {
protected LongDynamicProperty(String propName, Long defaultValue) {
super(propName, defaultValue);
}
@Override
public Long get() {
String configValue = ConfigManager.get(name);
if(StringUtils.isNotEmpty(configValue)) {
return Long.valueOf(configValue);
}
return defaultValue;
}
}
private static class BooleanDynamicProperty extends CustomDynamicProperty<Boolean> {
protected BooleanDynamicProperty(String propName, Boolean defaultValue) {
super(propName, defaultValue);
}
@Override
public Boolean get() {
String configValue = ConfigManager.get(name);
if(StringUtils.isNotEmpty(configValue)) {
return Boolean.valueOf(configValue);
}
return defaultValue;
}
}
public class DemoHystrixDynamicProperties implements HystrixDynamicProperties {
@Override
public HystrixDynamicProperty<String> getString(String name, String fallback) {
return new StringDynamicProperty(name, fallback);
}
@Override
public HystrixDynamicProperty<Integer> getInteger(String name, Integer fallback) {
return new IntegerDynamicProperty(name, fallback);
}
@Override
public HystrixDynamicProperty<Long> getLong(String name, Long fallback) {
return new LongDynamicProperty(name, fallback);
}
@Override
public HystrixDynamicProperty<Boolean> getBoolean(String name, Boolean fallback) {
return new BooleanDynamicProperty(name, fallback);
}
}
public class CustomCfgConfigurationSource implements PolledConfigurationSource {
private final static String CONFIG_KEY_PREFIX = "hystrix";
@Override
public PollResult poll(boolean initial, Object checkPoint) throws Exception {
Map<String, Object> map = load();
return PollResult.createFull(map);
}
private Map<String, Object> load() throws Exception{
Map<String, Object> map = new HashMap<>();
Set<String> keys = ConfigManager.keys();
for(String key : keys) {
if(key.startsWith(CONFIG_KEY_PREFIX)) {
map.put(key, ConfigManager.get(key));
}
}
return map;
}
}
public class CustomCfgPollingScheduler extends AbstractPollingScheduler {
private final static Logger logger = LoggerFactory.getLogger("CustomCfgPollingScheduler");
private final static String CONFIG_KEY_PREFIX = "hystrix";
@Override
public void startPolling(PolledConfigurationSource source, final Configuration config) {
super.startPolling(source, config);
//
ConfigManager.addListener(new ConfigListener() {
@Override
public void eventReceived(PropertyItem item, ChangeEventType type) {
String name = item.getName();
if(name.startsWith(CONFIG_KEY_PREFIX)) {
String newValue = item.getValue();
//新增&修改
if(ChangeEventType.ITEM_ADDED.equals(type) || ChangeEventType.ITEM_UPDATED.equals(type)) {
addOrChangeProperty(name, newValue, config);
}
//删除
else if(ChangeEventType.ITEM_REMOVED.equals(type)) {
deleteProperty(name, config);
}
else {
logger.error("error config change event type {}.", type);
}
}
}
});
}
private void addOrChangeProperty(String name, Object newValue, final Configuration config) {
if (!config.containsKey(name)) {
config.addProperty(name, newValue);
} else {
Object oldValue = config.getProperty(name);
if (newValue != null) {
if (!newValue.equals(oldValue)) {
config.setProperty(name, newValue);
}
} else if (oldValue != null) {
config.setProperty(name, null);
}
}
}
private void deleteProperty(String key, final Configuration config) {
if (config.containsKey(key)) {
config.clearProperty(key);
}
}
@Override
protected void schedule(Runnable pollingRunnable) {
//IGNORE OPERATION
}
@Override
public void stop() {
//IGNORE OPERATION
}
}
DynamicConfiguration dynamicConfiguration = new DynamicConfiguration(new CustomCfgConfigurationSource(), new CustomCfgPollingScheduler());
ConfigurationManager.install(dynamicConfiguration);
public class CustomCfgDynamicConfiguration extends ConcurrentMapConfiguration {
private final static Logger logger = LoggerFactory.getLogger("CustomCfgDynamicConfiguration");
private final static String CONFIG_KEY_PREFIX = "hystrix";
public CustomCfgDynamicConfiguration() {
super();
load();
initEvent();
}
/**
* 从配置中心全量加载Hystrix配置参数信息
*/
private void load() {
Set<String> keys = ConfigManager.keys();
for(String key : keys) {
if(key.startsWith(CONFIG_KEY_PREFIX)) {
map.put(key, ConfigManager.get(key));
}
}
}
/**
* 通过配置中心监听事件回调处理,针对Hystrix配置参数变更进行同步
*/
private void initEvent() {
ConfigManager.addListener(new ConfigListener() {
@Override
public void eventReceived(PropertyItem item, ChangeEventType type) {
String name = item.getName();
if(name.startsWith(CONFIG_KEY_PREFIX)) {
String newValue = item.getValue();
//新增&修改
if(ChangeEventType.ITEM_ADDED.equals(type) || ChangeEventType.ITEM_UPDATED.equals(type)) {
addOrChangeProperty(name, newValue);
}
//删除
else if(ChangeEventType.ITEM_REMOVED.equals(type)) {
deleteProperty(name);
}
else {
logger.error("error config change event type {}.", type);
}
}
}
});
}
/**
* 新增或修改参数值
* @param name
* @param newValue
*/
private void addOrChangeProperty(String name, Object newValue) {
if (!this.containsKey(name)) {
this.addProperty(name, newValue);
} else {
Object oldValue = this.getProperty(name);
if (newValue != null) {
if (!newValue.equals(oldValue)) {
this.setProperty(name, newValue);
}
} else if (oldValue != null) {
this.setProperty(name, null);
}
}
}
/**
* 删除参数值
* @param key
*/
private void deleteProperty(String key) {
if (this.containsKey(key)) {
this.clearProperty(key);
}
}
}
原文始发于微信公众号(分布式实验室):Hystrix实战经验分享
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论