我最近完成了Joe的书,非常喜欢它。 从那时起我就开始使用erlang编写一个软实时应用程序,我不得不说我对使用gen_server感到有些困惑。
我什么时候应该使用gen_server而不是简单的无状态模块? 我定义了一个无状态模块如下: - 一个模块,它将状态作为参数(非常类似于ETS / DETS),而不是在内部保存(如gen_server)
说一个发票管理器类型模块,它应该初始化并返回我随后传递给它的状态吗? SomeState = InvoiceManager:Init(), SomeState = InvoiceManager:AddInvoice(SomeState,AnInvoiceFoo)。
假设我需要发票管理员状态的多个实例(假设我的应用程序管理多个公司,每个公司都有自己的发票),如果他们每个人都有一个内部状态的gen_server来管理他们的发票,或者它更适合简单地拥有上面的无状态模块?
两者之间的界线在哪里?
(注意上面的发票管理示例只是一个例子来说明我的问题)
答案 0 :(得分:8)
我真的不认为你可以区分你所谓的无状态模块和gen_server。在这两种情况下都有一个递归的接收循环,它在至少一个参数中携带状态。这个主循环处理请求,根据请求工作,并在必要时将结果发送回请求者。主循环很可能会处理许多管理请求,这些请求可能不是主API /协议的一部分。
不同之处在于gen_server抽象出主接收循环并允许用户仅写入实际的用户代码。它还将为您处理许多管理OTP功能。主要区别在于用户代码位于另一个模块中,这意味着您可以更轻松地查看传递状态。除非你真的设法在一个大的接收循环中编写代码而不调用其他函数来完成工作,否则没有真正的区别。
哪种方法更好取决于您的需求。使用gen_server将简化您的代码并为您提供“免费”的附加功能,但它可能更具限制性。滚动你自己会给你更多的力量,但你也提供更多的可能性来搞砸了。它可能也快一点。你需要什么?
答案 1 :(得分:2)
这在很大程度上取决于您的需求和应用程序设计。当您需要进程之间的共享状态时,您必须使用进程来保持此状态。然后gen_server
,gen_fsm
或其他gen_*
是您的朋友。如果您的应用程序不是并发的,或者此设计不会为您带来其他好处,则可以避免此设计。例如,将应用程序分解为进程将导致更简单的设计。在其他情况下,有时您可以选择单一流程设计并使用“无状态”模块来提高性能等。 “无状态”模块是非常简单的无状态(纯功能)任务的最佳选择。 gen_server
通常是思考自然“过程”的最佳选择。当您希望在进程之间共享某些内容时,必须使用它(使用进程可能受到可伸缩性或并发性的限制)。
答案 2 :(得分:1)
使用这两个模型后,我必须说使用提供的gen_server可以帮助我更轻松地保持结构化。我想这就是为什么它被包含在OTP工具堆栈中的原因:gen_server是一个很好的方法来重复使用重复的锅炉板。
答案 3 :(得分:1)
如果您在多个进程上共享状态,那么您应该使用gen_server,如果状态只是一个进程的本地状态,则无状态模块可以正常运行。
答案 4 :(得分:1)
我认为您的发票(或其代表的任何内容)应该是持久性的,因此无论如何它们最终都会出现在ETS / Mnesia表中。如果是这样,您应该创建一个无状态模块,您可以在其中放置用于访问发票表的API。