5

我们有一个处理大型 json 有效负载的 erlang/elixir 应用程序(在 18/erts 7.3.1 上)。</p>

这是一个典型的工作流程:

  1. 侦听器从 rabbitmq 获取令牌并发送到 gen_server。

  2. gen_server 将令牌放入具有未来时间(当前 + n 秒)的 ETS 表中。gen_server 中的计划作业将从 ETS 中获取过期的令牌,并使用这些令牌启动几个短期进程。

  3. 这些短暂的进程从elasticsearch(使用hackney)下载30-50k json有效负载并处理它,然后将结果上传回elasticsearch,然后进程立即终止。我们跟踪这些过程并确认它们已经死亡。我们每秒处理 5-10 个这样的请求。

问题:我们看到一个不断增长的二进制空间,并在 48 小时内增长到几个 GB(通过观察者和调试打印看到)。手动 GC 也没有影响。

我们已经添加了“recon”并运行了 recon:bin_leak,但是这只释放了几个 KB,并且对不断增长的二进制空间没有影响。

堆栈:Erlang 18/erts 7.3.1、elixir 1.3.4、hackney 1.4.4、poison 2.2.0、timex 3.1.13 等,这些应用程序也没有占用内存。

过去有没有人遇到过类似的问题?将不胜感激任何解决方案。

2017 年 9 月 15 日更新:

我们将我们的应用程序更新到 Erlang 19/ERTS 8.3,并将 hackney 和毒库更新到最新版本,但仍然没有任何进展。这是 GenServer 中的一些日志,它使用 spawn/receive 或 send_after 定期向自身发送消息。在每个 handle_info 中,它查找一个 ets 表,如果找到任何“合格”条目,它就会产生新进程。如果不是,它只返回一个 {:noreply, state}。我们在函数入口处打印 VM 二进制空间信息(以 KB 为单位),日志如下所示。这是一天中的“安静”时间。可以看到二进制空间逐渐增大。:recon.bin_leak(N) 或 :erlang.garbage_collect() 再次对这种增长没有影响。

11:40:19.896 [警告] 二进制 1: 3544.1328125

11:40:24.897 [警告] 二进制 1: 3541.9609375

11:40:29.901 [警告] 二进制 1: 3541.9765625

11:40:34.903 [警告] 二进制 1: 3546.2109375

--- 一些处理 ---

12:00:47.307 [警告] 二进制 1: 7517.515625

--- 一些处理 ---

12:20:38.033 [警告] 二进制 1: 15002.1328125

在我们的旧 Scala/Akka 应用程序中,我们从未遇到过这样的情况,该应用程序多年来处理 30 倍以上的运行量而没有出现问题或重新启动。我写了两个应用程序。

4

1 回答 1

1

我们发现 memory_leak 来自一个私有的可重用库,该库将消息发送到 Graylog,并在通过 gen_udp 发送之前使用下面的函数压缩该数据。

defp compress(data) do
    zip = :zlib.open()
    :zlib.deflateInit(zip)
    output = :zlib.deflate(zip, data, :finish)
    :zlib.deflateEnd(zip)
    :zlib.close(zip)  #<--- was missing, hence the slow memory leak.
    output
end

相反,使用 term_to_binary(data, [:compressed]) 我可以省去一些麻烦。

感谢所有输入和评论。非常感激!

于 2017-09-20T12:36:58.640 回答