Блокировка Java InputStream - PullRequest
       6

Блокировка Java InputStream

3 голосов
/ 22 декабря 2011

Я использую InputStream для потоковой передачи файла по сети.

Однако, если моя сеть выходит из строя, процесс чтения файла блокируется методом read и никогда не восстанавливается, если сеть появляется снова.

Мне было интересно, как мне справиться с этим случаем, и не следует ли выдавать какое-то исключение, если InputStream исчезнет.

Код такой.

Url someUrl = new Url("http://somefile.com");
InputStream inputStream = someUrl.openStream();
byte[] byteArray = new byte[];
int size = 1024;
inputStream.read(byteArray,0,size);

Так что где-то после вызова read сеть отключается и метод read блокируется.

Как я могу справиться с этой ситуацией, поскольку чтение, похоже, не вызывает исключения.

Ответы [ 6 ]

0 голосов
/ 23 августа 2013

Функция inputStream.read () является блокирующей функцией и должна вызываться в потоке. Существует альтернативный способ избежать этой ситуации. InputStream также имеет метод available (). Возвращает количество байтов, которые можно прочитать из потока.

Вызывайте метод чтения, только если в потоке доступно несколько байтов.

int length = 0;
int ret = in.available();
if(ret != 0){           
   length = in.read(recv);
}

InputStream выдает IOException. Надеюсь, что эта информация полезна для вас.

0 голосов
/ 22 декабря 2011

Это не имеет большого значения.Все, что вам нужно сделать, это установить тайм-аут для вашего соединения.

URL url = ...; 
URLConnection conn = URL.openConnection(); 
conn.setConnectTimeout( 30000 );
conn.setReadTimeout(15000); 
InputStream is = conn.openStream();

В конце концов произойдет одно из следующих событий.Ваша сеть вернется, и ваши передачи будут возобновлены, стек TCP в конечном итоге истечет время ожидания, в этом случае выдается исключение, или сокет получит исключение закрытия / сброса сокета, и вы получите IOException.Во всех случаях поток отпустит вызов read (), и ваш поток вернется в пул, готовый обслуживать другие запросы, без необходимости делать что-либо дополнительное.

Например, если ваша сеть выходит из строявы не будете получать никаких новых подключений, поэтому тот факт, что этот поток связан, не будет иметь никакого значения, потому что у вас нет входящих подключений. Так что выход из вашей сети - не проблема.

Более вероятный сценарий - сервер, с которым вы разговариваете, может застрять и прекратить отправку вам данных, что также замедлит работу ваших клиентов.Именно здесь настройка таймаутов важна для написания большего количества кода, использования NIO или отдельных потоков и т. Д. Отдельные потоки просто увеличат нагрузку на вашу машину и, в конце концов, заставят вас отказаться от потока после тайм-аута, который в точности соответствует TCPдает тебе.Вы также можете разорвать свой сервер, потому что вы создаете новый поток для каждого запроса, и если вы начнете отказываться от потоков, вы можете легко получить сотни потоков, которые сидят без дела в ожидании тайм-аута на сокете.

Если у вас большой объем трафика на вашем сервере, проходящий через этот метод, и любая задержка во времени ответа от зависимости, например от внешнего сервера, повлияет на ваше время ответа.Поэтому вам придется выяснить, сколько времени вы готовы подождать, прежде чем просто выдадите ошибку, и попросить клиента повторить попытку, потому что сервер, с которого вы читаете этот файл, не отдает его достаточно быстро.

Другие идеи заключаются в локальном кэшировании файла, попытке ограничить количество поездок по сети и т. Д., Чтобы ограничить доступ к не отвечающему пользователю.То же самое может случиться с базами данных на внешних серверах.Если ваша БД не отправляет вам ответы достаточно быстро, это может привести к сбою в пуле потоков, так же как и к файлу, который не работает достаточно быстро.Так зачем беспокоиться о файловых серверах?Дальнейшая обработка ошибок не решит вашу проблему, а просто сделает ваш код тупым.

0 голосов
/ 22 декабря 2011

Иметь отдельный работающий поток, который имеет ссылку на ваш InputStream, и есть что-то, чтобы сбросить его таймер после получения последних данных - или что-то подобное.Если этот флаг не был сброшен через N секунд, тогда поток должен закрыть InputStream.Чтение (...) вызовет IOException, и вы сможете восстановить его.

То, что вам нужно, похоже на сторожевой таймер.Примерно так:

public class WatchDogThread extends Thread
{
    private final Runnable timeoutAction;
    private final AtomicLong lastPoke = new AtomicLong( System.currentTimeMillis() );
    private final long maxWaitTime;

    public WatchDogThread( Runnable timeoutAction, long maxWaitTime )
    {
        this.timeoutAction = timeoutAction;
        this.maxWaitTime = maxWaitTime;
    }

    public void poke()
    {
        lastPoke.set( System.currentTimeMillis() );
    }

    public void run()
    {
        while( Thread.interrupted() ) {
            if( lastPoke.get() + maxWaitTime < System.currentTimeMillis() ) {
                timeoutAction.run();
                break;
            }
            try {
                Thread.sleep( 1000 );
            } catch( InterruptedException e ) {
                break;
            }
        }
    }
}

public class Example
{
    public void method() throws IOException
    {
        final InputStream is = null;
        WatchDogThread watchDog =
            new WatchDogThread(
                new Runnable()
                {
                    @Override
                    public void run()
                    {
                        try {
                            is.close();
                        } catch( IOException e ) {
                            System.err.println( "Failed to close: " + e.getMessage() );
                        }
                    }
                },
                10000
            );
        watchDog.start();
        try {
            is.read();
            watchDog.poke();
        } finally {
            watchDog.interrupt();
        }
    }
}

РЕДАКТИРОВАТЬ:

Как уже было замечено, сокеты уже имеют время ожидания .Это было бы предпочтительнее, чем сторожевой поток.

0 голосов
/ 22 декабря 2011

вызов чтения может быть в состоянии блокировки.

Чтобы корректно обрабатывать эти случаи, необходимо найти, не работает сеть погоды или нет.

Пожалуйста, проверьте эту ветку, чтобы узнать, как обнаружить разрыв сети. Как обнаружить разрыв сети с помощью Java-программы?

0 голосов
/ 22 декабря 2011

Краткий ответ - использовать селекторы из пакета nio.Они позволяют неблокировать сетевые операции.

Если вы собираетесь использовать старые сокеты, вы можете попробовать некоторые примеры кода из здесь

0 голосов
/ 22 декабря 2011

При просмотре документации здесь: http://docs.oracle.com/javase/6/docs/api/java/io/InputStream.html

Похоже, что read вызывает исключение.

Есть несколько вариантов решения вашей конкретной проблемы.

Один из вариантов - отслеживать ход загрузки и сохранять этот статус в другом месте вашей программы.Затем, если загрузка не удалась, вы можете перезапустить ее и возобновить в точке сбоя.

Однако вместо этого я бы перезапустил загрузку, если она не удалась.Вам все равно придется перезапустить его, чтобы вы могли с самого начала повторить все это в случае сбоя.

...