2016-07-06 2 views
3

웹 서비스에 데이터를 게시하는 Erlang 서버에 하나의 기능이 있습니다. 이 웹 서비스에는 카운터가 필요합니다 (호출 된 횟수). 나는 그들이 왜 그것을 필요로하는지 정말로 모른다. 그러나 그것은 결정적이다.Erlang에서 함수 호출 수를 계산하는 방법은 무엇입니까?

몇 가지 이유로 외부 DB를 사용할 수 없습니다. 그래서 나는 영구적으로 서버에 저장된 일부 전역 변수를 가져야한다. 나는 카운터를 파일에 저장하고, 함수가 호출 될 때마다 그것을 추출하고 증가시키는 것에 대해 생각했다.

내 목표를 달성하는보다 효율적인 방법이 있습니까?

편집 이 기능은 동시에 여러 클라이언트에서 호출, 그래서 이것은 또 다른 문제를 추가 할 수있다 : 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_linkinit에 파일 이름을 전달하여 파일 이름을 구성 할 수있을뿐만 아니라 카운터의 여러 복사본을 실행할 수 있도록하려면 프로세스 이름을 등록하지 않아야합니다. 이 코드는 실제로 시작하기위한 POC입니다.

은 (AN SSD 디스크가있는 시스템에서 I 초당 file_counter:increment() 약 5000 회 수행 할 수 있었다.)

+0

답변 해 주셔서 감사합니다. 나는 Erlang을 처음 접했고 gen : server에 관한 문서를 읽기 전에 서버가 부서 지거나 전원이 꺼질 때 카운터를 저장할 것입니까? –

+0

내가 올린 코드는'increment'를 호출 할 때마다 파일에 카운터를 씁니다. 프로그램이 충돌하거나'increment'가 반환 된 후에 전원이 꺼지면 웹 서비스 요청을하기 전에 카운터는 롤백되지 않습니다. – Dogbert

관련 문제