gpt4 book ai didi

asynchronous - 如何在 Erlang 中进行并行调用并等待所有结果?

转载 作者:行者123 更新时间:2023-12-04 05:11:12 26 4
gpt4 key购买 nike

我正在使用 Erlang 开发手机游戏后端。对于每个 HTTP 请求,它可能需要查询不同的数据源,例如 PostgreSQL、MongoDB 和 Redis。我想并行地对这些数据源进行独立调用,但找不到明确的 Erlang 方法。

例如,

handle_request(?POST, <<"login">>, UserId, Token) ->
% Verify token from PostgreSQL
AuthResult = auth_service:login(UserId, Token),

% Get user data such as outfits and level from MongoDB
UserDataResult = userdata_service:get(UserId),

% Get messages sent to the user from Redis
MessageResult = message_service:get(UserId),

% How to run the above 3 calls in parallel?
% Then wait for all their results here?

% Combine the result and send back to client
build_response(AuthResult, UserDataResult, MessageResult).

每个服务最终都会调用相应的数据驱动程序(epgsql、eredis、mongo_erlang),这些驱动程序最终会出现一些 pooboy:transaction 和 gen_server:call。如何设计这些服务模块也尚未确定。

我想确保上面的 3 个数据调用可以并行运行,然后 handle_request 函数等待所有这 3 个调用完成,然后调用 build_response。我怎么能正确地做到这一点?

作为引用,在 NodeJS 中,我可能会这样做

var authPromise = AuthService.login(user_id, token);
var userDataPromise = UserdataService.get(user_id);
var messagePromise = MessageService.get(user_id);
Promise.all(authPromise, userDataPromise, messagePromise).then( function(values) {
buildResponse(values);
}

在 Scala 中,我可能会这样做

val authFuture = AuthService.login(userId, token)
val userDataFuture = UserdataService.get(userId)
val messageFuture = MessageService.get(userId)
for {
auth <- authFuture
userData <- userDataFuture
message <- messageFuture
} yield ( buildResponse(auth, userData, message )

显然,我认为这个问题是一个 promise / future / yield 问题。但是有人告诉我,如果我在 Erlang 中寻找 Promise,我可能会走错方向。 Erlang 实现这一目标的最佳实践是什么?

最佳答案

How to make parallel calls in Erlang and wait for all of the results?



您可以使用堆叠 receive条款。 Erlang 将在接收子句中永远等待,直到消息从进程到达(或者您可以使用 after 指定超时)——这类似于在 nodejs 中等待 promise :
-module(my).
-compile(export_all).

all_results() ->
Pid1 = spawn(?MODULE, getdata1, [self(), {10, 20}]),
Pid2 = spawn(?MODULE, getdata2, [self(), 30]),
Pid3 = spawn(?MODULE, getdata3, [self()]),

[receive {Pid1, Result1} -> Result1 end,
receive {Pid2, Result2} -> Result2 end,
receive {Pid3, Result3} -> Result3 end].

getdata1(From, {X, Y}) ->
%% mimic the time it takes to retrieve the data:
SleepTime = rand:uniform(100),
io:format("Sleeping for ~w milliseconds~n", [SleepTime]),
timer:sleep(SleepTime),

From ! {self(), X+Y}. %% send the data back to the main process

getdata2(From, Z) ->
SleepTime = rand:uniform(100),
io:format("Sleeping for ~w milliseconds~n", [SleepTime]),
timer:sleep(SleepTime),

From ! {self(), Z+1}.

getdata3(From) ->
SleepTime = rand:uniform(100),
io:format("Sleeping for ~w milliseconds~n", [SleepTime]),
timer:sleep(SleepTime),

From ! {self(), 16}.

请注意,此代码:
[receive {Pid1, Result1} -> Result1 end, 
receive {Pid2, Result2} -> Result2 end,
receive {Pid3, Result3} -> Result3 end].

相当于:
R1 = receive {Pid1, Result1} -> 
Result1
end,
R2 = receive {Pid2, Result2} ->
Result2
end,
R3 = receive {Pid3, Result3} ->
Result3
end,

[R1, R2, R3].

在外壳中:
~/erlang_programs$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3 (abort with ^G)

1> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}

2> timer:tc(my, all_results, []).
Sleeping for 66 milliseconds
Sleeping for 16 milliseconds
Sleeping for 93 milliseconds
{96356,[30,31,16]}

3> timer:tc(my, all_results, []).
Sleeping for 57 milliseconds
Sleeping for 30 milliseconds
Sleeping for 99 milliseconds
{100153,[30,31,16]}

4> timer:tc(my, all_results, []).
Sleeping for 66 milliseconds
Sleeping for 31 milliseconds
Sleeping for 24 milliseconds
{66426,[30,31,16]}

timer:tc()返回函数执行所需的时间(以微秒为单位)(1,000 微秒 = 1 毫秒)以及函数的返回值。例如,第一次 all_results()被称为完成需要 96.4 毫秒,而如果按顺序执行,单个进程将需要 66+16+93=175+ 毫秒才能完成。

关于asynchronous - 如何在 Erlang 中进行并行调用并等待所有结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54281392/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com