3

わかりました、私はここで夢中になります。私は自分のサーバーの NIO コードを書き直しており、実際に頭の痛い問題に直面しています。要するに、NIO を「正しく」取得するのは非常に難しいということです。http://rox-xmlrpc.sourceforge.net/niotut/にある Rox のチュートリアルを教えてくれる人もいました。、これは良い方向に進んでいるように見えますが、私が望むほど完全ではありません。たとえば、キュ​​ーに入れられた発信 ByteBuffers が送信された後にのみ、サーバー側で接続を閉じる方法を知る必要があります。SocketChannel.close() は突然であり、時期尚早に実行するとデータが失われる可能性があります。また、読み取り ByteBuffer よりも大きい大きなパケットを送信する必要があります。Rox コード (および私が調べた他のコード) は、これを扱っています。キャッチされていない例外が適切に処理されていないように見える場所もたくさんあります。私のテストではいくつかのエラーがあり、NIO の複雑さを考えると、それらを適切に処理する方法が明確ではありません。

いずれにせよ、これらの問題を解決しようとすると、より複雑な微妙な点が現れ、非常に複雑になっています。そこで、まったく別のアプローチを検討しています。多くの人が、NIO は非常にエラーが発生しやすく、不必要に混乱し、複雑であると言っています。彼らは、各ソケット接続が独自のスレッドで実行されるブロッキング IO を使用する「接続ごとのスレッド」モデルの使用を提唱しています。これは良いアイデアのように思えます。(NIO のように) すべての接続に対して 1 つのセレクター スレッドを使用することで、(スレッドの) オーバーヘッドが高くなりますが、フロント エンドのボトルネックが軽減されます。この感情は、http://paultyma.blogspot.com/2008/03/writing-java-multithreaded-servers.htmlhttp://mailinator.blogspot.com/2008/02/kill-myth-などの投稿にも反映されています。 please-nio-is-not-faster-than.html

コードは NIO に比べて単純なはずですが、いくつかのサンプル コードを見てみたいと思います。何も見つからないようです。問題は、この「接続ごとのスレッドが I/O をブロックする」戦略に、実際に良い Google 検索結果を得ることができるより良い名前があるとは思わないことです。この「古い」I/O方法を使用してスレッドプールを使用してスケールアップする方法を説明するために、チュートリアルまたは簡単な例にリンクしてもらえますか? または、他の知恵の言葉はありますか?どうもありがとう!

4

3 に答える 3

2

NIO を使用している場合は、フレームワークを使用することもお勧めします。私はApache Minaを使用してきましたが、これをお勧めします。

ブロッキング IO に関しては、基本的に、着信接続を受け入れ、各接続を処理する追加のスレッドを生成するリスナー スレッドが必要になります。以下は、もともと Apache Felix プロジェクトに提供されたリスナー コードの例です。完全であるが変更されたバージョンを探している場合は、ここでソースを参照できます。

