java - 通过 toStrict() 将 HttpEntity 转换为 String 后导致延迟的原因

标签 java multithreading java-8 akka akka-http

我将 akka-http 用于响应时间目标非常短的 Web 应用程序。我将路由 DSL completeWithFuture 方法与 CompletableFuture 链一起使用。

我注意到,当使用 CompletionStage 方法的 XXXasync 变体链接每个 future 并传递相同的执行器时,用于处理阶段的线程可以任意更改,导致某些请求的响应时间更长,以防所有线程使用指定的执行器。因此,我将我的自定义执行程序传递给第一个 CompletableFuture,并将所有后续阶段与普通变体链接在一起,以便为它们使用相同的线程。

问题:一个阶段通过 HttpEntity.toStrict() 将 HttpEntity 转换为字符串,该方法使用来自 akka.actor 的线程。默认调度程序。随着工作量的增加,越来越多的请求在下一阶段开始时超过了所需的响应时间,尽管将超时传递给 toStrict 远低于目标响应时间并且没有看到超时异常.

简化代码:

private Route handleRequest(final HttpRequest request) {
    return completeWithFuture(CompletableFuture.runAsync(() -> preprocessing(), systemDispatcher) // Dispatcher 1
            .thenCompose((preprocessingResult) -> // please ignore that preprocessingResult is not used in that simplified version
            entityToString(request.entity()).thenApply((requestString) -> generateResponse(requestString))));
}

public CompletionStage<String> entityToString(final HttpEntity entity) {
        long start = System.nanoTime();
        return entity.toStrict(bodyReadTimeoutMillis, materializer).thenApplyAsync((final Strict strict) -> {
            System.out.println(start-System.nanoTime()); // varies between <1ms and >500ms
            return strict.getData().utf8String();
        }, systemDispatcher); // Dispatcher 2
    }

所以我的猜测是从我的自定义执行程序的线程切换到 akkas 默认 actor 调度程序线程之一并返回导致了问题。

问题:对于我的 entityToString 方法的延迟是否有其他解释?有没有一种方法可以实现与 toStrict 相同的效果,即将整个可能分块的消息正文作为字符串获取,同时避免多次切换线程?

请注意,我需要 toStrict 方法的超时功能来中止对慢速 POST 请求的处理。

更新

前几天想了想,想实现akka保证的非阻塞读,不切换线程是不可能的。所以真正的问题是明显的高延迟可能是由 toStrict 之后的调度引起的。

我尝试使用不同的调度程序(请参阅上面代码中的注释 Dispatcher 1/Dispatcher 2)并在延迟的情况下记录 Dispatcher 2 的居民计数超过 50ms。我找不到合适的文档,但我认为这是计划任务的数量。 我用 10000 个请求、200 个并发连接运行 apache bench,并有 55 次延迟超过 50 毫秒。输出显示最多 80 个居民。

我在 Amazon 的 m3.2xlarge 实例(8 个 vCPU、30GB RAM、Ubuntu 16.04)上运行了该测试,没有其他进程消耗任何明显的 CPU 量。调度程序的类型为 fork-join-executor,parallelism-factor = 1。

具有更多可变数量的并发请求的实际流量会导致超过限制的请求率不断增加(高达 50%)。

处理请求的平均时间低于 1 毫秒。是什么导致了 toStrict 之后的罕见延迟以及如何避免它?

最佳答案

您可以尝试使用 akka-streams 获取实体内容,并以不同的方式实现超时要求,而不是实体读取。

entity.getDataBytes().runFold(ByteString.empty(), ByteString::concat, materializer)
.thenCompose(r -> r.utf8String());

关于java - 通过 toStrict() 将 HttpEntity 转换为 String 后导致延迟的原因,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39534348/

相关文章:

java - 删除构造函数中变量的重复初始化

Python 多处理从不加入

c# - 共享资源访问的优化同步

c++ - 线程如何运行循环直到加入?

java - 实例化具有默认方法的接口(interface)

java - Java 8 收集器问题类型不匹配 : cannot convert from List<Object> to List<String>

java 8 Instant.now() 显示错误的即时时间

java - 用 Java 创建天气应用程序

java - 如何显示数据库中的一行?

Java writeObject() 始终覆盖现有对象,从不追加?