2011-11-07 2 views
1

В моей программе, использующей java nio, socketchannel.write() становится очень медленным, когда он пытается последовательно писать сообщения 10 КБ. Измеренное время для записи полного сообщения 10 КБ составляет от 160 мс до 200 мс. Но время для написания полного сообщения размером 5 КБ составляет всего 0,8 мс.socketchannel.write() становится очень медленным, когда размер сообщения большой.

В селекторе у меня есть только Selection.OP_READ и не обрабатывать Selection.OP_WRITE. Когда получено большое полное сообщение, оно записывается в другой приемник 4 раза.

Есть ли у кого-то такая же проблема? Есть сообщение о socketchannel.write() медленно. Мой вопрос заключается в том, как чередовать изменения между OP_READ и OP_WRITE?

Если я добавляю inerval, например 150 мс, время отклика уменьшается. Есть ли способ найти, когда буфер заполнен, поэтому я могу позволить программе ждать. Моя операционная система - Windows XP.

Спасибо.

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

// это WriteData() часть с использованием NiO:

 while (buffer.hasRemaining()) { 
     try {   
       buffer.flip();     
       n = socket.write(buffer);   
       if(n == 0) {     
        key.interestOps(SelectionKey.OP_WRITE); 
        key.attach(buffer);    
        break; 
       }        
     } catch (IOException e) {    
      e.printStackTrace(); 
     } finally { 
      buffer.compact(); 
     } 
    } 

    if(buffer.position()==0) {     
     key.interestOps(SelectionKey.OP_READ); 
    } 
+0

Я предполагаю, что он пытается постоянно посылать один единственный TCP пакет 10KB, который терпит неудачу много. –

+0

Сообщения публикуются последовательно. Топология - это цепочка узлов. Интервал сообщения составляет 100 мс – susan

+1

@MartijnCourteaux No. Пакетирование TCP происходит на более низком уровне. Он не будет пытаться отправить IP-пакет> MTU пути. – EJP

ответ

1

Если запись занимает более 20 микро секунд, я хотел бы предложить вам иметь буферную полный вопрос. Я предполагаю, что вы используете блокировку NIO. Когда буфер отправки не заполнен, обычно требуется от 5 до 20 микросекунд. В прошлом я настроил свой сервер, чтобы убить любого медленного потребителя, который занимает 2 мс для записи. (Возможно, немного агрессивный.;)

Вы можете попробовать увеличить размер буфера отправки (Socket.setSendBufferSize (int), который также доступен для SocketChannels), но, похоже, вы пытаетесь отправить больше данных, чем ваша пропускная способность позволяет.

10 КБ не является большим сообщением, типичный размер буфера отправки составляет 64 КБ, поэтому для его заполнения вам нужно будет иметь 6-7 сообщений, не имеющих значения. Это может объяснить способ 5KB относительно быстро.

+0

Спасибо. Но я использую Java неблокирующий сокет. Когда сервер получает сообщение, он пересылает его на следующий прогон 4 раза. Я не меняю ключ выбора на OP_WRITE. Когда я пишу, клавиша выбора - OP_READ.Думаю, моя проблема в том, что полный буфер tcp. Если моя догадка правильная, как решить эту проблему? Будет ли полезно отключить сокет-канал? – susan

+0

Было бы полезно определить, почему у вас недостаточно полосы пропускания или медленного потребителя, и исправить ее. Если у вас нет контроля над ними, закрытие сокета - последнее средство. BTW: Если вы используете неблокирование, он не должен блокировать запись, он должен просто не писать какие-либо данные!? –

3

Я предлагаю, чтобы ваш процесс чтения был медленным, и это вызывает резервное копирование буфера приема, что вызывает резервное копирование вашего буфера отправки, что задерживает ваши отправления.

Или иначе вы неправильно написали код для неблокирующего режима. Если вы получаете нулевой результат из метода write(), вы должны (a) изменить interestOps на OP_WRITE и (b) вернуться к вашему циклу выбора. Когда вы получите OP_WRITE, вы должны повторить запись; если вы написали все данные, измените interestOps обратно на OP_READ, иначе оставьте все как есть и дождитесь следующего OP_WRITE. Если вы пытаетесь выполнить цикл во время записи в неблокирующем режиме даже при наличии записей с нулевой длиной, вы просто начнете вращаться, теряя процессорные циклы и время.

Modulo ошибки:

while (buffer.position() > 0) 
{ 
    try 
    { 
    buffer.flip(); 
    int count = ch.write(buffer); 
    if (count == 0) 
    { 
     key.interestOps(SelectionKey.OP_WRITE); 
     break; 
    } 
    } 
    finally 
    { 
    buffer.compact(); 
    } 
} 
if (buffer.position() == 0) 
{ 
    key.interestOps(SelectionKey.OP_READ); 
} 
+0

@susan Вы делаете совершенно новую регистрацию. Я сказал, чтобы изменить interestOps на OP_WRITE, и я также сказал, чтобы вернуть их обратно в OP_READ, когда вы получите успешную запись. Вы этого не делаете. Другая проблема заключается в том, что вы должны всегда * compact() после flip() и write() независимо от состояния записи, иначе буфер остается в недопустимом состоянии. Другим является то, что write() никогда не возвращает <0: см. Javadoc. – EJP

+0

Если есть пример кода, это будет очень полезно. Полезно ли изменить системный tcp-буфер Windows? – susan

+0

@susan См. Выше. Буферы отправки и приема сокета Windows, как известно, слишком малы при 8k, и всегда полезно поднять их до 32k или 48k. Однако я считаю, что у вас есть проблема с кодированием, на которую он не будет адресован. – EJP

Смежные вопросы