Netty学习--服务端启动(未完成)

首先看一个netty服务端启动的demo

bossGroup对应的是Server.java中的接收客户端请求的线程。

workerGroup对应的是Client.java中的线程。

public class Server {
 
    public static void main(String[] args) throws Exception {
        // 配置服务端的 NIO线程组
        // boss线程组用于网络事件的监听
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        // worker线程组用于SocketChannel的网络读写
        EventLoopGroup workerGroup = new NioEventLoopGroup();
 
        try {
            // NIO服务端的辅助启动类,目的是降低服务端开发的复杂度
            ServerBootstrap b = new ServerBootstrap();
            // 配置两大线程组
            b.group(bossGroup, workerGroup)
                    // 配置服务端channel
                    .channel(NioServerSocketChannel.class)
                    // 配置TCP基本属性
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    // 客户端创建连接时绑定基本属性
                    .childAttr(AttributeKey.newInstance("childAttr"), "childAttrValue")
                    // 配置服务端启动过程逻辑处理器,在这里ServerHandler对应ServerSocket的accept
                    .handler(new ServerHandler()) 
                    // 配置业务处理链 handler pipeline
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) {
                            // 配置业务处理handler,对数据流进行读写等处理
                            // ch.pipeline().addLast()
 
                        }
                    });
 
            // 前面都是一些属性配置的逻辑,真正的服务端启动在此处开始
            // 绑定端口,正式启动server端服务
            ChannelFuture f = b.bind(8888).sync();
            // 同步等待,直至服务端监听端口关闭
            f.channel().closeFuture().sync();
        } finally {
            // 优雅退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
 
}

ServerHandler

ublic class ServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("channelActive");
    }
 
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) {
        System.out.println("channelRegistered");
    }
 
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        System.out.println("handlerAdded");
    }
 
    @Override
    public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
        super.channelRead(ctx, msg);
 
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 耗时的操作
                String result = loadFromDB();
 
                ctx.channel().writeAndFlush(result);
                ctx.executor().schedule(new Runnable() {
                    @Override
                    public void run() {
                        // ...
                    }
                }, 1, TimeUnit.SECONDS);
 
            }
        }).start();
    }
 
    private String loadFromDB() {
        return "hello world!";
    }
}

 

两个问题:

服务端的socket在哪里初始化?netty在哪里调用到jdk的底层的socket api。

在哪里accept连接?

 

netty服务端启动的四个过程:

  • 创建服务端Channel:调用jdk底层的api创建jdk的channel,然后netty将其包装为自己的channel,同时创建一些基本组件,绑定在此channel上。
  • 初始化服务端Channel:创建完channel后,netty基于此channel做一些初始化工作,如初始化一些基本属性,添加一些逻辑处理器。
  • 注册selector:netty将jdk底层的channel注册到事件轮询器selector下面,并把netty的服务端channel作为一个attachment绑定在对应的jdk底层的服务端channel,这样在后续有事件轮询出来之后,就可以直接拿到这个attachment,这个attachment就是netty封装的一个服务端channel。
  • 端口绑定:调用jdk底层api,实现对本地端口监听。

 

创建服务端Channel

方法调用如下图:

 

 

服务端创建channel的入口为如下代码的bind方法。

ChannelFuture f = b.bind(8888).sync();

进入代码后在280行的doBind()方法中的281行,调用initAndRegister(),在这个方法中创建服务端的channel,通过反射的方式创建服务端Channel,反射操作在创建服务端 Channel 对象的工厂的 newChannel 方法,创建服务端 Channel 对象的工厂在ServerBootstrap 类的 channel 方法中确定。

如下所示

public B channel(Class<? extends C> channelClass) {
        if (channelClass == null) {
            throw new NullPointerException("channelClass");
        }
        return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
    }

 

