本章续上一章节没讲完的IO模型。 

3.2.2. Netty中的组件channel

io.netty.channel.Channel时Netty对网络的抽象,它组合了一组功能,包括不限于网络的读、写、客户端发起连接,主动关闭连接,关闭链路,获取通信双方的地址等,还包括获取该channel的eventLoop,获取缓冲区分配类BytebufferAllocator和pipeline等。
jdk自带了channel,定义如下

package java.nio.channels;

import java.io.IOException;
import java.io.Closeable;
public interface Channel extends Closeable {
    public boolean isOpen();
    public void close() throws IOException;
}

netty自实现了channel,

package io.netty.channel;
import io.netty.buffer.ByteBufAllocator;
import io.netty.util.AttributeMap;
import java.net.SocketAddress;

public interface Channel extends AttributeMap, ChannelOutboundInvoker, Comparable<Channel> {
ChannelId id();

    EventLoop eventLoop();

    Channel parent();

    ChannelConfig config();

    boolean isOpen();

    boolean isRegistered();

    boolean isActive();

    ChannelMetadata metadata();

    SocketAddress localAddress();

    SocketAddress remoteAddress();

    ChannelFuture closeFuture();

    boolean isWritable();

    long bytesBeforeUnwritable();

    long bytesBeforeWritable();

    Unsafe unsafe();

    ChannelPipeline pipeline();

    ByteBufAllocator alloc();

    Channel read();

    Channel flush();
}

为什么不适用JDK提供的channel,Netty自实现了channel,主要原因有:

  • jdk的serverSocket,socketChannel是SPI类接口,主要职责是网络IO操作,扩展它与重新开发一个Channel工作量差不多;
  • Netty的channel需要配合自身的框架特性,自定义channel功能上更加灵活。

channel中常用的方法:

  • channel.read(),从当前channel读取数据到第一个inbound缓冲区中,如果数据被成功读取,会触发一个channelHandler.channelRead(ChannelHanlderContext context, Object)事件。读取操作完后,紧接着会触发ChannelHandler.channelReadComplete(ChannelHandlerContext context)事件,这样业务的channelhandler可判断是否还需要读取数据。
  • ChannelFuture write(Object msg),将msg通过pipeline写入channel中。注意,write操作只是将数据存入到消息发送的环形数组中,并没有真正发送,调用flush操作后才会被发送。
  • ChannelFuture write(Object msg, ChannelPromise promise),与write功能相同,promise负责设置写入操作的结果。
  • ChannelFuture writeAndFlush(Object msg, ChannelPromise promise),相当于write和flush操作的结合ChannelFuture writeAndFlush(Object msg)
  • ChannelFuture close( ChannelPromise promise),主动关闭当前连接,promise处理操作结果并负责通知
  • ChannelFuture disconnect( ChannelPromise promise),请求断开与远程通信端的连接,promise获取操作结果的通知消息
  • ChannelFuture connect(SocketAddress address),与远程地址建立连接,如果请求连接被拒绝,操作结果为ConnectException。这个操作会触发channelHandler.connect(ChannelHandlerContext context, SocketAddress address, ChannelPromise promise)事件。
  • ChannelFuture bind(SocketAddress localAddress),绑定指定的本地地址,会触发事件

NioServerSocketChannel和NioSocketChannel,UnSafe类是channel的辅助接口,所以的IO读写都是同UnSafe类完成的。

ChannelPipeline和ChannelHandler

ChannelPipeline和ChannelHandler的关系类似Servlet和filter的关系,channelHandler对channel进行过滤,而ChannelPipeline提供环境,持有channelHandler事件拦截器的链表。可以方便的增加和删除channelHandler来实现对channel中数据流的处理。

public interface ChannelPipeline extends ChannelInboundInvoker, ChannelOutboundInvoker, Iterable<Map.Entry<String, ChannelHandler>> {
    ChannelPipeline addFirst(String var1, ChannelHandler var2);
    ChannelPipeline addFirst(EventExecutorGroup var1, String var2, ChannelHandler var3);
...
    ChannelPipeline fireChannelRead(Object var1);
    ChannelPipeline fireChannelReadComplete();
    ChannelPipeline fireChannelWritabilityChanged();
    ChannelPipeline flush();
}
public interface ChannelHandler {
    void handlerAdded(ChannelHandlerContext var1) throws Exception;
    void handlerRemoved(ChannelHandlerContext var1) throws Exception;

    /** @deprecated */
    @Deprecated
    void exceptionCaught(ChannelHandlerContext var1, Throwable var2) throws Exception;

    @Inherited
    @Documented
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Sharable {
    }
}

