6

私はScalaを初めて使用するので、問題を解決するためにしばらく時間を費やしましたが、質問は非常に単純かもしれません。私は単純なScalaTCPサーバーを持っています(アクターなし、シングルスレッド):

import java.io._
import java.net._

object Application {
  def readSocket(socket: Socket): String = {
    val bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream))
    var request = ""
    var line = ""
    do {
      line = bufferedReader.readLine()
      if (line == null) {
        println("Stream terminated")
        return request
      }
      request += line + "\n"
    } while (line != "")
    request
  }

  def writeSocket(socket: Socket, string: String) {
    val out: PrintWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream))
    out.println(string)
    out.flush()
  }

  def main(args: Array[String]) {
    val port = 8000
    val serverSocket = new ServerSocket(port)
    while (true) {
      val socket = serverSocket.accept()
      readSocket(socket)
      writeSocket(socket, "HTTP/1.1 200 OK\r\n\r\nOK")
      socket.close()
    }
  }
}

サーバーは着信要求をリッスンし、本文に1つの単語localhost:8000を含むHTTP応答を送信します。OK次に、次のようにApacheBenchmarkを実行します。

ab -c 1000 -n 10000 http://localhost:8000/

これは初めてうまく機能します。2回目に起動abするとハングし、次の出力が生成されnetstat -a | grep 8000ます。

....
tcp        0      0 localhost.localdo:43709 localhost.localdom:8000 FIN_WAIT2  
tcp        0      0 localhost.localdo:43711 localhost.localdom:8000 FIN_WAIT2  
tcp        0      0 localhost.localdo:43717 localhost.localdom:8000 FIN_WAIT2  
tcp        0      0 localhost.localdo:43777 localhost.localdom:8000 FIN_WAIT2  
tcp        0      0 localhost.localdo:43722 localhost.localdom:8000 FIN_WAIT2  
tcp        0      0 localhost.localdo:43725 localhost.localdom:8000 FIN_WAIT2  
tcp6       0      0 [::]:8000               [::]:*                  LISTEN     
tcp6      83      0 localhost.localdom:8000 localhost.localdo:43724 CLOSE_WAIT 
tcp6      83      0 localhost.localdom:8000 localhost.localdo:43786 CLOSE_WAIT 
tcp6       1      0 localhost.localdom:8000 localhost.localdo:43679 CLOSE_WAIT 
tcp6      83      0 localhost.localdom:8000 localhost.localdo:43735 CLOSE_WAIT 
tcp6      83      0 localhost.localdom:8000 localhost.localdo:43757 CLOSE_WAIT 
tcp6      83      0 localhost.localdom:8000 localhost.localdo:43754 CLOSE_WAIT 
tcp6      83      0 localhost.localdom:8000 localhost.localdo:43723 CLOSE_WAIT
....

それ以降、サーバーはこれ以上リクエストを処理しません。もう1つの詳細:ab同じパラメーターを持つ同じスクリプトは、同じマシン上で単純なNode.jsサーバーをスムーズにテストします。したがって、この問題は、再利用可能に設定した、開いているTCP接続の数とは関係ありません。

sudo sysctl -w net.ipv4.tcp_tw_recycle=1
sudo sysctl -w net.ipv4.tcp_tw_reuse=1

誰かが私が欠けているものについての手がかりを私に与えることができますか?

編集:ストリーム処理の終了が上記のコードに追加されました:

    if (line == null) {
      println("Stream terminated")
      return request
    }
4

2 に答える 2

2

いつか同じ問題に出くわす人のために、私自身の質問に対する(部分的な)回答を投稿します。まず、問題の性質はソースコードではなく、多数の接続を制限するシステム自体にあります。問題は、socket渡されたreadSocket関数が特定の条件下で破損しているように見えることです。つまり、関数を読み取ることができず、最初の呼び出しでbufferedReader.readLine()戻るか、null無期限にハングします。次の2つの手順により、一部のマシンでコードが機能するようになります。

  1. ソケットへの同時接続の数を増やす

    sysctl -w net.core.somaxconn=65535
    
  2. ServerSocket接続キューの長さを明示的に設定する2番目のパラメーターをコンストラクターに提供します。

    val maxQueue = 50000
    val serverSocket = new ServerSocket(port, maxQueue)
    

上記の手順でEC2m1.largeインスタンスの問題は解決しましたが、ローカルマシンでまだ問題が発生しています。より良い方法は、その種のものにAkkaを使用することです。

import akka.actor._
import java.net.InetSocketAddress
import akka.util.ByteString

class TCPServer(port: Int) extends Actor {

  override def preStart {
    IOManager(context.system).listen(new InetSocketAddress(port))
  }

  def receive = {
    case IO.NewClient(server) =>
      server.accept()
    case IO.Read(rHandle, bytes) => {
      val byteString = ByteString("HTTP/1.1 200 OK\r\n\r\nOK")
      rHandle.asSocket.write(byteString)
      rHandle.close()
    }
  }
}

object Application {
  def main(args: Array[String]) {
    val port = 8000
    ActorSystem().actorOf(Props(new TCPServer(port)))
  }
}
于 2012-05-30T06:07:38.130 に答える
0

まず、なしでこれを試すことをお勧めしabます。あなたは次のようなことをすることができます:

echo "I'm\nHappy\n" | nc -vv localhost 8000

次に、ストリームの終わりを処理することをお勧めします。これはBufferedReader.readLine()nullを返すところです。上記のコードは、空の文字列のみをチェックします。これを修正したら、もう一度やり直します。次に、すべてが良好に見えたら、abでテストします。問題が解決しない場合はお知らせください。

于 2012-05-26T23:31:36.533 に答える