教程:一起学习Hystrix--Hystrix请求处理

拈花ヽ惹草 提交于 2019-12-01 12:13:08

目录

  • Hystrix本系列博文
  • 请求缓存
  • 请求合并
  • 请求上下文设置
  • 声明

Hystrix本系列博文

    以下为博主写Hystrix系列的文章列表

     点击查看 Hystrix入门

    点击查看 Hystrix命令执行

    点击查看 Hystrix处理异常机制(降级方法)

    点击查看 Hystrix命令名称、分组、线程池

请求缓存

     您可以通过在 HystrixCommand HystrixObservableCommand 上实现 getCacheKey() 方法来启用请求缓存,如下所示:

public class CommandHelloRequestCache extends HystrixCommand<Boolean> {

    private final int value;

    protected CommandHelloRequestCache(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 CommandHelloRequestCache(2).execute());
                assertFalse(new CommandHelloRequestCache(1).execute());
                assertTrue(new CommandHelloRequestCache(0).execute());
                assertTrue(new CommandHelloRequestCache(58672).execute());
            } finally {
                context.shutdown();
            }
        }

    通常,请求上下文将被初始化,并通过封装用户请求或其他生命周期钩子的ServletFilter关闭。

     下面示例,展示了在请求上下文中,命令如何从缓存检索它们的值(以及如何查询一个对象确定它的值是否来自缓存 ):   

@Test
        public void testWithCacheHits() {
            HystrixRequestContext context = HystrixRequestContext.initializeContext();
            try {
                CommandHelloRequestCache command2a = new CommandHelloRequestCache(2);
                CommandHelloRequestCache command2b = new CommandHelloRequestCache(2);

                assertTrue(command2a.execute());
                // 第一次执行这个命令时不是从缓存返回的值
                assertFalse(command2a.isResponseFromCache());

                assertTrue(command2b.execute());
                // 第二次执行这个命令时是从缓存返回的值
                assertTrue(command2b.isResponseFromCache());
            } finally {
                context.shutdown();
            }

            // 开启一个新的请求上下文
            context = HystrixRequestContext.initializeContext();
            try {
                CommandHelloRequestCache command3b = new CommandHelloRequestCache(2);
                assertTrue(command3b.execute());
                // 这个新请求执行这个命令也不会从缓存返回值
                assertFalse(command3b.isResponseFromCache());
            } finally {
                context.shutdown();
            }
        }

请求合并

     请求合并者使多个请求在单个HystrixCommand实例被批量执行。自批处理创建后,合并者可以使用批次大小和运行时间作为执行批次处理的触发器。

     Hystrix 支持的请求合并有两种:请求范围和全局范围。这个可以在合并器构造函数中配置,默认情况下是请求范围。

     请求范围的合并器收集每个 HystrixRequestContext的批处理,而全局范围的合并器则会在多个 HystrixRequestContext中收集一批批处理。 因此,如果下游依赖项不能在单个命令调用中处理多个 HystrixRequestContext那么请求作用域合并者做出了恰当的选择。

     在Netflix(Hystrix开源者),只使用请求作用域的合并器,因为所有当前的系统都是建立在每个命令使用单个HystrixRequestContext的假设之上。 由于一批次仅为每个请求,在同一请求命令使用不同参数并行执行时,合并是有效的。

     下面是一个简单的例子,说明如何实现一个请求范围的 HystrixCollapser:

public class CommandHelloCollapser  extends HystrixCollapser<List<String>, String, Integer> {

    private final Integer key;

    public CommandHelloCollapser(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;
        }
    }
}

点击查看 完整源码以及单元测试

下面的单元测试展示了如何使用一个合并器自动将 CommandHelloCollapser 的4个执行在一个 HystrixCommand 中执行

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

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

                int numExecuted = HystrixRequestLog.getCurrentRequest().getAllExecutedCommands().size();

                System.err.println("num executed: " + numExecuted);

                // 断言此批次命令"Command Name:Hello Collapser"实际被执行了而却仅仅执行了它
                // 一次或两次(由于这个例子使用了真实的定时器,源于调度程序的不确定性)
                if (numExecuted > 2) {
                    fail("some of the commands should have been collapsed");
                }

                System.err.println("HystrixRequestLog.getCurrentRequest().getAllExecutedCommands(): " + HystrixRequestLog.getCurrentRequest().getAllExecutedCommands());

                int numLogs = 0;
                for (HystrixInvokableInfo<?> command : HystrixRequestLog.getCurrentRequest().getAllExecutedCommands()) {
                    numLogs++;

                    // 这个命令执行是我们所期望的
                    assertEquals("Command Name:Hello Collapser", command.getCommandKey().name());

                    System.err.println(command.getCommandKey().name() + " => command.getExecutionEvents(): " + command.getExecutionEvents());

                    // 确认是一个合并器的命令执行
                    assertTrue(command.getExecutionEvents().contains(HystrixEventType.COLLAPSED));
                    assertTrue(command.getExecutionEvents().contains(HystrixEventType.SUCCESS));
                }

                assertEquals(numExecuted, numLogs);
            } finally {
                context.shutdown();
            }
        }

请求上下文设置

    为了使用请求范围特性( 请求缓存,请求合并,请求日志 )必须管理 HystrixRequestContext 生命周期(或者实现一个可替代的 HystrixConcurrencyStrategy

    这意味着在一个请求前必须执行如下语句:

HystrixRequestContext context = HystrixRequestContext.initializeContext();

    在请求执行后执行以下语句:

context.shutdown();

    在一个标准的java web应用中,可以使用 Servlet Filter (过滤器)初始化 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>

   

声明

    转帖请注明原贴地址:https://my.oschina.net/u/2342969/blog/1817093

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!