在前兩個blog中,已經說了Erlang的歷史、應用場景、特點,這次主要演示一個Wordcount的示例,就是給定一個文本文件,統計這個文本文件中的單詞以及該單詞出現的次數。
今天和群友們討論了一個問題,突然一下子就上升到哲學角度上了,裝逼裝大發了。
PS:圖片中有錯別字,%s/財務和其他9個月/財務和其他9個人/g
不過真心想說的一點是,把Erlang系統,映射到現實中,很多奇葩問題,就能迎刃而解了。所以,在下面的簡要設計中,我就盡可能的代入一下現實世界吧。
環境安裝
mac 的話,用brew就行了:
brew install erlang
centos 用yum:
yum install erlang
Ubuntu 用apt-get:
apt-get install erlang
源碼安裝:
$ tar -zxf otp_src_17.4.tar.gz (官網上下載源碼)
cd otp_src_17.4
./configure && make -j && make install
注意,確保OpenSSL依賴庫已經安裝成功,建議安裝wxWidgets庫。
安裝好之后,在控制台terminal下輸入"erl",大概是這樣的效果:
簡要設計
基本流程設計大概是這樣的
1,使用本地計算機的多個終端模擬多個Erlang節點
2,使用一個Erlang進程作為主進程,並且按行讀取文本文件(這種讀取方式不見得是最好的,后續會細說)
3,主進程按照每行創建一個Erlang進程作為邏輯處理進程
4,邏輯處理進程進行實際的邏輯執行並將處理結果發送給主進程
5,主進程進行匯總並顯示一定數量的單詞以及該單詞出現的次數
|---------------------------------------------------------------------------------------------------------------
| 映射到現實世界中,怎么講?
| 1,team leader拆分項目需求點
| 2,leader按照每個需求點指派給一個team member
| 3,team member搞定各自的需求點並將結果告知leader
| 4,team leader匯總member的結果並項目結項
|---------------------------------------很自然的現實映射------------------------------------------------------
那那那,注意了,每個邏輯處理進程是可以分布在不同的Erlang節點上的,換言之,如果是在實際的環境中,邏輯處理進程是可以分布在不同的物理機器上。
對於主進程而言,主要做的事情包括:
1,按行讀取文本文件
2,創建邏輯處理進程
3,等待邏輯進程的結果返回
4,結果都返回之后,進行匯總並顯示最終處理結果
對於邏輯處理進程而言,需要做的事情有:
1,按照指定的字符來切割主進程發送的文本
2,按照單詞進行初步的計算和匯總
3,將處理結果發送給主進程
注意注意注意,主進程和邏輯處理進程之間的任務分派,結果傳遞,都是使用“消息通信的方式”,映射到現實生活中,也很好理解,team leader和team member之間進行溝通就是用說話的方式,人和人之間無法共享彼此的記憶。
簡要代碼分析
代碼分析的目的是為了說明Erlang的方便性,看不太懂代碼的同學不用着急,后面的blog會一點一點慢慢解釋的。
主體代碼就80多行的代碼。
首先模塊的頭部:

1 -module(wordcount). 2 3 -include_lib("stdlib/include/ms_transform.hrl"). 4 5 -export([start/2]). 6 -export([worker_execute/3]). 7 -export([insert_main_ets_table/2]). 8 9 -define(BUFSIZE, 1048576). 10 -define(FILEMODE, [binary, read, {read_ahead, ?BUFSIZE}]). 11 -define(SPLIT_CHAR_LIST, [<<"\n">>, <<" ">>, <<"\"">>, 12 <<"!">>, <<"&">>, <<".">>, 13 <<",">>, <<":">>, <<"--">>]).
定義了模塊名,導出的函數,一些宏,L11是切割文本的字符。
接着是模塊的入口函數,start/2 函數:

1 start(_, []) -> 2 "NodeList can not is empty"; 3 start(FileName, NodeList) -> 4 %% open the file 5 {ok, FD} = file:open(FileName, ?FILEMODE), 6 MainEts = 7 ets:new(mainets, [public, 8 {write_concurrency, true}]), 9 TaskOwnerNode = erlang:node(), 10 TaskResList = 11 spawn_worker_process(file:read_line(FD), FD, NodeList, 12 TaskOwnerNode, MainEts, []), 13 [begin 14 {WorkerNode, WorkerPid} = task:await(TaskRef), 15 io:format(" ** One Task return it's result from Node : ~p, worker pid : ~p ~n", 16 [WorkerNode, WorkerPid]), 17 ok 18 end || TaskRef <- TaskResList], 19 Ms = ets:fun2ms(fun({_, Times} = WordCount) when Times > 10 -> WordCount end), 20 FinalResult = ets:select(MainEts, Ms), 21 true = ets:delete(MainEts), 22 FinalResult.
start/2 函數會先打開指定的文本文件,然后創建一張ets表,用來做“最后的結果匯總”,L11處是調用了另一個函數用來“生成邏輯處理進程”,L13處是等待邏輯處理進程的返回結果,L19是用來做最后匯總並顯示一定數量(出現次數大於10次)的單詞以及該單詞出現的個數。
“生成邏輯處理進程”的函數是這樣的:

