hystrix文档翻译之如何使用

 

Hello World!

  使用HystrixCommand实现“Hello World”。

public class CommandHelloWorld extends HystrixCommand<String> {
    private final String name;
    public CommandHelloWorld(String name) {
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.name = name;
    }
    @Override
    protected String run() {
        // a real example would do work like a network call here
        return "Hello " + name + "!";
    }
}

  使用HystrixObservableCommand实现“Hello World”。

public class CommandHelloWorld extends HystrixObservableCommand<String> {
    private final String name;
    public CommandHelloWorld(String name) {
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.name = name;
    }
    @Override
    protected Observable<String> construct() {
        return Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> observer) {
                try {
                    if (!observer.isUnsubscribed()) {
                        // a real example would do work like a network call here
                        observer.onNext("Hello");
                        observer.onNext(name + "!");
                        observer.onCompleted();
                    }
                } catch (Exception e) {
                    observer.onError(e);
                }
            }
         } ).subscribeOn(Schedulers.io());
    }
}

同步执行

  使用HystrixCommand的execute方法同步执行。

String s = new CommandHelloWorld("World").execute();

  HystrixObservableCommand没有execute方法,但如果你确定Observable只会返回一个单一结果,你可以使用.toBlocking().toFuture().get()方法。

异步执行

  使用HystrixCommand的queue()方法实现异步:

Future<String> fs = new CommandHelloWorld("World").queue();

  通过get方式获得结果

String s = fs.get();

  HystrixObservableCommand没有queue方法,但如果你确定Observable只会返回一个单一结果,你可以使用.toBlocking().toFuture()方法。

响应执行

  通过下面两个方法可以获取Observable对象。

  • observe,返回一个Observable并且立即执行命令,因为observe方法内部使用了一个RepaySubject,所以也会接受到监听以前命令返回的结果。
  • toObservable,返回一个Observable但不回立即执行,当监听该对象时才执行。
Observable<String> ho = new CommandHelloWorld("World").observe();
// or Observable<String> co = new CommandHelloWorld("World").toObservable();
ho.subscribe(new Action1<String>() {
    @Override
    public void call(String s) {
         // value emitted here
    }
});

  使用单元测试执行

@Test
public void testObservable() throws Exception {

    Observable<String> fWorld = new CommandHelloWorld("World").observe();
    Observable<String> fBob = new CommandHelloWorld("Bob").observe();

    // blocking
    assertEquals("Hello World!", fWorld.toBlockingObservable().single());
    assertEquals("Hello Bob!", fBob.toBlockingObservable().single());

    // non-blocking 
    // - this is a verbose anonymous inner-class approach and doesn't do assertions
    fWorld.subscribe(new Observer<String>() {

        @Override
        public void onCompleted() {
            // nothing needed here
        }

        @Override
        public void onError(Throwable e) {
            e.printStackTrace();
        }

        @Override
        public void onNext(String v) {
            System.out.println("onNext: " + v);
        }

    });

    // non-blocking
    // - also verbose anonymous inner-class
    // - ignore errors and onCompleted signal
    fBob.subscribe(new Action1<String>() {

        @Override
        public void call(String v) {
            System.out.println("onNext: " + v);
        }

    });
}

响应式命令

  除了使用上面的方式来获取Observable对象,还可以通过HystrixObservableCommand来创建一个Observable对象。HystrixObservableCommand可以创建一个返回多个值的Observable对象。HystrixObservableCommand使用consruct方法来执行命令而不是run方法。通过一下两种方式来获取Observable对象:

  • observe,返回一个Observable并且立即执行命令,因为observe方法内部使用了一个RepaySubject,所以也会接受到监听以前命令返回的结果。
  • toObservable,返回一个Observable但不回立即执行,当监听该对象时才执行。

