为什么 Pry shell 在 Phoenix/cowboy 中超时这么快? (shell 进程退出原因:关闭)
Why does the Pry shell timeout so quickly in Phoenix/cowboy? (shell process exited with reason: shutdown)
当我使用 IEx.pry 或断点(通过 :int.break()
)时,交互式 shell 死得太快,我只有大约 10 秒的时间才能失去会话:
** (EXIT from #PID<0.606.0>) shell process exited with reason: shutdown
而且 10 秒不足以在 shell/debugger 中高效地调试我的代码。我最好的猜测是 :supervisor.child_spec
中有一个我需要覆盖的默认超时,但我不是 100% 确定。
这是我迄今为止尝试过的方法(以及为什么它们到目前为止没有奏效):
- 增加 MyApp.Application 中的 :shutdown parameter to :infinity in the child_specs to Supervisor.start_link()。
- 更新了 cowboy timeouts (idle_timeout, linger_timeout, request_timeout, shutdown_timeout) 因为 Observer 指出我的请求是牛仔进程。不过,我认为这不太可能是牛仔。如果默认是60s,我连60s都没有。
- 我没有使用 IEx.pry,而是尝试使用调试器(通过
:ni.break
)
- 只是注意到 我不能使用
mix test --trace
,它将超时设置为 :infinity。由于我正在尝试调试非测试端点操作,因此此标志无济于事。
人们如何使用 debugger/IEx.pry? (我来自 Ruby/JS 世界,所以我希望有时间检查变量。)人们不会像我一样遇到 10 秒超时吗?还是我缺少一些常见的配置来满足我的故障排除需求?
我在 application.ex 中的 Supervisor/child 配置:
注意 shutdown: :infinity
配置。
defmodule MyApp.Application do
use Application
def start(_type, _args) do
children = [
MyApp.Repo,
{MyApp.Web.Endpoint, [shutdown: :infinity]},
{Phoenix.PubSub, [name: MyApp.PubSub, adapter: Phoenix.PubSub.PG2]},
{MyApp.Middleware.Ets.AnEtsThing, [name: MyApp.Middleware.Ets.AnEtsThing, table_name: :my_app_config_2]},
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
def config_change(changed, _new, removed) do
MyApp.Web.Endpoint.config_change(changed, removed)
:ok
end
end
我的牛仔配置在dev.exs
config :my_app, MyApp.Web.Endpoint,
http: [
port: 4000,
protocol_options: [
request_timeout: 100_000_000,
shutdown_timeout: 100_000_000,
idle_timeout: 100_000_000,
linger_timeout: 100_000_000,
]
]
Cowboy 配置的控制台检查
这只是为了确认我在正确的地方配置了 Cowboy。我确认切换端口确实影响了端口。
iex(4)> MyApp.Web.Endpoint.config(:http)
[
port: 4001,
protocol_options: [
request_timeout: 100000000,
shutdown_timeout: 100000000,
idle_timeout: 100000000,
linger_timeout: 100000000
]
]
这是我在IEX控制台看到的 运行 iex -S mix phx.server
:
请注意,没有堆栈跟踪告诉我是什么杀死了我的窥探会话。
Interactive Elixir (1.10.3) - press Ctrl+C to exit (type h() ENTER for help)
Request to pry #PID<0.718.0> at MyApp.Foo.foo/3 (lib/my_app/foo.ex:16)
14:
15: def foo(_parent, _args, %{context: %{bar: bar}}) do
16: require IEx; IEx.pry
17:
18: some_code()
Allow? [Yn] <=== I type "Enter"
Interactive Elixir (1.10.3) - press Ctrl+C to exit (type h() ENTER for help)
pry(1)> DateTime.utc_now <=== I type this to show when the pry session starts
~U[2020-12-28 06:18:27.861251Z]
** (EXIT from #PID<0.718.0>) shell process exited with reason: shutdown
...
Interactive Elixir (1.10.3) - press Ctrl+C to exit (type h() ENTER for help)
pry(1)> DateTime.utc_now
~U[2020-12-28 06:18:40.420951Z] <== ~13 seconds, pry session dies
我发现了问题:问题出在我在一个单独的 JS 项目中设置的 10 秒超时。(哈哈糟糕!)我最终使用 Erlang Observer 添加了一个在 cowboys_clear
上进行详细跟踪,发现有关正在发送的特定 Cowboy 错误的更多内部细节,这让我意识到它是由 JS 发起的。
解释 JS(即客户端)超时如何影响 cowboy/Phoenix 调试
客户端会在 10 秒后反复关闭 HTTP 连接,导致 Cowboy(Phoenix 建立在其上)终止其 HTTP 流处理程序,这反过来会终止 Phoenix IEx.pry
会话。
这意味着我尝试增加 Cowboy 超时配置不会改变终止行为。我的超时被更改但没有被触发。
我如何通过 Erlang 的 :observer
解决 Cowboy 超时问题
虽然我的 JS 问题是特定于项目的,但您可能需要从 Phoenix 应用程序深入了解 Cowboy 的较低级别。这些
- 运行凤凰应用
iex -S mix phx.server
- 运行
:observer.start()
启动 Observer GUI
- 单击
Applications
选项卡,然后查找我的 Phoenix 端点(如 Elixir.MyApp.Web.Endpoint
)
- 右键单击其子项,单击每个
Dictionary
选项卡以找到 '$initial_call'
为 cowboy_clear
的选项卡(它嵌套了 [=85= 的 3 层) ])
- (在这里我能够确认我触发的 Pry 断点是这个
cowboy_clear
节点的后代,类似:self |> Process.info |> Keyword.get(:dictionary) |> Keyword.get(:"$ancestors")
。)
- 右键单击
cowboy_clear
和 select“跟踪进程树”- 我 select 编辑了所有选项。
- 将选项卡更改为“Trace Overview”,select pid,然后单击“Start Trace”
- 等待cowboy死掉,将trace保存到文本文件
- 搜索 'exit'、'shutdown' 或类似内容
- 就我而言,我发现
20:57:13:358740 (<0.5984.0>) exit {shutdown,{socket_error,closed,'The socket has been closed.'}}
- 此时我猜是JSb/c牛仔好像不是触发请求关闭的
额外思考:理论上我可以通过 运行 我在 GraphiQL 或 Postman 中的请求节省大量时间,因为那里不存在超时。
有用的资源
- Using Erlang Observer in Elixir - 我运行在调试模式下混合phx.server,运行来自IEx提示符的观察者命令
- Cowboy architecture and execution flow - 这有助于理解流程结构,所以我猜想我需要跟踪
cowboy_clear
- Plug.Cowboy - 灵药配置说明
- cowboy_http configuration options - 这些是我在意识到它是 JS 之前切换的牛仔超时配置
当我使用 IEx.pry 或断点(通过 :int.break()
)时,交互式 shell 死得太快,我只有大约 10 秒的时间才能失去会话:
** (EXIT from #PID<0.606.0>) shell process exited with reason: shutdown
而且 10 秒不足以在 shell/debugger 中高效地调试我的代码。我最好的猜测是 :supervisor.child_spec
中有一个我需要覆盖的默认超时,但我不是 100% 确定。
这是我迄今为止尝试过的方法(以及为什么它们到目前为止没有奏效):
- 增加 MyApp.Application 中的 :shutdown parameter to :infinity in the child_specs to Supervisor.start_link()。
- 更新了 cowboy timeouts (idle_timeout, linger_timeout, request_timeout, shutdown_timeout) 因为 Observer 指出我的请求是牛仔进程。不过,我认为这不太可能是牛仔。如果默认是60s,我连60s都没有。
- 我没有使用 IEx.pry,而是尝试使用调试器(通过
:ni.break
) - 只是注意到 我不能使用
mix test --trace
,它将超时设置为 :infinity。由于我正在尝试调试非测试端点操作,因此此标志无济于事。
人们如何使用 debugger/IEx.pry? (我来自 Ruby/JS 世界,所以我希望有时间检查变量。)人们不会像我一样遇到 10 秒超时吗?还是我缺少一些常见的配置来满足我的故障排除需求?
我在 application.ex 中的 Supervisor/child 配置:
注意 shutdown: :infinity
配置。
defmodule MyApp.Application do
use Application
def start(_type, _args) do
children = [
MyApp.Repo,
{MyApp.Web.Endpoint, [shutdown: :infinity]},
{Phoenix.PubSub, [name: MyApp.PubSub, adapter: Phoenix.PubSub.PG2]},
{MyApp.Middleware.Ets.AnEtsThing, [name: MyApp.Middleware.Ets.AnEtsThing, table_name: :my_app_config_2]},
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
def config_change(changed, _new, removed) do
MyApp.Web.Endpoint.config_change(changed, removed)
:ok
end
end
我的牛仔配置在dev.exs
config :my_app, MyApp.Web.Endpoint,
http: [
port: 4000,
protocol_options: [
request_timeout: 100_000_000,
shutdown_timeout: 100_000_000,
idle_timeout: 100_000_000,
linger_timeout: 100_000_000,
]
]
Cowboy 配置的控制台检查
这只是为了确认我在正确的地方配置了 Cowboy。我确认切换端口确实影响了端口。
iex(4)> MyApp.Web.Endpoint.config(:http)
[
port: 4001,
protocol_options: [
request_timeout: 100000000,
shutdown_timeout: 100000000,
idle_timeout: 100000000,
linger_timeout: 100000000
]
]
这是我在IEX控制台看到的 运行 iex -S mix phx.server
:
请注意,没有堆栈跟踪告诉我是什么杀死了我的窥探会话。
Interactive Elixir (1.10.3) - press Ctrl+C to exit (type h() ENTER for help)
Request to pry #PID<0.718.0> at MyApp.Foo.foo/3 (lib/my_app/foo.ex:16)
14:
15: def foo(_parent, _args, %{context: %{bar: bar}}) do
16: require IEx; IEx.pry
17:
18: some_code()
Allow? [Yn] <=== I type "Enter"
Interactive Elixir (1.10.3) - press Ctrl+C to exit (type h() ENTER for help)
pry(1)> DateTime.utc_now <=== I type this to show when the pry session starts
~U[2020-12-28 06:18:27.861251Z]
** (EXIT from #PID<0.718.0>) shell process exited with reason: shutdown
...
Interactive Elixir (1.10.3) - press Ctrl+C to exit (type h() ENTER for help)
pry(1)> DateTime.utc_now
~U[2020-12-28 06:18:40.420951Z] <== ~13 seconds, pry session dies
我发现了问题:问题出在我在一个单独的 JS 项目中设置的 10 秒超时。(哈哈糟糕!)我最终使用 Erlang Observer 添加了一个在 cowboys_clear
上进行详细跟踪,发现有关正在发送的特定 Cowboy 错误的更多内部细节,这让我意识到它是由 JS 发起的。
解释 JS(即客户端)超时如何影响 cowboy/Phoenix 调试
客户端会在 10 秒后反复关闭 HTTP 连接,导致 Cowboy(Phoenix 建立在其上)终止其 HTTP 流处理程序,这反过来会终止 Phoenix IEx.pry
会话。
这意味着我尝试增加 Cowboy 超时配置不会改变终止行为。我的超时被更改但没有被触发。
我如何通过 Erlang 的 :observer
解决 Cowboy 超时问题
虽然我的 JS 问题是特定于项目的,但您可能需要从 Phoenix 应用程序深入了解 Cowboy 的较低级别。这些
- 运行凤凰应用
iex -S mix phx.server
- 运行
:observer.start()
启动 Observer GUI - 单击
Applications
选项卡,然后查找我的 Phoenix 端点(如Elixir.MyApp.Web.Endpoint
) - 右键单击其子项,单击每个
Dictionary
选项卡以找到'$initial_call'
为cowboy_clear
的选项卡(它嵌套了 [=85= 的 3 层) ]) - (在这里我能够确认我触发的 Pry 断点是这个
cowboy_clear
节点的后代,类似:self |> Process.info |> Keyword.get(:dictionary) |> Keyword.get(:"$ancestors")
。) - 右键单击
cowboy_clear
和 select“跟踪进程树”- 我 select 编辑了所有选项。 - 将选项卡更改为“Trace Overview”,select pid,然后单击“Start Trace”
- 等待cowboy死掉,将trace保存到文本文件
- 搜索 'exit'、'shutdown' 或类似内容
- 就我而言,我发现
20:57:13:358740 (<0.5984.0>) exit {shutdown,{socket_error,closed,'The socket has been closed.'}}
- 此时我猜是JSb/c牛仔好像不是触发请求关闭的
额外思考:理论上我可以通过 运行 我在 GraphiQL 或 Postman 中的请求节省大量时间,因为那里不存在超时。
有用的资源
- Using Erlang Observer in Elixir - 我运行在调试模式下混合phx.server,运行来自IEx提示符的观察者命令
- Cowboy architecture and execution flow - 这有助于理解流程结构,所以我猜想我需要跟踪
cowboy_clear
- Plug.Cowboy - 灵药配置说明
- cowboy_http configuration options - 这些是我在意识到它是 JS 之前切换的牛仔超时配置