Build a comet application with mochiweb to accommodate millions of users (1)

Source: Internet
Author: User

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:

  1. Install and build Mochiweb
  2. Run:/your-mochiweb-path/scripts/new_mochiweb.erl mochiconntest
  3. 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.

  1. -Module (mochiconntest_web ).
  2. -Export ([start/1, stop/0, loop/2]).
  3. % External API
  4. Start (Options)->
  5. {DocRoot, Options1} = get_option (docroot, Options ),
  6. Loop = fun (Req)->
  7. ? MODULE: loop (Req, DocRoot)
  8. End,
  9. % We'll set our maximum to 1 million connections. (default: 2048)
  10. Mochiweb_http: Start ([{max, 1000000}, {name ,? Module}, {loop, loop} | options1]).
  11. Stop ()->
  12. Mochiweb_http: Stop (? Module ).
  13. Loop (req, docroot)->
  14. "/" ++ Path = Req: Get (PATH ),
  15. Case Req: Get (method)
  16. Method When method =: = 'get'; method =: = 'head'->
  17. Case path
  18. "Test/" ++ ID->
  19. Response = Req: OK ({"text/html; charset = UTF-8 ",
  20. [{"Server", "Mochiweb-Test"}],
  21. Chunked }),
  22. Response: write_chunk ("Mochiconntest welcomes you! Your Id: "++ Id ++"/n "),
  23. % Router: login (list_to_atom (Id), self ()),
  24. Feed (Response, Id, 1 );
  25. _->
  26. Req: not_found ()
  27. End;
  28. 'Post'->
  29. Case Path
  30. _->
  31. Req: not_found ()
  32. End;
  33. _->
  34. Req: respond ({501, [], []})
  35. End.
  36. Feed (Response, Path, N)->
  37. Receive
  38. % {Router_msg, Msg}->
  39. % Html = io_lib: Format ("recvd MSG #~ W :'~ S' <br/> ", [N, MSG]),
  40. % Response: write_chunk (HTML );
  41. After 10000->
  42. MSG = io_lib: Format ("chunk ~ W for ID ~ S/N ", [N, path]),
  43. Response: write_chunk (MSG)
  44. End,
  45. Feed (response, path, n + 1 ).
  46. % Internal API
  47. Get_option (option, options)->
  48. {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

    1. -Module (floodtest ).
    2. -Export ([start/2, timer/2, recv/1]).
    3. Start (Filename, Wait)->
    4. Inets: start (),
    5. Spawn (? MODULE, timer, [10000, self ()]),
    6. This = self (),
    7. Spawn (fun ()-> loadurls (filename, fun (u)-> This! {Loadurl, u} end, wait) end ),
    8. Recv ({0, 0 }).
    9. Recv (Stats)->
    10. {Active, closed, Chunks} = stats,
    11. Receive
    12. {Stats}-> IO: Format ("stats :~ W/N ", [stats])
    13. After 0-> Noop
    14. End,
    15. Receive
    16. {HTTP, {_ ref, stream_start, _ x }}-> Recv ({active + 1, closed, Chunks });
    17. {Http, {_ Ref, stream, _ X }}-> recv ({Active, Closed, Chunks + 1 });
    18. {Http, {_ Ref, stream_end, _ X }}-> recv ({Active-1, Closed + 1, Chunks });
    19. {Http, {_ Ref, {error, Why }}->
    20. Io: format ("Closed :~ W/n ", [Why]),
    21. Recv ({Active-1, Closed + 1, Chunks });
    22. {Loadurl, Url}->
    23. HTTP: Request (get, {URL, []}, [], [{sync, false}, {stream, self}, {version, 1.1}, {body_format, binary}]),
    24. Recv (Stats)
    25. End.
    26. Timer (T, WHO)->
    27. Receive
    28. After T->
    29. Who! {Stats}
    30. End,
    31. Timer (T, WHO ).
    32. % Read lines from a file with a specified delay between lines:
    33. For_each_line_in_file (name, Proc, mode, accum0)->
    34. {OK, device} = file: open (name, mode ),
    35. For_each_line (device, Proc, accum0 ).
    36. For_each_line (device, Proc, accum)->
    37. Case IO: get_line (device, "")
    38. EOF-> file: Close (device), accum;
    39. Line-> newaccum = proc (line, accum ),
    40. For_each_line (device, Proc, newaccum)
    41. End.
    42. Loadurls (filename, callback, wait)->
    43. For_each_line_in_file (filename,
    44. Fun (line, list)->
    45. Callback (string: strip (line, right, $/n )),
    46. Receive
    47. After wait->
    48. Noop
    49. End,
    50. List
    51. End,
    52. [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.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.