在服务端创建Channel的过程中,上述代码中传入的Class类为 NioServerSocketChannel.class,这个类是在配置ServerBootstrap时通过其channel()方法确定的,所以创建的服务端Channel对象也就是 NioServerSocketChannel 类的对象,实例化 NioServerSocketChannel 类时会调用该类及其父类的一系列构造方法,这一过程中将会创建 jdk 底层的 jdk channel 以及做一些基础的初始化工作,比如设置服务端Channel的阻塞模式,创建服务端Channel的id、unsafe、pipeline成员等。

相关代码调用层次如下:

 

 

每一个Channel在创建的时候都会为其分配一个新的ChannelPipeline,这项关联是永久性的,Channel既不能附加另外一个ChannelPipeline,也不能分离其当前的。ChannelPipeline中以链式结构存储ChannelHandlerContext,在刚开始创建ChannelPipeline的时候会先创建两个节点:head、tail节点,后续再往ChannelPipeline中添加ChannelHandler时可调用ChannelPipeline的addLast方法进行添加,处理过程中会将ChannelHandler封装为ChannelHandlerContext,然后添加进去。

 

服务端Channel创建完成后会对其进行初始化,初始化工作大概就是保存用户自定义的属性,然后配置服务端的pipeline,再通过保存的用户属性创建一个连接接入器,并将其添加到服务端的pipeline,连接接入器每次accept新的连接之后都会使用这些属性对新连接做一些配置。

 

服务端channel初始化

 

在通过反射创建完channel后,就会通过init()方法对channel进行初始化操作。初始化的代码调用层次如下:

配置用户自定义的ChannelOptions和ChannelAttrs,在服务端代码中用的不是很多

配置用户的childOptions和childAttrs,这两个属性是为通过服务端channel创建出来的新链接的channel创建,每次accept新连接,就会把用户自定义的两个属性配置上去 

配置服务端pipeline

添加ServerBootstrapAcceptor 添加连接器 (给accept的新连接,分配一个nio线程)

 

在ServerBootstrap类中141行的init方法中:

 

@Override
    void init(Channel channel) throws Exception {
    // 读取channelOptions final Map<ChannelOption<?>, Object> options = options0(); synchronized (options) {
      // 创建服务端channel的时候也会创建config channel.config().setOptions(options); }      // 为channel绑定用户自定义属性 final Map<AttributeKey<?>, Object> attrs = attrs0(); synchronized (attrs) { for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) { @SuppressWarnings("unchecked") AttributeKey<Object> key = (AttributeKey<Object>) e.getKey(); channel.attr(key).set(e.getValue()); } } ChannelPipeline p = channel.pipeline(); final EventLoopGroup currentChildGroup = childGroup; final ChannelHandler currentChildHandler = childHandler; final Entry<ChannelOption<?>, Object>[] currentChildOptions; final Entry<AttributeKey<?>, Object>[] currentChildAttrs; synchronized (childOptions) { currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size())); } synchronized (childAttrs) { currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size())); } p.addLast(new ChannelInitializer<Channel>() { @Override public void initChannel(Channel ch) throws Exception { final ChannelPipeline pipeline = ch.pipeline(); ChannelHandler handler = config.handler(); if (handler != null) { pipeline.addLast(handler); } // We add this handler via the EventLoop as the user may have used a ChannelInitializer as handler. // In this case the initChannel(...) method will only be called after this method returns. Because // of this we need to ensure we add our handler in a delayed fashion so all the users handler are // placed in front of the ServerBootstrapAcceptor. ch.eventLoop().execute(new Runnable() { @Override public void run() { pipeline.addLast(new ServerBootstrapAcceptor( currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)); } }); } }); }

  

  • 调用newChannel()创建服务端channel,即调用jdk创建底层channel,然后netty将其包装成自己的一个channel,同时创建一些基本组件绑定在此channel上,如pipeline
  • 调用init()方法,主要为服务端channel添加一个连接处理器

 

posted @ 2020-04-06 23:34  liekkas01  阅读(293)  评论(0编辑  收藏  举报