This is the ninth day of my participation in the First Challenge 2022. For details: First Challenge 2022.

Introduction to the

A channel is a data transmission and processing channel in NetTY. It is also an indispensable part of NetTY programs. In Netty, a channel is an interface and has different implementations for different data types or protocols.

Although channels are important, they are mysterious in code, and we rarely see direct use of channels. Is this really the case? What about ChannelGroup related to channel? Take a look.

The dragon has never seen the end of channel

In fact, netty code has a fixed template. First, create the corresponding Bootstrap and ServerBootstrap based on whether the netty code is server or client. Then configure the corresponding group method for this Bootstrap. Then configure the channel and handler for Bootstrap, and start Bootstrap.

This completes a standard Netty program. All you need to do is pick the right group, channel, and handler for it.

Let’s look at the simplest NioServerSocketChannel case:

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChatServerInitializer());

            b.bind(PORT).sync().channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
Copy the code

Here, we set the NioServerSocketChannel to the ServerBootstrap channel.

Is that it? Where is channel used?

Wait, let’s take a closer look at the last sentence in the try block:

b.bind(PORT).sync().channel().closeFuture().sync();
Copy the code

B.bind (PORT).sync() actually returns a ChannelFuture object, and by calling its channel method, returns the channel object associated with it.

We then call the channel.closeFuture() method. The closeFuture method returns a ChannelFuture object that will be notified when a channel is closed.

The sync method blocks synchronization until the channel is closed for subsequent eventGroup shutdown.

In the ServerBootstrap build template, a channel actually has two functions. The first function is to specify the ServerBootstrap channel, and the second function is to obtain the channel closing event through the channel, and finally shut down the entire Netty program.

Although we rarely see direct method calls to channels, channels are the soul of Netty.

Let’s take a look at the basic operations of the handler for specific message processing:

Public void channelActive(ChannelHandlerContext CTX) throws Exception {// Channel Active ctx.write("Channel Active! \r\n"); ctx.flush(); }Copy the code

Normally if we need to write data to a channel in a handler, we call the write method of the ChannelHandlerContext. What does this method have to do with a channel?

The first write method is a method in the ChannelOutboundInvoker interface, and both the ChannelHandlerContext and Channel inherit the ChannelOutboundInvoker interface, that is, ChannelHandlerContext and Channel both have write methods:

ChannelFuture write(Object msg);
Copy the code

Since we are using NioServerSocketChannel here, let’s look specifically at the implementation of write in NioServerSocketChannel.

After checking the code that we will find NioServerSocketChannel inherited from AbstractNioMessageChannel, AbstractNioMessageChannel inherited from AbstractNioChannel, AbstractNioChannel inherits from AbstractChannel, and this write method is implemented in AbstractChannel:

    public ChannelFuture write(Object msg) {
        return pipeline.write(msg);
    }
Copy the code

Channel’s write method actually calls Pipeline’s write method. Here is the write method in pipeLine:

    public final ChannelFuture write(Object msg) {
        return tail.write(msg);
    }
Copy the code

The tail is a AbstractChannelHandlerContext object.

This leads us to the conclusion that the write method in channel actually ends up calling the write method in ChannelHandlerContext.

So the above:

Ctx. write("Channel Active status! \r\n");Copy the code

It can actually be called directly from a channel:

Channel ch = b.bind(0).sync().channel(); // Write the message to a channel. Ch. WriteAndFlush ("Channel Active state! \r\n").sync();Copy the code

The channel and the channelGroup

A channel is the soul of Netty. To obtain the corresponding channel, Bootstrap can call:

b.bind(PORT).sync().channel()
Copy the code

We can also see from the above code that a Bootstrap corresponds to only one channel.

A channel has a parent() method that returns its parent channel, so channels are hierarchical,

Let’s look at channelGroup’s definition again:

public interface ChannelGroup extends Set<Channel>, Comparable<ChannelGroup> 
Copy the code

You can see that a ChannelGroup is actually a collection of channels. A ChannelGroup is used to build a collection of similar channels so that multiple channels can be managed in a unified manner.

One Bootstrap corresponds to only one channel, right? So where does the collection of channels come from?

In fact, in some complex applications, we may launch multiple bootstraps to handle different services, so there will be multiple channels accordingly.

If too many channels are created and they are homogeneous, unified management of these channels is required. That’s where channelGroup comes in.

Basic use of channelGroup

Let’s take a look at the basic usage of channelGroup. First, create a channelGroup:

ChannelGroup recipients =
           new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
Copy the code

Once you have a channelGroup, you can call the add method to add different channels to it:

   recipients.add(channelA);
   recipients.add(channelB);
Copy the code

It is also possible to send messages to these channels uniformly:

Exerts.write(Unpooled. CopiedBuffer (" This is a unified message from the channelGroup.", Charsetutil.utf_8));Copy the code

ChannelGroup basically provides the write, flush, flushAndWrite writeAndFlush, disconnect, close, newCloseFuture, etc, used for the collection of the channel for unified management.

If you have multiple channels, consider using a channelGroup.

ChannelGroup also has some features, let’s take a closer look.

Automatically remove a closed channel

A ChannelGroup is a collection of channels. Of course, we only want to save open channels. If a channel is in a close state, it would be too cumbersome to manually remove it from the ChannelGroup.

So in ChannelGroup, if a channel is closed, it is automatically removed from the ChannelGroup. How does this work?

Let’s take a look at the channelGroup add method:

   public boolean add(Channel channel) {
        ConcurrentMap<ChannelId, Channel> map =
            channel instanceof ServerChannel? serverChannels : nonServerChannels;

        boolean added = map.putIfAbsent(channel.id(), channel) == null;
        if (added) {
            channel.closeFuture().addListener(remover);
        }

        if (stayClosed && closed) {
            channel.close();
        }

        return added;
    }
Copy the code

As you can see, in the Add method, the channel is distinguished as a server channel or a non-server channel. It is then stored in the ConcurrentMap based on the channel ID.

If it succeeds, a closeFuture callback is added to the channel. When a channel is closed, this remover method is called:

private final ChannelFutureListener remover = new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { remove(future.channel()); }};Copy the code

