Reputation: 702
I'm still pretty new to netty so please bare with me. There seems to be plenty of questions asking why a specefic netty implementation is slow and how to make it faster. But my use case is a bit different. I want to avoid low level socket implementations (hence netty) but I also know that blocking the event group is bad. I know I can dynamically manage the pipeline. I'm not sure I know enough about netty to know if this is possible, and I've not tried much that I don't already know is bad (thread.sleep
for example). The protocol is HTTP
but I also need it to be useful for other protocols.
But what I don't know is, for a single connection on a shared port, how to slow down the response of the server to the client, and vice versa? Or put more aptly: where, and what, would I implement the slowness required? My guess is the encoder for the where; but because of netty's approach, i haven't the foggiest for the what.
Upvotes: 0
Views: 404
Reputation: 3363
You say that you know that Thread.sleep
is "bad" but it really depends on what you're trying to achieve and where you put the sleep. I believe that the best way to build this would be to use a DefaultEventExecutorGroup
to offload the processing of your slow-down ChannelHandler onto non-event-loop threads and then call Thread.sleep
in your handler.
From the ChannelPipeline javadoc, under the "Building a pipeline" section: https://netty.io/4.1/api/io/netty/channel/ChannelPipeline.html
A user is supposed to have one or more ChannelHandlers in a pipeline to receive I/O events (e.g. read) and to request I/O operations (e.g. write and close). For example, a typical server will have the following handlers in each channel's pipeline, but your mileage may vary depending on the complexity and characteristics of the protocol and business logic:
Protocol Decoder - translates binary data (e.g. ByteBuf) into a Java object. Protocol Encoder - translates a Java object into binary data. Business Logic Handler - performs the actual business logic (e.g. database access). and it could be represented as shown in the following example:
static final EventExecutorGroup group = new DefaultEventExecutorGroup(16); ... ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("decoder", new MyProtocolDecoder()); pipeline.addLast("encoder", new MyProtocolEncoder()); // Tell the pipeline to run MyBusinessLogicHandler's event handler methods // in a different thread than an I/O thread so that the I/O thread is not blocked by // a time-consuming task. // If your business logic is fully asynchronous or finished very quickly, you don't // need to specify a group. pipeline.addLast(group, "handler", new MyBusinessLogicHandler());
Be aware that while using DefaultEventLoopGroup will offload the operation from the EventLoop it will still process tasks in a serial fashion per ChannelHandlerContext and so guarantee ordering. Due the ordering it may still become a bottle-neck. If ordering is not a requirement for your use-case you may want to consider using UnorderedThreadPoolEventExecutor to maximize the parallelism of the task execution.
Upvotes: 1
Reputation: 702
I hope someone can post a better (more explanative) answer than this but basically all that's needed is to use a ChannelTrafficShapingHandler with some small enough values.
For instance, a 2kb response with read and write limit of 512b, maxTime of 6000ms, and a checkInterval of 1000ms forces the response to take 4000ms with the ChannelTrafficShapingHandler, and 50ms without it when running both client and server locally. I expect those times to increase dramatically when on the network wire.
final ChannelTrafficShapingHandler channelTrafficShapingHandler = new ChannelTrafficShapingHandler(
getRateInBytesPerSecond(), getRateInBytesPerSecond(), getCheckInterval(), getMaxTime());
ch.addLast(channelTrafficShapingHandler);
Upvotes: 0