Рабочий процесс Эрланга

Я новичок в Erlang, однако я пытался создать простую реализацию клиент-сервера. Сервер гипотетически будет создавать рабочие процессы для выполнения «тяжелой работы» в «базе данных», а затем возвращать вычисленные значения клиенту.

Мои текущие шаги:

  1. Создайте серверный процесс.
  2. Создайте рабочего.
  3. Отправьте работнику некоторую работу на основе ввода клиентов. (Вот тут я запутался)
  4. Отправьте данные от работника обратно клиенту.

Вот пример кода.

-module(server).
-compile(export_all).

server() ->
    receive
        {From, {client, Name}} ->
            io:format("Server has received request for ~p from ~p~n", [Name, From]),
            Worker = spawn(server, getNameFromDataBase(self()),[]),
            Worker ! Name,
            From ! LastName,%%data returned from worker
            server();
        {database, LastName} ->
            Data = LastName,
            server()
    end.


getNameFromDataBase(Server_Address) ->
    receive
        {name, Name} ->
            timer:sleep(5000), %doing difficult work!
            Server_Address ! {database, "Johnson"}
    end.



client(Server_Address) ->
    Server_Address ! {self(), {client, "Jim"}},
    receive
            {server, LastName} ->
                io:format("Server got person's last name~p~n", LastName)
    end.

Как сделать так, чтобы данные, которые вы получаете от воркера, были видны и могли быть отправлены клиенту?


person Alex. Smith    schedule 05.04.2018    source источник


Ответы (2)


Я вижу, что у вас есть две основные проблемы в процессе сервера:

  1. Отправка сообщения с сервера работнику базы данных.
  2. Отправка сообщения, полученного сервером от воркера, обратно клиенту.

Отправка сообщения исполнителю

Ваши следующие строки кода от L8 до L10 неверны.

%% ...
Worker = spawn(server, getNameFromDataBase(self()), []),
Worker ! Name,                                           
From ! LastName,                   
%% ...

Чтобы создать функцию как процесс, вы должны предоставить любой аргумент(ы) функции как члены списка в третьем аргументе spawn/3, а не вызывать функцию напрямую.

Worker = spawn(server, getNameFromDataBase, [self()]),

Отправка только Name Worker останется незамеченной, так как getNameFromDataBase ожидает {name, Name}. Это должно быть изменено на

Worker ! {name, Name}

Отправка результата (фамилия) клиенту

Нет смысла отправлять From ! LastName на L10, так как вы не получили LastName. Это должно быть перемещено во второе выражение соответствия, когда сервер получил {database, LastName} от работника. Кроме того, client ожидает {server, LastName}, а не LastName. Так что должно было быть From ! {server, LastName}.

Однако есть проблема. Вы не можете получить доступ к From в этой области, потому что она никогда не была определена.

%% ...
{database, LastName} ->
    From ! {server, LastName},      % `From` is not defined
    server()
%% ...

Что вы можете сделать, не изменяя кортеж сообщения, так это определить другую функцию server/1, например:

server(ClientPID) ->
    receive
        {_, LastName} ->
            ClientPID ! {server, LastName},
            server()
    end.

и вызовите это из server/0:

server() ->
    receive
        {From, {client, Name}} ->
            io:format("Server has received request for ~p from ~p~n", [Name, From]),
            Worker = spawn(?MODULE, getNameFromDataBase, [self()]),
            Worker ! {name, Name},
            server(From)      % call `server/1` with the client's PID
    end.

Наконец, чтобы завершить все, если вы еще этого не сделали, создайте инициирующую функцию для порождения процессов server и client. Вот завершенный код:

server() ->
    receive
        {From, {client, Name}} ->
            io:format("Server has received request for ~p from ~p~n", [Name, From]),
            Worker = spawn(?MODULE, getNameFromDataBase, [self()]),
            Worker ! {name, Name},
            server(From)
    end.

server(ClientPID) ->
    receive
        {_, LastName} ->
            ClientPID ! {server, LastName},
            server()
    end.

getNameFromDataBase(ServerAddr) ->
    receive
        {name, Name} ->
            io:format("worker received ~p~n",[Name]),
            timer:sleep(5000),
            ServerAddr ! {database, "Johnson"}
    end.

client(ServerPID) ->
    ServerPID ! {self(),{client, "Jim"}},
    receive
        {server, LastName} ->
            io:format("Client got person's last name ~p from Server ~n", [LastName])
    end.

run() ->
    ServerPID = spawn(?MODULE, server, []),
    spawn(?MODULE, client, [ServerPID]).

Последний совет: не используйте параметр компиляции export_all. Экспортируйте только необходимые функции для хорошей инкапсуляции.

person Pie 'Oh' Pah    schedule 05.04.2018

Вы получите сообщение от работника в ветке {database, LastName} -> получения. Просто включите информацию, необходимую для обработки, в само сообщение:

server() ->
    receive
        {From, {client, Name}} ->
            io:format("Server has received request for ~p from ~p~n", [Name, From]),
            Worker = spawn(server, getNameFromDataBase, [self()]),
            Worker ! {name, From, Name},
            server();
        {database, From, LastName} ->
            From ! LastName,
            server()
    end.

getNameFromDataBase(Server_Address) ->
    receive
        {name, From, Name} ->
            timer:sleep(5000), %doing difficult work!
            Server_Address ! {database, From, "Johnson"}
    end.

Обратите внимание, что аргументы должны передаваться в списке (третий аргумент spawn). Кроме того, если вы создаете работника только для немедленной отправки одного сообщения, вы также можете передать его содержимое в качестве аргументов; Я не вносил это изменение здесь, чтобы было легче увидеть основное изменение.

В качестве альтернативы вы можете сохранить From, соответствующий Worker, в состоянии сервера (аргумент для server()).

person Alexey Romanov    schedule 05.04.2018
comment
В этом случае клиент ожидает {server, LastName}, поэтому отправка только LastName с From ! LastName ничего не напечатает. - person Pie 'Oh' Pah; 05.04.2018