The remover method removes a channel from serverChannels or nonServerChannels. This ensures that only channels in the Open state are saved in the ChannelGroup.

Close both the serverChannel and acceptedChannel

Although the bind method of ServerBootstrap returns only one channel, for the server, There can be multiple Worker EventLoopGroups, so the Accepted Channel established after the client establishes a connection with the server is a sub-channel of the Server Channel.

In other words, a server has one Server channel and multiple Accepted Channels.

If we want to close all channels at the same time, we can use the close method of the ChannelGroup.

If a Server channel and a non-server channel are in the same ChannelGroup, all IO commands are sent to the Server channel first and then to the non-server channel.

So we can add the Server channel and non-server channel to the same ChannelGroup. At the end of the program, we can call the close method of ChannelGroup to achieve this goal:

ChannelGroup allChannels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); public static void main(String[] args) throws Exception { ServerBootstrap b = new ServerBootstrap(..) ; . b.childHandler(new MyHandler()); AddLast ("handler", new MyHandler()); Channel serverChannel = b.bind(..) .sync(); allChannels.add(serverChannel); . Wait for shutdown command... // Close serverChannel and all accepted connections.allchannels.close ().awaituninterruptibly (); } public class MyHandler extends ChannelInboundHandlerAdapter { @Override public void ChannelActive (ChannelHandlerContext CTX) {// Add accepted channel to allChannels allChannels.add(ctx.channel()); super.channelActive(ctx); }}Copy the code

ChannelGroupFuture

Also, like a channel, the channelGroup operation is asynchronous and returns a ChannelGroupFuture object.

Let’s look at ChannelGroupFuture’s definition:

public interface ChannelGroupFuture extends Future<Void>, Iterable<ChannelFuture>
Copy the code

ChannelGroupFuture is a Future. It is also a traverser for ChannelFuture, traversing all ChannelFuture returned by channels in ChannelGroup.

At the same time ChannelGroupFuture provided isSuccess isPartialSuccess, isPartialFailure method to determine whether a command part of success.

ChannelGroupFuture also provides the addListener method to listen for specific events.

conclusion

A channel is the core of Netty. If multiple channels are difficult to manage, you can use the channelGroup to manage them in a unified manner.

This article is available at www.flydean.com/04-1-netty-…

The most popular interpretation, the most profound dry goods, the most concise tutorial, many tips you didn’t know waiting for you to discover!

Welcome to pay attention to my public number: “procedures those things”, understand technology, more understand you!