降级

  通过添加fallback方法可以让命令执行异常时执行降级措施。你会为大多数的hystrix 命令实现降级方法,除了一下的情况:

  • 命令执行写操作

  如果hystrix命令是被设计成一个写操作而非读操作(HystrixCommand命令返回void或者HystrixObservableCommand返回一个空的Observable对象)。那么没有什么实现fallback方法。如果写出错了,应该把异常抛出给调用方。

  • 执行批量计算

  如果hystrix命令使用了缓存、或者批量、或者离线技术,也应该把异常抛给调用方,让调用放过处理而不是降级。

  无论你的命令是否有fallback,hystrix状态和熔断器状态都会更新。

  在HystrixCommand中实现getFallback方法,当run方法失败、超时、线程池拒绝、信号量拒绝、熔断时将会执行getFallback,并返回结果。

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 + "!";
    }
}

  run方法执行时将会抛出一个异常。但调用方会收到getFallback方法的返回结果。

  使用HystrixObservableCommand的resumeWithFallback方法返回一个Observable对象也可以在执行命令失败后被调用返回。因为Observable对象可能在发射了一些数据之后才发生异常,所以可能在执行resumeWithFallback前返回数据。Hystrix内部使用了rxjava的onErrorResumeNext来实现。

异常传播

  所有从run抛出的异常,除了HystrixBadRequestException都会执行getFallback方法并且进入熔断计算。你可以把你的异常封装成HystrixBadRequestException抛出,HystrixBadRequestException常常用于抛出参数错误,非系统异常不需要记入metric和执行降级的错误。

  执行异常类型

失败类型 异常类 异常原因
FAILURE HystrixRuntimeException underlying exception (user-controlled)
TIMEOUT HystrixRuntimeException TimeoutException
SHORT_CIRCUITED HystrixRuntimeException RuntimeException
THREAD_POOL_REJECTED HystrixRuntimeException RejectedExecutionException
SEMAPHORE_REJECTED HystrixRuntimeException RuntimeException
BAD_REQUEST HystrixBadRequestException underlying exception (user-controlled)

 命令名

  命令名默认是使用类名

String name = cls.getSimpleName();

  可以自定义命令名

public CommandHelloWorld(String name) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld")));
        this.name = name;
    }

  使用下面的方式来为每个command缓存Setter。  

private static final Setter cachedSetter = 
        Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
            .andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"));    
    public CommandHelloWorld(String name) {
        super(cachedSetter);
        this.name = name;
    }

  HystrixCommandKey是一个接口,并且提供了一个Factory来构造实现类。

HystrixCommandKey.Factory.asKey("HelloWorld")

组名

  Hystrix使用组名来合并命令,用于报表,报警,项目管理。

  如果没有指定线程池key,默认使用组名作为线程池key。

  HystrixCommandGroupKey是一个接口,并且提供了一个Factory来构造实现类。

HystrixCommandGroupKey.Factory.asKey("ExampleGroup")

线程池key

  线程池key唯一代表了一个线程池,用于监控、metrics、命令执行。默认使用HystrixCommandGroupKey。也可以显式指明:

 public CommandHelloWorld(String name) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"))
                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")));
        this.name = name;
    }

  HystrixThreadPoolKey是一个接口,并且提供了一个Factory来构造实现类。

HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")

  当多个命令属于同一个组,但是又要相互直接隔离时,可以显示指定HystrixThreadPoolKey。

缓存

  通过实现HystrixCommand或HystrixObservableCommand的getCacheKey方法 ,可以开启请求缓存。