例えば

    /*
    * Licensed to the Apache Software Foundation (ASF) under one or more
    * contributor license agreements.  See the NOTICE file distributed with
    * this work for additional information regarding copyright ownership.
    * The ASF licenses this file to You under the Apache License, Version 2.0
    * (the "License"); you may not use this file except in compliance with
    * the License.  You may obtain a copy of the License at
    *
    *      http://www.apache.org/licenses/LICENSE-2.0
    *
        * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    */
    package org.apache.felix.shell.remote;
    
    
    import java.io.IOException;
    import java.io.PrintStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.net.SocketException;
    
    
    /**
     * Implements a simple listener that will accept a single connection.
     * <p/>
     *
     * @author Dieter Wimberger (wimpi)
     */
    class Listener
    {
    
        private int m_Port;
        private Thread m_ListenerThread;
        private boolean m_Stop = false;
        private ServerSocket m_ServerSocket;
        private AtomicInteger m_UseCounter;
        private int m_MaxConnections;
    
    
        /**
         * Activates this listener on a listener thread (telnetconsole.Listener).
         */
        public void activate()
        {
            //configure from system property
            try
            {
                m_Port = Integer.parseInt( System.getProperty( "osgi.shell.telnet.port", "6666" ) );
            }
            catch ( NumberFormatException ex )
            {
                Activator.getServices().error( "Listener::activate()", ex );
            }
            try
            {
                m_MaxConnections = Integer.parseInt( System.getProperty( "osgi.shell.telnet.maxconn", "2" ) );
            }
            catch ( NumberFormatException ex )
            {
                Activator.getServices().error( "Listener::activate()", ex );
            }
            m_UseCounter = new AtomicInteger( 0 );
            m_ListenerThread = new Thread( new Acceptor(), "telnetconsole.Listener" );
            m_ListenerThread.start();
        }//activate
    
    
        /**
         * Deactivates this listener.
         * <p/>
         * The listener's socket will be closed, which should cause an interrupt in the
         * listener thread and allow for it to return. The calling thread joins the listener
         * thread until it returns (to ensure a clean stop).
         */
        public void deactivate()
        {
            try
            {
                m_Stop = true;
                //wait for the listener thread
                m_ServerSocket.close();
                m_ListenerThread.join();
            }
            catch ( Exception ex )
            {
                Activator.getServices().error( "Listener::deactivate()", ex );
            }
        }//deactivate
    
        /**
         * Class that implements the listener's accept logic as a <tt>Runnable</tt>.
         */
        private class Acceptor implements Runnable
        {
    
            /**
             * Listens constantly to a server socket and handles incoming connections.
             * One connection will be accepted and routed into the shell, all others will
             * be notified and closed.
             * <p/>
             * The mechanism that should allow the thread to unblock from the ServerSocket.accept() call
             * is currently closing the ServerSocket from another thread. When the stop flag is set,
             * this should cause the thread to return and stop.
             */
            public void run()
            {
                try
                {
                    /*
                        A server socket is opened with a connectivity queue of a size specified
                        in int floodProtection.  Concurrent login handling under normal circumstances
                        should be handled properly, but denial of service attacks via massive parallel
                        program logins should be prevented with this.
                    */
                    m_ServerSocket = new ServerSocket( m_Port, 1 );
                    do
                    {
                        try
                        {
                            Socket s = m_ServerSocket.accept();
                            if ( m_UseCounter.get() >= m_MaxConnections )
                            {
                                //reject with message
                                PrintStream out = new PrintStream( s.getOutputStream() );
                                out.print( INUSE_MESSAGE );
                                out.flush();
                                //close
                                out.close();
                                s.close();
                            }
                            else
                            {
                                m_UseCounter.increment();
                                //run on the connection thread
                                Thread connectionThread = new Thread( new Shell( s, m_UseCounter ) );
                                connectionThread.start();
                            }
                        }
                        catch ( SocketException ex )
                        {
                        }
                    }
                    while ( !m_Stop );
    
                }
                catch ( IOException e )
                {
                    Activator.getServices().error( "Listener.Acceptor::activate()", e );
                }
            }//run
    
        }//inner class Acceptor
    
        private static final String INUSE_MESSAGE = "Connection refused.\r\n"
            + "All possible connections are currently being used.\r\n";
    
    }//class Listener

他の例は、ここここにあります。

負荷が増えると、ブロッキング モデルに対する NIO の利点が発揮されることに注意してください。ある時点から、スレッドの作成、管理、およびコンテキストの切り替えのための余分な作業量によって、システムのパフォーマンスが制限されます。

于 2010-01-28T15:42:49.903 に答える
0

NIO を直接使用する代わりに、Grizzlyなどのより高いレベルのフレームワークの使用を検討することもできます。フレームワークを使用すると、NIO の微妙な点ではなく、ユース ケースに集中できるはずです。

于 2009-08-05T10:46:18.927 に答える
0

JDK の sample/nio ディレクトリを調べることをお勧めします。これには、あなたが言及した2つを含む多くの簡単な例があります。

于 2009-08-01T06:30:20.347 に答える