ergo-services / ergo

An actor-based Framework with network transparency for creating event-driven architecture in Golang. Inspired by Erlang. Zero dependencies.
https://docs.ergo.services
MIT License
3.67k stars 143 forks source link

rpc:call/4, rpc:cast/4, erpc:cast/4 from Erlang to Ergo does not work #121

Closed leonlee2013 closed 1 year ago

leonlee2013 commented 1 year ago

Erlang project has rpc:call/4, rpc:cast/4, need ergo to be compatible。 I tried to fix the bug that rpc:call/4, rpc:cast/4 from Erlang to Ergo does not work. https://github.com/leonlee2013/ergo/commit/a98e83493f99f4682ce93e19a5e3674d43e02936 https://github.com/leonlee2013/ergo/commit/d9db4bc2ed7677767f91622d98967b211151f5ad

Here is the test code

package main

import (
    "flag"
    "fmt"

    "github.com/ergo-services/ergo"
    "github.com/ergo-services/ergo/etf"
    "github.com/ergo-services/ergo/node"
)

var (
    ServerName string
    NodeName   string
    Cookie     string
)

func init() {
    flag.StringVar(&ServerName, "server", "example", "server process name")
    flag.StringVar(&NodeName, "name", "go_node1@127.0.0.1", "node name")
    flag.StringVar(&Cookie, "cookie", "go_erlang_cookie", "cookie for interaction with erlang cluster")
}

func main() {
    flag.Parse()

    fmt.Println("")
    fmt.Println("to stop press Ctrl-C")
    fmt.Println("")

    node, err := ergo.StartNode(NodeName, Cookie, node.Options{})
    if err != nil {
        panic(err)
    }

    testFun1 := func(a ...etf.Term) etf.Term {
        fmt.Printf("go handle --->>> %#v\n", a)
        return a[len(a)-1]
    }
    if e := node.ProvideRPC("testMod", "testFun", testFun1); e != nil {
        panic(err)
    }

    fmt.Println("Start erlang node with the command below:")
    fmt.Printf("    $ erl -name %s -setcookie %s\n\n", "erl-"+node.Name(), Cookie)

    fmt.Println("----- to make call request from 'erl'-shell:")
    fmt.Printf("gen_server:call({%s,'%s'}, hi).\n", ServerName, NodeName)
    fmt.Printf("gen_server:call({%s,'%s'}, {echo, 1,2,3}).\n", ServerName, NodeName)
    fmt.Println("----- to send cast message from 'erl'-shell:")
    fmt.Printf("gen_server:cast({%s,'%s'}, {cast, 1,2,3}).\n", ServerName, NodeName)
    fmt.Println("----- send atom 'stop' to stop server :")
    fmt.Printf("gen_server:cast({%s,'%s'}, stop).\n", ServerName, NodeName)

    node.Wait()
}

Erlang input and output:

(erl-go_node1@127.0.0.1)50> rpc:call('go_node1@127.0.0.1', testMod, testFun, [a, b,111]).
111
(erl-go_node1@127.0.0.1)51> rpc:cast('go_node1@127.0.0.1', testMod, testFun, [a, b,111]).
true

Log output of Go

go handle --->>> []etf.Term{"a", "b", 111}
go handle --->>> []etf.Term{"a", "b", 111}

Looking forward to your review and merge!!!

CLAassistant commented 1 year ago

CLA assistant check
All committers have signed the CLA.

leonlee2013 commented 1 year ago

I found that there was also a problem with erpc:cast/4, I fixed it

I used otp_24.1.6 for testing

$ erl -name erl-go_node1@127.0.0.1 -setcookie go_erlang_cookie
Erlang/OTP 24 [erts-12.1.5] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]

Eshell V12.1.5  (abort with ^G)
(erl-go_node1@127.0.0.1)1> erpc:cast('go_node1@127.0.0.1', testMod, testFun, [1,23,4545]).
ok

erlang lib:

%%otp_24.1.6  kernel-8.1.3/src/rpc.erl

%%call
-ifdef(SERVER_SIDE_ERPC_IS_MANDATORY).
call(N,M,F,A,T) ->
    ?RPCIFY(erpc:call(N, M, F, A, T)).
-else. %% ! defined SERVER_SIDE_ERPC_IS_MANDATORY
call(N,M,F,A,T) ->
    DL = try
             deadline(T)
         catch
             error:_ ->
                 error(badarg)
         end,
    case ?RPCIFY(erpc:call(N, M, F, A, T)) of
        {badrpc, notsup} ->
            case time_left(DL) of
                0 ->
                    {badrpc, timeout};
                Timeout ->
                    do_srv_call(N, {call,M,F,A,group_leader()}, Timeout)
            end;
        Res ->
            Res
    end.
-endif. %% ! defined SERVER_SIDE_ERPC_IS_MANDATORY

%%cast
-ifdef(SERVER_SIDE_ERPC_IS_MANDATORY).
cast(Node, Mod, Fun, Args) ->
    try  
        ok = erpc:cast(Node, Mod, Fun, Args)
    catch
        error:{erpc, badarg} ->
            error(badarg)
    end, 
    true.
-else.
cast(Node, Mod, Fun, Args) when is_atom(Node),
                                is_atom(Mod),
                                is_atom(Fun),
                                is_list(Args) ->
    _ = case node_has_feature(Node, erpc) of
            false ->
                gen_server:cast({?NAME,Node},
                                {cast,Mod,Fun,Args,group_leader()});
            true ->
                try  
                    ok = erpc:cast(Node, Mod, Fun, Args)
                catch
                    error:{erpc, badarg} ->
                        error(badarg)
                end  
        end, 
    true;
cast(_, _, _, _) ->
    error(badarg).
-endif.

erpc/rpc

rpc:call/4 = erpc:call/4 -> dist:  dist:distProtoSPAWN_REQUEST
rpc:cast/4  ->  dist:distProtoREG_SEND
erpc:cast/4 -> dist:distProtoSPAWN_REQUEST