public class CommandUsingRequestCache extends HystrixCommand<Boolean> {
    private final int value;
    protected CommandUsingRequestCache(int value) {
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.value = value;
    }
    @Override
    protected Boolean run() {
        return value == 0 || value % 2 == 0;
    }
    @Override
    protected String getCacheKey() {
        return String.valueOf(value);
    }
}

  因为请求缓存依赖HystrixRequestContext,所以在执行前需要先进行初始化。

     @Test
        public void testWithoutCacheHits() {
            HystrixRequestContext context = HystrixRequestContext.initializeContext();
            try {
                assertTrue(new CommandUsingRequestCache(2).execute());
                assertFalse(new CommandUsingRequestCache(1).execute());
                assertTrue(new CommandUsingRequestCache(0).execute());
                assertTrue(new CommandUsingRequestCache(58672).execute());
            } finally {
                context.shutdown();
            }
        }

  通常通过ServletFilter来初始化和关闭HystrixRequestContext。

 @Test
        public void testWithCacheHits() {
            HystrixRequestContext context = HystrixRequestContext.initializeContext();
            try {
                CommandUsingRequestCache command2a = new CommandUsingRequestCache(2);
                CommandUsingRequestCache command2b = new CommandUsingRequestCache(2);
                assertTrue(command2a.execute());
                // this is the first time we've executed this command with
                // the value of "2" so it should not be from cache
                assertFalse(command2a.isResponseFromCache());
                assertTrue(command2b.execute());
                // this is the second time we've executed this command with
                // the same value so it should return from cache
                assertTrue(command2b.isResponseFromCache());
            } finally {
                context.shutdown();
            }

            // start a new request context
            context = HystrixRequestContext.initializeContext();
            try {
                CommandUsingRequestCache command3b = new CommandUsingRequestCache(2);
                assertTrue(command3b.execute());
                // this is a new request context so this 
                // should not come from cache
                assertFalse(command3b.isResponseFromCache());
            } finally {
                context.shutdown();
            }
        }

请求合并

   请求合并可以让多个请求合并成一到HystrixCommand中执行。可以设置合并请求数量和合并等待时间来触发请求合并。有两种作用域:请求级别和全局,通过构造函数指定,默认使用请求级别。

  请求级别的合并通过一个HystrixRequestContext实现。全局的合并通过多个HystrixRequestContexts;如果依赖服务不能处理多个HystrixRequestContexts,最好使用请求级别作用域。

  使用请求级别合并:

public class CommandCollapserGetValueForKey extends HystrixCollapser<List<String>, String, Integer> {
    private final Integer key;
    public CommandCollapserGetValueForKey(Integer key) {
        this.key = key;
    }
    @Override
    public Integer getRequestArgument() {
        return key;
    }
    @Override
    protected HystrixCommand<List<String>> createCommand(final Collection<CollapsedRequest<String, Integer>> requests) {
        return new BatchCommand(requests);
    }
    @Override
    protected void mapResponseToRequests(List<String> batchResponse, Collection<CollapsedRequest<String, Integer>> requests) {
        int count = 0;
        for (CollapsedRequest<String, Integer> request : requests) {
            request.setResponse(batchResponse.get(count++));
        }
    }
    private static final class BatchCommand extends HystrixCommand<List<String>> {
        private final Collection<CollapsedRequest<String, Integer>> requests;
        private BatchCommand(Collection<CollapsedRequest<String, Integer>> requests) {
                super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
                    .andCommandKey(HystrixCommandKey.Factory.asKey("GetValueForKey")));
            this.requests = requests;
        }
        @Override
        protected List<String> run() {
            ArrayList<String> response = new ArrayList<String>();
            for (CollapsedRequest<String, Integer> request : requests) {
                // artificial response for each argument received in the batch
                response.add("ValueForKey: " + request.getArgument());
            }
            return response;
        }
    }
}

   测试请求合并

@Test
public void testCollapser() throws Exception {
    HystrixRequestContext context = HystrixRequestContext.initializeContext();
    try {
        Future<String> f1 = new CommandCollapserGetValueForKey(1).queue();
        Future<String> f2 = new CommandCollapserGetValueForKey(2).queue();
        Future<String> f3 = new CommandCollapserGetValueForKey(3).queue();
        Future<String> f4 = new CommandCollapserGetValueForKey(4).queue();

        assertEquals("ValueForKey: 1", f1.get());
        assertEquals("ValueForKey: 2", f2.get());
        assertEquals("ValueForKey: 3", f3.get());
        assertEquals("ValueForKey: 4", f4.get());

        // assert that the batch command 'GetValueForKey' was in fact
        // executed and that it executed only once
        assertEquals(1, HystrixRequestLog.getCurrentRequest().getExecutedCommands().size());
        HystrixCommand<?> command = HystrixRequestLog.getCurrentRequest().getExecutedCommands().toArray(new HystrixCommand<?>[1])[0];
        // assert the command is the one we're expecting
        assertEquals("GetValueForKey", command.getCommandKey().name());
        // confirm that it was a COLLAPSED command execution
        assertTrue(command.getExecutionEvents().contains(HystrixEventType.COLLAPSED));
        // and that it was successful
        assertTrue(command.getExecutionEvents().contains(HystrixEventType.SUCCESS));
    } finally {
        context.shutdown();
    }
}

 请求上下文

  如果想要使用请求缓存、请求合并、请求日志的请求级别功能,我们必需管理HystrixRequestContext生命周期。

  在请求开始之前执行下面的代码  