1 spawn_worker_process(eof, _FD, _NodeList, _TaskOwnerNode, _MainEts, Res) -> 2 Res; 3 spawn_worker_process({ok, Data}, FD, NodeList, TaskOwnerNode, MainEts, Res) -> 4 Node = get_random_node(NodeList), 5 TaskRef = task:async(Node, ?MODULE, worker_execute, 6 [Data, TaskOwnerNode, MainEts]), 7 spawn_worker_process(file:read_line(FD), FD, NodeList, 8 TaskOwnerNode, MainEts, [TaskRef | Res]).
使用遍歷的方式讀取文本文件,然后從“傳入的node列表”參數中,隨機取一個node,並將該文本作為參數,在node上執行worker_execute函數。
worker_execute函數:

1 worker_execute(Data, TaskOwnerNode, MainEts) -> 2 %% <<"this is an example\n">> 3 %% binary split 4 [_ | WordList] = 5 lists:reverse(binary:split(Data, ?SPLIT_CHAR_LIST, [global])), 6 TempEts = ets:new(tempets, [set]), 7 ok = lists:foreach(fun(UK) -> 8 case K = string:to_lower(erlang:binary_to_list(UK)) of 9 [] -> 10 ingore; 11 _ -> 12 true = etsCountAdd(TempEts, K, {2, 1}, {K, 1}) 13 end 14 end, WordList), 15 rpc:call(TaskOwnerNode, ?MODULE, 16 insert_main_ets_table, [MainEts, ets:tab2list(TempEts)]), 17 ets:delete(TempEts), 18 {erlang:node(), erlang:self()}. 19 20 insert_main_ets_table(MainEts, WorkerResult) -> 21 [etsCountAdd(MainEts, K, {2, V}, {K, V}) || {K, V} <- WorkerResult], 22 ok. 23 24 etsCountAdd(EtsTab, Key, UpdateValue, InsertValue) -> 25 try ets:insert_new(EtsTab, InsertValue) of 26 false -> 27 ets:update_counter(EtsTab, Key, UpdateValue), 28 true; 29 _ -> 30 true 31 catch _:_ -> 32 false 33 end.
L4是按照預先設定的字符切割文本,然后創建一張臨時的ets表用來做初步的匯總,然后使用ets update_counter 的方式將{word, times}寫入到臨時的ets表中,然后通過rpc call 調用主進程所在node 執行insert_main_ets_table 的操作,將邏輯處理進程的處理結果返回給主進程。
至此,代碼基本上就是這些。接下來,來執行以下,看看究竟是怎樣的效果。
效果演示
先啟動4個Erlang節點,名字叫做't1@127.0.0.1','t2@127.0.0.1','t3@127.0.0.1','t4@127.0.0.1',啟動之后,大概是這樣的。
然后在其中任意一個節點上調用wordcount:start/2 函數。
從上面的紅框框中,可以看出,這些邏輯處理進程是隨機分布在不同的Erlang節點上的。
然后,我們看看最終的統計結果:
1 [{"he",22}, 2 {"a",54}, 3 {"of",50}, 4 {"is",13}, 5 {"and",59}, 6 {"her",23}, 7 {"vivienne",13}, 8 {"the",92}, 9 {"it",12}, 10 {"in",34}, 11 {"you",42}, 12 {"was",15}, 13 {"she",22}, 14 {"his",22}, 15 {"with",23}, 16 {"him",13}, 17 {"have",11}, 18 {"as",13}, 19 {"will",12}, 20 {"to",42}, 21 {"said",24}, 22 {"i",36}, 23 {"hartley",29}, 24 {"be",11}, 25 {"that",25}, 26 {"at",13}]
OK,就是這么簡單,並發進程就是能這么用,分布式節點就是這么方便。
附
全部的主體代碼:

