Now that we’ve learned about ChannelPipeline, ChannelHandler, and EventLoop, the question is: How do you organize them into a practical, running application? The answer is to use Bootstrap. Bootstrap is the process of configuring an application and getting it running, that is, putting all the framework components together and enabled in the background

Bootstrap

The bootstrap class hierarchy contains an abstract superclass and two concrete subclasses

If you consider the client and the server as two applications, their functions are different: the server is committed to using a parent Channel to accept client connections and creating child channels for communication between them; The client probably only needs a single Channel for all network interactions. Common Bootstrap steps between the two approaches are handled by AbstractBootstrap, while client-specific Bootstrap steps are handled by Bootstrap or ServerBootstrap, respectively

Boot client

The Bootstrap class is used by clients or applications that use connectionless protocols. The API for this class is shown in the table below:

The Bootstrap class is responsible for creating channels for clients and applications that use connectionless protocols

The code listing shows booting a client that uses NIO TCP transport

EventLoopGroup group = new NioEventLoopGroup();
// Create an instance of the Bootstrap class to create and connect new clients
Bootstrap bootstrap = new Bootstrap();
/ / set EventLoopGroup
bootstrap.group(group)
    // Specify the Channel implementation to use
    .channel(NioSocketChannel.class)
    // Set ChannelInboundHandler for Channel events and data
    .handler(new SimpleChannelInboundHandler<ByteBuf>() {
        @Override
        protected void channeRead0(
        	ChannelHandlerContext channelHandlerContext,
            ByteBuf byteBuf) throws Exception {
            Syetem.out.println("Received data"); }});// Connect to remote host
ChannelFuture future = bootstrap.connect(
	new InetSocketAddress("www.manning.com".80));future.addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture channelFuture) throws Exception {
        if(channelFuture.isSuccess()) {
            System.out.println("Connection established");
        } else {
            System.err.println("Connection attempt failed"); channelFuture.cause().printStackTrace(); }}})Copy the code

Boot server

The following table lists the methods of the ServerBootstrap class

The implementation of ServerChannel is responsible for creating subchannels that represent accepted connections. ServerBootstrap provides methods childHandler(), childAttr(), and childOption() to simplify the task of applying Settings to ChannelConfig of accepted subchannels

The following figure shows that ServerBootstrap creates a ServerChannel when the bind() method is called, and that the ServerChannel manages multiple subchannels

The code for the boot server looks like this:

NioEventLoopGroup group = new NioEventLoopGroup();
/ / create ServerBootstrap
ServerBootstrap bootstrap = new ServerBootstrap();
/ / set EventLoopGroup
bootstrap.group(group)
    // Specify the Channel implementation to use
    .channel(NioServerSocketChannel.class)
    // Set the ChannelInboundHandler used to handle the IO and data of accepted subchannels
    .childHandler(new SimpleChannelInboundHandler<ByteBuf>() {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx,
                                   ByteBuf byteBuf) throws Exception {
            System.out.println("Received data"); }}); ChannelFuturefuture = bootstrap.bind(new InetSocketAddress(8080));
future.addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture channelFuture) throws Exception {
        if(channelFuture.isSuccess()) {
            System.out.println("Server bound");
        } else {
            System.out.println("Bound attempt failed"); channelFuture.cause().printStackTrace(); }}})Copy the code

Bootstrap the client from a Channel

Suppose your server is required to act as a client for a third party, in which case a client Channel needs to be channelled from an already accepted subchannel

We can create new Bootstrap instances in the same way we bootstrapped the client, but this requires you to define an EventLoop for each newly created client Channel, which generates additional threads. And when data is exchanged between a subchannel and a client Channel, a context switch inevitably occurs

A better solution: Pass the subchannel EventLoop to the Group () method of Bootstrap to share the EventLoop. Avoid additional thread creation and context switching

