2

次のようにノード間関数呼び出し (rpc) をテストしていました。

defmodule RpcTest do
  def run do 
    Task.Supervisor.async( {DBServer.DistSupervisor, :'dbserver@hostname'},  fn -> "test" end)
    |> Task.await
    |> IO.inspect
  end
end

次にdbserver、ノードを実行し、 を呼び出しTask.Supervisor.start_link(name: DBServer.DistSupervisor)dbserverrcp を受信し、別のノードで上記のコードを実行します。dbclient

以下のようにrpc を正しく実行できます。

dbserver

$ iex --sname dbserver --cookie a -S mix
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Compiling 1 file (.ex)
Interactive Elixir (1.3.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(dbserver@hostname)1> Task.Supervisor.start_link(name: DBServer.DistSupervisor)
{:ok, #PID<0.101.0>}
iex(dbserver@hostname)2> 

dbclient

$ elixir --sname dbclient --cookie a -S mix run -e RpcTest.run
"test"

ただし、コードを"test"から"test test" に変更した後、dbclient ノードが機能しません。

ここにエラーメッセージがあります

dbserver

iex(dbserver@hostname)2> 
12:54:57.430 [error] Task #PID<0.110.0> started from {:"dbclient@hostname", #PID<13423.52.0>} terminating
** (BadFunctionError) expected a function, got: #Function<0.113878361/0 in RpcTest>
    :erlang.apply/2
    (elixir) lib/task/supervised.ex:94: Task.Supervised.do_apply/2
    (elixir) lib/task/supervised.ex:45: Task.Supervised.reply/5
    (stdlib) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Function: #Function<0.113878361/0 in RpcTest>
    Args: []

dbclient

$ elixir --sname dbclient --cookie a -S mix run -e RpcTest.run
Compiling 1 file (.ex)
** (EXIT from #PID<0.52.0>) an exception was raised:
    ** (BadFunctionError) expected a function, got: #Function<0.113878361/0 in RpcTest.run/0>
        :erlang.apply/2
        (elixir) lib/task/supervised.ex:94: Task.Supervised.do_apply/2
        (elixir) lib/task/supervised.ex:45: Task.Supervised.reply/5
        (stdlib) proc_lib.erl:240: :proc_lib.init_p_do_apply/3

さらに、手動で再起動た後も機能します。dbserver

興味深いことに、それiexどんなコードでも機能します。

$ iex --sname dbclient --cookie a -S mix
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Compiling 1 file (.ex)
Interactive Elixir (1.3.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(dbclient@hostname)1> Task.Supervisor.async( {DBServer.DistSupervisor, :'dbserver@hostname'},  fn -> "test" end) |>  
...(dbclient@hostname)1> Task.await |>                                      
...(dbclient@hostname)1> IO.inspect                                          
"test"                                       
"test"
iex(dbclient@hostname)2> Task.Supervisor.async( {DBServer.DistSupervisor, :'dbserver@hostname'},  fn -> "test test" end) |> 
...(dbclient@hostname)2> Task.await |>                                      
...(dbclient@hostname)2> IO.inspect  
"test test"                                       
"test test"

私の質問は、

  • ノード内の関数を別のノードから呼び出すのは間違った方法ですか?
  • ノード間関数呼び出しを使用する場合、クライアント コードを変更するたびに呼び出し先ノードを再実行する必要がありますか?
  • iexと の間で動作が異なるのはなぜelixir scriptですか?

コードとプロジェクト全体は、https ://github.com/ayamamori/rpc_test/blob/master/lib/rpc_test.ex にアップロードされています。

前もって感謝します!

4

1 に答える 1

1

IEx で宣言された関数とモジュールで宣言された関数には、匿名であっても微妙な違いがあります。

次のモジュールがあるとします。

defmodule FunInfo do
  def info do
    :erlang.fun_info(fn -> "test" end)
  end
end

現在、IEx では:

iex(1)> FunInfo.info
[pid: #PID<0.91.0>, module: FunInfo, new_index: 0,
 new_uniq: <<98, 250, 216, 183, 54, 136, 109, 221, 200, 243, 9, 84, 249, 185, 187, 173>>,
 index: 0, uniq: 51893957, name: :"-info/0-fun-0-", arity: 0, env: [], 
 type: :local]

同じ関数ですが、IEx で宣言されています。

iex(2)> :erlang.fun_info(fn -> "test" end)
[pid: #PID<0.91.0>, module: :erl_eval, new_index: 20,
 new_uniq: <<103, 57, 49, 11, 11, 201, 159, 65, 226, 96, 121, 97, 18, 82, 151, 208>>,
 index: 20, uniq: 54118792, name: :"-expr/5-fun-3-", arity: 0,
 env: [{[], :none, :none,
   [{:clause, 12, [], [],
     [{:bin, 0,
       [{:bin_element, 0, {:string, 0, 'test'}, :default, :default}]}]}]}],
 type: :local]

関数の情報についていくつか強調することができます。FunInfoモジュールで定義されているものについては、次のものがあります。

  • module: FunInfo
  • :new_uniq:および:uniq識別子。
  • :env関数の AST が含まれていません。

一方、IEx での 1 つの定義については、次のようになります。

  • module: :erl_eval
  • :new_uniq:および:uniq識別子。
  • :env無名関数のASTを持っています。

モジュールで宣言された無名関数

:new_uniqとの:uniq値は、無名関数のソース コードを変更するたびに変わります。2 つのノードが同じモジュールの異なるバージョンを持っている場合、この無名関数は異なる識別子を:new_uniq持ちます。:uniqサーバーが知らない匿名関数を実行しようとすると、失敗します。

IEx で宣言された無名関数

:new_uniqとの:uniq値は、宣言する関数ごとに変わるわけではありません。それら:erl_evalはすでにコンパイルされているモジュール内にあり、ソースコードを変更していません。また、IEx で宣言されたすべての関数には AST が含まれ:envているため、リモート ノードにその関数の実行を要求すると、関数の本体も送信されます。

iex(1)> :erlang.fun_info(fn -> "test" end)
[pid: #PID<0.91.0>, module: :erl_eval, new_index: 20,
 new_uniq: <<103, 57, 49, 11, 11, 201, 159, 65, 226, 96, 121, 97, 18, 82, 151, 208>>,
 index: 20, uniq: 54118792, name: :"-expr/5-fun-3-", arity: 0,
 env: [{[], :none, :none,
       [{:clause, 26, [], [],
         [{:bin, 0,
           [{:bin_element, 0, {:string, 0, 'test'}, :default, :default}]}]}]}],
 type: :local]

別の無名関数を作成すると、:new_uniq:uniqの値は変更されませんが、:env値は正しい AST で変更されます。

iex(2)> :erlang.fun_info(fn -> "test test" end)
[pid: #PID<0.91.0>, module: :erl_eval, new_index: 20,
new_uniq: <<103, 57, 49, 11, 11, 201, 159, 65, 226, 96, 121, 97, 18, 82, 151, 208>>,
 index: 20, uniq: 54118792, name: :"-expr/5-fun-3-", arity: 0,
 env: [{[], :none, :none,
       [{:clause, 27, [], [],
         [{:bin, 0,
           [{:bin_element, 0, {:string, 0, 'test test'}, :default, :default}]}]}]}],
 type: :local]

これがあなたの質問に答えることを願っています。

于 2016-07-04T11:57:46.147 に答える