tomcat-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Mark Thomas <ma...@apache.org>
Subject Re: Tomcat WebSocket does not always send asynchronous messages
Date Tue, 07 Mar 2017 14:55:41 GMT
On 07/03/17 11:03, Mark Thomas wrote:
> On 07/03/17 08:28, Pesonen, Harri wrote:
>> Hello, we have a problem that Tomcat WebSocket does not always send asynchronous
messages. This problem is random, and it has been reproduced in Tomcat 8.5.6 and 8.5.11. Synchronized
operations work fine, and also the asynchronous operations work except in one special case.
When there is a big message that we want to send to client, we split it into 16 kB packets
for technical reasons, and then we send them very quickly after each other using
>>
>> /**
>> * Initiates the asynchronous transmission of a binary message. This method returns
before the message
>> * is transmitted. Developers provide a callback to be notified when the message has
been
>> * transmitted. Errors in transmission are given to the developer in the SendResult
object.
>> *
>> * @param data       the data being sent, must not be {@code null}.
>> * @param handler the handler that will be notified of progress, must not be {@code
null}.
>> * @throws IllegalArgumentException if either the data or the handler are {@code null}.
>> */
>> void sendBinary(ByteBuffer data, SendHandler handler);
>>
>> Because there can be only one ongoing write to socket, we use Semaphore that is released
on the SendHandler callback:
>>
>> public void onResult(javax.websocket.SendResult result) {
>>     semaphore.release();
>>
>> So the code to send is actually:
>>
>> semaphore.acquireUninterruptibly();
>> async.sendBinary(buf, asyncHandler);
>>
>> This works fine in most cases. But when we send one 16 kB packet and then immediately
one smaller packet (4 kB), then randomly the smaller packet is not actually sent, but only
after we call
>>
>> async.sendPing(new byte[0])
>>
>> in another thread. sendPing() is called every 20 seconds to keep the WebSocket connection
alive. This means that the last packet gets extra delay on client, which varies between 0
- 20 seconds.
>>
>> We have an easy workaround to the problem. If we call flushBatch() after each sendBinary(),
then it works great, but this means that the sending is not actually asynchronous, because
flushBatch() is synchronous.
>> Also we should not be forced to call flushBatch(), because we are not enabling batching.
Instead we make sure that it is disabled:
>>
>> if (async.getBatchingAllowed()) {
>>     async.setBatchingAllowed(false);
>>
>> So the working code is:
>>
>> semaphore.acquireUninterruptibly();
>> async.sendBinary(buf, asyncHandler);
>> async.flushBatch();
>>
>> Normally the code works fine without flushBatch(), if there is delay between the
messages, but when we send the messages right after each other, then the last small message
is not always sent immediately.
>> I looked at the Apache WebSocket code, but it was not clear to me what is happening
there.
>> Any ideas what is going on here? Any ideas how I could troubleshoot this more?
> 
> Thanks for providing such a clear description of the problem you are seeing.
> 
> It sounds like there is a race condition somewhere in the WebSocket
> code. With the detail you have provided, I think there is a reasonable
> chance of finding via code inspection.

Some follow-up questions to help narrow the search.

This is server side, correct?

Are you using the compression extension? If yes, do you see the problem
without it?

When you say "we split it into 16 kB packets" do you mean you split it
into multiple WebSocket messages?

If you insert a short delay before sending the final 4kB does that
reduce the frequency of the problem?

Thanks,

Mark


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Mime
View raw message