是否可以同时运行不同的Erlang OTP版本?

8yparm6h  于 2022-12-08  发布在  Erlang
关注(0)|答案(2)|浏览(204)

我正在尝试将苹果的voip推送通知添加到我们的应用程序中。我们的后端提供程序是由Erlang的Ejabberd服务器使用apns4erl server 1.0.4编写的。
目前,apns4erl 2具有发送voip推送通知的能力。但是它需要OTP 19+来编译,而我们的系统运行在OTP 17.3上。
所以我可以知道,是否有可能运行这两个OTP在同一时间?我不能升级OTP到19+。和新的库需要19+。
是否有好的方法使这个需求成为可能,或者我需要将新库移植到旧库中?
谢谢你,
埃里克

rur96b6h

rur96b6h1#

  • Keep in mind while reading this that you should really find a way to update your existing service to keep up to date with newer runtimes. I've dealt with being stuck on a legacy runtime only because someone thought they needed to fork a particular module somewhere in a way that made it impossible to upgrade -- and that was just a nightmare.*

TL;DR: (But you should read it anyway)

Yes, I just confirmed that you can connect an R17 and an R20 node via disterl and send messages:
R17 node:

ceverett@changa:/opt/erlang/R17.5/bin$ ./erl -name bar -cookie walnut
Erlang/OTP 17 [erts-6.4] [source] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V6.4  (abort with ^G)
(bar@changa.shinden.tsuriai.jp)1> P = spawn(fun Wait() -> receive {From, Message} -> From ! {received, Message}, Wait() end end).
<0.44.0>
(bar@changa.shinden.tsuriai.jp)2> global:register_name(waiter, P).
yes

R20 node:

ceverett@changa:~$ erl -name foo -cookie walnut                                                                                                                                                                                                                              
Erlang/OTP 20 [RELEASE CANDIDATE 2] [erts-9.0] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:10] [hipe] [kernel-poll:false]                                                                                                                                         
                                                                                                                                                                                                                                                                             
Eshell V9.0  (abort with ^G)
(foo@changa.shinden.tsuriai.jp)1> net_kernel:connect('bar@changa.shinden.tsuriai.jp').
true
(foo@changa.shinden.tsuriai.jp)2> global:send(waiter, {self(), "blah blah blah"}).
<7489.44.0>
(foo@changa.shinden.tsuriai.jp)3> flush().
Shell got {received,"blah blah blah"}
ok

Note that above the R20 node was started first, so that was the version of EPMD that was running. I have no idea whether that would matter, nor do I know if EPMD has changed between R17 and R20.
This is all undocumented functionality. Read below for a much more future-proof way to do this.
The documented way to connect two nodes of different versions is with the +R runtime flag. I regard this as a wildly unreliable hack (precisely as unreliable as what I demonstrated above) unless you've tested it thoroughly first -- and it may have unintended side effects depending on the versions involved (and no telling what is coming in the future). But this is an actual runtime flag and it obviously exists for a reason. See legoscia's answer for more detail about this.

Discussion

Whether or not two versions of Erlang's runtime are compatible over disterl, writing network applications in Erlang is really easy. You can always connect two of any different things over TCP.
The simple solution to this would be to write a network application in Erlang using the current version of Erlang (R20.1 at the moment) that receives Apple voip pushes, and forwards them to your main application.
Write:

  • A single TCP socket handling process inside your R17 system.
  • The Apple VOIP push service handler in R20 and a TCP socket connecting process that talks to the R17 TCP socket handler.

Treat the Apple VOIP service within your system as if it exists as a native part of your application. The socket handler in the R17 node is the VOIP service. Make sure you write its interface functions with that in mind -- later if you can migrate your code to R20 then you won't have to worry with this detail because it will be already abstracted by the internal protocol in Erlang.
As for the push updates themselves, you can create whatever sort of protocol you want.
Erlang's external term format has not changed between R17 and R20, so you will be able to send native messages between the two nodes by having the Apple VOIP side socket handler (on the R20 node) do something like:

notify_node(Socket, VOIP_Data) ->
    Message = term_to_binary({push, VOIP_Data}),
    ok = gen_tcp:send(Socket, Message),
    log(info, "Message sent").

And on the receiving node (the R17 node):

loop(Parent, Debug, State = #s{socket = Socket}) ->
    receive
        {tcp, Socket, Bin} ->
            {push, VOIP_Data} = binary_to_term(Bin, [safe]),
            {ok, NewState} = do_stuff(VOIP_Data, State)
            loop(Parent, Debug, NewState);
        %% Your other stuff
        %% OTP system stuff
    end.

You could write the R17 side as a gen_server also, listening for:

handle_info({tcp, Socket, Bin}, State = #s{socket = Socket}) ->
    %% whatever

I just happen to most often see socket handling processes as proc_lib processes instead of gen_servers most of the time. But it doesn't matter in most cases.
Another approach is to use binaries:

notify_node(Socket, VOIP_Data) ->
    Message = <<"PUSH ", VOIP_Data>>,
    ok = gen_tcp:send(Socket, Message),
    log(info, "Message sent").

And on the receiving node (the R17 node):

loop(Parent, Debug, State = #s{socket = Socket}) ->
    receive
        {tcp, Socket, <<"PUSH ", VOIP_Data/binary>>} ->
            {ok, NewState} = do_stuff(VOIP_Data, State)
            loop(Parent, Debug, NewState);
        %% Your other stuff
        %% OTP system stuff
    end.

It really depends on the nature of VOIP_Data . If it is a binary itself and the R20 Apple push service should just pass it along without inspecting it, then the raw binary method is easy. If the R20 side is going to be interpreting the message and converting it to an Erlang message of its own then you'll do much better with the binary_to_term/1 / term_to_binary/2 form.

mznpcxlj

mznpcxlj2#

也许吧
zxq9's answer显示了它实际上是有效的,同时也建议了一种替代方法。这个答案更一般地讲了使用Erlang发行版连接两个不同的发行版。
the erl man page中,您可以找到+R标志:
+R ReleaseNumber
设置兼容模式。
默认情况下,分发机制不向后兼容。此标志将模拟器设置为与早期Erlang/OTP版本ReleaseNumber兼容的模式。版本号必须在<current release>-2..<current release>范围内。这将限制模拟器,使其可以与运行该早期版本的Erlang节点(以及C和Java节点)通信。

    • 备注**

确保分布式Erlang系统的所有节点(Erlang-、C-和Java节点)都是同一Erlang/OTP版本,或者来自两个不同的Erlang/OTP版本X和Y,其中所有Y节点都具有兼容模式X。
这反映了手册的"兼容性"部分所述内容:

    • Erlang分布**

Erlang节点可以在至少两个先前版本和两个后续版本之间进行通信。
因此,理论上,如果19节点以+R 17标志启动,Erlang 17和19应该能够通信。
但事实上,这个标志目前并没有打开任何兼容性特性(参见源代码),而且自从R16B中的compatibility for R9 was removed以来,它就没有这样做过。X版本和Y版本是否可以相互连接的答案是"试试看"。
另请参阅this answer,以获取显示不同Erlang版本之间连接的表格。

相关问题