为什么 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% 确定。

这是我迄今为止尝试过的方法(以及为什么它们到目前为止没有奏效):

人们如何使用 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 的较低级别。这些

  1. 运行凤凰应用iex -S mix phx.server
  2. 运行 :observer.start() 启动 Observer GUI
  3. 单击 Applications 选项卡,然后查找我的 Phoenix 端点(如 Elixir.MyApp.Web.Endpoint
  4. 右键单击其子项,单击每个 Dictionary 选项卡以找到 '$initial_call'cowboy_clear 的选项卡(它嵌套了 [=85= 的 3 层) ])
  5. (在这里我能够确认我触发的 Pry 断点是这个 cowboy_clear 节点的后代,类似:self |> Process.info |> Keyword.get(:dictionary) |> Keyword.get(:"$ancestors")。)
  6. 右键单击 cowboy_clear 和 select“跟踪进程树”- 我 select 编辑了所有选项。
  7. 将选项卡更改为“Trace Overview”,select pid,然后单击“Start Trace”
  8. 等待cowboy死掉,将trace保存到文本文件
  9. 搜索 'exit'、'shutdown' 或类似内容
  10. 就我而言,我发现 20:57:13:358740 (<0.5984.0>) exit {shutdown,{socket_error,closed,'The socket has been closed.'}}
  11. 此时我猜是JSb/c牛仔好像不是触发请求关闭的

额外思考:理论上我可以通过 运行 我在 GraphiQL 或 Postman 中的请求节省大量时间,因为那里不存在超时。

有用的资源