【问题标题】:erlang: failed to spawn(node, fun): badfun errorerlang:生成失败(节点,乐趣):badfun 错误
【发布时间】:2015-07-05 10:02:16
【问题描述】:

远程节点位于不同的机器上。

我从本地节点测试:

$ erl -name foobar
Erlang/OTP 17 [erts-6.2] [source] [64-bit] [smp:2:2] [async-threads:10] [kernel-poll:false]

Eshell V6.2  (abort with ^G)
(foobar@debian.localdomain)1> Aliyun='aliyun@localhost2.localdomain'.
'aliyun@localhost2.localdomain'
(foobar@debian.localdomain)2> spawn(Aliyun, fun() -> io:format("hello~n") end).
<6108.86.0>
(foobar@debian.localdomain)3> 
=ERROR REPORT==== 4-Jul-2015::21:03:27 ===
Error in process <0.86.0> on node 'aliyun@localhost2.localdomain' with exit value: {{badfun,#Fun<erl_eval.20.90072148>},[{erlang,apply,2,[]}]}


(foobar@debian.localdomain)3> spawn(Aliyun, io, format, ["hello~n"]).          
hello
<6108.87.0>
(foobar@debian.localdomain)4> net_adm:ping(Aliyun).
pong

您可以看到 spawn(node,module,function,args) 有效,但 spawn(node,fun) 无效。

远程节点上的 Erlang 版本是 R15,而本地节点上的版本是 R17。是这个原因吗?因为代码格式不同?我不清楚 Erlang 在将 fun 类型传递给远程节点时如何对其进行编组。在字节码中?

请帮忙!

【问题讨论】:

    标签: erlang


    【解决方案1】:

    正如您收到的错误消息所示,匿名函数在此上下文中基本上被视为在erl_eval 模块中定义。如果发送节点和接收节点上的erl_eval 版本相同,则一切正常,因为在这种情况下,erl_eval 的两个副本具有相同的版本和校验和,因此接收节点能够正确评估从发送节点传递的匿名函数。但是如果两个节点有不同的erl_eval 模块,那么匿名函数的求值就会失败。

    一个有趣的尝试是在 R15 节点上定义匿名函数,通过term_to_binary/1 将其转换为二进制,将生成的二进制发送或复制到 17.x 节点,通过将其转换回术语binary_to_term/1,然后将生成的术语作为匿名函数传递给您的 spawn 调用。首先,在 R15 节点上:

    (r15@myhost)1> F = fun() -> io:format("hello~n") end.
    (r15@myhost)2> Bin = term_to_binary(F).
    <<131,112,0,0,2,179,0,158,45,156,12,16,101,74,154,214,21,
      222,196,219,108,205,131,0,0,0,20,0,0,...>>
    (r15@myhost)3> file:write_file("/tmp/fun", Bin).
    ok
    

    现在将二进制文件读入 17.x 节点,并将 spawn 调用返回到 R15 节点:

    (r17@myhost)1> {ok, Bin} = file:read_file("/tmp/fun").
    {ok,<<131,112,0,0,2,179,0,158,45,156,12,16,101,74,154,
          214,21,222,196,219,108,205,131,0,0,0,20,...>>}
    (r17@myhost)2> F = binary_to_term(Bin).
    #Fun<erl_eval.20.82930912>
    (r17@myhost)3> spawn(r15@myhost, F).
    hello
    <7101.90.0>
    

    如您所见——您也应该亲自尝试一下——spawn 调用按预期工作,因为匿名函数是在 R15 节点上创建的,并且它也在此处进行评估。 17.x 节点只是通过它。

    【讨论】:

    • 为什么限制为同版本的erl_eval?只要 fun 与目标版本兼容,即引​​用的函数和语义兼容,那么它应该运行在目标 Erlang 上,这样更灵活,因为除了准备好的模块之外,节点之间可以交换任意兼容代码.
    • 您如何提议验证模块功能和语义的兼容性,以便与该模块相关联的任意匿名函数可以跨不同节点正确执行?解决这个问题通常是不切实际的。因此,Erlang 运行时会检查模块的 MD5 校验和以验证兼容性。
    猜你喜欢
    • 2015-09-25
    • 2020-10-12
    • 2018-10-31
    • 1970-01-01
    • 2021-06-10
    • 2013-09-21
    • 2012-01-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多