HystrixRequestContext context = HystrixRequestContext.initializeContext();

  在请求结束后执行下面的代码:

context.shutdown();

  在java web应用中可以使用ServletFilter来管理HystrixRequestContext

public class HystrixRequestContextServletFilter implements Filter {

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
     throws IOException, ServletException {
        HystrixRequestContext context = HystrixRequestContext.initializeContext();
        try {
            chain.doFilter(request, response);
        } finally {
            context.shutdown();
        }
    }
}

  在web.xml中添加一下配置

<filter>
      <display-name>HystrixRequestContextServletFilter</display-name>
      <filter-name>HystrixRequestContextServletFilter</filter-name>
      <filter-class>com.netflix.hystrix.contrib.requestservlet.HystrixRequestContextServletFilter</filter-class>
    </filter>
    <filter-mapping>
      <filter-name>HystrixRequestContextServletFilter</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>

公共特性

  在下面的章节中,会讲HystrixCommand和HystrixObservableCommand的公共特性。

快速失败

  大多数的使用是执行一个命令,并且没有fallback方法,如果碰到异常就会抛出

public class CommandThatFailsFast extends HystrixCommand<String> {
    private final boolean throwException;
    public CommandThatFailsFast(boolean throwException) {
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.throwException = throwException;
    }
    @Override
    protected String run() {
        if (throwException) {
            throw new RuntimeException("failure from CommandThatFailsFast");
        } else {
            return "success";
        }
    }

  单元测试

@Test
public void testSuccess() {
    assertEquals("success", new CommandThatFailsFast(false).execute());
}
@Test
public void testFailure() {
    try {
        new CommandThatFailsFast(true).execute();
        fail("we should have thrown an exception");
    } catch (HystrixRuntimeException e) {
        assertEquals("failure from CommandThatFailsFast", e.getCause().getMessage());
        e.printStackTrace();
    }
}

  使用HystrixObservableCommand实现需要实现resumeWithFallback方法。

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

 静默失败

  静默失败是让fallback返回一个空结果。

public class CommandThatFailsSilently extends HystrixCommand<String> {
    private final boolean throwException;
    public CommandThatFailsSilently(boolean throwException) {
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.throwException = throwException;
    }
    @Override
    protected String run() {
        if (throwException) {
            throw new RuntimeException("failure from CommandThatFailsFast");
        } else {
            return "success";
        }
    }
    @Override
    protected String getFallback() {
        return null;
    }
}

  单元测试

@Test
public void testSuccess() {
    assertEquals("success", new CommandThatFailsSilently(false).execute());
}
@Test
public void testFailure() {
    try {
        assertEquals(null, new CommandThatFailsSilently(true).execute());
    } catch (HystrixRuntimeException e) {
        fail("we should not get an exception as we fail silently with a fallback");
    }
}

  另一种实现

    @Override
    protected List<String> getFallback() {
        return Collections.emptyList();
    }

  使用HystrixObservableCommand实现

@Override
    protected Observable<String> resumeWithFallback() {
        return Observable.empty();
    }

静态fallback

  fallfack返回一个固定的静态值,例如如果命令失败返回true。

 @Override
    protected Boolean getFallback() {
        return true;
    }

  使用HystrixObservableCommand

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

存根fallback

