2

我在使用多个请求扩展我的应用时遇到问题。

每个请求都会向一个参与者发送一个请求,然后该参与者会产生其他参与者。这很好,但是,在负载下(5+ 一次询问),ask将消息传递给目标参与者需要大量时间。最初的设计是平均分配请求,但这会造成瓶颈。例子:

在此处输入图像描述

在这张图片中,ask在查询计划解析器之后发送。但是,当 Actor 收到此消息时,会有几秒钟的间隔。这仅在负载下(5+ 请求/秒)才会出现。我首先认为这是一个饥饿问题。

设计:每个planner-executor 是每个请求的单独实例。它每次都会产生一个新的“请求接受者”角色(它在收到消息时记录“请求分数”)。

  • 我给了actorsystem一个自定义的全局执行器(大的)。我注意到即使在这个巨大的延迟期间,线程也没有超过核心线程池大小
  • 我确保子演员中的所有执行上下文都使用了正确的执行上下文
  • 确保actor内部的所有阻塞调用都使用了future
  • 我给了父actor(和所有子actor)一个自定义调度器,核心大小为50,最大大小为100。即使在这些延迟期间,它也没有要求更多(它保持在50)
  • 最后,我尝试为每个请求创建一个全新的 Actorsystem(在 planner-executor 内)。这也没有明显的效果!

我对此有点难过。从这些测试来看,它看起来不像是线程饥饿问题。回到第一点,我不知道为什么消息需要越来越长的时间来传递我发出的更多并发请求。到达此点之前的 Zipkin 跟踪在到达此处之前不会因更多请求而降级ask。在此之前,服务器能够处理多个步骤,例如验证请求、与数据库对话,然后最终进入计划执行程序。所以我怀疑应用程序本身的 cpu 时间用完了。

4

1 回答 1

2

我们在 Akka 上遇到了非常相似的问题。我们观察到请求模式在 peek 负载时向目标参与者传递消息的巨大延迟。

大多数这些问题都与堆内存消耗有关,而不是因为调度程序的使用。

最后,我们通过调整以下一些配置和更改来解决这些问题。

1) 确保停止不再需要的实体/参与者。如果它是一个持久的演员,那么您可以随时在需要时将其带回来。参考:https ://doc.akka.io/docs/akka/current/cluster-sharding.html#passivation

2) 如果您使用集群分片,请检查 akka.cluster.sharding.state-store-mode。通过将其更改为持久性,我们获得了 50% 以上的 TPS。

3)最小化您的日志条目(将其设置为信息级别)。

4) 调整您的日志以频繁地向您的日志系统发布消息。相应地更新批次大小、批次计数和间隔。这样内存就被释放了。在我们的例子中,巨大的堆内存用于缓冲日志消息并批量发送。如果间隔更长,那么您可能会填满堆内存,这会影响性能(需要更多的 GC 活动)。

5) 在单独的调度程序上运行阻塞操作。

6)使用自定义序列化器(protobuf)并避免使用JavaSerializer。

7) 将以下 JAVA_OPTS 添加到您的 jar

export JAVA_OPTS="$JAVA_OPTS -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=2 -Djava.security.egd=file:/dev/./urandom"

最主要的是 XX:MaxRAMFraction=2 这将使用超过 60% 的可用内存。默认情况下,它的 4 意味着您的应用程序将仅使用可用内存的四分之一,这可能还不够。

参考:https ://blog.csanchez.org/2017/05/31/running-a-jvm-in-a-container-without-getting-killed/

问候,

维诺特

于 2018-06-20T05:59:58.090 回答