用户在操作Netty不需要自己创建pipeline,因为bootstrap在启动的时候,会为每一个channel创建一个独立的pipeline,对于使用者来说,只需要将channelHandler加入到pipeline中。ChannelPipeline支持运行时动态添加或删除handler,并且ChannelPipeline是线程安全的。

自定义实现channelHandler,通常只需要继承ChannelHandlerAdapter就行,对需要处理的方法进行覆盖。

ChannelFuture和Promise

Netty中所有的操作都是异步的,为了获取操作的结果,设计了ChannelFuture。ChannelFuture有两种状态,当一个IO操作开始时,会创建一个ChannelFuture,状态是unCompleted。一旦IO操作完成,会被设置为completed状态。

public interface ChannelFuture extends Future<Void> {
    Channel channel();
    ChannelFuture addListener(GenericFutureListener<? extends Future<? super Void>> var1);
    ChannelFuture addListeners(GenericFutureListener<? extends Future<? super Void>>... var1);
    ChannelFuture removeListener(GenericFutureListener<? extends Future<? super Void>> var1);
    ChannelFuture removeListeners(GenericFutureListener<? extends Future<? super Void>>... var1);
    ChannelFuture sync() throws InterruptedException;
    ChannelFuture syncUninterruptibly();
    ChannelFuture await() throws InterruptedException;
    ChannelFuture awaitUninterruptibly();
    boolean isVoid();
}

强烈建议通过增加ChannelFuture的监听器GenericFutureListener处理。通过GenericFutureListener代替get的原因是:异步IO时,如果不设置超时时间,有可能导致线程一直被挂起,甚至挂死;一旦设置超时时间,如果时间到达后事件还未完成,就会出现异常,所以通过异步回调的方式最佳。

此外,不要在ChannelHandler中调用ChannelFuture.await()方法,可能会导致死锁,原因是:由IO线程负责异步通知发起IO操作的用户线程,如果IO线程和用户线程是同一个,就会导致IO线程等待自己通知完成操作,陷入相互等待。

public interface Promise<V> extends Future<V> {
    Promise<V> setSuccess(V var1);
    boolean trySuccess(V var1);
    Promise<V> setFailure(Throwable var1);
    boolean tryFailure(Throwable var1);
    boolean setUncancellable();
    Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> var1);
    Promise<V> addListeners(GenericFutureListener<? extends Future<? super V>>... var1);
    Promise<V> removeListener(GenericFutureListener<? extends Future<? super V>> var1);
    Promise<V> removeListeners(GenericFutureListener<? extends Future<? super V>>... var1);
    Promise<V> await() throws InterruptedException;
    Promise<V> awaitUninterruptibly();
    Promise<V> sync() throws InterruptedException;
    Promise<V> syncUninterruptibly();
}

Promise是可写的Future,因为jdk自带的Future并没有提供写方法,Netty通过promise对future扩展,用于设置IO操作的结果。

3.2.3 Selector

Selector能够检测多个注册的通道channel上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的处理,这样就可以只用一个单线程去管理多个通道,即IO多路复用。当channel真正有读写事件发生时,线程才会进行读写,大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程,也避免了多线程之间的上下文切换开销。

jdk中selector的定义:

public abstract class Selector implements Closeable {
    // 初始化selector
    protected Selector() { }
    public static Selector open() throws IOException {
        return SelectorProvider.provider().openSelector();
    }
    public abstract boolean isOpen();
    public abstract SelectorProvider provider();
    // 返回selector的keyset
    public abstract Set<SelectionKey> keys();
    // 返回选中的key
    public abstract Set<SelectionKey> selectedKeys();
    /**
     * Selects a set of keys whose corresponding channels are ready for I/O
     * operations. 非阻塞方法
    */ 
    public abstract int selectNow() throws IOException;

    // 阻塞方法,直到有一个IO ready,或者主动调用selector的wakeUp方法,或者等待timeout过期后返回
    public abstract int select(long timeout)
        throws IOException;
    public abstract int select() throws IOException;
    public abstract Selector wakeup();
    public abstract void close() throws IOException;
}

netty中将selector封装到了NioEventLoop中。