  通过咋fallback会返回一个包含请求信息和状态的对象,例如:cookies,请求参数,请求头,失败前service的返回结果。fallback可以通过请求作用域来获取信息,更典型的是在使用命令时通过构造函数设置这些值。

public class CommandWithStubbedFallback extends HystrixCommand<UserAccount> {

    private final int customerId;
    private final String countryCodeFromGeoLookup;

    /**
     * @param customerId
     *            The customerID to retrieve UserAccount for
     * @param countryCodeFromGeoLookup
     *            The default country code from the HTTP request geo code lookup used for fallback.
     */
    protected CommandWithStubbedFallback(int customerId, String countryCodeFromGeoLookup) {
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.customerId = customerId;
        this.countryCodeFromGeoLookup = countryCodeFromGeoLookup;
    }

    @Override
    protected UserAccount run() {
        // fetch UserAccount from remote service
        //        return UserAccountClient.getAccount(customerId);
        throw new RuntimeException("forcing failure for example");
    }

    @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);
    }

    public static class UserAccount {
        private final int customerId;
        private final String name;
        private final String countryCode;
        private final boolean isFeatureXPermitted;
        private final boolean isFeatureYPermitted;
        private final boolean isFeatureZPermitted;

        UserAccount(int customerId, String name, String countryCode,
                boolean isFeatureXPermitted,
                boolean isFeatureYPermitted,
                boolean isFeatureZPermitted) {
            this.customerId = customerId;
            this.name = name;
            this.countryCode = countryCode;
            this.isFeatureXPermitted = isFeatureXPermitted;
            this.isFeatureYPermitted = isFeatureYPermitted;
            this.isFeatureZPermitted = isFeatureZPermitted;
        }
    }
}

单元测试

 @Test
    public void test() {
        CommandWithStubbedFallback command = new CommandWithStubbedFallback(1234, "ca");
        UserAccount account = command.execute();
        assertTrue(command.isFailedExecution());
        assertTrue(command.isResponseFromFallback());
        assertEquals(1234, account.customerId);
        assertEquals("ca", account.countryCode);
        assertEquals(true, account.isFeatureXPermitted);
        assertEquals(true, account.isFeatureYPermitted);
        assertEquals(false, account.isFeatureZPermitted);
    }

使用HystrixObservableCommand  

@Override
protected Observable<Boolean> resumeWithFallback() {
    return Observable.just( new UserAccount(customerId, "Unknown Name",
                                            countryCodeFromGeoLookup, true, true, false) );
}

  如果使用返回多个数据,那么需要知道在异常前返回了哪些数据,然后在fallback继续处理。

@Override
protected Observable<Integer> construct() {
    return Observable.just(1, 2, 3)
            .concatWith(Observable.<Integer> error(new RuntimeException("forced error")))
            .doOnNext(new Action1<Integer>() {
                @Override
                public void call(Integer t1) {
                    lastSeen = t1;
                }
                
            })
            .subscribeOn(Schedulers.computation());
}

@Override
protected Observable<Integer> resumeWithFallback() {
    if (lastSeen < 4) {
        return Observable.range(lastSeen + 1, 4 - lastSeen);
    } else {
        return Observable.empty();
    }
}

缓存fallback

  有时候,如果执行异常需要在缓存中获取数据来代替。因为从缓存中获取数据也可能需要网络请求,所以也需要通过HystrixCommand或HystrixObservableCommand来处理

  fallback使用的线程池必须和调用方法的隔离开,如果隔离开,那么当调用服务超负载是,fallback也会不可执行。下面的例子显示了CommandWithFallbackViaNetwork如何在他的getFallback中执行FallbackViaNetwork。如果FallbackViaNetwork执行失败了,他也有自己的一个fallback,返回null。

  为了让FallbackViaNetwork和CommandWithFallbackViaNetwork不在同一个线程池中执行,通过HystrixThreadPoolKey.Factory.asKey("RemoteServiceXFallback")设置了他自己的线程池。

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;
        }
    }
}

  一些系统有主备模式,一般备用模式只用在失败的情况下,这种模式就是前一个例子讲的情况。然后自动切换主备并不是一种理想的方案,当主模式失败时,他会忽略报警。

  为了解决这个问题,可以使用手动切换主备模式。

  主备的HystrixCommand实现是相互线程隔离的,他们有不同的表现特性。

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;
    }
    @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 {
                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 {
                ConfigurationManager.getConfigInstance().setProperty("primarySecondary.usePrimary", false);
                assertEquals("responseFromSecondary-20", new CommandFacadeWithPrimarySecondary(20).execute());
            } finally {
                context.shutdown();
                ConfigurationManager.getConfigInstance().clear();
            }
        }
    }
}

 无网络请求的依赖

  如果执行的依赖不需要网络请求,可以设置executionIsolationStrategy为ExecutionIsolationStrategy.SEMAPHORE通过信号量来隔离。

