我需要在Erlang中序列化一个函数,将它发送到另一个注释,反序列化并在那里执行它。我遇到的问题是文件。如果函数从不在第二个节点中的文件中读取,则会出现错误。有没有办法在Erlang中区分可序列化和不可序列化的结构?因此,如果函数使用文件或pid,那么它无法序列化?
由于
答案 0 :(得分:1)
有些情况甚至不会执行此功能或将以完全错误的方式执行。
Erlang中的每个函数,即使是一个匿名的函数,都属于某个模块,在它内部构造的模块中,确切地说。如果这个函数已经在REPL中构建,那么它将被绑定到erl_eval模块,这更加危险(我将进一步解释,为什么)。
说,你启动两个节点,其中一个节点有一个名为'foo'的模块,一个没有加载这样的模块(并且无法加载它)。如果你在模块'foo'中构造一个lambda,将它发送到第二个节点并尝试调用它,你将失败{error,undef}。
还有另一个有趣的问题。尝试制作两个版本的模块'foo',在每个模块中实现一个'bar'函数并在其中实现一个lambda(但lambda会不同)。尝试调用已发送的lambda时,你会遇到另一个错误。
我认为,将Lambda发送到不同节点可能还有其他棘手的部分,但相信我,已经相当多了。
即使有一种方法可以从lambda中捕获闭包变量(如果你看一下二进制化的lambda,它内部使用的所有外部变量都是从第二个字节开始列出的),它们不是只有潜在的pid或端口来源。
考虑一个简单的例子:在lambda中调用self()
函数。它会回归什么?对,一个pid。好的,我们可以解析二进制文件并捕获此函数调用,以及十几个其他内置函数。但是当你调用一些外部函数时你会怎么做? ets:lookup(sometable, somekey)
? some_module:some_function_that_returns_god_knows_what()
?你不知道他们会回来什么。
使用文件时,始终发送文件名,而不是描述符。如果您需要文件的位置或其他内容,也请发送它。在他们打开的过程之外,不应该知道文件描述符。
正如我所提到的,尽一切努力避免将lambda发送到其他节点。很难说如何避免这种情况,因为我不知道你的确切任务。也许,您可以发送要执行的函数列表,例如:
[{module1, parse_query},
{module1, dispatch_parsed_query},
{module2, validate_response},
{module2, serialize_query}]
并通过此函数序列传递参数(确保所有模块都存在)。也许,您实际上可以坚持使用一些将在整个集群中频繁更改和部署的模块。也许,您可能希望切换到JS / Lua并使用外部启动的端口(Riak使用spidermonkey来处理用于Map / Reduce请求的JS编写的lambdas)。最后,您实际上可以获取模块的目标代码,将其发送到另一个节点并将其加载到那里。请记住,它也不安全。你可以打破一些正在运行的进程,丢失一些构造的lambda,等等。