如何更好的使用Hystrix的API

426 阅读3分钟

一、hystrix执行

1、同步执行

  1. 简单的继承HystrixCommand,具体的执行逻辑在run方法中
  2. 同步执行直接调用command的execute方法
class HelloHystrixCommand extends HystrixCommand<String> {

    private String name;

    HelloHystrixCommand(String name) {
        super(HystrixCommandGroupKey.Factory.asKey("test"));
        this.name = name;
    }

    @Override
    protected String run() throws Exception {
        TimeUnit.MILLISECONDS.sleep(20);
        return "hello " + name;
    }
}

// 1、直接执行,同步调用
long start = System.currentTimeMillis();
System.out.println(start);
System.out.println(new HelloHystrixCommand("world").execute());
System.out.println(System.currentTimeMillis() - start);

2、异步执行

  1. 利用java的future来异步执行
  2. execute就相当于queue().get()
Future<String> future = new HelloHystrixCommand("bob").queue();
try {
    System.out.println(future.get(20, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
} catch (TimeoutException e) {
    System.out.println("获取超时!");
}

3、Reactive执行

  1. 上面两个示例都是HystrixCommand来进行执行的
  2. HystrixCommand是hystrix通过封装run方法获取Observable,然后获取future来达到同步和异步的效果的
  3. HystrixObserableCommand是自己通过construct方法封装observable,自己创建数据源,然后调用observe和toObservable再获取future,效果和HystrixCommand一样,方便使用Rxjava开发的用户
// 自己封装Observable,不依赖hystrix进行封装
class HelloObservableCommand extends HystrixObservableCommand<String> {

    private String value;
    public HelloObservableCommand(String value) {
        super(HystrixCommandGroupKey.Factory.asKey("observable"));
        this.value = value;
    }

    @Override
    protected Observable<String> construct() {
        return Observable.just(value);
    }
}

// 获取construct创建的Observable对象
HelloObservableCommand observableCommand = new HelloObservableCommand("world");
// observableCommand.observe().subscribe(value -> System.out.println("hello " + value));
observableCommand.toObservable().subscribe(value -> System.out.println("hello " + value));

二、失败转移

可以在HystrixCommand中重写getFallback方法来应对execute和queue方法发生错误的情况

class FailbackHystrixCommand extends HystrixCommand<String> {

    private String name;

    FailbackHystrixCommand(String name) {
        super(HystrixCommandGroupKey.Factory.asKey("test"));
        this.name = name;
    }

    @Override
    protected String run() throws Exception {
        throw new RuntimeException("run exception");
    }


    @Override
    protected String getFallback() {
        return "run execute exception!";
    }
}

@Test
public void fallback() {
    FailbackHystrixCommand fail = new FailbackHystrixCommand("fail");
    System.out.println(fail.execute());
}

三、命令名称&命令组&线程池名称

  1. 命令组名称必须要指定,它仅仅是一个逻辑概念,它的作用是将多个同类型的命令进行归类,比如将某个业务的command归为同一类
  2. 通过HystrixCommandGroupKey.Factory.asKey("test")指定命令组
  3. 命令名称可以不指定,默认是类名,也可以自己指定
  4. 通过HystrixCommandKey.Factory.asKey("testKey")指定命令名称
  5. 线程池名称的作用是当某些命令属于同一个group,但是属于不同单元,它就类似于group的细分类别
  6. 通过HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")设置线程池key名称
class HelloHystrixCommand extends HystrixCommand<String> {

    private String name;

    HelloHystrixCommand(String name) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("test"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("testKey"))
                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("testThreadKey")));
        this.name = name;
    }

    @Override
    protected String run() throws Exception {
        TimeUnit.MILLISECONDS.sleep(20);
        return "hello " + name;
    }
}

四、请求缓存

  1. 所谓请求缓存,可以自定义缓存命中策略,使得后面命中缓存策略的key走缓存逻辑,而不用再去调用run方法来执行
  2. 我们可以在getCacheKey方法中自定义缓存命中策略
  3. 只有在同一个context下才会命中缓存,跨context之前缓存不会命中
  4. 比如说缓存命中逻辑为请求的参数一致,那么后面的相同请求参数就会走缓存
class HystrixCommandWithCache extends HystrixCommand<String> {
    private String data;

    HystrixCommandWithCache(String data) {
        super(HystrixCommandGroupKey.Factory.asKey("cache"));
        this.data = data;
    }


    @Override
    protected String run() throws Exception {
        return "run " + data;
    }

    @Override
    protected String getCacheKey() {
        // 自定义缓存命中策略为相同data参数即为命中策略
        return data;
    }
}

public void commandWithCache() {
    HystrixRequestContext context = HystrixRequestContext.initializeContext();
    HystrixCommandWithCache man = new HystrixCommandWithCache("man");
    HystrixCommandWithCache other = new HystrixCommandWithCache("other");
    HystrixCommandWithCache man1 = new HystrixCommandWithCache("man");

    System.out.println(man.execute());
    // 第一次访问所以返回为false
    System.out.println(man.isResponseFromCache());
    System.out.println(other.execute());
    // data参数不同,返回false
    System.out.println(other.isResponseFromCache());
    System.out.println(man1.execute());
    // man第二次访问,返回为true
    System.out.println(man1.isResponseFromCache());

    context.shutdown();
    
    HystrixRequestContext context1 = HystrixRequestContext.initializeContext();
    HystrixCommandWithCache man2 = new HystrixCommandWithCache("man");
    System.out.println(man2.execute());
    // 跨context不会命中缓存
    System.out.println(man2.isResponseFromCache());
    context1.shutdown();
}

五、Request Collapsing

六、HystrixRequestContext

  1. 如果想使用request级别的特性,那么就必须使用这个HystrixRequestContext

1、创建方式

HystrixRequestContext context = HystrixRequestContext.initializeContext();

2、web应用使用HystrixRequestContext

每一个请求都开启一个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生效
<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>

附录:

参考 github.com/Netflix/Hys…