深入浅出SpringCloud原理及实战「Netflix系列之Hystrix」针对于限流熔断组件Hystrix的回退降级实现方案和机制

Posted 洛神灬殇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入浅出SpringCloud原理及实战「Netflix系列之Hystrix」针对于限流熔断组件Hystrix的回退降级实现方案和机制相关的知识,希望对你有一定的参考价值。

回退降级

降级

所谓降级,就是指在在Hystrix执行非核心链路功能失败的情况下,我们如何处理,比如我们返回默认值等。如果我们要回退或者降级处理,代码上需要实现HystrixCommand.getFallback()方法或者是HystrixObservableCommand. resumeWithFallback()。

public class CommandHelloFailure extends HystrixCommand<String> 

    private final String name;

    public CommandHelloFailure(String name) 
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.name = name;
    

    @Override
    protected String run() 
        throw new RuntimeException("this command always fails");
    

    @Override
    protected String getFallback() 
        return "Hello Failure " + name + "!";
    

Hystrix的降级回退方式

Hystrix一共有如下几种降级回退模式:

Fail Fast 快速失败

 @Override
    protected String run() 
        if (throwException) 
            throw new RuntimeException("failure from CommandThatFailsFast");
         else 
            return "success";
        
    

如果我们实现的是HystrixObservableCommand.java则重写resumeWithFallback方法

@Override
    protected Observable<String> resumeWithFallback() 
        if (throwException) 
            return Observable.error(new Throwable("failure from CommandThatFailsFast"));
         else 
            return Observable.just("success");
        
    

Fail Silent 无声失败

返回null,空Map,空List

@Override
    protected String getFallback() 
        return null;
    
@Override
    protected List<String> getFallback() 
        return Collections.emptyList();
    
@Override
    protected Observable<String> resumeWithFallback() 
        return Observable.empty();
    

Fallback: Static 返回默认值

回退的时候返回静态嵌入代码中的默认值,这样就不会导致功能以Fail Silent的方式被清楚,也就是用户看不到任何功能了。而是按照一个默认的方式显示。

@Override
    protected Boolean getFallback() 
        return true;
    
@Override
    protected Observable<Boolean> resumeWithFallback() 
        return Observable.just( true );
    

Fallback: Stubbed 自己组装一个值返回

当我们执行返回的结果是一个包含多个字段的对象时,则会以Stubbed 的方式回退。Stubbed 值我们建议在实例化Command的时候就设置好一个值。以countryCodeFromGeoLookup为例,countryCodeFromGeoLookup的值,是在我们调用的时候就注册进来初始化好的。

CommandWithStubbedFallback command = new CommandWithStubbedFallback(1234, “china”);主要代码如下:

public class CommandWithStubbedFallback extends HystrixCommand<UserAccount> 

protected CommandWithStubbedFallback(int customerId, String countryCodeFromGeoLookup) 
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.customerId = customerId;
        this.countryCodeFromGeoLookup = countryCodeFromGeoLookup;
    
    @Override
    protected UserAccount getFallback() 
        /**
         * Return stubbed fallback with some static defaults, placeholders,
         * and an injected value 'countryCodeFromGeoLookup' that we'll use
         * instead of what we would have retrieved from the remote service.
         */
        return new UserAccount(customerId, "Unknown Name",
                countryCodeFromGeoLookup, true, true, false);
    

Fallback: Cache via Network 利用远程缓存

通过远程缓存的方式。在失败的情况下再发起一次remote请求,不过这次请求的是一个缓存比如redis。由于是又发起一起远程调用,所以会重新封装一次Command,这个时候要注意,执行fallback的线程一定要跟主线程区分开,也就是重新命名一个ThreadPoolKey。

public class CommandWithFallbackViaNetwork extends HystrixCommand<String> 
    private final int id;

    protected CommandWithFallbackViaNetwork(int id) 
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RemoteServiceX"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("GetValueCommand")));
        this.id = id;
    

    @Override
    protected String run() 
        //        RemoteServiceXClient.getValue(id);
        throw new RuntimeException("force failure for example");
    

    @Override
    protected String getFallback() 
        return new FallbackViaNetwork(id).execute();
    

    private static class FallbackViaNetwork extends HystrixCommand<String> 
        private final int id;

        public FallbackViaNetwork(int id) 
            super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RemoteServiceX"))
                    .andCommandKey(HystrixCommandKey.Factory.asKey("GetValueFallbackCommand"))
                    // use a different threadpool for the fallback command
                    // so saturating the RemoteServiceX pool won't prevent
                    // fallbacks from executing
                    .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("RemoteServiceXFallback")));
            this.id = id;
        

        @Override
        protected String run() 
            MemCacheClient.getValue(id);
        

        @Override
        protected String getFallback() 
            // the fallback also failed
            // so this fallback-of-a-fallback will 
            // fail silently and return null
            return null;
        
    

Primary + Secondary with Fallback 主次方式回退(主要和次要)

