code

2017年3月1日 星期三

Erlang筆記9 - 簡單client-server架構 in Erlang

Erlang processes DONT share memories

erlang processes是小型的erlang VM。這是erlang語言層級的process,不是OS層級的,所以在任何erlang VM上都能保持一致行為。

兩個erlang processes之間不share memory,這很合理,連單一erlang process之內都不能change memory data了,何況是share給另一個erlang process!

這讓scalability變得很容易,因為彼此processes是獨立的,所以增加workload就可以簡單地增加人力(processes)來因應。

不分享記憶,就不用保護記憶,就沒有lock & keys。
沒有lock & keys,就不會有一大堆相關的錯誤,特別容易出現在concurrent programming。

Error Detection

既然processes沒有share memory,怎麼辦法溝通?
靠send messages。(erlang是一個pure message-passing language),不過messages有沒有被其他人收到或是處裡的話,還要再send messages去詢問對方 (除非對方send ACK回來)。

另外一個process死掉會broadcast出來給其他processes知道,某個跟他“link關聯"的process會接手 (稱為linked pair)。這樣就能做error detection。


Concurrent Primitives

Erlang concurrent program只需要以下三個primitive operations就可以構成一個concurrent program:

1. spawn: 產生一個erlang process。如果要refer到這個產生的process的話,就靠此 operation回傳的Pid。

另一個版本為:


2. send message: 傳送message 給某一個Pid。比較特別的是 ! 是send operator


此operator "似乎" (還待證實)會回傳送出去的message,所以我們可以chaining 這個operators:

Pid1 ! Pid2 ! … ! Msg ,代表對Pid1 Pid2 ... 送出Msg 這個messageㄡ



3. receive:
如果想要等待某個message,就可以用此operation配合上pattern-matching:
receive的語法如下:


Process Example

先寫一個process專門計算面積的:

-module(area_server0).-export([loop/0]).

loop() ->
  receive    {rectangle, Width, Ht} ->
      io:format("Area of rectangle is ~p~n",[Width * Ht]),
      loop();
    {square, Side} ->
      io:format("Area of square is ~p~n", [Side * Side]),
      loop()
  end.

這個server process簡單來說就是等待message,如果match {rectangle,W,H} 或是 {square, S} pattern的話,就印出面積,然後等待下一次的message進來。

我們可以在Erlang console spawn此server process以及其receiver:

6> Pid = spawn(area_server0, loop, []).
<0.64.0>

有了Pid之後,用 ! operator來傳送符合pattern的message給Pid:

9> Pid ! {square, 10}.
Area of square is 100
{square,10}


這樣就完成了一次簡單的messaging, 可以看到console另外也印出了message,這表示 ! operator的確是回傳message (書上是說 Pid ! Msg被定義成Msg),有待查證到底哪一個對。


Client-Server Example

真的要變成一個client-server架構的話,至少還缺了client的Pid,因為沒有這個server等於沒辦法將計算結果回傳給client。

改寫server如下:

-module(area_server0).-export([loop/0]).

loop() ->
  receive    {FromPid, {rectangle, Width, Ht}} ->
      FromPid ! Width * Ht,
      loop();
    {FromPid, {square, Side}} ->
      FromPid ! Side * Side,
      loop()
  end.

可以看到pattern matching改了,而且server process也回送message 給FromPid,所以我們的client也必須要是一個Erlang process才能收到server的message,我們先把server延伸成有client能力(能夠發request也能等待回應):

rpc(ToPid, Request) ->
  ToPid ! {self(), Request},
  receive {Response} ->
    Response  end.

如果發以下指令:
1> Pid = spawn(server, loop, []).
<0.57.0>
2> server:rpc(Pid, {square, 10}).

是可以回傳面積的,可是在IntelliJ Erlang plugin的console不知道為什麼出不來,不過在一班的erlang console是可以的。


client如何只回應特定的server?

pattern matching這邊發揮美麗的特性:

rpc(ToPid, Request) ->
  ToPid ! {self(), Request},
  receive    {ToPid, Response} ->
    io:format("Area of rectangle is from ~p, response = ~p ~n",[ToPid, Response]),
    Response  end.

loop() ->
  receive    {FromPid, {rectangle, Width, Ht}} ->
    FromPid ! {self(), Width * Ht},
    loop();

    {FromPid, {square, Side}} ->
      FromPid ! {self(), Side * Side},
      loop()
  end.


沒有留言:

張貼留言