Netty源碼簡析之服務端Channel的創建

1 引入netty的maven依賴如下
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.6.Final</version>
</dependency>
 
2 netty的基本組件如下
 
 
  • Channel:Java Stream是以流的方式一個一個byte通過滑動窗口來接收,是連續性的,造成阻塞,只能一次性接收完。而NIO Channel非阻塞,但是必須通過Buffer來中轉。
  • Buffer:可理解爲一個數組的封裝,例如 IntBuffer、CharBuffer、ByteBuffer 等分別對應 int[]、char[]、byte[] 等。有Byte/Short/Long/Int/Float/Double/Char/MappedByteBuffer。
    • buffer四個基本屬性:
      • capacity:當前buffer的最大容量,如int[] buffer = new int[1024];  那capacity=1024 
      • position:當前buffer真實已用的下一個位置,如buffer最大容量=1024,現在真實用了500,那position=500
      • limit:讀情況下,limit=buffer實際大小;寫情況下,limit=capacity
      • mark:記錄當前postion位置,即標記上一次讀寫位置
    • mark <= position <= limit <= capacity
  • Selector:輪詢channel,輪詢到某個channel是讀寫時間的就緒狀態,就取出來
netty啓動過程分爲:
- 創建服務端channel
- 初始化服務端channel
- 註冊selector
 
3 以下是一個簡單的netty服務段程序代碼(在後續部分講解會用到)
public static void main(String[] args) throws Exception {
    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    EventLoopGroup workerGroup = new NioEventLoopGroup();
 
    try {
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)  // A點 設置channel
                .childOption(ChannelOption.TCP_NODELAY, true)
                .childAttr(AttributeKey.newInstance("childAttr"), "childAttrValue")
                .handler(new ServerHandler())
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) {
                        ch.pipeline().addLast(new AuthHandler());
                    }
                });
        ChannelFuture f = b.bind(8888).sync();
        f.channel().closeFuture().sync();
    } finally {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
}
 
4 服務端Channel的創建過程
/** AbstractBootStrap.java類中
* Create a new {@link Channel} and bind it.
*/
public ChannelFuture bind(int inetPort) {
    return bind(new InetSocketAddress(inetPort));
}
 
/** AbstractBootStrap.java類中
* Create a new {@link Channel} and bind it.
*/
public ChannelFuture bind(SocketAddress localAddress) {
    validate();
    if (localAddress == null) {
        throw new NullPointerException("localAddress");
    }
    return doBind(localAddress);
}
然後調用initAndRegister()創建服務端channel
// AbstractBootStrap.java類中
private ChannelFuture doBind(final SocketAddress localAddress) {
    final ChannelFuture regFuture = initAndRegister();
    final Channel channel = regFuture.channel();
    ...........
}
最終發現是通過newChannel()創建的,點進去發現是由clazz.newInstance()反射來創建一個channel
final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        channel = channelFactory.newChannel();
        init(channel);
        ...................
}
閱讀上面的代碼可知channel是通過反射來創建的,但是對應的對象channelFactory是如何初始化的?
首先我們看3的A點,可以看到通過.chanel(NioServerSocketChannel.class)設置了channelFactory,往下繼續跟
 
 
點擊.channel()方法後進入查看如下
public B channel(Class<? extends C> channelClass) {
    ...........
    return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
上面可知 NioServerSocketChannel.class 傳入ReflectiveChannelFactory中,然後賦值給了裏面的一個clazz參數。然後最後包裝好會賦值給AbstractBootStrap.java的channelFactory屬性,最終複製代碼如下
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
if (channelFactory == null) {
throw new NullPointerException("channelFactory");
}
if (this.channelFactory != null) {
throw new IllegalStateException("channelFactory set already");
}
 
this.channelFactory = channelFactory;
return (B) this;
}
總結:通過channel(NioServerSocketChannel.class),傳入NioServerSocketChannel.class,然後使用ReflectiveChannelFactory包裝後賦值給channelFactory屬性。然後最終調用clazz.newInstance()來實例化一個channel實例。
 
通過上面我們可以知道channel是通過反射來創建的,接下來我們來看看他創建對應的NioServerSocketChannel.java類,其構造方法如下:
public NioServerSocketChannel() {
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
首先點擊newSocket()方法如下,可見調用的jdk底層來創建socket(點擊openServerSocketChannel() 可見其在jdk1.8的包中)
private static ServerSocketChannel newSocket(SelectorProvider provider) {
    try {
        return provider.openServerSocketChannel();
    } catch (IOException e) {
        throw new ChannelException("Failed to open a server socket.", e);
    }
}
然後繼續點擊this(newSocket(DEFAULT_SELECTOR_PROVIDER))的this,找到當前對應的方法如下:
public NioServerSocketChannel(ServerSocketChannel channel) {
    super(null, channel, SelectionKey.OP_ACCEPT);
    config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
點擊super()層層往上看,最後找到代碼如下:
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    super(parent);
    this.ch = ch;
    this.readInterestOp = readInterestOp;
    try {
        ch.configureBlocking(false);   //設置非阻塞
    .................
}
由上可見其默認設置了非阻塞(通過 ch.configureBlocking(false)  設置),繼續看上面的super(parent)方法如下:
protected AbstractChannel(Channel parent) {
    this.parent = parent;
    id = newId();
    unsafe = newUnsafe();
    pipeline = newChannelPipeline();
}
可看到其最終賦值了(id,unsafe,pipeline)這三個屬性,這幾個屬性將會在後續用到。
 
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章