这个有点类似我们日常开发中需要上线一个新功能,但为了防止新功能上线失败可以回退到老的代码,我们会做一个开关比如使用zookeeper做一个配置开关,可以动态切换到老代码功能。那么Hystrix它是使用通过一个配置来在两个command中进行切换。

/**
 * Sample @link HystrixCommand pattern using a semaphore-isolated command
 * that conditionally invokes thread-isolated commands.
 */
public class CommandFacadeWithPrimarySecondary extends HystrixCommand<String> 

    private final static DynamicBooleanProperty usePrimary = DynamicPropertyFactory.getInstance().getBooleanProperty("primarySecondary.usePrimary", true);

    private final int id;

    public CommandFacadeWithPrimarySecondary(int id) 
        super(Setter
                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("SystemX"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("PrimarySecondaryCommand"))
                .andCommandPropertiesDefaults(
                        // we want to default to semaphore-isolation since this wraps
                        // 2 others commands that are already thread isolated
                        // 采用信号量的隔离方式
                        HystrixCommandProperties.Setter()
                                .withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)));
        this.id = id;
    

    //通过DynamicPropertyFactory来路由到不同的command
    @Override
    protected String run() 
        if (usePrimary.get()) 
            return new PrimaryCommand(id).execute();
         else 
            return new SecondaryCommand(id).execute();
        
    

    @Override
    protected String getFallback() 
        return "static-fallback-" + id;
    

    @Override
    protected String getCacheKey() 
        return String.valueOf(id);
    

    private static class PrimaryCommand extends HystrixCommand<String> 

        private final int id;

        private PrimaryCommand(int id) 
            super(Setter
                    .withGroupKey(HystrixCommandGroupKey.Factory.asKey("SystemX"))
                    .andCommandKey(HystrixCommandKey.Factory.asKey("PrimaryCommand"))
                    .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("PrimaryCommand"))
                    .andCommandPropertiesDefaults(
                            // we default to a 600ms timeout for primary
                            HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(600)));
            this.id = id;
        

        @Override
        protected String run() 
            // perform expensive 'primary' service call
            return "responseFromPrimary-" + id;
        

    

    private static class SecondaryCommand extends HystrixCommand<String> 

        private final int id;

        private SecondaryCommand(int id) 
            super(Setter
                    .withGroupKey(HystrixCommandGroupKey.Factory.asKey("SystemX"))
                    .andCommandKey(HystrixCommandKey.Factory.asKey("SecondaryCommand"))
                    .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("SecondaryCommand"))
                    .andCommandPropertiesDefaults(
                            // we default to a 100ms timeout for secondary
                            HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(100)));
            this.id = id;
        

        @Override
        protected String run() 
            // perform fast 'secondary' service call
            return "responseFromSecondary-" + id;
        

    

    public static class UnitTest 

        @Test
        public void testPrimary() 
            HystrixRequestContext context = HystrixRequestContext.initializeContext();
            try 
                //将属性"primarySecondary.usePrimary"设置为true,则走PrimaryCommand;设置为false,则走SecondaryCommand
                ConfigurationManager.getConfigInstance().setProperty("primarySecondary.usePrimary", true);
                assertEquals("responseFromPrimary-20", new CommandFacadeWithPrimarySecondary(20).execute());
             finally 
                context.shutdown();
                ConfigurationManager.getConfigInstance().clear();
            
        

        @Test
        public void testSecondary() 
            HystrixRequestContext context = HystrixRequestContext.initializeContext();
            try 
                //将属性"primarySecondary.usePrimary"设置为true,则走PrimaryCommand;设置为false,则走SecondaryCommand
                ConfigurationManager.getConfigInstance().setProperty("primarySecondary.usePrimary", false);
                assertEquals("responseFromSecondary-20", new CommandFacadeWithPrimarySecondary(20).execute());
             finally 
                context.shutdown();
                ConfigurationManager.getConfigInstance().clear();
            
        
    


以上是关于深入浅出SpringCloud原理及实战「Netflix系列之Hystrix」针对于限流熔断组件Hystrix的回退降级实现方案和机制的主要内容,如果未能解决你的问题,请参考以下文章

深入浅出SpringCloud原理及实战「Netflix系列之Fegin」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(下)

深入浅出SpringCloud原理及实战「Netflix系列之Hystrix」针对于限流熔断组件Hystrix的超时机制的原理和实现分析

深入浅出SpringCloud原理及实战「Netflix系列之Hystrix」针对于限流熔断组件Hystrix的基本参数和实现原理介绍分析

深入浅出Dubbo3原理及实战「SpringCloud-Alibaba系列」基于Nacos作为注册中心进行发布SpringCloud-alibaba生态的RPC接口实战

深入浅出SpringCloud原理及实战「Netflix系列之Ribbon」针对于负载均衡组件Ribbon的基本参数和实现原理介绍分析

深入浅出SpringCloud原理及实战「SpringCloud-Gateway系列」微服务API网关服务的Gateway全流程开发实践指南(入门篇)