Implementing an EventLoop share involves setting up the EventLoop by calling the Group () method

ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup())
    .channel(NioServerSocketChannel.class)
    .childHandler(
		new SimpleChannelInboundHandler<ByteBuf>() {
            ChannelFuture connectFuture;
            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                // Create a Bootstrap instance to connect to a remote host
                Bootstrap bootstrap = new Bootstrap();
                bootstrap.channel(NioSockerChannel.class).handler(
                	new SimpleChannelInboundHandler<ByteBuf>() {
                        @Override
                        protected void channelRead0(
                        	ChannelHandlerContext ctx, ByteBuf in) throws Exception {
                            System.out.println("Received data"); }});// Use a subchannel EventLoop
                bootstrap.group(ctx.channel().eventLoop());
                connectFuture = bootstrap.connect(new InetSocketAddress("www.manning.com".80));
            }
            @Override
            protected void channelRead0(
            	ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
                if(connectFuture.isDone) {
                    // When the connection is complete, the data operation is performed}}});Copy the code

Add multiple ChannelHandlers during boot

The handler() or childHandler() methods were called in the previous bootstrap to add a single channelHandler() method to add a single channelHandler. If we need multiple ChannelHandlers, Netty provides a special ChannelInboundHandlerAdapter subclass:

public abstract class ChannelInitializer<C extends Channel> extends ChannelInboundHandlerAdapter
Copy the code

It defines the following methods

protected abstract void initChannel(C ch) throws Exception;
Copy the code

This method provides an easy way to add multiple channelhandlers to a ChannelPipeline, All you need to do is provide your ChannelInitializer implementation to Bootstrap or ServerBootstrap instances. Once a Channel is registered with its EventLoop, your initChannel() version is called, and when that method returns, the ChannelInitializer instance will remove itself from the ChannelPipeline

ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup())
    .channel(NioServerSocketChannel.class)
    // Register an instance of ChannelInitializerImpl to set up ChannelPipeline
    .childHandler(new ChannelInitializerImpl());
ChannelFuture future = bootstrap.bind(new InetSocketAddress(8080));
future.sync();

final class ChannelInitializerImpl extends ChannelInitializer<Channel> {
    @Override
    protected void initChannel(Channel ch) throws Exception {
        CHannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new HttpClientCodec());
        pipeline.addLast(newHttpObjectAggregator(Integer.MAX_VALUE)); }}Copy the code

Use Netty’s ChannelOption and properties

Manually configuring each Channel when it is created can be rather tedious; you can use the option() method to apply ChannelOption to boot, and its value is automatically applied to all channels you create. Available Channeloptions include details about the underlying connection, such as keep-alive or timeout properties and buffer Settings

Netty applications are typically integrated with an organization’s proprietary software, and channels may even be used outside of the normal Netty life cycle. Netty provides AttributeMap abstraction and AttributeKey when certain common attributes and data are not available. Using these tools, you can safely associate any type of data with client and server channels

For example, consider a server application that tracks the relationship between a user and a Channel by storing the user’s ID as an attribute of a Channel

// Create an AttributeKey to identify the attribute
final AttributeKey<Integer> id = AttributeKey.newInstance("ID");
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(new NioEventLoopGroup())
    .channel(NioSocketChannel.class)
    .handler(
		new SimpleChannelInboundHandler<ByteBuf>() {
        	@Override
            public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
                // Use AttributeKey to retrieve the attribute and its value
                Integer idValue = ctx.channel().attr(id).get();
            }
            
            @Override
            public void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
                System.out.println("Received data"); }});/ / set ChannelOption
bootstrap.option(ChannelOption.SO_KEEPALIVE, true)
    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);
// Store the ID attribute
bootstrap.attr(id, 123456);
ChannelFuture future = bootstrap.connect(new InetSocketAddress("www.maning.com".80));
future.syncUninterruptibly();
Copy the code

Guide DatagramChannel

The previous ones were tcp-based SocketChannels, but the Bootstrap class can also be used for connectionless protocols. For this purpose, Netty provides various implementations of DatagramChannel. The only difference is that instead of calling connect(), only bind() is called

Bootstrap bootstrap = new Bootstrap();
bootstrap.group(new OioEventLoopGroup())
    .channel(OioSocketChannel.class)
    .handler(
		new SimpleChannelInboundHandler<DatagramPacket>() {
            @Override
            public void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
                System.out.println("Received data"); }}); ChannelFuturefuture = bootstrap.bind(new InetSocketAddress(0));
Copy the code

Shut down

Bootstrap gets your application started and needs to be shut down gracefully. You can have the JVM handle everything on exit, but that doesn’t fit the definition of elegance

Most importantly, you need to close the EventLoopGroup, which will handle any pending events and tasks and then release all active threads. By calling EventLoopGroup. ShutdownGracefully () method, which will return a Future, the Future will be completed in the closed when receiving the notification. ShutdownGracefully is an asynchronous operation where you block and wait until it completes, or register a listener with the returned Future

EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = newBootstrap(); bootstrap.group(group) .channel(NioServerSocketChannel.class); . Future<? >future = group.shutdownGracefully();
future.syncUniterruptibly();
Copy the code

conclusion

Thank you for accompanying the readers of daxian all the time. Daxian prepared a learning material for you here today, and also hope to help you to get the link if you need it