Permalink
Browse files

Add initial Ranch guide

  • Loading branch information...
1 parent 9cafa5e commit 85510e373fa6db2b6faca0ef0e7081cf99dba146 @essen essen committed Aug 22, 2012
Showing with 550 additions and 54 deletions.
  1. +3 −54 README.md
  2. +50 −0 guide/embedded.md
  3. +80 −0 guide/internals.md
  4. +25 −0 guide/introduction.md
  5. +171 −0 guide/listeners.md
  6. +61 −0 guide/protocols.md
  7. +28 −0 guide/toc.md
  8. +132 −0 guide/transports.md
View
@@ -35,57 +35,6 @@ Quick start
Getting Started
---------------
-Ranch accepts connections received on a given port and using a given
-transport, like TCP or SSL, and forward them to a given protocol
-handler. Acceptors and protocol handler processes are of course
-supervised automatically.
-
-Ranch does nothing by default. You need to explicitly request Ranch
-to listen on a port with your chosen transport and protocol handlers.
-To do so, you must start a listener.
-
-A listener is a special kind of supervisor that manages both the
-acceptor pool and the protocol processes. It is named and can thus be
-started and stopped at will.
-
-An acceptor pool is a pool of processes whose only role is to accept
-new connections. It's good practice to have many of these processes
-as they are very cheap and allow much quicker response when you get
-many connections. Of course, as with everything else, you should
-**benchmark** before you decide what's best for you.
-
-Ranch includes both TCP and SSL transport handlers, abstracted through
-a single common interface.
-
-You can start and stop listeners by calling `ranch:start_listener/6` and
-`ranch:stop_listener/1` respectively.
-
-The following example demonstrates the startup of a very simple listener.
-
-``` erlang
-application:start(ranch),
-%% Name, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
-ranch:start_listener(my_echo_listener, 100,
- ranch_tcp, [{port, 1234}],
- my_echo_protocol, [{log, "echo.log"}]
-).
-```
-
-Writing a protocol handler
---------------------------
-
-The only exported function a protocol handler needs is the start_link/4
-function, with arguments ListenerPid, Socket, Transport and Opts. ListenerPid
-is the pid to the listener's gen_server, managing the connections. Socket is of
-course the client socket; Transport is the module name of the chosen transport
-handler and Opts is protocol options defined when starting the listener.
-
-After initializing your protocol, it is recommended to call the
-function ranch:accept_ack/1 with the ListenerPid as argument,
-as it will ensure Ranch has been able to fully initialize the socket.
-Anything you do past this point is up to you!
-
-If you need to change some socket options, like enabling raw mode for example,
-you can call the <em>Transport:setopts/2</em> function. It is the protocol's
-responsability to manage the socket usage, there should be no need for an user
-to specify that kind of options while starting a listener.
+* [Read the guide](https://github.com/extend/ranch/blob/master/guide/toc.md)
+* Look at the examples in the ```examples/``` directory
+* Build API documentation with ```make docs```; open ```doc/index.html```
View
@@ -0,0 +1,50 @@
+Embedded mode
+=============
+
+Purpose
+-------
+
+Embedded mode allows you to insert Ranch listeners directly
+in your supervision tree. This allows for greater fault tolerance
+control by permitting the shutdown of a listener due to the
+failure of another part of the application and vice versa.
+
+Embedding
+---------
+
+To embed Ranch in your application you can simply add the child specs
+to your supervision tree. This can all be done in the ```init/1``` function
+of one of your application supervisors.
+
+Ranch requires at the minimum two kinds of child specs for embedding.
+First, you need to add ```ranch_sup``` to your supervision tree, only once,
+regardless of the number of listeners you will use. Then you need to
+add the child specs for each listener.
+
+Ranch has a convenience function for getting the listeners child specs
+called ```ranch:child_spec/6```, that works like ```ranch:start_listener/6```,
+except that it doesn't start anything, it only returns child specs.
+
+As for ```ranch_sup```, the child spec is simple enough to not require a
+convenience function.
+
+The following example adds both ```ranch_sup``` and one listener to another
+application's supervision tree.
+
+``` erlang
+init([]) ->
+ RanchSupSpec = {ranch_sup, {ranch_sup, start_link, []},
+ permanent, 5000, supervisor, [ranch_sup]},
+ ListenerSpec = ranch:child_spec(echo, 100,
+ ranch_tcp, [{port, 5555}],
+ echo_protocol, []
+ ),
+ {ok, {{one_for_one, 10, 10}, [RanchSupSpec, ListenerSpec]}}.
+```
+
+Remember, you can add as many listener child specs as needed, but only
+one ```ranch_sup``` spec!
+
+It is recommended that your architecture makes sure that all listeners
+are restarted if ```ranch_sup``` fails. See the Ranch internals chapter for
+more details on how Ranch does it.
View
@@ -0,0 +1,80 @@
+Internals
+=========
+
+This chapter may not apply to embedded Ranch as embedding allows you
+to use an architecture specific to your application, which may or may
+not be compatible with the description of the Ranch application.
+
+Architecture
+------------
+
+Ranch is an OTP application.
+
+Like all OTP applications, Ranch has a top supervisor. It is responsible
+for supervising the ```ranch_server``` process and all the listeners that
+will be started.
+
+The ```ranch_server``` gen_server is the central process keeping track of the
+listeners, the acceptors and the connection processes. It does so through
+the use of a public ets table called ```ranch_server``` too. This allows
+some operations to be sequential by going through the gen_server, while
+others just query the ets table directly, ensuring there is no bottleneck
+for the most common operations.
+
+Because the most common operation is keeping track of the number of
+connections currently being used for each listener, the ets table
+has ```write_concurrency``` enabled, allowing us to perform all these
+operations concurrently using ```ets:update_counter/3```. To read the number
+of connections we simply increment the counter by 0, which allows us
+to stay in a write context and still receive the counter's value.
+
+For increased fault tolerance, the owner of the ets table is
+```ranch_sup``` and not ```ranch_server``` as you could expect. This way,
+if the ```ranch_server``` gen_server fails, it doesn't lose any information
+and the restarted process can continue as if nothing happened. Note that
+this usage is not recommended by OTP.
+
+Listeners are grouped into the ```ranch_listener_sup``` supervisor and
+consist of three kinds of processes: the listener gen_server, the
+acceptor processes and the connection processes, both grouped under
+their own supervisor. All of these processes are registered to the
+```ranch_server``` gen_server with varying amount of information.
+
+All socket operations, including listening for connections, go through
+transport handlers. Accepted connections are given to the protocol handler.
+Transport handlers are simple callback modules for performing operations on
+sockets. Protocol handlers start a new process, which receives socket
+ownership, with no requirements on how the code should be written inside
+that new process.
+
+Efficiency considerations
+-------------------------
+
+Note that for everything related to efficiency and performance,
+you should perform the benchmarks yourself to get the numbers that
+matter to you. Generic benchmarks found on the web may or may not
+be of use to you, you can never know until you benchmark your own
+system.
+
+* * *
+
+The second argument to ```ranch:start_listener/6``` is the number of
+processes that will be accepting connections. Care should be taken
+when choosing this number.
+
+First of all, it should not be confused with the maximum number
+of connections. Acceptor processes are only used for accepting and
+have nothing else in common with connection processes. Therefore
+there is nothing to be gained from setting this number too high,
+in fact it can slow everything else down.
+
+Second, this number should be high enough to allow Ranch to accept
+connections concurrently. But the number of cores available doesn't
+seem to be the only factor for choosing this number, as we can
+observe faster accepts if we have more acceptors than cores. It
+might be entirely dependent on the protocol, however.
+
+Our observations suggest that using 100 acceptors on modern hardware
+is a good solution, as it's big enough to always have acceptors ready
+and it's low enough that it doesn't have a negative impact on the
+system's performances.
View
@@ -0,0 +1,25 @@
+Introduction
+============
+
+Purpose
+-------
+
+Ranch is a socket acceptor pool for TCP protocols.
+
+Ranch aims to provide everything you need to accept TCP connections
+with a small code base and low latency while being easy to use directly
+as an application or to embed into your own.
+
+Prerequisites
+-------------
+
+It is assumed the developer already knows Erlang and has some experience
+with socket programming and TCP protocols.
+
+In order to run the examples available in this user guide, you will need
+Erlang and rebar installed and in your $PATH.
+
+Please see the [rebar repository](https://github.com/basho/rebar) for
+downloading and building instructions. Please look up the environment
+variables documentation of your system for details on how to update the
+$PATH information.
View
@@ -0,0 +1,171 @@
+Listeners
+=========
+
+Purpose
+-------
+
+A listener is a set of processes whose role is to listen on a port
+for new connections. It manages a pool of acceptor processes, each
+of them indefinitely accepting connections. When it does, it starts
+a new process executing the protocol handler code. All the socket
+programming is abstracted through the user of transport handlers.
+
+The listener takes care of supervising all the acceptor and connection
+processes, allowing developers to focus on building their application.
+
+Starting and stopping
+---------------------
+
+Ranch does nothing by default. It is up to the application developer
+to request that Ranch listens for connections.
+
+A listener can be started and stopped at will.
+
+When starting a listener, a number of different settings are required:
+ * A name to identify it locally and be able to interact with it.
+ * The number of acceptors in the pool.
+ * A transport handler and its associated options.
+ * A protocol handler and its associated options.
+
+Ranch includes both TCP and SSL transport handlers, respectively
+```ranch_tcp``` and ```ranch_ssl```.
+
+A listener can be started by calling the ```ranch:start_listener/6```
+function. Before doing so however, you must ensure that the ```ranch```
+application is started.
+
+To start the ```ranch``` application:
+
+``` erlang
+ok = application:start(ranch).
+```
+
+You are then ready to start a listener. Let's call it ```tcp_echo```. It will
+have a pool of 100 acceptors, use a TCP transport and forward connections
+to the ```echo_protocol``` handler.
+
+``` erlang
+{ok, _} = ranch:start_listener(tcp_echo, 100,
+ ranch_tcp, [{port, 5555}],
+ echo_protocol, []
+).
+```
+
+You can try this out by compiling and running the ```tcp_echo``` example in the
+examples directory. To do so, open a shell in the ```examples/tcp_echo/```
+directory and run the following commands:
+
+```
+% rebar get-deps compile
+% ./start.sh
+Listening on port 5555
+```
+
+You can then connect to it using telnet and see the echo server reply
+everything you send to it. Then when you're done testing, you can use
+the ```Ctrl+]``` key to escape to the telnet command line and type
+```quit``` to exit.
+
+```
+% telnet localhost 5555
+Trying 127.0.0.1...
+Connected to localhost.
+Escape character is '^]'.
+Hello!
+Hello!
+It works!
+It works!
+^]
+
+telnet> quit
+Connection closed.
+```
+
+Listening on a random port
+--------------------------
+
+You do not have to specify a specific port to listen on. If you give
+the port number 0, or if you omit the port number entirely, Ranch will
+start listening on a random port.
+
+You can retrieve this port number by calling ```ranch:get_port/1```. The
+argument is the name of the listener you gave in ```ranch:start_listener/6```.
+
+``` erlang
+{ok, _} = ranch:start_listener(tcp_echo, 100,
+ ranch_tcp, [{port, 0}],
+ echo_protocol, []
+).
+Port = ranch:get_port(tcp_echo).
+```
+
+Listening on a port =< 1024
+---------------------------
+
+This is currently not possible. We recommend the use of load balancing
+or NAT firewall rules if the need arise. Proxies can sometimes also be
+used although that's a less efficient solution.
+
+Limiting the number of concurrent connections
+---------------------------------------------
+
+The ```max_connections``` transport option allows you to limit the number
+of concurrent connections. It defaults to 1024. Its purpose is to
+prevent your system from being overloaded and ensuring all the
+connections are handled optimally.
+
+``` erlang
+{ok, _} = ranch:start_listener(tcp_echo, 100,
+ ranch_tcp, [{port, 5555}, {max_connections, 100}],
+ echo_protocol, []
+).
+```
+
+You can disable this limit by setting its value to the atom ```infinity```.
+
+``` erlang
+{ok, _} = ranch:start_listener(tcp_echo, 100,
+ ranch_tcp, [{port, 5555}, {max_connections, infinity}],
+ echo_protocol, []
+).
+```
+
+You may not always want connections to be counted when checking for
+```max_connections```. For example you might have a protocol where both
+short-lived and long-lived connections are possible. If the long-lived
+connections are mostly waiting for messages, then they don't consume
+much resources and can safely be removed from the count.
+
+To remove the connection from the count, you must call the
+```ranch_listener:remove_connection/1``` from within the connection process,
+with the listener pid as the only argument.
+
+``` erlang
+ranch_listener:remove_connection(ListenerPid).
+```
+
+As seen in the chapter covering protocols, this pid is received as the
+first argument of the protocol's ```start_link/4``` callback.
+
+Upgrading
+---------
+
+Ranch allows you to upgrade the protocol options. This takes effect
+immediately and for all subsequent connections.
+
+To upgrade the protocol options, call ```ranch:set_protocol_options/2```
+with the name of the listener as first argument and the new options
+as the second.
+
+``` erlang
+ranch:set_protocol_options(tcp_echo, NewOpts).
+```
+
+All future connections will use the new options.
+
+You can also retrieve the current options similarly by
+calling ```ranch:get_protocol_options/1```.
+
+``` erlang
+Opts = ranch:get_protocol_options(tcp_echo).
+```
Oops, something went wrong.

0 comments on commit 85510e3

Please sign in to comment.