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.