1 -module(wordcount). 2 3 -include_lib("stdlib/include/ms_transform.hrl"). 4 5 -export([start/2]). 6 -export([worker_execute/3]). 7 -export([insert_main_ets_table/2]). 8 9 -define(BUFSIZE, 1048576). 10 -define(FILEMODE, [binary, read, {read_ahead, ?BUFSIZE}]). 11 -define(SPLIT_CHAR_LIST, [<<"\n">>, <<" ">>, <<"\"">>, 12 <<"!">>, <<"&">>, <<".">>, 13 <<",">>, <<":">>, <<"--">>]). 14 15 start(_, []) -> 16 "NodeList can not is empty"; 17 start(FileName, NodeList) -> 18 %% open the file 19 {ok, FD} = file:open(FileName, ?FILEMODE), 20 MainEts = 21 ets:new(mainets, [public, 22 {write_concurrency, true}]), 23 TaskOwnerNode = erlang:node(), 24 TaskResList = 25 spawn_worker_process(file:read_line(FD), FD, NodeList, 26 TaskOwnerNode, MainEts, []), 27 [begin 28 {WorkerNode, WorkerPid} = task:await(TaskRef), 29 io:format(" ** One Task return it's result from Node : ~p, worker pid : ~p ~n", 30 [WorkerNode, WorkerPid]), 31 ok 32 end || TaskRef <- TaskResList], 33 Ms = ets:fun2ms(fun({_, Times} = WordCount) when Times > 10 -> WordCount end), 34 FinalResult = ets:select(MainEts, Ms), 35 true = ets:delete(MainEts), 36 FinalResult. 37 38 spawn_worker_process(eof, _FD, _NodeList, _TaskOwnerNode, _MainEts, Res) -> 39 Res; 40 spawn_worker_process({ok, Data}, FD, NodeList, TaskOwnerNode, MainEts, Res) -> 41 Node = get_random_node(NodeList), 42 TaskRef = task:async(Node, ?MODULE, worker_execute, 43 [Data, TaskOwnerNode, MainEts]), 44 spawn_worker_process(file:read_line(FD), FD, NodeList, 45 TaskOwnerNode, MainEts, [TaskRef | Res]). 46 47 get_random_node(NodeList) -> 48 NodeListLen = erlang:length(NodeList), 49 lists:nth(get_random(NodeListLen), NodeList). 50 51 get_random(Num) -> 52 {Res, _} = random:uniform_s(Num, erlang:now()), 53 Res. 54 55 worker_execute(Data, TaskOwnerNode, MainEts) -> 56 %% <<"this is an example\n">> 57 %% binary split 58 [_ | WordList] = 59 lists:reverse(binary:split(Data, ?SPLIT_CHAR_LIST, [global])), 60 TempEts = ets:new(tempets, [set]), 61 ok = lists:foreach(fun(UK) -> 62 case K = string:to_lower(erlang:binary_to_list(UK)) of 63 [] -> 64 ingore; 65 _ -> 66 true = etsCountAdd(TempEts, K, {2, 1}, {K, 1}) 67 end 68 end, WordList), 69 rpc:call(TaskOwnerNode, ?MODULE, 70 insert_main_ets_table, [MainEts, ets:tab2list(TempEts)]), 71 ets:delete(TempEts), 72 {erlang:node(), erlang:self()}. 73 74 insert_main_ets_table(MainEts, WorkerResult) -> 75 [etsCountAdd(MainEts, K, {2, V}, {K, V}) || {K, V} <- WorkerResult], 76 ok. 77 78 etsCountAdd(EtsTab, Key, UpdateValue, InsertValue) -> 79 try ets:insert_new(EtsTab, InsertValue) of 80 false -> 81 ets:update_counter(EtsTab, Key, UpdateValue), 82 true; 83 _ -> 84 true 85 catch _:_ -> 86 false 87 end.
說明,其中的一個task模塊,是我自己封裝的,在這:https://github.com/redink-toys/task
文本文件:

