Article 1
In this series, I will describe in detail how to use mochiweb to achieve massive connections and demonstrate how to create a Comet application using Mochiweb, each mochiweb connection is registered by a router that sends messages to different users. A running application can handle 1 million of the concurrent connections, and it is important that we know how much memory is needed to make it work normally.
In the first article:
- Build a basic mochiweb comet application that sends messages to customers every 10 seconds.
- Adjust the Linux kernel to handle a large number of TCP connections.
- Build a flood control test tool to open massive connections
- Check the memory size required for each connection.
Subsequent serialization in this series will cover how to build an actual message routing system and the skills to save memory overhead, as well as concurrent connection tests between 0.1 million and 1 million.
I assume that you are familiar with Linux Command Line commands and Erlang.
BuildMochiwebTest the application
Introduction:
- Install and build Mochiweb
- Run:/your-mochiweb-path/scripts/new_mochiweb.erl mochiconntest
- Cd mochiconntest and then edit src/mochiconntest_web.erl
These codes (mochiconntest_web.erl) only accept the connection and use the data block transmission to send the initial welcome message, and send the message to each client every 10 seconds.
- -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)
- Method When method =: = 'get'; method =: = 'head'->
- Case path
- "Test/" ++ ID->
- Response = Req: OK ({"text/html; charset = UTF-8 ",
- [{"Server", "Mochiweb-Test"}],
- Chunked }),
- Response: write_chunk ("Mochiconntest welcomes you! Your Id: "++ Id ++"/n "),
- % Router: login (list_to_atom (Id), self ()),
- Feed (Response, Id, 1 );
- _->
- Req: not_found ()
- End;
- 'Post'->
- Case Path
- _->
- Req: not_found ()
- End;
- _->
- Req: respond ({501, [], []})
- End.
- Feed (Response, Path, N)->
- Receive
- % {Router_msg, Msg}->
- % Html = io_lib: Format ("recvd MSG #~ W :'~ S' <br/> ", [N, MSG]),
- % Response: write_chunk (HTML );
- After 10000->
- MSG = io_lib: Format ("chunk ~ W for ID ~ S/N ", [N, path]),
- Response: write_chunk (MSG)
- End,
- Feed (response, path, n + 1 ).
- % Internal API
- Get_option (option, options)->
- {Proplists: get_value (option, options), proplists: delete (option, options )}.
Start yourMochiwebApplications
Make &./start-dev.sh
Mochiweb listens to all interfaces through port 8000 by default. If you are working on the desktop, you can use any Web browser to view http: // localhost: 8000/test/foo.
Below is the test command line code
$ Lynx -- source "http: // localhost: 8000/test/foo"
Mochiconntest welcomes you! Your Id: foo <br/>
Chunk 1 for id foo <br/>
Chunk 2 for id foo <br/>
Chunk 3 for id foo <br/>
^ C
It runs correctly. Next we will make him suffer.
For a large numberTcpConnection AdjustmentLinuxKernel
Before testing a large number of connections, you need to leave some time to adjust the TCP settings of the kernel. Otherwise, your test will fail and you will see a large number of out of socket memory messages (if you prefer, nf_conntrack: table full, dropping packet .)
The following is my sysctl settings. This is only one of the feasible methods, and there may be other methods.
# Adjust the universal gigabit network:
Net. Core. rmem_max = 16777216
Net. Core. wmem_max = 16777216
Net. ipv4.tcp _ rmem = 4096 87380 16777216
Net. ipv4.tcp _ WMEM = 4096 65536 16777216
Net. ipv4.tcp _ syncookies = 1
# Let the kernel give more memory capacity to TCP
# For a large number of socket connections (100 K +) you need
Net. ipv4.tcp _ mem = 50576 64768 98152
Net. Core. netdev_max_backlog = 2500
Net. ipv4.netfilter. ip_conntrack_max = 1048576
Save the above content to/etc/sysctl. conf and run sysctl-P to apply these configurations. You do not need to repeat them. Now your kernel will be able to process more connections, yay.
Create massive connections
There are many ways to implement this function. Tsung is quite strong, and there are many other httpd (AB, httperf, httpload, etc.) that send a large number of spam requests ). None of the tests can perfectly match a comet application. I 've been trying to use Erlang to implement an HTTP client solution, so I wrote a basic test to create a large number of connections. It's just because you can do this, not because you have .. each connection corresponds to a process, which is a significant waste of resources. I use a process to load the URL from a file, and another process to create and receive messages sent from all HTTP connections (and a timer process that prints a report every 10 seconds ), All data received from the server is discarded, but it increases the counter count so that we can track how many HTTP data blocks are delivered.
Floodtest. erl
- -Module (floodtest ).
- -Export ([start/2, timer/2, recv/1]).
- Start (Filename, Wait)->
- Inets: start (),
- Spawn (? MODULE, timer, [10000, self ()]),
- This = self (),
- Spawn (fun ()-> loadurls (filename, fun (u)-> This! {Loadurl, u} end, wait) end ),
- Recv ({0, 0 }).
- Recv (Stats)->
- {Active, closed, Chunks} = stats,
- Receive
- {Stats}-> IO: Format ("stats :~ W/N ", [stats])
- After 0-> Noop
- End,
- Receive
- {HTTP, {_ ref, stream_start, _ x }}-> Recv ({active + 1, closed, Chunks });
- {Http, {_ Ref, stream, _ X }}-> recv ({Active, Closed, Chunks + 1 });
- {Http, {_ Ref, stream_end, _ X }}-> recv ({Active-1, Closed + 1, Chunks });
- {Http, {_ Ref, {error, Why }}->
- Io: format ("Closed :~ W/n ", [Why]),
- Recv ({Active-1, Closed + 1, Chunks });
- {Loadurl, Url}->
- HTTP: Request (get, {URL, []}, [], [{sync, false}, {stream, self}, {version, 1.1}, {body_format, binary}]),
- Recv (Stats)
- End.
- Timer (T, WHO)->
- Receive
- After T->
- Who! {Stats}
- End,
- Timer (T, WHO ).
- % Read lines from a file with a specified delay between lines:
- For_each_line_in_file (name, Proc, mode, accum0)->
- {OK, device} = file: open (name, mode ),
- For_each_line (device, Proc, accum0 ).
- For_each_line (device, Proc, accum)->
- Case IO: get_line (device, "")
- EOF-> file: Close (device), accum;
- Line-> newaccum = proc (line, accum ),
- For_each_line (device, Proc, newaccum)
- End.
- Loadurls (filename, callback, wait)->
- For_each_line_in_file (filename,
- Fun (line, list)->
- Callback (string: strip (line, right, $/n )),
- Receive
- After wait->
- Noop
- End,
- List
- End,
- [Read], []).
For each connection, we need to request a temporary port, that is, the file descriptor. By default, a maximum of 1024 ports can be opened. To avoid opening too many files, you need to modify the ulimit value of your shell file. You can modify/etc/security/limits. conf, but you need to exit/log on at one time. Now you only need to sudo and modify the current shell file (if you do not want to run as root after modifying ulimit, use su to return to your non-privileged user ):
$ Sudo bash
# Ulimit-n 999999
# Erl
You may also set the temporary port range to be too large, which is limited to 65535.
# Echo "1024 65535">/proc/sys/net/ipv4/ip_local_port_range
Generate a website file for the flood control test program
(For I in 'seq 1 100'; do echo "http: // localhost: 10000/test/$ I"; done)>/tmp/mochi-urls.txt
From the erlang prompt, You can compile and start floodtest. erl:
Erl> c (floodtest ).
Erl> floodtest: start ("/tmp/mochi-urls.txt", 100 ).
This will create 10 new connections per second (that is, one connection per 100 milliseconds)
This will be output in the form of {Active, Closed, Chunks}, where Active is the number of established connections, and Closed is the number terminated for some reason, chunks are the number of data blocks transmitted by mochiweb. Closed should be kept as 0, and Chunks should be greater than Active, because each Active connection will receive multiple data blocks (one per 10 seconds ),
Yes10,000Active connectionsMochiweb beamThe capacity of the resident memory of the process is450 MB-That is, each connection45KB.As expected, the CPU usage is almost none.
Conclusion
This is a reasonable first attempt. Each connection 45 KB seems a bit high-using c may make every connection close to 4.5KB (just a guess, if anyone has experience, please comment ). If you use c and erlang to compare and implement this function, consider the amount of code and the encoding time, and I want to increase the memory overhead to be more excitable.
In subsequent serialization, I will overwrite and construct a message route (this can be achieved by removing the comments from lines 25 and 41-43 of mochiconntest_web.erl) in addition, some methods will be discussed to reduce the overall memory overhead. I will also share the test results with kb and 1 Mbit/s connections.