2016-07-06 89 views
3

我在將數據發佈到Web服務的Erlang服務器上有一個函數。這個Web服務需要計數器(它被調用了多少次)。我真的不知道他們爲什麼需要它,但這是至關重要的。如何統計Erlang中的函數調用次數?

由於某些原因,我無法使用外部數據庫。所以我需要永久存儲在服務器上的全局變量。我曾考慮將計數器保存到文件中,每次調用函數時都會提取並增加它。

有沒有更高效的方法來實現我的目標?

編輯 功能可以在同一時間許多不同的客戶端調用,所以這增加了另外一個問題:如果有3個客戶端同時調用的函數,函數將遞增計數器只有一次,我會得到一個錯誤。我如何讓這個函數等待另一個完成然後執行?

在此先感謝。

+0

更有效的。將在啓動時讀取的計數器,並將其存儲在ETS(或有狀態處理) ,然後在關機時寫回。但是:這不是100%可靠的。如果您「忘記」了一些電話並嘗試使用較早的號碼撥打電話,會發生什麼情況? –

+0

Web服務將返回某種錯誤,以下調用將不起作用。這就是爲什麼保持櫃檯正確的關鍵。 –

+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_linkinit來配置文件名,並且如果您希望能夠運行計數器的多個副本,則不要使用名稱註冊進程。這裏的代碼真的是一個POC,讓你開始。

(在與SSD磁盤的系統,我能夠每秒執行file_counter:increment()約5000倍。)

+0

謝謝你的回答。我對Erlang非常陌生,在閱讀有關gen:server的文檔之前,它是否會保存計數器以防服務器崩潰或電源消失? –

+0

我發佈的代碼會在每次調用'increment'返回之前將計數器寫入文件。如果程序在'increment'返回之後並且在發出Web服務請求之前崩潰/斷電,那麼計數器將不會回滾,不會。 – Dogbert