public final class NioEventLoop extends SingleThreadEventLoop {
    ...
    private Selector selector;
    private Selector unwrappedSelector;
    NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider, SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
        super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
        if (selectorProvider == null) {
            throw new NullPointerException("selectorProvider");
        } else if (strategy == null) {
            throw new NullPointerException("selectStrategy");
        } else {
            this.provider = selectorProvider;
            SelectorTuple selectorTuple = this.openSelector();
            this.selector = selectorTuple.selector;
            this.unwrappedSelector = selectorTuple.unwrappedSelector;
            this.selectStrategy = strategy;
        }
    }

    private SelectorTuple openSelector() {
        final AbstractSelector unwrappedSelector;
        try {
            unwrappedSelector = this.provider.openSelector();
        } catch (IOException var7) {
            throw new ChannelException("failed to open a new selector", var7);
        }

        if (DISABLE_KEYSET_OPTIMIZATION) {
            return new SelectorTuple(unwrappedSelector);
        } else {
            final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();
            Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object>() {
                public Object run() {
                    try {
                        return Class.forName("sun.nio.ch.SelectorImpl", false, PlatformDependent.getSystemClassLoader());
                    } catch (Throwable var2) {
                        return var2;
                    }
                }
            });
            if (maybeSelectorImplClass instanceof Class && ((Class)maybeSelectorImplClass).isAssignableFrom(unwrappedSelector.getClass())) {
                final Class<?> selectorImplClass = (Class)maybeSelectorImplClass;
                Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        try {
                            Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
                            Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");
                            Throwable cause = ReflectionUtil.trySetAccessible(selectedKeysField);
                            if (cause != null) {
                                return cause;
                            } else {
                                cause = ReflectionUtil.trySetAccessible(publicSelectedKeysField);
                                if (cause != null) {
                                    return cause;
                                } else {
                                    selectedKeysField.set(unwrappedSelector, selectedKeySet);
                                    publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
                                    return null;
                                }
                            }
                        } catch (NoSuchFieldException var4) {
                            return var4;
                        } catch (IllegalAccessException var5) {
                            return var5;
                        }
                    }
                });
                if (maybeException instanceof Exception) {
                    this.selectedKeys = null;
                    Exception e = (Exception)maybeException;
                    logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, e);
                    return new SelectorTuple(unwrappedSelector);
                } else {
                    this.selectedKeys = selectedKeySet;
                    logger.trace("instrumented a special java.util.Set into: {}", unwrappedSelector);
                    return new SelectorTuple(unwrappedSelector, new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));
                }
            } else {
                if (maybeSelectorImplClass instanceof Throwable) {
                    Throwable t = (Throwable)maybeSelectorImplClass;
                    logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, t);
                }

                return new SelectorTuple(unwrappedSelector);
            }
        }
    }

}

Netty对SelectionKey的优化

jdk中对selectorImpl定义有:

public abstract class SelectorImpl extends AbstractSelector {
    protected Set<SelectionKey> selectedKeys = new HashSet();
    protected HashSet<SelectionKey> keys = new HashSet();
    private Set<SelectionKey> publicKeys;
    private Set<SelectionKey> publicSelectedKeys;

    protected SelectorImpl(SelectorProvider sp) {
        super(sp);
        keys = new HashSet<>();
        selectedKeys = new HashSet<>();
        publicKeys = Collections.unmodifiableSet(keys);
        publicSelectedKeys = Util.ungrowableSet(selectedKeys);
    }
}

Netty中selectedKeys对象

final class SelectedSelectionKeySet extends AbstractSet<SelectionKey> {
    SelectionKey[] keys = new SelectionKey[1024];
    int size;
    SelectedSelectionKeySet() {}
    public boolean add(SelectionKey o) {
        if (o == null) {
            return false;
        } else {
            this.keys[this.size++] = o;
            if (this.size == this.keys.length) {
                this.increaseCapacity();
            }

            return true;
        }
    }
    ...
}

修改成了数组。HashSet的add方法在发生哈希冲突时的时间复杂度是O(n), jdk优化后再O(log n), 为此Netty通过反射机制, 将底层的这个HashSet用数组替换了, 毕竟向数组中添加数据的时间复杂度是O(1).

补:EventLoop和EventLoopGroup

首先我们看一下EventLoop的类图结构

java netty框架主动断开连接 netty断开连接channel会如何_java netty框架主动断开连接

可以看到EventLoop继承自eventLoopGroup, 同时继承了OrderedEventExecutor。 EventLoop内部包含了线程池所有的执行方法,也包含了Scheduled调度方法,可以独立进行任务调度。同时,OrderedEventExecutor可以判断一个线程是否属于当前的eventLoop以及eventLoop所在的组。

public interface EventLoop extends OrderedEventExecutor, EventLoopGroup {
    EventLoopGroup parent();
}

public interface EventLoopGroup extends EventExecutorGroup {
    EventLoop next();
    ChannelFuture register(Channel var1);
    ChannelFuture register(ChannelPromise var1);
    /** @deprecated */
    @Deprecated
    ChannelFuture register(Channel var1, ChannelPromise var2);
}

