0

背景

我有一个伞式应用程序,里面有许多较小的应用程序。其中一个名为 的应用程序A需要能够旋转和监督另一个名为 的应用程序B

B本身就是一个应用程序,它公开一个公共 API 并有一个 GenServer,负责接收请求,然后将其重定向到逻辑模块等。

问题

所以,我有两个要求:

  1. 我必须能够B独立启动并将其作为普通的独立应用程序运行。
  2. A如果需要,必须能够B在其子项中包含并重新启动/管理它。

我在这里遇到的问题是,我的代码可以实现 1 或 2,但不能同时实现。

代码

因此,以下是 app 的重要代码B

应用程序.ex

defmodule B.Application do
  @moduledoc false

  use Application

  alias B.Server
  alias Plug.Cowboy

  @test_port 8082

  @spec start(any, nil | maybe_improper_list | map) :: {:error, any} | {:ok, pid}
  def start(_type, args) do
    # B.Server is a module containing GenServer logic and callbacks
    children = children([Server])

    opts = [strategy: :one_for_one, name: B.Supervisor]
    Supervisor.start_link(children, opts)
  end

end

server.ex(简体)

defmodule B.Server do
  use GenServer

  alias B.HTTPClient

  #############
  # Callbacks #
  #############

  @spec start_link(any) :: :ignore | {:error, any} | {:ok, pid}
  def start_link(_args), do: GenServer.start_link(__MODULE__, nil, name: __MODULE__)

  @impl GenServer
  @spec init(nil) :: {:ok, %{}}
  def init(nil), do: {:ok, %{}}

  @impl GenServer
  def handle_call({:place_order, order}, _from, _state), do:
    {:reply, HTTPClient.place_order(order), %{}}

  @impl GenServer
  def handle_call({:delete_order, order_id}, _from, _state), do:
    {:reply, HTTPClient.delete_order(order_id), %{}}

  @impl GenServer
  def handle_call({:get_all_orders, item_name}, _from, _state), do:
    {:reply, HTTPClient.get_all_orders(item_name), %{}}

  ##############
  # Public API #
  ##############

  def get_all_orders(item_name), do:
    GenServer.call(__MODULE__, {:get_all_orders, item_name})

  def place_order(order), do:
    GenServer.call(__MODULE__, {:place_order, order})

  def delete_order(order_id), do:
    GenServer.call(__MODULE__, {:delete_order, order_id})

end

这里是入口点B

b.ex

defmodule B do
  @moduledoc """
  Port for http client.
  """

  alias B.Server

  defdelegate place_order(order), to: Server

  defdelegate delete_order(order_id), to: Server

  defdelegate get_all_orders(item_name), to: Server

  @doc false
  defdelegate child_spec(args), to: Server
end

b.ex基本上是服务器的外观,带有一些额外的上下文信息,例如规格、类型定义等(为简洁起见,此处省略)。

如何A管理生命周期?

据我了解,监督树是在application.ex应用程序文件中指定的。所以,据我了解,我创建了这个应用程序文件A

defmodule A.Application do
  @moduledoc false

  use Application

  alias B

  def start(_type, _args) do
    children = [B]

    opts = [strategy: :one_for_one, name: A.Supervisor]
    Supervisor.start_link(children, opts)
  end

end

哪个应该工作,但它没有。

A's 文件夹内时,如果我运行iex -S mix,而不是很好地启动,我会收到以下错误:

** (Mix) Could not start application a: A.Application.start(:normal, []) returned an error: shutdown: failed to start child: B.Server
    ** (EXIT) already started: #PID<0.329.0>

我目前对该问题的理解是A'application.ex 文件与 'application 文件冲突B

问题

  1. 我该如何解决这个冲突?
4

1 回答 1

1

如果我正确理解了要求,A想要最终停止应用程序并在监督下B重生过程,B

Application.stop/1正是这样做的。

defmodule A.Application do
  ...
  def start(_type, _args) do
    Application.stop(:b) # ⇐ THIS

    children = [B]

    opts = [strategy: :one_for_one, name: A.Supervisor]
    Supervisor.start_link(children, opts)
  end

end

请注意,它需要应用程序的名称,如mix.exs文件中(以及×××.app编译后的文件中)。

于 2021-01-04T17:37:17.230 回答