2013-02-06 32 views
0

我正在构建一个可以以两种模式运行的应用程序。沙盒模式和生产模式。如何在erlang中更改gen_server的回调模块? (gen_server:swap_handler)

在沙箱模式下,我想在我的gen_server中对数据库进行很多检查:if table does not exist then create it;如果列不存在,则添加它;如果列类型不允许我想要存储的值,则更改它等。

在生产模式下,如果表不存在或列与值的类型不匹配,则会失败,没问题。

所以,为了避免繁琐的代码,如“case state#state.is_sandbox of true - > ...”, 我想为我的gen_server有两个不同的模块,并且我想更改current模块在handle_call或handle_info中。实际上,我只是想从沙箱转到生产,但我认为如果它以这种方式工作,它可能会倒退。

谢谢。

回答

3

您可以将module(模块的名称)添加到gen_server中的状态。然后你将需要2个模块 - 沙箱和生产都实现相同的功能(你可以为它创建一个行为)。

gen_server回调将调用module:function这将是一个函数,从沙箱或生产模块。该module可以在gen_server的init功能进行设置,来改变它,只需添加新的功能(S)到gen_server:

use_production() -> 
    gen_server:cast(production). 

.... 

handle_cast(production, State) -> 
    {noreply, State#state{module = production}). 

同为沙箱模块。

一个gen_server的回调与module的一个例子:

handle_call(Msg, _From, #state{module = Module} = State) -> 
    Module:function(Msq), 
    {reply, ok, State}. 

function必须在两个沙箱和生产模块来实现。

+0

有了这个解决方案,我的服务器需要在每次调用时检查其Module变量的值,然后调用相应的函数。它工作,你不觉得它可能会很慢吗? – niahoo

+0

您需要做的唯一事情就是从状态'#state {module = Module}'获取模块名称。您不必检查它是沙箱还是生产模块,您只需从该模块调用一个函数即可。我不认为它很慢 - 调用'Module:function'就像从不同的模块调用一个正常的函数。 – juro

+0

从不同的模块,是的。我相信当编译器知道在编译时调用的模块时,编译器会优化调用。我会检查这个。有了你的解决方案,我将有3个模块,调用者和两个模块只有服务器端回调,这是相当不错的。 – niahoo

1

你可以使用模块名称os:getenv/1(当然你必须设置在不同的环境不同的名称之前)

+0

这需要在每次服务器调用时检查os/getenv,不是吗? – niahoo

+0

其实我的意思是你可以创建合适的gen_server实例,开始使用来自env的值,或者根据env值(如果你想在你的应用中产生许多gen_servers)的feeds和superspec。 – asdf

+0

好的。所以我想要的是在运行时更改模块,而不是在启动时选择好的模块。但是你让我觉得我可以杀死gen_server并重启另一个。可能是一个解决方案! – niahoo

1

你可以使用一个gen_event与单个处理器,它可让你返回一个swap_handler元组(请参阅gen_event/handle_*

另外,您不必在gen_server模型中使用case语句。如果您的状态包含沙箱变量,则可以通过绑定标题中的沙箱值来为您的回调函数定义不同的子句。例如:

handle_call(do_stuff, _From, State = #state{sandbox = true}) -> 
    do_sandbox_stuff(); 
handle_call(do_stuff, _From, State) -> 
    do_nonsandbox_stuff(). 

在此设置自动二郎选择正确的条款基础上,沙箱变量的值解雇,无需你定义一个单独的处理程序,或使用一个case语句。这样在函数子句中绑定变量对于效率来说也是很好的做法(因为变量被绑定在函数体外部,所以绑定过程在调度器中完成,并且因此不计算函数的执行时间,而所有的匹配在一个案例中的功能体内部完成)

+0

你认为我可以在我的gen_server上调用gen_event:swap_handler吗?黑客什么的? (我不知道在子句上的模式匹配比较快,谢谢,但是我真正的问题是关于代码:我想要一个有很多代码的沉重模块,以及一个易于阅读和维护的小模块) – niahoo

+0

No. gen_event实际上支持多个处理程序,但不管这些进程的内部结构是否有很大不同。 gen_server甚至接受swap_handler消息是非常不可能的。 –

+0

不,gen_server不**支持swap_handler!它只支持'gen_server:call'和'gen_server:cast',所有其他消息都以handle_info结尾!检查代码。 gen_server和gen_fsm的基本思想是你给它一个模块,其中包含描述服务器要做什么的必要回调。即使你做代码升级,你也可以使用相同的模块。虽然'gen_event:swap_handler'会改变一个模块,但它并不适用于这种类型的使用。 – rvirding

1

而不是gen_server你可以使用gen_fsm,有限状态机,它很容易处理这种情况。您只有多个状态,根据状态调用不同模块中的功能。它基本上为您完成所有处理,无需携带明确的state参数。这基本上是手工实施FSM。

+0

模块参数适合我的情况,因为我的两个模块共享相同的接口,所以代码非常短,'清晰'。我会尝试gen_fsm进行比较。 – niahoo