Netty源码解读------------ServerBootstrap的启动(一)

在阅读Netty源码之前,务必要学习nio的知识,否则很难理解。

自jdk1.4自后,便引入了nio的类库。为了提高nio的系统性能以及吞吐量,有人就在其基础上再次进行封装,便出了Netty框架。现在不少问nio的技术面试官,大部分会问到你用过Netty框架吗?读过它的源码吗?由此,我便尝试着解读其源码,一起和大家分享,^__^。

  • Netty主要类
    • ServerBootstrap
    • NioServerSocketChannelFactory
    • NioWorker
    • PipeFactorySinkeFactory
    • DefaultChannelPipeline
    • Binder
    • NioServerSocketPipelineSink
    • NioServerSocketChannel
1. 首先分析它是如何启动监听的
package com.netty.test;  

import org.jboss.netty.bootstrap.ServerBootstrap;  
import org.jboss.netty.channel.*;  
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;  
import org.jboss.netty.handler.codec.string.StringDecoder;  
import org.jboss.netty.handler.codec.string.StringEncoder;  

import java.net.InetSocketAddress;  
import java.util.concurrent.Executors;  

/** 
 * God Bless You! 
 * Author: Fangniude 
 * Date: 2013-07-15 
 */  
public class NettyServer {  
    public static void main(String[] args) {  
        ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));  

        // Set up the default event pipeline.  
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {  
            public ChannelPipeline getPipeline() throws Exception {  
                return Channels.pipeline(new StringDecoder(), new StringEncoder(), new ServerHandler());  
            }  
        });  
        // Bind and start to accept incoming connections.  
        Channel bind = bootstrap.bind(new InetSocketAddress(8000));  
        System.out.println("Server已经启动,监听端口: " + bind.getLocalAddress() + ", 等待客户端注册。。。");  
    }  

    private static class ServerHandler extends SimpleChannelHandler { 
        @Override  
        public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {  
            if (e.getMessage() instanceof String) {
                String message = (String) e.getMessage();  
                System.out.println("Client发来:" + message);  
                e.getChannel().write("Server已收到刚发送的:" + message);  
                System.out.println("\n等待客户端输入。。。");  
            }  

            super.messageReceived(ctx, e);  
        }  

        @Override  
        public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {  
            super.exceptionCaught(ctx, e);  
        }  

        @Override  
        public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {  
            System.out.println("有一个客户端注册上来了。。。");  
            System.out.println("Client:" + e.getChannel().getRemoteAddress());  
            System.out.println("Server:" + e.getChannel().getLocalAddress());  
            System.out.println("\n等待客户端输入。。。");  
            super.channelConnected(ctx, e);  
        }  
    }  
} 

上面这段代码是启动服务器端的代码。
在ServerBootstrap里面bind方法是这样写的。

 public Channel bind(final SocketAddress localAddress) {
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        }
        //存储被注册的channel队列
        final BlockingQueue<ChannelFuture> futureQueue =
            new LinkedBlockingQueue<ChannelFuture>();
        //这是一个解析触发事件 实现了SimpleChannelUpstreamHandler
        ChannelHandler binder = new Binder(localAddress, futureQueue);
        //默认是空
        ChannelHandler parentHandler = getParentHandler();
        //存储事件和触发事件的对象
        ChannelPipeline bossPipeline = pipeline();
        bossPipeline.addLast("binder", binder);
        if (parentHandler != null) {
            bossPipeline.addLast("userHandler", parentHandler);
        }
        //通过ChannelFactory创建一个ServerSocketChannel对象
        Channel channel = getFactory().newChannel(bossPipeline);

        // Wait until the future is available.
        ChannelFuture future = null;
        boolean interrupted = false;
        do {
            try {
                //获取NioServerSocketChannel对象
                future = futureQueue.poll(Integer.MAX_VALUE, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                interrupted = true;
            }
        } while (future == null);

        if (interrupted) {
            Thread.currentThread().interrupt();
        }

        // Wait for the future.
        future.awaitUninterruptibly();
        if (!future.isSuccess()) {
            future.getChannel().close().awaitUninterruptibly();
            throw new ChannelException("Failed to bind to: " + localAddress, future.getCause());
        }

        return channel;
    }