IN GILT letters on the ground glass of the door of room No. 962 were the words: "Robbins & Hartley, Brokers." The clerks had gone. It was past five, and with the solid tramp of a drove of prize Percherons, scrub- women were invading the cloud-capped twenty-story office building. A puff of red-hot air flavoured with lemon peelings, soft-coal smoke and train oil came in through the half-open windows. Robbins, fifty, something of an overweight beau, and addicted to first nights and hotel palm-rooms, pretended to be envious of his partner's commuter's joys. "Going to be something doing in the humidity line to-night," he said. "You out-of-town chaps will be the people, with your katydids and moonlight and long drinks and things out on the front porch." Hartley, twenty-nine, serious, thin, good-looking, ner- vous, sighed and frowned a little. "Yes," said he, "we always have cool nights in Floral- hurst, especially in the winter." A man with an air of mystery came in the door and went up to Hartley. "I've found where she lives," he announced in the portentous half-whisper that makes the detective at work a marked being to his fellow men. Hartley scowled him into a state of dramatic silence and quietude. But by that time Robbins had got his cane and set his tie pin to his liking, and with a debonair nod went out to his metropolitan amusements. "Here is the address," said the detective in a natural tone, being deprived of an audience to foil. Hartley took the leaf torn out of the sleuth's dingy memorandum book. On it were pencilled the words "Vivienne Arlington, No. 341 East --th Street, care of Mrs. McComus." "Moved there a week ago," said the detective. "Now, if you want any shadowing done, Mr. Hartley, I can do you as fine a job in that line as anybody in the city. It will be only $7 a day and expenses. Can send in a daily typewritten report, covering -- " "You needn't go on," interrupted the broker. "It isn't a case of that kind. I merely wanted the address. How much shall I pay you?" "One day's work," said the sleuth. "A tenner will cover it." Hartley paid the man and dismissed him. Then he left the office and boarded a Broadway car. At the first large crosstown artery of travel he took an eastbound car that deposited him in a decaying avenue, whose ancient structures once sheltered the pride and glory of the town. Walking a few squares, he came to the building that he sought. It was a new flathouse, bearing carved upon its cheap stone portal its sonorous name, "The Vallambrosa." Fire-escapes zigzagged down its front -- these laden with household goods, drying clothes, and squalling children evicted by the midsummer heat. Here and there a pale rubber plant peeped from the miscellaneous mass, as if wondering to what kingdom it belonged -- vegetable, animal or artificial. Hartley pressed the "McComus" button. The door latch clicked spasmodically -- now hospitably, now doubt- fully, as though in anxiety whether it might be admitting friends or duns. Hartley entered and began to climb the stairs after the manner of those who seek their friends in city flat-houses -- which is the manner of a boy who climbs an apple-tree, stopping when he comes upon what he wants. On the fourth floor he saw Vivienne standing in an open door. She invited him inside, with a nod and a bright, genuine smile. She placed a chair for him near a window, and poised herself gracefully upon the edge of one of those Jekyll-and-Hyde pieces of furniture that are masked and mysteriously hooded, unguessable bulks by day and inquisitorial racks of torture by night. Hartley cast a quick, critical, appreciative glance at her before speaking, and told himself that his taste in choosing had been flawless. Vivienne was about twenty-one. She was of the purest Saxon type. Her hair was a ruddy golden, each filament of the neatly gathered mass shining with its own lustre and delicate graduation of colour. In perfect harmony were her ivory-clear complexion and deep sea-blue eyes that looked upon the world with the ingenuous calmness of a mermaid or the pixie of an undiscovered mountain stream. Her frame was strong and yet possessed the grace of absolute naturalness. And yet with all her North- ern clearness and frankness of line and colouring, there seemed to be something of the tropics in her -- something of languor in the droop of her pose, of love of ease in her ingenious complacency of satisfaction and comfort in the mere act of breathing -- something that seemed to claim for her a right as a perfect work of nature to exist and be admired equally with a rare flower or some beauti- ful, milk-white dove among its sober-hued companions. She was dressed in a white waist and dark skirt - that discreet masquerade of goose-girl and duchess. "Vivienne," said Hartley, looking at her pleadingly, "you did not answer my last letter. It was only by nearly a week's search that I found where you had moved to. Why have you kept me in suspense when you knew how anxiously I was waiting to see you and hear from you?" The girl looked out the window dreamily. "Mr. Hartley," she said hesitatingly, "I hardly know what to say to you. I realize all the advantages of your offer, and sometimes I feel sure that I could be contented with you. But, again, I am doubtful. I was born a city girl, and I am afraid to bind myself to a quiet sub- urban life." "My dear girl," said Hartley, ardently, "have I not told you that you shall have everything that your heart can desire that is in my power to give you? You shall come to the city for the theatres, for shopping and to visit your friends as often as you care to. You can trust me, can you not?" "To the fullest," she said, turning her frank eyes upon him with a smile. "I know you are the kindest of men, and that the girl you get will be a lucky one. I learned all about you when I was at the Montgomerys'." "Ah!" exclaimed Hartley, with a tender, reminiscent light in his eye; "I remember well the evening I first saw you at the Montgomerys'. Mrs. Montgomery was sound- ing your praises to me all the evening. And she hardly did you justice. I shall never forget that supper. Come, Vivienne, promise me. I want you. You'll never regret coming with me. No one else will ever give you as pleasant a home." The girl sighed and looked down at her folded hands. A sudden jealous suspicion seized Hartley. "Tell me, Vivienne," he asked, regarding her keenly, "is there another -- is there some one else ?" A rosy flush crept slowly over her fair cheeks and neck. "You shouldn't ask that, Mr. Hartley," she said, in some confusion. "But I will tell you. There is one other -- but he has no right -- I have promised him nothing." "His name?" demanded Hartley, sternly. "Townsend." "Rafford Townsend!" exclaimed Hartley, with a grim tightening of his jaw. "How did that man come to know you? After all I've done for him -- " "His auto has just stopped below," said Vivienne, bending over the window-sill. "He's coming for his answer. Oh I don't know what to do!" The bell in the flat kitchen whirred. Vivienne hurried to press the latch button. "Stay here," said Hartley. "I will meet him in the hall." Townsend, looking like a Spanish grandee in his light tweeds, Panama hat and curling black mustache, came up the stairs three at a time. He stopped at sight of Hartley and looked foolish. "Go back," said Hartley, firmly, pointing downstairs with his forefinger. "Hullo!" said Townsend, feigning surprise. "What's up? What are you doing here, old man?" "Go back," repeated Hartley, inflexibly. "The Law of the Jungle. Do you want the Pack to tear you in pieces? The kill is mine." "I came here to see a plumber about the bathroom connections," said Townsend, bravely. "All right," said Hartley. "You shall have that lying plaster to stick upon your traitorous soul. But, go back." Townsend went downstairs, leaving a bitter word to be wafted up the draught of the staircase. Hartley went back to his wooing. "Vivienne," said he, masterfully. "I have got to have you. I will take no more refusals or dilly-dallying." "When do you want me?" she asked. "Now. As soon as you can get ready." She stood calmly before him and looked him in the eye. "Do you think for one moment," she said, "that I would enter your home while Héloise is there?" Hartley cringed as if from an unexpected blow. He folded his arms and paced the carpet once or twice. "She shall go," he declared grimly. Drops stood upon his brow. "Why should I let that woman make my life miserable? Never have I seen one day of freedom from trouble since I have known her. You are right, Vivienne. Héloise must be sent away before I can take you home. But she shall go. I have decided. I will turn her from my doors." "When will you do this?" asked the girl. Hartley clinched his teeth and bent his brows together. "To-night," he said, resolutely. "I will send her away to-night." "Then," said Vivienne, "my answer is 'yes.' Come for me when you will." She looked into his eyes with a sweet, sincere light in her own. Hartley could scarcely believe that her sur- render was true, it was so swift and complete. "Promise me," he said feelingly, "on your word and honour." "On my word and honour," repeated Vivienne, softly. At the door he turned and gazed at her happily, but yet as one who scarcely trusts the foundations of his joy. "To-morrow," he said, with a forefinger of reminder uplifted. "To-morrow," she repeated with a smile of truth and candour. In an hour and forty minutes Hartley stepped off the train at Floralhurst. A brisk walk of ten minutes brought him to the gate of a handsome two-story cottage set upon a wide and well-tended lawn. Halfway to the house he was met by a woman with jet-black braided hair and flowing white summer gown, who half strangled him without apparent cause. When they stepped into the hall she said: "Mamma's here. The auto is coming for her in half an hour. She came to dinner, but there's no dinner." "I've something to tell you," said Hartley. "I thought to break it to you gently, but since your mother is here we may as well out with it." He stooped and whispered something at her ear. His wife screamed. Her mother came running into the hall. The dark-haired woman screamed again- the joyful scream of a well-beloved and petted woman. "Oh, mamma!" she cried ecstatically, "what do you think? Vivienne is coming to cook for us! She is the one that stayed with the Montgomerys a whole year. And now, Billy, dear," she concluded, "you must go right down into the kitchen and discharge Héloise. She has been drunk again the whole day long."
總結
並發進程
分布式
對了,上面有提到,“按行讀取文本文件”不見得是最好的方式。如果一個文本文件中有很多行,但是每行的文本又非常小,就有可能出現問題。本blog的示例,主要是為了演示Erlang編程語言在並發進程和分布式特點上的優勢。在實際的場景中,斷然需要做一些結構上的調整。歡迎討論。
“Erlang基礎 -- 介紹”這次blog應該就是一個換行符,下一次主要開始shell系統、變量和常見的數據類型。