Java: полные примеры кода блокировки ввода-вывода для потокового соединения по сравнению с NIO? - PullRequest
3 голосов
/ 01 августа 2009

Хорошо, я схожу с ума здесь. Я переписывал код NIO для своего сервера и столкнулся с некоторыми реальными головными болями. Суть в том, что получить NIO "правильно" очень сложно. Некоторые люди указали мне на учебник по Rox по адресу http://rox -xmlrpc.sourceforge.net / niotut / , который, кажется, идет по правильному пути, но не настолько завершен, как хотелось бы. Например, мне нужно знать, как закрыть соединение на стороне сервера только после отправки исходящих байтовых буферов в очередь. SocketChannel.close () является внезапным и может потерять данные, если сделать это преждевременно. Мне также нужно отправить большие пакеты, которые больше, чем чтение ByteBuffer. Код Rox (или любой другой код, на который я смотрел) имеет дело с этим. Есть также много мест, где кажется, что необученные исключения не обрабатываются должным образом. В моих тестах есть некоторые ошибки, и не ясно, как правильно их обрабатывать, учитывая сложность NIO.

Во всяком случае, когда я пытаюсь решить эти проблемы, появляются все больше хитрых тонкостей, и это становится довольно сложным. Поэтому я рассматриваю совершенно другой подход. Многие люди говорят, что NIO очень подвержен ошибкам, излишне запутан и сложен. Они рекомендуют использовать модель «поток на соединение», которая использует блокировку ввода-вывода, когда каждое соединение с сокетом выполняется в своем собственном потоке. Это кажется хорошей идеей и уменьшило бы узкое место на переднем конце, имея один поток селектора для всех соединений (как в NIO), за счет более высоких издержек (для потоков). Это мнение подтверждается сообщениями типа http://paultyma.blogspot.com/2008/03/writing-java-multithreaded-servers.html и http://mailinator.blogspot.com/2008/02/kill-myth-please-nio-is-not-faster-than.html

Код должен быть простым по сравнению с NIO, но я действительно хочу посмотреть пример кода. Кажется, я ничего не могу найти. Проблема в том, что я не думаю, что у этой стратегии «блокировка ввода / вывода для потоков» есть более подходящее название, для которого я могу получить хорошие результаты Google. Может кто-нибудь связать меня с некоторыми учебными пособиями или простыми примерами, чтобы объяснить использование этого «старого» метода ввода-вывода и масштабирование его с помощью пула потоков? Или есть другие слова мудрости? Большое спасибо!

Ответы [ 3 ]

1 голос
/ 28 января 2010

Если вы работаете с NIO, я бы также предложил использовать Framework. Я работал с Apache Mina , и я бы порекомендовал его.

Что касается блокирования ввода-вывода, по сути вам понадобится поток слушателя, который принимает входящие соединения и порождает дополнительные потоки, которые будут обрабатывать каждое соединение. Вот пример такого кода слушателя, который изначально был внесен в проект 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 перед блокирующей моделью проявляется, когда у вас больше нагрузки. С определенного момента объем дополнительной работы по созданию потоков, управлению ими и переключению контекста будет ограничивать производительность вашей системы.

0 голосов
/ 05 августа 2009

Вы также можете рассмотреть возможность использования инфраструктуры более высокого уровня, например Grizzly , вместо непосредственного использования NIO. Фреймворк должен позволить вам сконцентрироваться на вашем случае использования, а не на тонкостях NIO.

0 голосов
/ 01 августа 2009

Я предлагаю вам заглянуть в каталог sample / nio в вашем JDK. Здесь есть несколько простых примеров, в том числе два, которые вы упомянули.

...