下面看下 Channel channel = getFactory().newChannel(bossPipeline); 里面的写法。

 NioServerSocketChannel(
            ChannelFactory factory,
            ChannelPipeline pipeline,
            ChannelSink sink) {

        super(factory, pipeline, sink);

        try {
            socket = ServerSocketChannel.open();
        } catch (IOException e) {
            throw new ChannelException(
                    "Failed to open a server socket.", e);
        }

        try {
            socket.configureBlocking(false);
        } catch (IOException e) {
            try {
                socket.close();
            } catch (IOException e2) {
                logger.warn(
                        "Failed to close a partially initialized socket.", e2);
            }

            throw new ChannelException("Failed to enter non-blocking mode.", e);
        }

        config = new DefaultServerSocketChannelConfig(socket.socket());
        //注意这里, 这里是触发打开事件, 看这个里面的实现
        fireChannelOpen(this);
    }
        /**
     * Sends a {@code "channelOpen"} event to the first
     * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
     * the specified {@link Channel}.  If the specified channel has a parent,
     * a {@code "childChannelOpen"} event will be sent, too.
     */
    public static void fireChannelOpen(Channel channel) {
        // Notify the parent handler.
        if (channel.getParent() != null) {
            fireChildChannelStateChanged(channel.getParent(), channel);
        }
        //这个pipeline就是之前在ServerBootstrap放进去的DefaultPipeLine对象,当时放了2个handler进去了。 sendUpStream则是取出向上去取里面的handler对象, 然后做处理.这里触发的是OPEN事件,发的是TRUE值,后面会用到。
        channel.getPipeline().sendUpstream(
                new UpstreamChannelStateEvent(
                        channel, ChannelState.OPEN, Boolean.TRUE));
    }

void sendUpstream(DefaultChannelHandlerContext ctx, ChannelEvent e) {
        try {
            //看handlerUpstream代码
            ((ChannelUpstreamHandler) ctx.getHandler()).handleUpstream(ctx, e);
        } catch (Throwable t) {
            notifyHandlerException(e, t);
        }
    }
