Have you met Netty?
Mafinar Rashid Khan
Product Manager
Panacea Systems Limited
@mafinar
An NIO based, Non-Blocking, Event Driven, Performant Client Server Framework.
servers and protocols made easy
..."easy" in the Java sense of things...
Why do I care?
Building Microservices, TCP Servers, Chat Servers, High Performance Frameworks, Implementing protocols...
... Finagle, Akka, Http-Kit, Vert.x, Play Framework, Immutant, Scalatra...
Check out the power they pack: TechEmpower
A little bit of personal experience...
An escape from the linear way of thinking
And hello
abstraction games!!!
Embrace your metaphors
Make Netty fit your brain... :)
So how DOES Netty fit your brain???
READ the next slide to find out...
Let's pick a metaphor- Call Center, Restaurant, Bank, Elevator... take your pick!!! We go event driven and async with it
You basically have something that waits for something to happen, and when it happens, it does a series of jobs, calls in the proper favors, and serves the request.
Remember the words: Channel, Context, Pipeline, Handler, Initializer, Bootstrap, EventLoop, Future, Listener
Since we're talking Java here, most of them are, wait for it...
INTERFACES :-/
-
Channels are basically like the English Channel, a body of
waterdata connecting stuff -
Handlers handle channels and dictates when to react, when to not...
-
Pipeline contains a series of handlers to be worked on those channels
-
The executor dispatches workers that calls the handlers from the pipeline
-
Everything happens asynchronously with lots of futures and handlers lurking about
It all starts with the eventloop.. the boss, and the workers...
EventLoopGroup bossGroup =
new NioEventLoopGroup();
EventLoopGroup workerGroup =
new NioEventLoopGroup();
You then need to bootstrap them
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(
new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch)
throws Exception {
ch.pipeline().addLast(
new EchoServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
Now comes the business part...
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ctx.write(msg);
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
So you got some event-loops who wait and react on events...
Bootstrap a server, denoting the eventloops and configuring the behaviors
Add handlers into the pipeline (they are like middlewares)
There can be upstream and downstream pipelines, and there are some cool built in ones...
There's this initializer...
It bootstraps the pipeline inside the bootstrap.. adds the sequence of ChannelHandlers a request needs to go through...
Finally, you create the handler class.
The ChannelContext variable it acts as a mediator from other handlers in the pipeline
So it's like, the ChannelHandler says, "When you add me for the first time, and give me a context, I do THIS", "When I receive a message, I do THIS", "If you disconnect me, I do THIS"
"this" = Handling of the event
"context" = Information from incoming or outgoing elements
"red words" = the EVENT in Event Driven
public class SecureChatServerHandler extends SimpleChannelInboundHandler<String> {
static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
public void channelActive(final ChannelHandlerContext ctx) {
ctx.pipeline().get(SslHandler.class).handshakeFuture().addListener(
new GenericFutureListener<Future<Channel>>() {
@Override
public void operationComplete(Future<Channel> future) throws Exception {
channels.add(ctx.channel());
}
});
}
@Override
public void messageReceived(ChannelHandlerContext ctx, String msg)
throws Exception {
for (Channel c: channels) {
if (c != ctx.channel()) {
c.writeAndFlush("[" + ctx.channel().remoteAddress() + "] " + msg + '\n');
} else {
c.writeAndFlush("[you] " + msg + '\n');
}
}
if ("bye".equals(msg.toLowerCase())) {ctx.close();}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
That was the chat example taken from their example site...
Just look at its Initializer...
public class SecureChatServerInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslCtx;
public SecureChatServerInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(sslCtx.newHandler(ch.alloc()));
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new SecureChatServerHandler());
}
}
Here's a trick, you can take control of a lot of in built ChannelHandlers, even make a few, to better modularize the work-flow. (Food for thought: PostgresHandler, RedisHandler, RedirectHandler)
Once you completely understand the workflow Netty gives you...
It will become intuitive, and easy..
... even if you work with other languages
That EchoServer???
Here is it with Clojure...
(defn echo [req] (let [s @req] (s/connect s s))))
Netty may be for a smaller niche...
... but this is the age of MicroServices.. so you never know!
THANK YOU
>:d<
Have you met netty?
By Mafinar Khan
Have you met netty?
My slides on the JUGBD 4.0 on Netty
- 2,337