2015年11月30日

使用Erlang產生大量的process

假設我們寫了一個檢查登入帳號、密碼的function,想要測試如果該function 被大量同時呼叫的話,系統會不會發生什麼狀況?這一方面也可以稍微測試一下資料庫同時能撐的連線量,因此我們可以撰寫下列的程式碼來做測試。

下列為簡易的檢查密碼的function(註:為求簡潔,忽略了帳號不存在的狀況)

%至mnesia db取帳號後檢查密碼
check(Id, Pwd)->
    [Account] = mnesia:dirty_read(account, Id),
    case Account#account.pwd of
        Pwd->
            true;
        _ ->
            false
    end

下列為產生大量concurrent process來呼叫check(Id,Pwd)

stress_test(NProcs)->
    CtrF = fun() ->
        check(UserId, Pwd) %呼叫檢查帳號、密碼的function
    end,
    [spawn(CtrF) || _X <- lists:duplicate(NProcs, 0)].

上述的程式碼會產生數量為NProcs的Process來執行CtrF 的function,重點會是在最後一行 [spawn(CtrF) || _ <- lists:duplicate(NProcs, 0)],這是使用erlang的list comprehensions來控制程式的執行(有點類似java的for迴圈),我們先了解一下最簡單的list comprehensions,我們先看下列的code,下列的code會在console上印出1~6,

[io:format("~p~n",[X]) || X <- [1,2,3,4,5,6]]

上述執行後畫面上會出現

1
2
3
4
5
6
[ok,ok,ok,ok,ok,ok]

說明: 有1個List,裡面有6個成員分別是1,2,3,4,5,6,接著我們一次取一個成員出來並將之bind到X,每次取出成員後都會執行io:format將X的值給印出來

接下來我們來看要如何產生list裡的內容?假設我們要產生1個包含10個0的list,可以使用lists:duplicate/2,如下

lists:duplicate(10, 0)
執行結果為
[0,0,0,0,0,0,0,0,0,0]

最後我們再回頭來看 [spawn(CtrF) || _X <- lists:duplicate(NProcs, 0)].分解步驟如下

  1. 假設NProcs是1萬(我們要產生1萬個process來同時呼叫check(Id, Pwd))
  2. 首先用lists:duplicate/2來產生含1萬個0的list ex[0,0,0,0.....0],要用1來取代0也可以,反正重點在於list的長度而非list的內容
  3. 每次從list取出1個,並將bind到_X。為何X前要加底線?因為我們並不在意從list取出來的值是多少,所以變數X並不會用到,所以要在X前加底線,否則編譯器會出現緊告
  4. 最後每次都會執行spawn(CtrF),spawn在erlang裡是指會建立1個concurrent process,並執行CtrF function

短短幾行就可達到想要的處理邏輯,這就是Erlang語言的特色,不過還是得先適應function programming的思考方式才行