私は手で構築したファイル サーバーのストレス テストにsiegeを使用しています。小さなファイル (1KB 未満) ではかなりうまく機能しますが、1MB のファイルでテストすると、期待どおりに機能しません。
以下は、小さなファイルを使用したテストの結果です。
neevek@~$ siege -c 1000 -r 10 -b http://127.0.0.1:9090/1KB.txt
** SIEGE 2.71
** Preparing 1000 concurrent users for battle.
The server is now under siege.. done.
Transactions: 10000 hits
Availability: 100.00 %
Elapsed time: 9.17 secs
Data transferred: 3.93 MB
Response time: 0.01 secs
Transaction rate: 1090.51 trans/sec
Throughput: 0.43 MB/sec
Concurrency: 7.29
Successful transactions: 10000
Failed transactions: 0
Longest transaction: 1.17
Shortest transaction: 0.00
以下は、1MB のファイルを使用したテストの結果です。
neevek@~$ siege -c 1000 -r 10 -b http://127.0.0.1:9090/1MB.txt
** SIEGE 2.71
** Preparing 1000 concurrent users for battle.
The server is now under siege...[error] socket: read error Connection reset by peer sock.c:460: Connection reset by peer
[error] socket: unable to connect sock.c:222: Connection reset by peer
[error] socket: unable to connect sock.c:222: Connection reset by peer
[error] socket: unable to connect sock.c:222: Connection reset by peer
[error] socket: read error Connection reset by peer sock.c:460: Connection reset by peer
[error] socket: unable to connect sock.c:222: Connection reset by peer
[error] socket: read error Connection reset by peer sock.c:460: Connection reset by peer
[error] socket: read error Connection reset by peer sock.c:460: Connection reset by peer
[error] socket: read error Connection reset by peer sock.c:460: Connection reset by peer
[error] socket: read error Connection reset by peer sock.c:460: Connection reset by peer
がsiege
上記のエラーで終了しても、ファイル サーバーは固定数のWRITABLE
SelectionKey でスピンします。つまりSelector.select()
、50 などの固定数を返し続けます。
上記のテストでは、ファイル サーバーが 50 を超える同時接続を受け入れることができないように見えます。これは、小さなファイルでテストを実行すると、サーバーが 1 つまたは 2 つの SelectionKeys であることに気付き、大きなselect
ファイルで実行すると、select
毎回50まで。
仕方なく増やそうとしbacklog
ましたSocket.bind()
。
問題の原因は何ですか?
編集
より詳しい情報:
1MB のファイルでテストしたところ、エラーでsiege
終了し、ファイル サーバーは198の接続しか受け入れないことに気付きましたが、 1000 の同時接続 x 10 ラウンド (1000*10=10000) を指定してサーバーをフラッディングしました。Broken pipe
編集2
同じ問題を再現するために、次のコード (単一のクラス) でテストしました。このコードでは、接続のみを受け入れ、読み取りも書き込みもsiege
行いません。また、Selector が選択できるキーは 1000 個未満であることにも気付きました。以下のコードを試して問題を確認してください。Connection reset
Broken pipe
public class TestNIO implements Runnable {
ServerSocketChannel mServerSocketChannel;
Selector mSelector;
public static void main(String[] args) throws Exception {
new TestNIO().start();
}
public TestNIO () throws Exception {
mSelector = Selector.open();
}
public void start () throws Exception {
mServerSocketChannel = ServerSocketChannel.open();
mServerSocketChannel.configureBlocking(false);
mServerSocketChannel.socket().bind(new InetSocketAddress(9090));
mServerSocketChannel.socket().setSoTimeout(150000);
mServerSocketChannel.register(mSelector, SelectionKey.OP_ACCEPT);
int port = mServerSocketChannel.socket().getLocalPort();
String serverName = "http://" + InetAddress.getLocalHost().getHostName() + ":" + port;
System.out.println("Server start listening on " + serverName);
new Thread(this).start();
}
@Override
public void run() {
try {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
while (true) {
int num = mSelector.select();
System.out.println("SELECT = " + num + "/" + mSelector.keys().size());
if (num > 0) {
Iterator<SelectionKey> keys = mSelector.selectedKeys().iterator();
while (keys.hasNext()) {
final SelectionKey key = keys.next();
if (key.isValid() && key.isAcceptable()) {
accept(key);
}
}
// clear the selected keys
mSelector.selectedKeys().clear();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void accept (SelectionKey key) throws IOException {
SocketChannel socketChannel = mServerSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.socket().setSoTimeout(1000000);
socketChannel.socket().setKeepAlive(true);
// since we are connected, we are ready to READ
socketChannel.register(mSelector, SelectionKey.OP_READ);
}
}