私はエリクシールとフェニックスフレームワークにはかなり新しいので、私の質問はちょっとばかげているかもしれません。エリクシル+フェニックスチャネルメモリ消費量
私はElixir + Phoenix Frameworkをバックエンドとして、Angular 2をフロントエンドとして使用しています。私はフェニックスチャンネルをフロントエンド/バックエンドインターチェンジのチャンネルとして使用しています。そして私は奇妙な状況を発見しました。バックエンドからフロントエンドまで大量のデータを送信すると、特定のチャネルプロセスのメモリ消費量が数百MBにまで上がります。そして、各接続(各チャネルプロセス)は、送信が終了した後でも、そのような量のメモリを消費します。ここで
は、バックエンドのチャネルの説明からコードスニペットです:
defmodule MyApp.PlaylistsUserChannel do
use MyApp.Web, :channel
import Ecto.Query
alias MyApp.Repo
alias MyApp.Playlist
# skipped ... #
# Content list request handler
def handle_in("playlists:list", _payload, socket) do
opid = socket.assigns.opid + 1
socket = assign(socket, :opid, opid)
send(self, :list)
{:reply, :ok, socket}
end
# skipped ... #
def handle_info(:list, socket) do
payload = %{opid: socket.assigns.opid}
result =
try do
user = socket.assigns.current_user
playlists = user
|> Playlist.get_by_user
|> order_by([desc: :updated_at])
|> Repo.all
%{data: playlists}
catch
_ ->
%{error: "No playlists"}
end
payload = payload |> Map.merge(result)
push socket, "playlists:list", payload
{:noreply, socket}
end
私は、このようなデータの量に対処するためのフロントエンドの能力をテストするために60000件のレコードを持つセットを作成しますが、副作用を持っています - 私は特定のチャネルプロセスのメモリ消費量が167Mbであることを発見しました。だから私はいくつかの新しいブラウザウィンドウを開き、 "playlists:list"リクエストの後に新しいチャンネルプロセスのメモリ消費量がこの量まで増えました。
通常の動作ですか?私は、データベースクエリとデータオフロード時に高いメモリ消費が予想されますが、依頼が完了しても依然として同じです。
更新1。ですから、@Dogbertと@michalmuskalaの大きな助けを借りて、手作業によるガベージコレクションメモリを解放した後にメモリが解放されることがわかりました。
私はrecon_exライブラリと少し掘るしようとした次の例を見つけた:
iex([email protected])19> :recon.proc_count(:memory, 3)
[{#PID<0.4410.6>, 212908688,
[current_function: {:gen_server, :loop, 6},
initial_call: {:proc_lib, :init_p, 5}]},
{#PID<0.4405.6>, 123211576,
[current_function: {:cowboy_websocket, :handler_loop, 4},
initial_call: {:cowboy_protocol, :init, 4}]},
{#PID<0.12.0>, 689512,
[:code_server, {:current_function, {:code_server, :loop, 1}},
{:initial_call, {:erlang, :apply, 2}}]}]
#PID<0.4410.6>
はElixir.Phoenix.Channel.Serverあると#PID<0.4405.6>
はcowboy_protocolです。
次の私は一緒に行きました:
iex([email protected])20> :recon.proc_count(:binary_memory, 3)
[{#PID<0.4410.6>, 31539642,
[current_function: {:gen_server, :loop, 6},
initial_call: {:proc_lib, :init_p, 5}]},
{#PID<0.4405.6>, 19178914,
[current_function: {:cowboy_websocket, :handler_loop, 4},
initial_call: {:cowboy_protocol, :init, 4}]},
{#PID<0.75.0>, 24180,
[Mix.ProjectStack, {:current_function, {:gen_server, :loop, 6}},
{:initial_call, {:proc_lib, :init_p, 5}}]}]
と:
iex([email protected])22> :recon.bin_leak(3)
[{#PID<0.4410.6>, -368766,
[current_function: {:gen_server, :loop, 6},
initial_call: {:proc_lib, :init_p, 5}]},
{#PID<0.4405.6>, -210112,
[current_function: {:cowboy_websocket, :handler_loop, 4},
initial_call: {:cowboy_protocol, :init, 4}]},
{#PID<0.775.0>, -133,
[MyApp.Endpoint.CodeReloader,
{:current_function, {:gen_server, :loop, 6}},
{:initial_call, {:proc_lib, :init_p, 5}}]}]
そして最後に、問題の状態はもちろん、実際にガベージコレクションの後に(recon.bin_leak以降の処理 - 私が実行した場合:これらのプロセスのpidを含むerlang.garbage_collection()は結果も同じです)。
{#PID<0.4405.6>, 34608,
[current_function: {:cowboy_websocket, :handler_loop, 4},
initial_call: {:cowboy_protocol, :init, 4}]},
...
{#PID<0.4410.6>, 5936,
[current_function: {:gen_server, :loop, 6},
initial_call: {:proc_lib, :init_p, 5}]},
私が手動でガベージコレクションを実行しない場合、メモリは "決して"(少なくとも、16時間待っています)空きになります。
覚えておいて欲しいのは、Postgresから70,000レコードを取り出して、バックエンドからフロントエンドにメッセージを送った後、私はそのようなメモリ消費をしています。モデルは非常に単純です:
schema "playlists" do
field :title, :string
field :description, :string
belongs_to :user, MyApp.User
timestamps()
end
レコードが自動生成され、次のようになります。
description: null
id: "da9a8cae-57f6-11e6-a1ff-bf911db31539"
inserted_at: Mon Aug 01 2016 19:47:22 GMT+0500 (YEKT)
title: "Playlist at 2016-08-01 14:47:22"
updated_at: Mon Aug 01 2016 19:47:22 GMT+0500 (YEKT)
私は本当にここにどんなアドバイスをいただければ幸いです。私はこのような膨大な量のデータを送信するつもりはないと信じていますが、より小さなデータセットでさえ、多くのクライアント接続の場合に大量のメモリを消費する可能性があります。おそらくこの状況は、もっと一般的な問題を隠してしまうでしょう(しかし、それはちょっと仮定です)。
明らかに正常ではありません。切断してプロセスが終了すると、メモリ使用量が低下しますか?また、 ':observer.start'を実行して、どのプロセスがメモリを使用しているのか、何が何であるのかを確認してください。 – Dogbert
jsonで送信している生データにはどのくらいのメモリが必要ですか? 60_000レコード?プロセスメモリは、それを処理するために少なくともそのサイズ(おそらく途方もなくゴミのために)以上に成長する必要があります。 – michalmuskala
@Dogbert私はobserverでこのメモリ消費を見出しました。この量はElixir.Phoenix.Channel.Server:init/1プロセスによって消費されたので、私はPhoenixチャネルのメモリ消費量について質問しています。そして、はい、切断プロセスが終了し、メモリが解放された後。 ところで、私はcowboy_protocol:init/4が接続ごとに100Mbを食べることを発見しました。 – heathen