धूमकेतु - 1,000,000 उपयोगकर्ताओं के भार के साथ मोचीवेब के लिए आवेदन। भाग 2/3

भाग 1

भाग ३



भाग 1 में, हमने एक (थोड़ा बेकार) mochiweb एप्लिकेशन बनाया जो हर 10 सेकंड में ग्राहकों को एक संदेश भेजता है। हमने लिनक्स कर्नेल की स्थापना की, और मेमोरी उपयोग की जांच के लिए कई कनेक्शन स्थापित करने के लिए एक उपकरण बनाया। हमें पता चला कि प्रत्येक कनेक्शन के लिए लगभग 45 Kb की आवश्यकता होती है।



भाग 2 में, हम अपने एप्लिकेशन को कुछ उपयोगी में बदल देंगे, और मेमोरी की खपत को कम करेंगे:

• लॉगिन / लॉगआउट / एपीआई भेजने के साथ एक संदेश राउटर को लागू करना;

• राउटर के साथ काम करने के लिए मोचीवेब एप्लिकेशन अपडेट करें;

• वितरित एर्गैंग सिस्टम को स्थापित करना, इसलिए हम विभिन्न नोड्स पर राउटर चला सकते हैं;

• बड़ी संख्या में संदेशों के साथ राउटर परीक्षण उपकरण बनाना;

• मेमोरी को बचाने के लिए मोचीवब एप्लिकेशन को अनुकूलित करते हुए, 24 घंटे के लिए मेमोरी उपयोग को शेड्यूल करें।



इसका मतलब है कि हम संदेश वितरण तर्क और मोचीवेब एप्लिकेशन को अलग कर देंगे। भाग 1 से बाढ़ की उपयोगिता के साथ मिलकर, हम औद्योगिक के करीब स्थितियों में आवेदन के संचालन का परीक्षण कर सकते हैं।

संदेश राउटर लागू करना



राउटर API में केवल 3 फ़ंक्शन होते हैं:

• लॉगिन (आईडी, पीआईडी) संदेश प्राप्त करने के लिए प्रक्रिया को पंजीकृत करता है;

• लॉगआउट (पीआईडी) संदेश प्राप्त करना बंद कर देता है;

• भेजें (Id, Msg) क्लाइंट को संदेश भेजता है।

ध्यान दें कि एक प्रक्रिया के लिए अलग-अलग आईडी से लॉग इन करना संभव है।



