2016-07-06 3 views
3

У меня есть одна функция на моем сервере Erlang, который отправляет данные в веб-службу. Эта веб-служба требует счетчика (сколько раз оно было вызвано). Я действительно не знаю, зачем им это нужно, но это важно.Как подсчитать количество вызовов функций в Erlang?

По некоторым причинам я не могу использовать внешнюю БД. Поэтому мне нужно иметь постоянную глобальную переменную, хранящуюся на сервере. Я думал о том, чтобы сохранять счетчик файла, извлекать и увеличивать его каждый раз, когда вызывается функция.

Есть ли более эффективный способ достижения моей цели?

EDIT Функция может быть вызвана из многих различных клиентов в то же время, так что это добавляет еще одну проблему: Если 3 клиента вызовите функцию одновременно, функция будет увеличивать счетчик только один раз, и я получаю сообщение об ошибке. Как заставить функцию ждать, пока другой закончит, а затем выполнит?

Заранее спасибо.

+0

Более эффективным будет читать счетчик при запуске и хранить его в ETS (или с сохранением состояния процесса) , а затем запишите его при выключении. Однако: это не на 100% надежнее. Что произойдет, если вы «забудете» о количестве вызовов и попробуйте позвонить с более ранним номером? –

+0

Веб-служба вернет некоторую ошибку, и следующие вызовы не имеют смысла. Вот почему так важно держать счетчик в порядке. –

+0

@Roger Lipscombe –

ответ

4

Если 3 клиента одновременно вызывают функцию, функция будет увеличивать счетчик только один раз, и я получу ошибку. Как заставить функцию ждать, пока другой закончит, а затем выполнит?

Я бы использовал простой счетчик, построенный с помощью gen_server, и только получить доступ к счетчику на нем. Использование gen_server, как это, гарантирует, что вы никогда не получите условие гонки доступа к файлу.

Вот что-то, чтобы вы начали:

-module(file_counter). 
-export([start_link/0, start/0, increment/0]). 
-behaviour(gen_server). 
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). 

start_link() -> 
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). 

start() -> 
    gen_server:start({local, ?MODULE}, ?MODULE, [], []). 

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

init([]) -> 
    {ok, "file_counter.txt"}. 

handle_call(increment, _From, File) -> 
    Counter = case file:read_file(File) of 
     {ok, Binary} -> binary_to_integer(Binary); 
     {error, enoent} -> 0 
    end, 
    ok = file:write_file(File, integer_to_binary(Counter + 1)), 
    {reply, Counter, File}. 

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

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

terminate(_Reason, _State) -> 
    ok. 

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

Демо:

1> c(file_counter). 
{ok,file_counter} 
2> file_counter:start_link(). 
{ok,<0.40.0>} 
3> file_counter:increment(). 
0 
4> file_counter:increment(). 
1 
5> file_counter:increment(). 
2 
6> [ spawn_link(file_counter, increment, []) || _ <- lists:seq(1, 9998) ]. 
[<0.45.0>,<0.46.0>,<0.47.0>,<0.48.0>,<0.49.0>,<0.50.0>, 
<0.51.0>,<0.52.0>,<0.53.0>,<0.54.0>,<0.55.0>,<0.56.0>, 
<0.57.0>,<0.58.0>,<0.59.0>,<0.60.0>,<0.61.0>,<0.62.0>, 
<0.63.0>,<0.64.0>,<0.65.0>,<0.66.0>,<0.67.0>,<0.68.0>, 
<0.69.0>,<0.70.0>,<0.71.0>,<0.72.0>,<0.73.0>|...] 
7> file_counter:increment(). 
10001 

Просто позвоните file_counter:increment(), прежде чем принимать каждый вызов и использовать возвращаемое ей значение как кол.

Редактировать: Это просто быстрый модуль, который я написал. Вероятно, вы должны изменить имя файла, передав его start, start_link и init, а также не зарегистрировать процесс с именем, если хотите иметь возможность запуска нескольких копий счетчика. Код здесь действительно POC, чтобы вы начали.

(В системе с SSD-диском, я был в состоянии выполнить file_counter:increment() около 5000 раз в секунду.)

+0

Спасибо за ответ. Я действительно новичок в Erlang и до того, как я прочитал документ о gen: server, сохранит ли счетчик в случае сбоя сервера или отключения питания? –

+0

Код, который я отправил, будет записывать счетчик в файл перед каждым вызовом 'increment'. Если ваша программа выйдет из строя/питание отключится после возврата «increment» и до того, как вы сделаете запрос веб-службы, счетчик не откатится, нет. – Dogbert

Смежные вопросы