1

長いテキストの壁を前もってお詫び申し上げます。

私はゲームサーバーを書いています。基本的に、接続を受け入れるスレッド、各プレイヤー用の個別のスレッド、およびすべての更新を処理するスレッドがあります。個々のプレーヤー スレッドの唯一の責任は、実際のクライアントに対してパケットを読み書きすることです。すべての I/O は古き良きjava.ioパッケージを使用して行われることに注意してください。

とにかく、いくつかのコンテンツを書き終えたとき、ある種のストレステストを実行したいと決めました. そこで、友人にボット フラッディング プログラムを送信しました。このプログラムは、偽のプレイヤーをサーバーにすばやく接続します。ローカルで実行するよりも、接続がローカルでない方が現実的であると考えました。テストからわかったことは、プレーヤーの更新サイクル タイムが非常に優れていることでした。ただし、約 200 人のプレーヤーが接続されると、サイクル タイムが劇的に急増し始めます (15 ミリ秒から9000 ミリ秒を超えることもあります)。)。最初は、アクセプタ スレッドに関係しているのではないかと考えていました。サイクルが遅くなると、「A player has connected from ...」というメッセージを受け取る頻度が減っていることに気付いたからです。しかし、更新サイクルの個々のステップにかかる時間を測定することにした後、それが原因ではないことがわかりました。ボトルネックはプレーヤーの更新でした。プレイヤーの更新方法を一番下までスクロールしたところ、すぐに原因がわかりました。

問題は、各プレーヤーの更新の最後に、更新パケットが送信されることです。サイズが数キロバイトになることもある大きなパケットは、構築されると、ソケットの出力ストリームを介して送信されます。多くの人が知っているように、パケットの書き込みはブロッキング操作です。そして、そのブロッキングが、一見ランダムに見えるサイクル タイムのスパイクの原因でした。数百人のプレーヤーが接続すると、サーバーはそれらすべてのプレーヤーを更新しようとします。ただし、場合によっては、接続の 1 つが少し遅くなることがあります。したがって、サーバーは、遅いソケットを介して接続されたプレーヤーを処理しようとするまで、プレーヤーを処理し続けます。更新パケットを作成すると、それを送信しようとします。ただし、ソケットが遅いため、送信は長時間ブロックされます。送信操作がブロックされている間、他のプレーヤーは更新されていません。その結果、サイクルに非常に長い時間がかかる場合がありました。

今、私はこの問題にアプローチする方法に行き詰まっています。理想的な状況は、サーバーがパケットを作成し、そのパケットを個々のプレーヤー スレッドに渡し、それを送信する (そして、独自の小さなスレッドで楽しくブロックする) ことです。更新パケットが構築されると、サーバー スレッドは更新パケットを格納し、パケットが構築されたことを知らせる何らかのフラグを立てる可能性があります。これは問題ないように思えますが、問題は、多くの場合、個々のプレーヤー スレッドが独自のブロック操作に巻き込まれることです。パケットの受信を待機しているため、プレイヤー スレッドがブロックされています。したがって、更新パケットは読み取りによるブロックが完了すると書き込まれますが、更新パケットが実際に構築された後に送信されるのではないかと心配しています。これにより、プレーヤーが遅れているように見えます。

そこで、皆さんに提案をお願いします。この問題にどのように取り組みますか? 誰かが私に NIO を使用することを勧めましたが、それではコードの大部分を書き直す必要があるため、別のルートを取るため、可能性を使い果たしたいと考えています。

4

1 に答える 1

1

@Drakosha がコメントで述べたように、複数のスレッドがサービスを提供するメッセージ キューを使用できます。

多分このようなもの:

import java.util.concurrent.LinkedBlockingDeque;
import static java.lang.System.*;
class Message{
    String message;
    public Message(String message) {this.message=message;}
    public String toString(){return message;}
}
public class Test {
static LinkedBlockingDeque<Message> outgoing=new LinkedBlockingDeque<>();
    public static void main(String[] args) {
        for(int i=0;i<10;i++) new Player(outgoing,i).start();
        for(int i=0;i<3;i++)  new Sender(outgoing,i).start();
    }
}
class Player extends Thread{
    private LinkedBlockingDeque<Message> outgoing;
    private int id;
    public Player(LinkedBlockingDeque<Message> outgoing,int id){this.outgoing=outgoing; this.id=id;}
    public void run(){
        for(int i=0;i<10;i++){
            try {
                outgoing.putLast(new Message(String.format("Player %d's message",id)));
                Thread.sleep(20);
            } catch (InterruptedException e) {e.printStackTrace();}
        }
    }
}
class Sender extends Thread{
    private LinkedBlockingDeque<Message> outgoing;
    private int id;
    public Sender(LinkedBlockingDeque<Message> outgoing,int id){this.outgoing=outgoing; this.id=id;}
    public void run(){
        while(true){
            Message m = null;
            try {
                m = outgoing.takeFirst();
                //send message
                if(Math.random()>0.95){
                    out.printf("Sender %d is hanging! %n",id);
                    Thread.sleep(1000);//Slow socket is blocking!
                }
            } catch (InterruptedException e) {e.printStackTrace();}
            out.printf("Sender %d sent message \"%s\" %n",id,m);
        }
    }
}
于 2013-01-27T07:16:03.277 に答える