Package org.apache.cassandra.net
Class AbstractMessageHandler
- java.lang.Object
-
- io.netty.channel.ChannelHandlerAdapter
-
- io.netty.channel.ChannelInboundHandlerAdapter
-
- org.apache.cassandra.net.AbstractMessageHandler
-
- All Implemented Interfaces:
io.netty.channel.ChannelHandler
,io.netty.channel.ChannelInboundHandler
,FrameDecoder.FrameProcessor
- Direct Known Subclasses:
CQLMessageHandler
,InboundMessageHandler
public abstract class AbstractMessageHandler extends io.netty.channel.ChannelInboundHandlerAdapter implements FrameDecoder.FrameProcessor
Core logic for handling inbound message deserialization and execution (in tandem withFrameDecoder
). Handles small and large messages, corruption, flow control, dispatch of message processing to a suitable consumer. # Interaction withFrameDecoder
AnAbstractMessageHandler
implementation sits on top of aFrameDecoder
in the Netty pipeline, and is tightly coupled with it.FrameDecoder
decodes inbound frames and relies on a suppliedFrameDecoder.FrameProcessor
to act on them.AbstractMessageHandler
provides two implementations of that interface: -process(Frame)
is the default, primary processor, and is expected to be implemented by subclasses -AbstractMessageHandler.UpToOneMessageFrameProcessor
, supplied to the decoder when the handler is reactivated after being put in waiting mode due to lack of acquirable reserve memory capacity permits Return value ofFrameDecoder.FrameProcessor.process(Frame)
determines whether the decoder should keep processing frames (iftrue
is returned) or stop until explicitly reactivated (iffalse
is). To reactivate the decoder (once notified of available resource permits),FrameDecoder.reactivate()
is invoked. # FramesAbstractMessageHandler
operates on frames of messages, and there are several kinds of them: 1.FrameDecoder.IntactFrame
that are contained. As names suggest, these contain one or multiple fully contained messages believed to be uncorrupted. Guaranteed to not contain an part of an incomplete message. SeeprocessFrameOfContainedMessages(ShareableBytes, Limit, Limit)
. 2.FrameDecoder.IntactFrame
that are NOT contained. These are uncorrupted parts of a large message split over multiple parts due to their size. Can represent first or subsequent frame of a large message. SeeprocessFirstFrameOfLargeMessage(IntactFrame, Limit, Limit)
andprocessSubsequentFrameOfLargeMessage(Frame)
. 3.FrameDecoder.CorruptFrame
with corrupt header. These are unrecoverable, and force a connection to be dropped. 4.FrameDecoder.CorruptFrame
with a valid header, but corrupt payload. These can be either contained or uncontained. - contained frames with corrupt payload can be gracefully dropped without dropping the connection - uncontained frames with corrupt payload can be gracefully dropped unless they represent the first frame of a new large message, as in that case we don't know how many bytes to skip SeeprocessCorruptFrame(CorruptFrame)
. Fundamental frame invariants: 1. A contained frame can only have fully-encapsulated messages - 1 to n, that don't cross frame boundaries 2. An uncontained frame can hold a part of one message only. It can NOT, say, contain end of one large message and a beginning of another one. All the bytes in an uncontained frame always belong to a single message. # Small vs large messages A single handler is equipped to process both small and large messages, potentially interleaved, but the logic differs depending on size. Small messages are deserialized in place, and then handed off to an appropriate thread pool for processing. Large messages accumulate frames until completion of a message, then hand off the untouched frames to the correct thread pool for the verb to be deserialized there and immediately processed. SeeAbstractMessageHandler.LargeMessage
and subclasses for concreteAbstractMessageHandler
implementations for details of the large-message accumulating state-machine, andInboundMessageHandler.ProcessMessage
and its inheritors for the differences in execution. # Flow control (backpressure) To prevent message producers from overwhelming and bringing nodes down with more inbound messages that can be processed in a timely manner,AbstractMessageHandler
provides support for implementations to provide their own flow control policy. Before we attempt to process a message fully, we first infer its size from the stream. This inference is delegated to implementations as the encoding of the message size is protocol specific. Having assertained the size of the incoming message, we then attempt to acquire the corresponding number of memory permits. If we succeed, then we move on actually process the message. If we fail, the frame decoder deactivates until sufficient permits are released for the message to be processed and the handler is activated again. Permits are released back once the message has been fully processed - the definition of which is again delegated to the concrete implementations. Every connection has an exclusive number of permits allocated to it. In addition to it, there is a per-endpoint reserve capacity and a global reserve capacityResourceLimits.Limit
, shared between all connections from the same host and all connections, respectively. So long as long as the handler stays within its exclusive limit, it doesn't need to tap into reserve capacity. If tapping into reserve capacity is necessary, but the handler fails to acquire capacity from either endpoint of global reserve (and it needs to acquire from both), the handler and its frame decoder become inactive and register with aAbstractMessageHandler.WaitQueue
of the appropriate type, depending on which of the reserves couldn't be tapped into. Once enough messages have finished processing and had their permits released back to the reserves,AbstractMessageHandler.WaitQueue
will reactivate the sleeping handlers and they'll resume processing frames. The reason we 'split' reserve capacity into two limits - endpoing and global - is to guarantee liveness, and prevent single endpoint's connections from taking over the whole reserve, starving other connections. One permit per byte of serialized message gets acquired. When inflated on-heap, each message will occupy more than that, necessarily, but despite wide variance, it's a good enough proxy that correlates with on-heap footprint.
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description protected class
AbstractMessageHandler.LargeMessage<H>
static interface
AbstractMessageHandler.OnHandlerClosed
static class
AbstractMessageHandler.WaitQueue
A special-purpose wait queue to park inbound message handlers that failed to allocate reserve capacity for a message in.
-
Field Summary
Fields Modifier and Type Field Description protected io.netty.channel.Channel
channel
protected long
corruptFramesRecovered
protected long
corruptFramesUnrecovered
protected FrameDecoder
decoder
protected ResourceLimits.Limit
endpointReserveCapacity
protected AbstractMessageHandler.WaitQueue
endpointWaitQueue
protected ResourceLimits.Limit
globalReserveCapacity
protected AbstractMessageHandler.WaitQueue
globalWaitQueue
protected AbstractMessageHandler.LargeMessage<?>
largeMessage
protected int
largeThreshold
protected AbstractMessageHandler.OnHandlerClosed
onClosed
protected long
queueCapacity
protected long
receivedBytes
protected long
receivedCount
protected long
throttledCount
protected long
throttledNanos
-
Constructor Summary
Constructors Constructor Description AbstractMessageHandler(FrameDecoder decoder, io.netty.channel.Channel channel, int largeThreshold, long queueCapacity, ResourceLimits.Limit endpointReserveCapacity, ResourceLimits.Limit globalReserveCapacity, AbstractMessageHandler.WaitQueue endpointWaitQueue, AbstractMessageHandler.WaitQueue globalWaitQueue, AbstractMessageHandler.OnHandlerClosed onClosed)
-
Method Summary
All Methods Instance Methods Abstract Methods Concrete Methods Modifier and Type Method Description protected ResourceLimits.Outcome
acquireCapacity(ResourceLimits.Limit endpointReserve, ResourceLimits.Limit globalReserve, int bytes)
protected boolean
acquireCapacity(ResourceLimits.Limit endpointReserve, ResourceLimits.Limit globalReserve, int bytes, long currentTimeNanos, long expiresAtNanos)
Try to acquire permits for the inbound message.void
channelInactive(io.netty.channel.ChannelHandlerContext ctx)
void
channelRead(io.netty.channel.ChannelHandlerContext ctx, java.lang.Object msg)
protected abstract void
fatalExceptionCaught(java.lang.Throwable t)
void
handlerAdded(io.netty.channel.ChannelHandlerContext ctx)
protected abstract java.lang.String
id()
boolean
process(FrameDecoder.Frame frame)
Frame processor that the frames should be handed off to.protected abstract void
processCorruptFrame(FrameDecoder.CorruptFrame frame)
protected abstract boolean
processFirstFrameOfLargeMessage(FrameDecoder.IntactFrame frame, ResourceLimits.Limit endpointReserve, ResourceLimits.Limit globalReserve)
protected abstract boolean
processOneContainedMessage(ShareableBytes bytes, ResourceLimits.Limit endpointReserve, ResourceLimits.Limit globalReserve)
protected boolean
processSubsequentFrameOfLargeMessage(FrameDecoder.Frame frame)
protected boolean
processUpToOneMessage(ResourceLimits.Limit endpointReserve, ResourceLimits.Limit globalReserve)
void
releaseCapacity(int bytes)
protected void
releaseProcessedCapacity(int size, Message.Header header)
Invoked to release capacity for a message that has been fully, successfully processed.-
Methods inherited from class io.netty.channel.ChannelInboundHandlerAdapter
channelActive, channelReadComplete, channelRegistered, channelUnregistered, channelWritabilityChanged, exceptionCaught, userEventTriggered
-
Methods inherited from class io.netty.channel.ChannelHandlerAdapter
ensureNotSharable, handlerRemoved, isSharable
-
-
-
-
Field Detail
-
decoder
protected final FrameDecoder decoder
-
channel
protected final io.netty.channel.Channel channel
-
largeThreshold
protected final int largeThreshold
-
largeMessage
protected AbstractMessageHandler.LargeMessage<?> largeMessage
-
queueCapacity
protected final long queueCapacity
-
endpointReserveCapacity
protected final ResourceLimits.Limit endpointReserveCapacity
-
endpointWaitQueue
protected final AbstractMessageHandler.WaitQueue endpointWaitQueue
-
globalReserveCapacity
protected final ResourceLimits.Limit globalReserveCapacity
-
globalWaitQueue
protected final AbstractMessageHandler.WaitQueue globalWaitQueue
-
onClosed
protected final AbstractMessageHandler.OnHandlerClosed onClosed
-
corruptFramesRecovered
protected long corruptFramesRecovered
-
corruptFramesUnrecovered
protected long corruptFramesUnrecovered
-
receivedCount
protected long receivedCount
-
receivedBytes
protected long receivedBytes
-
throttledCount
protected long throttledCount
-
throttledNanos
protected long throttledNanos
-
-
Constructor Detail
-
AbstractMessageHandler
public AbstractMessageHandler(FrameDecoder decoder, io.netty.channel.Channel channel, int largeThreshold, long queueCapacity, ResourceLimits.Limit endpointReserveCapacity, ResourceLimits.Limit globalReserveCapacity, AbstractMessageHandler.WaitQueue endpointWaitQueue, AbstractMessageHandler.WaitQueue globalWaitQueue, AbstractMessageHandler.OnHandlerClosed onClosed)
-
-
Method Detail
-
channelRead
public void channelRead(io.netty.channel.ChannelHandlerContext ctx, java.lang.Object msg)
- Specified by:
channelRead
in interfaceio.netty.channel.ChannelInboundHandler
- Overrides:
channelRead
in classio.netty.channel.ChannelInboundHandlerAdapter
-
handlerAdded
public void handlerAdded(io.netty.channel.ChannelHandlerContext ctx)
- Specified by:
handlerAdded
in interfaceio.netty.channel.ChannelHandler
- Overrides:
handlerAdded
in classio.netty.channel.ChannelHandlerAdapter
-
process
public boolean process(FrameDecoder.Frame frame) throws java.io.IOException
Description copied from interface:FrameDecoder.FrameProcessor
Frame processor that the frames should be handed off to.- Specified by:
process
in interfaceFrameDecoder.FrameProcessor
- Returns:
- true if more frames can be taken by the processor, false if the decoder should pause until it's explicitly resumed.
- Throws:
java.io.IOException
-
processOneContainedMessage
protected abstract boolean processOneContainedMessage(ShareableBytes bytes, ResourceLimits.Limit endpointReserve, ResourceLimits.Limit globalReserve) throws java.io.IOException
- Throws:
java.io.IOException
-
processFirstFrameOfLargeMessage
protected abstract boolean processFirstFrameOfLargeMessage(FrameDecoder.IntactFrame frame, ResourceLimits.Limit endpointReserve, ResourceLimits.Limit globalReserve) throws java.io.IOException
- Throws:
java.io.IOException
-
processSubsequentFrameOfLargeMessage
protected boolean processSubsequentFrameOfLargeMessage(FrameDecoder.Frame frame)
-
processCorruptFrame
protected abstract void processCorruptFrame(FrameDecoder.CorruptFrame frame) throws Crc.InvalidCrc
- Throws:
Crc.InvalidCrc
-
fatalExceptionCaught
protected abstract void fatalExceptionCaught(java.lang.Throwable t)
-
processUpToOneMessage
protected boolean processUpToOneMessage(ResourceLimits.Limit endpointReserve, ResourceLimits.Limit globalReserve) throws java.io.IOException
- Throws:
java.io.IOException
-
acquireCapacity
protected boolean acquireCapacity(ResourceLimits.Limit endpointReserve, ResourceLimits.Limit globalReserve, int bytes, long currentTimeNanos, long expiresAtNanos)
Try to acquire permits for the inbound message. In case of failure, register with the right wait queue to be reactivated once permit capacity is regained.
-
acquireCapacity
protected ResourceLimits.Outcome acquireCapacity(ResourceLimits.Limit endpointReserve, ResourceLimits.Limit globalReserve, int bytes)
-
releaseCapacity
public void releaseCapacity(int bytes)
-
releaseProcessedCapacity
protected void releaseProcessedCapacity(int size, Message.Header header)
Invoked to release capacity for a message that has been fully, successfully processed. Normally no different from invokingreleaseCapacity(int)
, but is necessary for the verifier to be able to delay capacity release for backpressure testing.
-
channelInactive
public void channelInactive(io.netty.channel.ChannelHandlerContext ctx)
- Specified by:
channelInactive
in interfaceio.netty.channel.ChannelInboundHandler
- Overrides:
channelInactive
in classio.netty.channel.ChannelInboundHandlerAdapter
-
id
protected abstract java.lang.String id()
-
-