public class CommandUsingSemaphoreIsolation extends HystrixCommand<String> {
    private final int id;
    public CommandUsingSemaphoreIsolation(int id) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
                // since we're doing an in-memory cache lookup we choose SEMAPHORE isolation
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)));
        this.id = id;
    }
    @Override
    protected String run() {
        // a real implementation would retrieve data from in memory data structure
        return "ValueFromHashMap_" + id;
    }

}

清除缓存

  如果当数据改变时,需要清除缓存,可以通过HystrixRequestCache.clear()来清除。

public class CommandUsingRequestCacheInvalidation {

    /* represents a remote data store */
    private static volatile String prefixStoredOnRemoteDataStore = "ValueBeforeSet_";

    public static class GetterCommand extends HystrixCommand<String> {
        private static final HystrixCommandKey GETTER_KEY = HystrixCommandKey.Factory.asKey("GetterCommand");
        private final int id;
        public GetterCommand(int id) {
            super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GetSetGet"))
                    .andCommandKey(GETTER_KEY));
            this.id = id;
        }
        @Override
        protected String run() {
            return prefixStoredOnRemoteDataStore + id;
        }
        @Override
        protected String getCacheKey() {
            return String.valueOf(id);
        }
        /**
         * Allow the cache to be flushed for this object.
         * 
         * @param id
         *            argument that would normally be passed to the command
         */
        public static void flushCache(int id) {
            HystrixRequestCache.getInstance(GETTER_KEY,
                    HystrixConcurrencyStrategyDefault.getInstance()).clear(String.valueOf(id));
        }

    }
    public static class SetterCommand extends HystrixCommand<Void> {

        private final int id;
        private final String prefix;

        public SetterCommand(int id, String prefix) {
            super(HystrixCommandGroupKey.Factory.asKey("GetSetGet"));
            this.id = id;
            this.prefix = prefix;
        }
        @Override
        protected Void run() {
            // persist the value against the datastore
            prefixStoredOnRemoteDataStore = prefix;
            // flush the cache
            GetterCommand.flushCache(id);
            // no return value
            return null;
        }
    }
}

单元测试

@Test
        public void getGetSetGet() {
            HystrixRequestContext context = HystrixRequestContext.initializeContext();
            try {
                assertEquals("ValueBeforeSet_1", new GetterCommand(1).execute());
                GetterCommand commandAgainstCache = new GetterCommand(1);
                assertEquals("ValueBeforeSet_1", commandAgainstCache.execute());
                // confirm it executed against cache the second time
                assertTrue(commandAgainstCache.isResponseFromCache());
                // set the new value
                new SetterCommand(1, "ValueAfterSet_").execute();
                // fetch it again
                GetterCommand commandAfterSet = new GetterCommand(1);
                // the getter should return with the new prefix, not the value from cache
                assertFalse(commandAfterSet.isResponseFromCache());
                assertEquals("ValueAfterSet_1", commandAfterSet.execute());
            } finally {
                context.shutdown();
            }
        }
    }

  如果想使用Hystrix,那么需要将依赖服务调用通过HystrixCommand来封装。

  使用Hystrix之前的结构

  使用Hystrix之后的结构

 

 

 

posted @ 2017-12-19 11:51  zwh1988  阅读(364)  评论(0编辑  收藏  举报