/**
     * {@inheritDoc}  Down-casts the received upstream event into more
     * meaningful sub-type event and calls an appropriate handler method with
     * the down-casted event.
     */
    public void handleUpstream(
            ChannelHandlerContext ctx, ChannelEvent e) throws Exception {

        if (e instanceof MessageEvent) {
            messageReceived(ctx, (MessageEvent) e);
        } else if (e instanceof WriteCompletionEvent) {
            WriteCompletionEvent evt = (WriteCompletionEvent) e;
            writeComplete(ctx, evt);
        } else if (e instanceof ChildChannelStateEvent) {
            ChildChannelStateEvent evt = (ChildChannelStateEvent) e;
            if (evt.getChildChannel().isOpen()) {
                childChannelOpen(ctx, evt);
            } else {
                childChannelClosed(ctx, evt);
            }
        } else if (e instanceof ChannelStateEvent) {
            ChannelStateEvent evt = (ChannelStateEvent) e;
            switch (evt.getState()) {
            case OPEN:
                //发送的是OPEN事件,触发这里。
                if (Boolean.TRUE.equals(evt.getValue())) {
                    //Binder类里面的方法
                    channelOpen(ctx, evt);
                } else {
                    channelClosed(ctx, evt);
                }
                break;
            case BOUND:
                if (evt.getValue() != null) {
                    channelBound(ctx, evt);
                } else {
                    channelUnbound(ctx, evt);
                }
                break;
            case CONNECTED:
                if (evt.getValue() != null) {
                    channelConnected(ctx, evt);
                } else {
                    channelDisconnected(ctx, evt);
                }
                break;
            case INTEREST_OPS:
                channelInterestChanged(ctx, evt);
                break;
            default:
                ctx.sendUpstream(e);
            }
        } else if (e instanceof ExceptionEvent) {
            exceptionCaught(ctx, (ExceptionEvent) e);
        } else {
            ctx.sendUpstream(e);
        }
    }
 private final class Binder extends SimpleChannelUpstreamHandler {

        private final SocketAddress localAddress;
        private final BlockingQueue<ChannelFuture> futureQueue;
        private final Map<String, Object> childOptions =
            new HashMap<String, Object>();

        Binder(SocketAddress localAddress, BlockingQueue<ChannelFuture> futureQueue) {
            this.localAddress = localAddress;
            this.futureQueue = futureQueue;
        }

        @Override
        public void channelOpen(
                ChannelHandlerContext ctx,
                ChannelStateEvent evt) {

            try {
                evt.getChannel().getConfig().setPipelineFactory(getPipelineFactory());

                // Split options into two categories: parent and child.
                Map<String, Object> allOptions = getOptions();
                Map<String, Object> parentOptions = new HashMap<String, Object>();
                for (Entry<String, Object> e: allOptions.entrySet()) {
                    if (e.getKey().startsWith("child.")) {
                        childOptions.put(
                                e.getKey().substring(6),
                                e.getValue());
                    } else if (!e.getKey().equals("pipelineFactory")) {
                        parentOptions.put(e.getKey(), e.getValue());
                    }
                }

                // Apply parent options.
                evt.getChannel().getConfig().setOptions(parentOptions);
            } finally {

                ctx.sendUpstream(evt);
            }

            //这里就是之前ServerBootstrap里面的futureQueue那个对象。存放了一个
            boolean finished = futureQueue.offer(evt.getChannel().bind(localAddress));
            assert finished;
        }

下节将会讲解Netty的socket监听以及读取信息。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Netty5.0 架构剖析和解读 作者:李林锋 版权所有 email neu_lilinfeng@ © Netty5.0 架构剖析和解读1 1. 概述2 1.1. JAVA 的IO演进2 1.1.1. 传统BIO通信的弊端2 1.1.2. Linux 的网络IO模型简介4 1.1.3. IO复用技术介绍7 1.1.4. JAVA的异步IO8 1.1.5. 业界主流的NIO框架介绍10 2.NIO入门10 2.1. NIO服务端10 2.2. NIO客户端13 3.Netty码分析16 3.1. 服务端创建16 3.1.1. 服务端启动辅助类ServerBootstrap16 3.1.2. NioServerSocketChannel 的注册21 3.1.3. 新的客户端接入25 3.2. 客户端创建28 3.2.1. 客户端连接辅助类Bootstrap28 3.2.2. 服务端返回ACK应答,客户端连接成功32 3.3. 读操作33 3.3.1. 异步读取消息33 3.4. 写操作39 3.4.1. 异步消息发送39 3.4.2. Flush操作42 4.Netty架构50 4.1. 逻辑架构50 5. 附录51 5.1. 作者简介51 5.2. 使用声明51 1. 概述 1.1.JAVA 的IO演进 1.1.1. 传统BIO通信的弊端 在JDK 1.4推出JAVANIO1.0之前,基于JAVA 的所有Socket通信都采用 BIO 了同步阻塞模式( ),这种一请求一应答的通信模型简化了上层的应用开发, 但是在可靠性和性能方面存在巨大的弊端。所以,在很长一段时间,大型的应 C C++ 用服务器都采用 或者 开发。当并发访问量增大、响应时间延迟变大后, 采用JAVABIO作为服务端的软件只有通过硬件不断的扩容来满足访问量的激 增,它大大增加了企业的成本,随着集群的膨胀,系统的可维护性也面临巨大 的挑战,解决这个问题已经刻不容缓。 首先,我们通过下面这幅图来看下采用BIO 的服务端通信模型:采用BIO 通信模型的 1connect NewThread1 WebBrowse 2connect 2handle(Req) WebBrowse 3connect Acceptor NewThread2 WebBrowse WebBrowse 4connect NewThread3 3sendResponsetopeer NewThread4 图1.1.1-1 BIO通信模型图 服务端,通常由一个独立的Accepto 线程负责监听客户端的连接,接收到客户 端连接之后为客户端连接创建一个新的线程处理请求消息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值