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.
This is next (and currently last) on the list of things I want to look
at before starting the release process for 9.0.x and 8.5.x.
Mark
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]