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.7k stars 143 forks source link

Any example show how to send a message to erlang node? #24

Closed seconemouse closed 3 years ago

seconemouse commented 3 years ago

erlang code:

-module(kvs).                                 
-export([start/0,store/2,lookup/1]).          
start()->                                     
    register(kvs,spawn(fun() -> loop() end)). 

store(Key,Value)->                            
    io:format("xxx\n"),                       
    rpc({store,Key,Value}).                   

lookup(Key)->                                 
    rpc({lookup,Key}).                        

rpc(Q)->                                      
    kvs ! {self(), Q},                        
    receive                                   
        {kvs, Reply} ->                       
            Reply                             
    end.                                      

loop() ->                                     
    receive                                   
        {From, {store, Key, Value}} ->        
            put(Key, {ok, Value}),            
            From ! {kvs, true},               
            loop();                           
        {From, {lookup, Key}} ->              
            From ! {kvs, get(Key)},           
            loop()                            
    end.                                      

I use simple/GenServer.go

process.Call(etf.Tuple{"kvs", "erlmain@localhost"}, etf.Term(etf.List{"weather", "fine"}))

can not recv the result from erlang node erlmain@localhost

halturin commented 3 years ago

Thanks for the question. I would recommend rewriting your erlang code to the OTP design patterns using gen_server behavior. The reason you don't receive the message because you didn't specify the correct pattern for that. Process.Call does the same as gen_server:call - a sending message has pattern {'$gen_call', From, Message}.

There are 3 way to send a message:

As of your erlang code... here is how it should look like (if you don't want to follow OTP design)

receive                                   
        {'$gen_call', _From, Message} ->        % in case of using Process.Call
            io:format("Got 'call' message: ~p", [Message]),
            % you have to reply on it with correct answer like this https://github.coml/halturin/ergo/blob/master/gen_server.go#L81
            % otherwise Process.Call fail with timeout
            loop();                           
        {'$gen_cast', Message} ->  % in case of using Process.Cast            
            % you don't have to reply here
            io:format("Got 'cast' message: ~p", [Message]), 
            loop();          
        OtherMessage -> % in case of using Process.Send             
            io:format("Got 'other' message: ~p", [OtherMessage]),
            loop()
    end.
seconemouse commented 3 years ago

Thanks a lot,I'll try it later

halturin commented 3 years ago

Let's leave it open for the others who get the same question.

seconemouse commented 3 years ago

Great, It works.

erl:

-module(my_bank).
-behavior(gen_server).
-export([start/0,stop/0,new_account/1,deposit/2,withdraw/2,
        init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

%%====================================================================
%% server interface function
%%====================================================================
start()->
    gen_server:start_link({local,?MODULE}, ?MODULE,[],[]).

stop()->
    gen_server:call(?MODULE,stop).

new_account(Who)->
    gen_server:call(?MODULE,{new,Who}).

deposit(Who,Amount)->
    gen_server:call(?MODULE,{add,Who,Amount}).

withdraw(Who,Amount)->
    gen_server:call(?MODULE,{remove,Who,Amount}).

%%====================================================================
%% gen_server callbacks
%%====================================================================
init([]) ->
    Init=ets:new(?MODULE,[]),
    io:format("init ~p ~p",[Init,?MODULE]),
    {ok, Init}.

handle_call({new,Who}, _From, State) ->
    Reply = case ets:lookup(State,Who) of
        []->ets:insert(State,{Who,0}),
            {welcome,Who};
        [_]->{exsits,Who,you_already_are_a_customer}
    end,
    {reply, Reply, State}; 
handle_call({add,Who,Amount},_From,State)->
    Reply = case ets:lookup(State,Who) of
        []->not_a_customer;
        [{Who,Balance}]->
            NewBalance=Balance+Amount,
            ets:insert(State,{Who,NewBalance}),
            {thanks,Who,you_balance_is,NewBalance}
    end,
    {reply, Reply, State}; 
handle_call({remove,Who,Amount},_From,State)->
    Reply = case ets:lookup(State,Who) of
        []->not_a_customer;
        [{Who,Balance}] when Amount =< Balance ->
            NewBalance=Balance-Amount,
            ets:insert(State,{Who,NewBalance}),
            {thanks,Who,your_balance_is,NewBalance};
        [{Who,Balance}]->
            {sorry,Who,you_only_have,Balance,in_the_bank}
    end,
    {reply, Reply, State}.

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    io:format("is terminate.",[]),
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

go

ret, _ := process.Call(etf.Tuple{"my_bank", "erlmain@locahost"}, etf.Tuple{etf.Atom("remove"), "tyt", 1})