यह उदाहरण राउटर मॉड्यूल Pids और Ids के बीच द्विदिश नक्शे को संग्रहीत करने के लिए 2 ets तालिकाओं का उपयोग करता है। (#state प्रविष्टि में pid2id और id2pid):

-module(router). -behaviour(gen_server). -export([start_link/0]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([send/2, login/2, logout/1]). -define(SERVER, global:whereis_name(?MODULE)). % will hold bidirectional mapping between id <–> pid -record(state, {pid2id, id2pid}). start_link() -> gen_server:start_link({global, ?MODULE}, ?MODULE, [], []). % sends Msg to anyone logged in as Id send(Id, Msg) -> gen_server:call(?SERVER, {send, Id, Msg}). login(Id, Pid) when is_pid(Pid) -> gen_server:call(?SERVER, {login, Id, Pid}). logout(Pid) when is_pid(Pid) -> gen_server:call(?SERVER, {logout, Pid}). init([]) -> % set this so we can catch death of logged in pids: process_flag(trap_exit, true), % use ets for routing tables {ok, #state{ pid2id = ets:new(?MODULE, [bag]), id2pid = ets:new(?MODULE, [bag]) } }. handle_call({login, Id, Pid}, _From, State) when is_pid(Pid) -> ets:insert(State#state.pid2id, {Pid, Id}), ets:insert(State#state.id2pid, {Id, Pid}), link(Pid), % tell us if they exit, so we can log them out io:format("~w logged in as ~w\n",[Pid, Id]), {reply, ok, State}; handle_call({logout, Pid}, _From, State) when is_pid(Pid) -> unlink(Pid), PidRows = ets:lookup(State#state.pid2id, Pid), case PidRows of [] -> ok; _ -> IdRows = [ {I,P} || {P,I} <- PidRows ], % invert tuples % delete all pid->id entries ets:delete(State#state.pid2id, Pid), % and all id->pid [ ets:delete_object(State#state.id2pid, Obj) || Obj <- IdRows ] end, io:format("pid ~w logged out\n",[Pid]), {reply, ok, State}; handle_call({send, Id, Msg}, _From, State) -> % get pids who are logged in as this Id Pids = [ P || { _Id, P } <- ets:lookup(State#state.id2pid, Id) ], % send Msg to them all M = {router_msg, Msg}, [ Pid ! M || Pid <- Pids ], {reply, ok, State}. % handle death and cleanup of logged in processes handle_info(Info, State) -> case Info of {'EXIT', Pid, _Why} -> % force logout: handle_call({logout, Pid}, blah, State); Wtf -> io:format("Caught unhandled message: ~w\n", [Wtf]) end, {noreply, State}. handle_cast(_Msg, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}.
      
      







Mochiweb आवेदन अद्यतन



मान लेते हैं कि उपयोगकर्ता URL के आधार पर एक पूर्णांक आईडी द्वारा दर्शाया गया है, जिसके साथ यह mochiweb से जुड़ता है और इस पहचानकर्ता को संदेश राउटर के साथ पंजीकृत करने के लिए उपयोग करता है। हर 10 सेकंड में अवरुद्ध होने के बजाय, राउइके से संदेश प्राप्त होने पर मोचीवेब अवरुद्ध हो जाता है, और क्लाइंट को हर अनुरोध के लिए एक HTTP संदेश भेजता है जो राउटर इसे भेजता है:



• क्लाइंट http: // localhost: 8000 / test / 123 के माध्यम से mochiweb से जुड़ता है;

• Mochiweb आवेदन संदेश राउटर में पहचानकर्ता '123' के साथ इस संबंध के लिए पिड पंजीकृत करता है;

• यदि आप '123' पते पर राउटर के लिए एक संदेश भेजते हैं, तो इसे सही मोचीवब प्रक्रिया में भेजा जाएगा और इस उपयोगकर्ता के लिए ब्राउज़र में दिखाई देगा।



यहाँ mochiconntest_web.erl का अद्यतन संस्करण है:

 -module(mochiconntest_web). -export([start/1, stop/0, loop/2]). %% External API start(Options) -> {DocRoot, Options1} = get_option(docroot, Options), Loop = fun (Req) -> ?MODULE:loop(Req, DocRoot) end, % we'll set our maximum to 1 million connections. (default: 2048) mochiweb_http:start([{max, 1000000}, {name, ?MODULE}, {loop, Loop} | Options1]). stop() -> mochiweb_http:stop(?MODULE). loop(Req, DocRoot) -> "/" ++ Path = Req:get(path), case Req:get(method) of Method when Method =:= 'GET'; Method =:= 'HEAD' -> case Path of "test/" ++ Id -> Response = Req:ok({"text/html; charset=utf-8", [{"Server","Mochiweb-Test"}], chunked}), % login using an integer rather than a string {IdInt, _} = string:to_integer(Id), router:login(IdInt, self()), feed(Response, IdInt, 1); _ -> Req:not_found() end; 'POST' -> case Path of _ -> Req:not_found() end; _ -> Req:respond({501, [], []}) end. feed(Response, Id, N) -> receive {router_msg, Msg} -> Html = io_lib:format("Recvd msg #~w: '~s'", [N, Msg]), Response:write_chunk(Html) end, feed(Response, Id, N+1). %% Internal API get_option(Option, Options) -> {proplists:get_value(Option, Options), proplists:delete(Option, Options)}.
      
      







यह काम करता है!



अब चलो सब कुछ क्रम में करते हैं - हम 2 एर्गैंग शेल का उपयोग करेंगे, एक मोचीवेब के लिए और एक राउटर के लिए। संशोधित करें-dev.sh का उपयोग mochiweb शुरू करने और erl में निम्नलिखित अतिरिक्त पैरामीटर जोड़ने के लिए किया जाता है:

• -sname n1 का नाम erlang नोड 'n1'

• कर्नेल-पोल को सक्षम करने के लिए + K सत्य।

• + पी 134217727 - अधिकतम प्रक्रियाओं की संख्या जो आप स्पॉन कर सकते हैं वह 32768 है। हमें प्रत्येक कनेक्शन के लिए एक प्रक्रिया की आवश्यकता है, लेकिन मुझे नहीं पता है कि विशेष रूप से इसकी कितनी आवश्यकता होगी। 134 217 727 - "मैन एर्ल" के अनुसार अधिकतम मूल्य।



अब do &&//start-dev.sh करें और आपको एक ग्रीटिंग देखना चाहिए: (n1 @ localhost) 1> - अब आपका मोचीवेब एप्लिकेशन काम करता है, और एरलांग नोड का एक नाम है।



अब एक और erangang खोल चलाएँ:

 erl -sname n2
      
      





फिलहाल, उन दो erangang नोड्स को एक दूसरे के बारे में पता नहीं है, इसे ठीक करें:

 (n2@localhost)1> nodes(). [] (n2@localhost)2> net_adm:ping(n1@localhost). pong (n2@localhost)3> nodes(). [n1@localhost]
      
      





अब संकलित करें और रूटर शुरू करें:

 (n2@localhost)4> c(router). {ok,router} (n2@localhost)5> router:start_link(). {ok,<0.38.0>}
      
      







अब मज़े के लिए, अपने ब्राउज़र में http: // localhost: 8000 / test / 123 खोलें (या कंसोल से lynx --source " http: // localhost: 8000 / test / 123 " का उपयोग करें)। उस शेल की जांच करें जिसमें आपने राउटर शुरू किया था, आपको यह देखना चाहिए कि एक उपयोगकर्ता ने लॉग इन किया है।



अब आप राउटर को संदेश भेज सकते हैं और देख सकते हैं कि वे आपके ब्राउज़र में कैसे दिखाई देते हैं। अभी के लिए, केवल स्ट्रिंग्स का उपयोग करें क्योंकि हम आउटपुट के लिए ~ s पैरामीटर का उपयोग करते हैं, और परमाणु के परिणामस्वरूप त्रुटि होगी:

 (n2@localhost)6> router:send(123, "Hello World"). (n2@localhost)7> router:send(123, "Why not open another browser window too?"). (n2@localhost)8> router:send(456, "This message will go into the void unless you are connected as /test/456 too").
      
      





अपने ब्राउज़र की जाँच करें, आपको एक संदेश मिला है :)



वितरित एर्गैंग सिस्टम को चलाना



यह विभिन्न मशीनों पर एक राउटर और मोचीवेब चलाने के लिए समझ में आता है। मान लें कि आपके पास परीक्षण के लिए कई अतिरिक्त मशीनें हैं, तो आपको वितरित नोड्स के रूप में erlang गोले चलाना चाहिए, अर्थात, -sname n1 (और n2 के लिए समान) के बजाय nname@1@host1.example.com का उपयोग करें। सुनिश्चित करें कि वे एक दूसरे को net_adm: पिंग (...) के साथ ऊपर के उदाहरण में देख सकते हैं।



राउटर.रेल में लाइन 16 पर ध्यान दें, राउटर प्रक्रिया ('राउटर') का नाम विश्व स्तर पर पंजीकृत है, यही कारण है कि हम वितरित मैक्रो को gen_server कॉल्स में राउटर के स्थान की पहचान करने के लिए उपयोग करते हैं, यहां तक ​​कि वितरित सिस्टम पर भी:

 -define(SERVER, global:whereis_name(?MODULE)).
      
      





एक वितरित प्रणाली में प्रक्रियाओं के लिए एक वैश्विक नाम रजिस्ट्री सिर्फ कई चीजों में से एक है जो आपको इरलांग के साथ मुफ्त में मिलती है।



बड़ी संख्या में संदेश उत्पन्न करें



वास्तविक परिवेश में, हम कुछ सक्रिय उपयोगकर्ताओं और कई निष्क्रिय उपयोगकर्ताओं के साथ "लंबी-पूंछ" जैसा एक पैटर्न देख सकते हैं। हालांकि, इस परीक्षण के लिए हम यादृच्छिक उपयोगकर्ताओं को अंधाधुंध नकली संदेश भेजेंगे।

 -module(msggen). -export([start/3]). start(0, _, _) -> ok; start(Num, Interval, Max) -> Id = random:uniform(Max), router:send(Id, "Fake message Num = " ++ Num), receive after Interval -> start(Num -1, Interval, Max) end.
      
      







यह कोड 1 और अधिकतम हर इंटरवल एमएस के बीच रैंडम यूजर आईडी को न्यू मैसेज भेजेगा।



यदि आप राउटर और मोचीवेब एप्लिकेशन चलाते हैं, तो आप इसे एक्शन में देख सकते हैं : http: // localhost: 8000 / test / 3 और रन पर जाएं:

 erl -sname test (test@localhost)1> net_adm:ping(n1@localhost). pong (test@localhost)2> c(msggen). {ok,msggen} (test@localhost)3> msggen:start(20, 10, 5). ok
      
      





20 संदेश 1 और 5 के बीच यादृच्छिक पहचानकर्ताओं को भेजे जाएंगे, प्रत्येक 10 एमएस, एक संदेश प्रत्येक। शायद आप भाग्यशाली हैं और आपको कुछ संदेश प्राप्त होंगे।



हम उनमें से कई को संदेशों के लिए कई स्रोतों के समानांतर भी चला सकते हैं। यहां 10 प्रक्रियाओं का एक उदाहरण दिया गया है, प्रत्येक ने प्रत्येक संदेश के बीच 100 एमएस की देरी के साथ पहचानकर्ताओं को 20 संदेश भेजे हैं:

 [ spawn(fun() -> msggen:start(20, 100, 5), io:format("~w finished.\n", [self()]) end) || _ <- lists:seq(1,10) ]. [<0.97.0>,<0.98.0>,<0.99.0>,<0.100.0>,<0.101.0>,<0.102.0>, <0.103.0>,<0.104.0>,<0.105.0>,<0.106.0>] <0.101.0> finished. <0.105.0> finished. <0.106.0> finished. <0.104.0> finished. <0.102.0> finished. <0.98.0> finished. <0.99.0> finished. <0.100.0> finished. <0.103.0> finished. <0.97.0> finished.
      
      







C10K



हमारे पास व्यापक पैमाने पर परीक्षण के लिए सभी भाग हैं; क्लाइंट हमारे mochiweb एप्लिकेशन से जुड़ते हैं, जो उन्हें एक संदेश राउटर के साथ पंजीकृत करता है। हम राउटर को भेजने के लिए नकली संदेशों की एक बड़ी मात्रा उत्पन्न कर सकते हैं, जो उन्हें किसी भी पंजीकृत ग्राहकों को भेजेंगे। चलो भाग 1 से फिर से 10,000 समवर्ती कनेक्शनों की जांच करते हैं, लेकिन इस बार हम जुड़े हुए सभी ग्राहकों को छोड़ देंगे, जबकि हम सिस्टम के माध्यम से बहुत सारे संदेश चलाते हैं।



मान लीजिए कि आपने अपने कर्नेल आदि को कॉन्फ़िगर करने के लिए भाग 1 में दिए गए निर्देशों का पालन किया है। आपके पास पहले से ही एक मोचीवेब एप्लिकेशन और राउटर चल रहा है, तो चलो उन्हें और अधिक ट्रैफ़िक में डाल दें।

जुड़े हुए ग्राहकों के बिना, मोचीवेब लगभग 40 एमबी मेमोरी का उपयोग करता है:

 $ ps -o rss= -p `pgrep -f 'sname n1'` 40156
      
      





मैं समय प्रदर्शित करने के लिए इस घृणित आदेश के साथ आया था, मोचीवेब आवेदन की वर्तमान मेमोरी उपयोग, और हर 60 सेकंड में स्थापित कनेक्शन की संख्या:

 $ MOCHIPID=`pgrep -f 'name n1'`; while [ 1 ] ; do NUMCON=`netstat -n | awk '/ESTABLISHED/ && $4=="127.0.0.1:8000"' | wc -l`; MEM=`ps -o rss= -p $MOCHIPID`; echo -e "`date`\t`date +%s`\t$MEM\t$NUMCON"; sleep 60; done | tee -a mochimem.log
      
      





यदि किसी को समय के साथ, किसी एक प्रक्रिया के लिए मेमोरी उपयोग को चित्रित करने का सबसे अच्छा तरीका पता है, तो कृपया एक टिप्पणी छोड़ दें।



अब नए erl शेल में भाग 1 से बाढ़ को चलाएँ:

 erl> floodtest:start("/tmp/mochi-urls.txt", 10).   100    ,   10 000    . Stats: {825,0,0} Stats: {1629,0,0} Stats: {2397,0,0} Stats: {3218,0,0} Stats: {4057,0,0} Stats: {4837,0,0} Stats: {5565,0,0} Stats: {6295,0,0} Stats: {7022,0,0} Stats: {7727,0,0} Stats: {8415,0,0} Stats: {9116,0,0} Stats: {9792,0,0} Stats: {10000,0,0} ...
      
      





मेमोरी उपयोग की जाँच करें:

 Mon Oct 20 16:57:24 BST 2008 1224518244 40388 1 Mon Oct 20 16:58:25 BST 2008 1224518305 41120 263 Mon Oct 20 16:59:27 BST 2008 1224518367 65252 5267 Mon Oct 20 17:00:32 BST 2008 1224518432 89008 9836 Mon Oct 20 17:01:37 BST 2008 1224518497 90748 10001 Mon Oct 20 17:02:41 BST 2008 1224518561 90964 10001 Mon Oct 20 17:03:46 BST 2008 1224518626 90964 10001 Mon Oct 20 17:04:51 BST 2008 1224518691 90964 10001
      
      





10,000 समवर्ती कनेक्शन पहुंच गए हैं (प्लस मैं फ़ायरफ़ॉक्स में खोला गया था), और मोचीवेब की मेमोरी खपत लगभग 90 एमबी (90964 KB) है।



अब कुछ संदेश भेजें:

 erl> [ spawn(fun() -> msggen:start(1000000, 100, 10000) end) || _ <- lists:seq(1,100) ]. [<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>,<0.74.0>,<0.75.0>,<0.76.0>, <0.77.0>,<0.78.0>,<0.79.0>,<0.80.0>,<0.81.0>,<0.82.0>, <0.83.0>,<0.84.0>,<0.85.0>,<0.86.0>,<0.87.0>,<0.88.0>, <0.89.0>,<0.90.0>,<0.91.0>,<0.92.0>,<0.93.0>|...]
      
      





100 प्रक्रियाएं 1 से 10,000 तक यादृच्छिक आईडी पर 10 संदेश प्रति सेकंड में एक लाख संदेश भेजती हैं। इसका मतलब है कि राउटर प्रति सेकंड 1000 संदेशों को संसाधित करता है, और औसतन हमारे प्रत्येक 10 k ग्राहकों को प्रत्येक 10 सेकंड में एक संदेश प्राप्त होगा।



बाढ़ के उत्पादन की जाँच करें, और आप देखेंगे कि ग्राहकों को http संदेश प्राप्त होते हैं (याद रखें कि ये {NumConnected, NumClosed, NumChunksRecvd} हैं):

 ... Stats: {10000,0,5912} Stats: {10000,0,15496} Stats: {10000,0,25145} Stats: {10000,0,34755} Stats: {10000,0,44342} ...
      
      





प्रत्येक प्रक्रिया के लिए 10 प्रति सेकंड के एक लाख संदेशों को काम करने में 27 घंटे लगेंगे। नीचे पहले 10 मिनट में मेमोरी का उपयोग किया गया है:

 Mon Oct 20 16:57:24 BST 2008 1224518244 40388 1 Mon Oct 20 16:58:25 BST 2008 1224518305 41120 263 Mon Oct 20 16:59:27 BST 2008 1224518367 65252 5267 Mon Oct 20 17:00:32 BST 2008 1224518432 89008 9836 Mon Oct 20 17:01:37 BST 2008 1224518497 90748 10001 Mon Oct 20 17:02:41 BST 2008 1224518561 90964 10001 Mon Oct 20 17:03:46 BST 2008 1224518626 90964 10001 Mon Oct 20 17:04:51 BST 2008 1224518691 90964 10001 Mon Oct 20 17:05:55 BST 2008 1224518755 90980 10001 Mon Oct 20 17:07:00 BST 2008 1224518820 91120 10001 Mon Oct 20 17:08:05 BST 2008 1224518885 98664 10001 Mon Oct 20 17:09:10 BST 2008 1224518950 106752 10001 Mon Oct 20 17:10:15 BST 2008 1224519015 114044 10001 Mon Oct 20 17:11:20 BST 2008 1224519080 119468 10001 Mon Oct 20 17:12:25 BST 2008 1224519145 125360 10001
      
      





आप देख सकते हैं कि आकार पहले ही 40 एमबी से 90 एमबी तक बढ़ गया है जब सभी 10 k ग्राहक जुड़े हुए हैं, और कुछ समय बाद 125 एमबी तक।



यह इंगित करने योग्य है कि बाढ़ का उपयोग लगभग कोई CPU नहीं करता है, msggen CPU के 2% का उपयोग करता है, और राउटर और मोचीवेब 1% से कम हैं।



24 घंटे के भीतर पूरा होने के बाद परिणाम



मोचीवब प्रक्रिया की मेमोरी उपयोग की निगरानी करते हुए आवेदन ने 24 घंटे काम किया। 10,000 जुड़े ग्राहक, 1000 संदेश प्रति सेकंड यादृच्छिक ग्राहकों को भेजे गए।

ग्राफ को खींचने के लिए gnuplot को बाध्य करने के लिए निम्नलिखित चाल का उपयोग किया गया था:

 (echo -e "set terminal png size 500,300\nset xlabel \"Minutes Elapsed\"\nset ylabel \"Mem (KB)\"\nset title \"Mem usage with 10k active connections, 1000 msg/sec\"\nplot \"-\" using 1:2 with lines notitle" ; awk 'BEGIN{FS="\t";} NR%10==0 {if(!t){t=$2} mins=($2-t)/60; printf("%d %d\n",mins,$3)}' mochimem.log ; echo -e "end" ) | gnuplot > mochimem.png
      
      









यह ग्राफ़ दिखाता है कि मेमोरी उपयोग (10k सक्रिय कनेक्शन और 1000 msg / सेकंड के साथ) 24 घंटे की अवधि में 250 एमबी के भीतर संरेखित है। इस तथ्य के कारण दो निचले छोर दिखाई दिए कि मैंने ब्याज के लिए प्रदर्शन किया:

 erl> [erlang:garbage_collect(P) || P <- erlang:processes()].
      
      





यह सभी प्रक्रियाओं को कचरा इकट्ठा करने के लिए मजबूर करता है, और लगभग 100 एमबी मेमोरी को मुक्त करता है। अब हम मैन्युअल रूप से कचरा संग्रह के लिए मजबूर किए बिना स्मृति को संरक्षित करने के तरीके तलाश रहे हैं।



Mochiweb में मेमोरी का उपयोग कम करें



ध्यान दें, मोचीवब एप्लिकेशन केवल संदेश भेजता है और फिर तुरंत उन्हें भूल जाता है, भेजे गए संदेशों की संख्या के साथ स्मृति उपयोग में वृद्धि नहीं होनी चाहिए।



मैं एक नौसिखिया हूं जब यह एरलंग मेमोरी प्रबंधन की बात आती है, लेकिन मैं यह सुझाव देने जा रहा हूं कि अगर मैं इसे अधिक बार कचरा इकट्ठा करने के लिए मजबूर कर सकता हूं, तो यह हमें उस मेमोरी का अधिकतर रीडायरेक्ट करने की अनुमति देगा, और अंततः हमें एक खाली सिस्टम के साथ अधिक उपयोगकर्ताओं की सेवा करने की अनुमति देगा। स्मृति।



प्रलेखन के एक अध्ययन से कुछ परिणाम मिले:



erlang: system_flag (fullsweep_after, संख्या)

गूढ़, लेकिन यह केवल नई प्रक्रियाओं पर लागू होता है और वीएम में सभी प्रक्रियाओं को प्रभावित करता है, न केवल हमारी मोचीवब प्रक्रियाओं को।



अगला:

erlang: system_flag (min_heap_size, MinHeapSize)

यह उपयोगी हो सकता है, लेकिन मुझे पूरा यकीन है कि हमारे मोचीवब प्रक्रियाओं को वैसे भी डिफ़ॉल्ट मान से अधिक ढेर की आवश्यकता होती है। मैं मोचीवब स्रोत कोड को बदलने की आवश्यकता से बचना चाहूंगा।



पास में, मैंने देखा:

एरलैंग: हाइबरनेट (मॉड्यूल, फंक्शन, आर्ग्स)

उचित लगता है - आइए प्रत्येक संदेश के बाद सोने की कोशिश करें और देखें कि क्या होता है।



Mochiconntest_web.erl संपादित करें और निम्नलिखित बदलें:

• फ़ंक्शन की अंतिम पंक्ति (प्रतिक्रिया, आईडी, एन) को बदलें ताकि यह स्वयं को कॉल करने के बजाय स्लीप मोड में चला जाए;

• कॉल हाइबरनेट () प्राप्त करने पर अवरुद्ध होने के बजाय तुरंत राउटर को एक संदेश भेजने के लिए;

• फ़ीड / 3 निर्यात करने के लिए याद रखें।



अद्यतित mochiconntest_web.erl संदेशों के बीच हाइबरनेशन के साथ:

 -module(mochiconntest_web). -export([start/1, stop/0, loop/2, feed/3]). %% External API start(Options) -> {DocRoot, Options1} = get_option(docroot, Options), Loop = fun (Req) -> ?MODULE:loop(Req, DocRoot) end, % we'll set our maximum to 1 million connections. (default: 2048) mochiweb_http:start([{max, 1000000}, {name, ?MODULE}, {loop, Loop} | Options1]). stop() -> mochiweb_http:stop(?MODULE). loop(Req, DocRoot) -> "/" ++ Path = Req:get(path), case Req:get(method) of Method when Method =:= 'GET'; Method =:= 'HEAD' -> case Path of "test/" ++ IdStr -> Response = Req:ok({"text/html; charset=utf-8", [{"Server","Mochiweb-Test"}], chunked}), {Id, _} = string:to_integer(IdStr), router:login(Id, self()), % Hibernate this process until it receives a message: proc_lib:hibernate(?MODULE, feed, [Response, Id, 1]); _ -> Req:not_found() end; 'POST' -> case Path of _ -> Req:not_found() end; _ -> Req:respond({501, [], []}) end. feed(Response, Id, N) -> receive {router_msg, Msg} -> Html = io_lib:format("Recvd msg #~w: '~w' ", [N, Msg]), Response:write_chunk(Html) end, % Hibernate this process until it receives a message: proc_lib:hibernate(?MODULE, feed, [Response, Id, N+1]). %% Internal API get_option(Option, Options) -> {proplists:get_value(Option, Options), proplists:delete(Option, Options)}.
      
      







मैंने ये बदलाव किए, मोचीवब को फिर से बनाया, फिर से वही परीक्षण किया।



24 घंटे के भीतर निष्पादन के बाद परिणाम proc_lib: हाइबरनेट ()







हाइबरनेट () का उपयोग करने का मतलब है कि मोचीवीब एप्लिकेशन मेमोरी को 10 एमबी कनेक्शन के साथ 78 एमबी पर संरेखित किया गया है, भाग 1 में देखे गए 450 एमबी की तुलना में बहुत बेहतर है। सीपीयू उपयोग में कोई उल्लेखनीय वृद्धि नहीं हुई।



इसलिए ...



हमने Mochiweb के लिए एक धूमकेतु एप्लिकेशन बनाया जो हमें पूर्णांक आईडी द्वारा पहचाने गए उपयोगकर्ताओं को मनमाने संदेश भेजने की अनुमति देता है। 24 घंटे के लिए प्रति सेकंड 1000 संदेशों को चलाने के बाद, 10,000 जुड़े हुए उपयोगकर्ताओं के साथ, हमने 80 एमबी मेमोरी या 8 केबी प्रति उपयोगकर्ता के उपयोग को देखा। हमने अच्छे ग्राफिक्स भी बनाए।



यह वास्तव में प्रगति है।



अगले चरण



भाग 3 में, मैं उपयोगकर्ताओं की संख्या 1 मिलियन तक बढ़ाऊंगा। मैं पर्याप्त मेमोरी के साथ एक मल्टीप्रोसेसर मशीन पर परीक्षण आयोजित करूंगा। मैं 1 मिलियन कनेक्शन का अनुकरण करने के लिए कुछ अतिरिक्त चालें और अनुकूलन भी दिखाऊंगा।



एप्लिकेशन एक प्रकार के "पब-उप" प्रणाली में विकसित होगा, जहां सदस्यता उपयोगकर्ताओं की आईडी से जुड़ी होती है और आवेदन द्वारा सहेजी जाती है। हम सामाजिक नेटवर्क डेटा के एक विशिष्ट सेट का उपयोग करेंगे: दोस्तों। यह उपयोगकर्ता को अपनी उपयोगकर्ता आईडी के साथ लॉग इन करने की अनुमति देगा और स्वचालित रूप से अपने किसी मित्र द्वारा उत्पन्न किसी भी घटना को प्राप्त करेगा।



All Articles