2016-07-06 17 views
3

Ich habe eine Funktion auf meinem Erlang-Server, der Daten an den Web-Service sendet. Dieser Webservice benötigt den Zähler (wie oft er aufgerufen wurde). Ich weiß nicht wirklich, warum sie es brauchen, aber es ist entscheidend.Wie man die Anzahl der Funktionsaufrufe in Erlang zählt?

Ich kann die externe DB aus bestimmten Gründen nicht verwenden. Also muss ich eine globale Variable dauerhaft auf dem Server gespeichert haben. Ich dachte darüber nach, Zähler gegen eine Datei zu speichern, und extrahiere und inkrementiere sie jedes Mal, wenn die Funktion aufgerufen wird.

Gibt es einen effizienteren Weg, um mein Ziel zu erreichen?

EDIT Die Funktion kann von vielen verschiedenen Clients gleichzeitig aufgerufen werden, so fügt dies ein anderes Problem: Wenn drei Kunden die Funktion gleichzeitig aufrufen, nur einmal der Zähler zählt hoch, Funktion und ich werde einen Fehler. Wie lasse ich die Funktion warten, bis eine andere beendet und dann ausgeführt wird?

Vielen Dank im Voraus.

+0

Effizienter wäre den Zähler beim Start zu lesen und speichern sie in ETS (oder ein Stateful-Prozess) , und schreibe es dann beim Herunterfahren zurück. Jedoch: das ist nicht 100% zuverlässig. Was passiert, wenn Sie mehrere Anrufe "vergessen" und versuchen, mit einer früheren Nummer anzurufen? –

+0

Der Webservice gibt eine Art von Fehler zurück und die folgenden Aufrufe ergeben keinen Sinn. Deshalb ist es so wichtig, den Zähler richtig zu halten. –

+0

@Roger Lipscombe –

Antwort

4

Wenn 3 Clients die Funktion gleichzeitig aufrufen, erhöht die Funktion den Zähler nur einmal und ich erhalte einen Fehler. Wie lasse ich die Funktion warten, bis eine andere beendet und dann ausgeführt wird?

Ich würde einen einfachen Zähler mit gen_server gebaut und nur den Zähler auf der Festplatte von ihm zugreifen. Mit einer gen_server wie dieser würde sichergestellt, dass Sie nie eine Dateizugriff Race-Bedingung erhalten.

Hier ist etwas für den Anfang:

-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}. 

Demo:

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 

Rufen Sie file_counter:increment() vor jedem Gespräch und den Wert, indem sie es als die Zählung wieder verwenden.

Edit: Dies ist nur ein schnelles Modul, das ich schrieb. Sie sollten den Dateinamen möglicherweise konfigurierbar machen, indem Sie ihn an start, start_link und übergeben, und den Prozess nicht mit einem Namen registrieren, wenn Sie mehrere Kopien des Zählers ausführen möchten. Der Code hier ist wirklich ein POC, um loszulegen.

(Auf einem System mit einer SSD Festplatte, war ich in der Lage file_counter:increment() etwa 5000-mal pro Sekunde auszuführen.)

+0

Danke für die Antwort. Ich bin wirklich neu in Erlang und bevor ich doc über gen: server lese, wird es den Zähler speichern, falls der Server zerbricht oder der Strom ausgeht? –

+0

Der Code, den ich geschrieben habe, schreibt den Zähler in die Datei, bevor jeder Aufruf von 'increment' zurückkehrt. Wenn Ihr Programm abstürzt/der Strom ausfällt, nachdem "increment" zurückgegeben wurde und bevor Sie die Web-Service-Anfrage stellen, wird der Zähler nicht zurückgesetzt, nein. – Dogbert