Go to file
2016-04-18 00:38:00 +03:00
src Clarify client side logging and return for error cases 2016-04-18 00:38:00 +03:00
test Clarify client side logging and return for error cases 2016-04-18 00:38:00 +03:00
.gitignore Initial commit 2016-04-14 10:28:53 +03:00
Makefile First version 2016-04-14 10:40:33 +03:00
README.md Clarify client side logging and return for error cases 2016-04-18 00:38:00 +03:00
rebar.config Correct repo for thrift dependency 2016-04-14 17:38:45 +03:00
rebar.config.script First version 2016-04-14 10:40:33 +03:00
rebar.lock Correct repo for thrift dependency 2016-04-14 17:38:45 +03:00
wercker.yml First version 2016-04-14 10:40:33 +03:00

RPC wercker status

Erlang реализация Библиотеки RPC вызовов для общения между микросервисами.

На текущий момент RPC реализован с помощью Thrift протокола поверх http.

API

Сервер

Получить child_spec RPC сервера:

1> EventHandler = my_event_handler.  %% реализует rpc_event_handler behaviour
2> Service = my_money_service.  %% реализует thrift_service behaviour (генерируется из .thrift файла)
3> ThriftHandler = my_money_thrift_service_handler.  %% реализует rpc_thrift_handler behaviour
4> Opts = [].
5> Handlers = [{"/v1/thrift_money_service",{Service, ThriftHandler, Opts}}].
6> ServerSpec = rpc_server:child_spec(money_service_sup, #{
6>     handlers => Handlers,
6>     event_handler => EventHandler,
6>     ip => {127,0,0,1},
6>     port => 8022,
6>     net_opts => []
6> }).

Теперь можно поднять RPC сервер в рамках supervision tree приложения. Например:

7> {ok, _} = supervisor:start_child(MySup, ServerSpec).

Клиент

Сделать синхронный RPC вызов:

8> Url = <<"localhost:8022/v1/thrift_money_service">>.
9> Function = give_me_money.  %% thrift метод
10> Args = [100, <<"rub">>].
11> Request = {Service, Function, Args}.
12> Client = rpc_client:new(<<"myUniqRequestID1">>, EventHandler).
13> {ok, Result, _NextClient} = rpc_client:call(Client, Request, #{url => Url}).

В случае вызова thrift oneway функции (thrift реализация cast), rpc_client:call/3 вернет только {ok, NextClient}.

Если сервер бросает exception, описанный в .thrift файле сервиса, rpc_client:call/3 бросит это же исключение в виде: throw:{Exception, NextClient}, а в случае ошибки RPC вызова: error:{Reason, NextClient}.

rpc_client:call_safe/3 - аналогична call/3, но в случае исключений, не бросает их, а возвращает в виде tuple: {Class, Error, NextClient} либо {error, Reason, NextClient, Stacktace}.

14> Args1 = [1000000, <<"usd">>].
15> Request1 = {Service, Function, Args1}.
16> Client1 = rpc_client:new(<<"myUniqRequestID2">>, EventHandler).
17> {throw, #take_it_easy{}, _NextClient1} = rpc_client:call_safe(Client1, Request1, #{url => Url}).

rpc_client:call_async/5 позволяет сделать call асинхронно и обработать результат в callback функции. rpc_client:call_async/5 требует также sup_ref() для включения процесса, обрабатывающего RPC вызов, в supervision tree приложения.

18> Callback = fun({ok, Res, _NextClient2}) -> io:format("Rpc succeeded: ~p~n", [Res]);
18>     ({throw, Error, _NextClient2}) -> io:format("Service exception: ~p~n", [Error]);
18>     ({error, rpc_failed, _NextClient2}) -> io:format("Rpc failed")
18> end.
19> Client2 = rpc_client:new(<<"myUniqRequestID3">>, EventHandler).
20> {ok, Pid, _NextClient2} = rpc_client:call_async(SupRef, Callback, Client2, Request, #{url => Url}).

Можно создать пул соединений для thrift клиента: rpc_thrift_client:start_pool/2 и затем использовать его при работе с rpc_client:

21> Pool = my_client_pool.
22> ok = rpc_thrift_client:start_pool(Pool, 10).
23> Client3 = rpc_client:new(<<"myUniqRequestID3">>, EventHandler).
24> {ok, Result, _NextClient3} = rpc_client:call(Client, Request, #{url => Url, pool => Pool}).

Закрыть пул можно с помошью rpc_thrift_client:stop_pool/1.

Важно!

В предыдущих примерах новый thrift клиент Client создаётся с помощью rpc_client:new/2 перед каждым RPC вызовом. В реальном микросервисе, использующем эту библиотеку, в большинстве случаев RPC запрос будет результатом обработки внешнего RPC вызова к этому микросервису. В таком случае Client будет получен в рамках ThriftHandler:handle_function/5 (реализация rpc_thrift_handler behaviour) на стороне RPC сервера. Это значение Client надо использовать при первом call вызове rpc_client API (т.е. вызывать rpc_client:new/2 не надо). Полученный в результате NextClient (в том числе и через exception в случае с rpc_client:call/3) следует передать в следующий API вызов rpc_client и.т.д.

Также важно заметить, что в ThriftHandler:handle_function/5 передается переменная RpcId (map с тройкой ключей span id, parent id и trace id, идентифицирующих RPC запрос в дереве обработки запросов), которую следует использовать в хэндлере при логировании событий, относящихся к обработке RPC вызова.

Те же требования касаются и значений Client и RpcId, которые передаются в функцию ThriftHandler:handle_error/5.

Все это необходимо, для корректного логирования RPC ID библиотекой, которое позволяет построить полное дерево RPC вызовов между микросервисами в рамках обработки бизнес сценария.