public interface EventExecutorGroup extends ScheduledExecutorService, Iterable<EventExecutor> {
    boolean isShuttingDown();
    Future<?> shutdownGracefully();
    Future<?> shutdownGracefully(long var1, long var3, TimeUnit var5);
    Future<?> terminationFuture();
    /** @deprecated */
    @Deprecated
    void shutdown();
    /** @deprecated */
    @Deprecated
    List<Runnable> shutdownNow();
    EventExecutor next();
    Iterator<EventExecutor> iterator();
    Future<?> submit(Runnable var1);
    <T> Future<T> submit(Runnable var1, T var2);
    <T> Future<T> submit(Callable<T> var1);
    ScheduledFuture<?> schedule(Runnable var1, long var2, TimeUnit var4);
    <V> ScheduledFuture<V> schedule(Callable<V> var1, long var2, TimeUnit var4);
    ScheduledFuture<?> scheduleAtFixedRate(Runnable var1, long var2, long var4, TimeUnit var6);
    ScheduledFuture<?> scheduleWithFixedDelay(Runnable var1, long var2, long var4, TimeUnit var6);
}

EventLoopGroup是一组EventLoop,Channel 一般会调用 EventLoopGroup 的 register 方法来绑定其中一个 EventLoop,后续这个 Channel 上的 io 事件都由此 EventLoop 来处理(保证了 io 事件处理时的线程安全)。

public interface Channel extends AttributeMap, ChannelOutboundInvoker, Comparable<Channel> {
    ...
    public interface Unsafe {
        ...
        void register(EventLoop var1, ChannelPromise var2);  // 将channel绑定到eventLoop上
        void bind(SocketAddress var1, ChannelPromise var2);
        void connect(SocketAddress var1, SocketAddress var2, ChannelPromise var3);
        void disconnect(ChannelPromise var1);
        ...
    }
}


// 具体的执行register方法
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            if (eventLoop == null) {
                throw new NullPointerException("eventLoop");
            } else if (AbstractChannel.this.isRegistered()) {
                promise.setFailure(new IllegalStateException("registered to an event loop already"));
            } else if (!AbstractChannel.this.isCompatible(eventLoop)) {
                promise.setFailure(new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
            } else {
                AbstractChannel.this.eventLoop = eventLoop;
                if (eventLoop.inEventLoop()) {
                    this.register0(promise);
                } else {
                    try {
                        eventLoop.execute(new Runnable() {
                            public void run() {
                                AbstractUnsafe.this.register0(promise);
                            }
                        });
                    } catch (Throwable var4) {
                        AbstractChannel.logger.warn("Force-closing a channel whose registration task was not accepted by an event loop: {}", AbstractChannel.this, var4);
                        this.closeForcibly();
                        AbstractChannel.this.closeFuture.setClosed();
                        this.safeSetFailure(promise, var4);
                    }
                }
            }
        }

常见主要有以下两种EventLoopGroup:

  • 1)NioEventLoopGroup:处理IO事件,普通任务,定时任务;
  • 2)DefaultEventLoopGroup:处理普通任务,定时任务。
public class EventLoopTest {
    public static void main(String[] args) {
        NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(2);
        for(int i = 10; i>0; i--){
            System.out.println(eventLoopGroup.next());
        }
    }
}

输出:

io.netty.channel.nio.NioEventLoop@45018215
io.netty.channel.nio.NioEventLoop@65d6b83b
io.netty.channel.nio.NioEventLoop@45018215
io.netty.channel.nio.NioEventLoop@65d6b83b
io.netty.channel.nio.NioEventLoop@45018215
io.netty.channel.nio.NioEventLoop@65d6b83b
io.netty.channel.nio.NioEventLoop@45018215
io.netty.channel.nio.NioEventLoop@65d6b83b
io.netty.channel.nio.NioEventLoop@45018215
io.netty.channel.nio.NioEventLoop@65d6b83b 

可以看到这里会对生成的两个NioEventLoop循环打印。

public static void main(String[] args) {
        NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(2);
        // 普通的线程提交
        eventLoopGroup.next().execute(EventLoopTest::print);
        // 定时线程提交
        eventLoopGroup.next().scheduleAtFixedRate(EventLoopTest::print, 1,1, TimeUnit.SECONDS);
    }

    private static void print() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(sdf.format(new Date())+ " " +Thread.currentThread());
    }

 2022-10-23 11:00:25 Thread[nioEventLoopGroup-2-1,10,main]
2022-10-23 11:00:26 Thread[nioEventLoopGroup-2-2,10,main]
2022-10-23 11:00:27 Thread[nioEventLoopGroup-2-2,10,main]
2022-10-23 11:00:28 Thread[nioEventLoopGroup-2-2,10,main]