背景
我有一个伞式应用程序,里面有许多较小的应用程序。其中一个名为 的应用程序A
需要能够旋转和监督另一个名为 的应用程序B
。
B
本身就是一个应用程序,它公开一个公共 API 并有一个 GenServer,负责接收请求,然后将其重定向到逻辑模块等。
问题
所以,我有两个要求:
- 我必须能够
B
独立启动并将其作为普通的独立应用程序运行。 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
。
问题
- 我该如何解决这个冲突?