私は現在、Erlangで書かれたコードの一部で極端な状態をテストしています。
複数の受け入れ機能を持つために、 learnyousomeerlang.comのスーパーバイザーの手法を実装しました。
ここでは、スーパーバイザーのSSL接続を処理するためにコードが少し変更されています。
-module(mymodule).
-behaviour(supervisor).
-export([start/0, start_socket/0]).
-define(SSL_OPTIONS, [{active, true},
{mode, list},
{reuseaddr, true},
{cacertfile, "./ssl_key/server/gd_bundle.crt"},
{certfile, "./ssl_key/server/cert.pem"},
{keyfile, "./ssl_key/server/key.pem"},
{password, "********"}
]).
-export([init/1]).
start_link() ->
application:start(crypto),
crypto:start(),
application:start(public_key),
application:start(ssl),
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
{ok, LSocket} = ssl:listen(4242, ?SSL_OPTIONS),
spawn_link(fun empty_listeners/0),
{ok, {{simple_one_for_one, 60, 3600},
[{socket,
{mymodule_serv, start_link, [LSocket]}, % pass the socket!
temporary, 1000, worker, [mymodule_serv]}
]}}.
empty_listeners() ->
[start_socket() || _ <- lists:seq(1,100)],
ok.
start_socket() ->
supervisor:start_child(?MODULE, []).
接続しているすべてのクライアントを表すgen_serverのコードは次のとおりです。
-module(mymodule_serv).
-behaviour(gen_server).
-export([start_link/1]).
-export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3, handle_info/2]).
start_link(Socket) ->
gen_server:start_link(?MODULE, Socket, []).
init(Socket) ->
gen_server:cast(self(), accept),
{ok, #client{socket=Socket, pid=self()}}.
handle_call(_E, _From, Client) ->
{noreply, Client}.
handle_cast(accept, C = #client{socket=ListenSocket}) ->
{ok, AcceptSocket} = ssl:transport_accept(ListenSocket),
mymodule:start_socket(),
ssl:ssl_accept(AcceptSocket),
ssl:setopts(AcceptSocket, [{active, true}, {mode, list}]),
{noreply, C#client{socket=AcceptSocket, state=connecting}}.
[...]
複数のサーバーから一度に10.000近くの接続を起動することができます。sslがC++コードを受け入れるまでに10秒かかりますが(複数の受け入れが保留されていない場合もあります)、Erlangではこれはまったく異なります。1秒あたり最大20の接続を受け入れます(netstat情報によると、C ++は1秒あたり1Kの接続のようなものを受け入れます)
10K接続が受け入れられるのを待っている間、私も手動で接続しようとしています。
openssl s_client -ssl3 -ign_eof -connect myserver.com:4242
私がするとき3つのケースが起こります:
- 接続は単にタイムアウトします
- 30秒待ってから接続します。少なくとも
- 接続はほぼ直接発生します
2台のコンソールで手動で接続しようとすると、最初に行われたハンドシェイクが必ずしも最初に接続を試みたとは限りません...これは特にわかりました。
サーバー構成は次のとおりです。
- 2xIntel®Xeon®E5620
- 8x 2.4GHz
- 24 Go RAM
私はErlangシェルを次のように始めています:
$erl +S 8:8
編集1:
gen_tcpとの接続を受け入れ、その後SSL接続にアップグレードしようとしました。それでも同じ問題ですが、1秒間に10を超える接続を受け入れることはありません... ssl:ssl_acceptはこれを行っていますか?アーランがこれをスケーリングするのを妨げる何かをロックしますか?
編集2:
erlangで作成された他のSSLサーバーを見回したところ、SSL / TLS接続に何らかのドライバーを使用しているようです。私の例は、RabbitMQとEjabberDです。Erlangコードにssl:ssl_acceptが含まれているところはどこにもありません。私はあまり調査していませんが、TCPソケットをSSL/TLSにアップグレードするために独自のドライバーを作成したようです。ErlangのモジュールSSLに問題があるからですか?SSL / TLSにカスタムドライバーを使用している理由を誰かが知っていますか?
これについて何か考えはありますか?