Jetty NIO (二)

这次重点分析两个方法,一个是SelectorSet.doSelect, 一个是EndPoint.schedule, 这两个方法代表最核心的JettyNIO实现过程。后面就是Http协议的具体处理业务逻辑了。

Jetty将各种IO模型混合在一个统一的框架中,并没有剥离开来,不知为何要采用如此复杂的设计?一个SelectorSet.doSelect简化的NIO流程如下所示:

public class SelectSet implements Dumpable
    {
        public void doSelect() throws IOException
        {
            try
            {
                _selecting=Thread.currentThread();
                final Selector selector=_selector;
                // Stopped concurrently ?
                if (selector == null)
                    return;

                // Make any key changes required
                Object change;
                int changes=_changes.size();
                while (changes-->0 && (change=_changes.poll())!=null)
                {
                    Channel ch=null;
                    SelectionKey key=null;

                    try
                    {
                        if (change instanceof EndPoint)
                        {
                            // Update the operations for a key.
                            SelectChannelEndPoint endpoint = (SelectChannelEndPoint)change;
                            ch=endpoint.getChannel();
                            endpoint.doUpdateKey();
                        }
                        else if (change instanceof ChangeTask)
                        {
                            ((Runnable)change).run();
                        }
                        else if (change instanceof Runnable)
                        {
                            dispatch((Runnable)change);
                        }
                        else
                            throw new IllegalArgumentException(change.toString());
                    }
                    catch (CancelledKeyException e)
                    {
                    }
                    catch (Throwable e)
                    {
                    }
                }


                // Do and instant select to see if any connections can be handled.
                int selected=selector.selectNow();

                long now=System.currentTimeMillis();

                // if no immediate things to do
                if (selected==0 && selector.selectedKeys().isEmpty())
                {
                    // If we are in pausing mode
                    if (_pausing)
                    {
                        try
                        {
                            Thread.sleep(__BUSY_PAUSE); // pause to reduce impact of  busy loop
                        }
                        catch(InterruptedException e)
                        {
                            LOG.ignore(e);
                        }
                        now=System.currentTimeMillis();
                    }

                    //--------------------------------------------------------------------------------------//
					// 此段代码较为复杂,Timeout是一个拥有超时判断功能的任务队列,在Jetty中主要服务于异步Servlet, 
					// selector中注册的也就包含两个方向的事件,一个方向是判断客户端的连接是否可读,另一个方向就是判断
					// 异步Servlet是否完成了后端的业务,可以向客户端输出结果。线程运行到此处是由于Selector中目前没有任何IO事件发生,
					// 因此再查看Timeout中的下一个异步Servlet何时可以完成异步任务,如果此时_changes(前端IO事件)中再无待select的任务,则
					// 等待下一个异步servlet完成,防止过度轮询造成的cpu浪费。
					// 在轮询上的另一优化点是,如果忙select的次数过多(默认是10w次),则启用selector的pause模式(默认关闭),该模式下,
					// 上次select无事件后,则会休眠固定时间后,再次执行select操作。
                    // workout how long to wait in select
                    _timeout.setNow(now);
                    long to_next_timeout=_timeout.getTimeToNext();

                    long wait = _changes.size()==0?__IDLE_TICK:0L;
                    if (wait > 0 && to_next_timeout >= 0 && wait > to_next_timeout)
                        wait = to_next_timeout;

                    // If we should wait with a select
                    if (wait>0)
                    {
                        long before=now;
                        selector.select(wait);
                        now = System.currentTimeMillis();
                        _timeout.setNow(now);

                        // If we are monitoring for busy selector
                        // and this select did not wait more than 1ms
                        if (__MONITOR_PERIOD>0 && now-before <=1)
                        {
                            // count this as a busy select and if there have been too many this monitor cycle
                            if (++_busySelects>__MAX_SELECTS)
                            {
                                // Start injecting pauses
                                _pausing=true;

                                // if this is the first pause
                                if (!_paused)
                                {
                                    // Log and dump some status
                                    _paused=true;
                                    LOG.warn("Selector {} is too busy, pausing!",this);
                                }
                            }
                        }
                    }
                }
                //--------------------------------------------------------------------------------------//

                // have we been destroyed while sleeping
                if (_selector==null || !selector.isOpen())
                    return;

                // 接着一个一个来处理发生的IO事件
                for (SelectionKey key: selector.selectedKeys())
                {
                    SocketChannel channel=null;

                    try
                    {
						// 为何要有这一段判断?
                        if (!key.isValid())
                        {
                            key.cancel();
                            SelectChannelEndPoint endpoint = (SelectChannelEndPoint)key.attachment();
                            if (endpoint != null)
                                endpoint.doUpdateKey();
                            continue;
                        }

                        Object att = key.attachment();
						// 该Channel之前已经发生过IO事件,因此key.attachment已经包含了一个EndPoint
                        if (att instanceof SelectChannelEndPoint)
                        {
                            if (key.isReadable()||key.isWritable())
                                ((SelectChannelEndPoint)att).schedule();
                        }
						// 服务器端SocketChannel已经和客户端完成了Http连接的建立
						// 为该新连接创建EndPoint并加入Selector轮询
                        else if (key.isConnectable())
                        {
                            // Complete a connection of a registered channel
                            channel = (SocketChannel)key.channel();
                            boolean connected=false;
                            try
                            {
                                connected=channel.finishConnect();
                            }
                            catch(Exception e) {}
                            finally
                            {
                                if (connected)
                                {
                                    key.interestOps(SelectionKey.OP_READ);
                                    SelectChannelEndPoint endpoint = createEndPoint(channel,key);
                                    key.attach(endpoint);
                                    endpoint.schedule();
                                }
                                else
                                {
                                    key.cancel();
                                }
                            }
                        }
						// 该连接第一次有IO事件发生,为该连接建立EndPoint,上一步已经建立了,为什么要重复建立?
                        else
                        {
                            // Wrap readable registered channel in an endpoint
                            channel = (SocketChannel)key.channel();
                            SelectChannelEndPoint endpoint = createEndPoint(channel,key);
                            key.attach(endpoint);
                            if (key.isReadable())
                                endpoint.schedule();
                        }
                        key = null;
                    }
                    catch (CancelledKeyException e) {}
                    catch (Exception e) {
                        try
                        {
                            if (channel!=null)
                                channel.close();
                        }
                        catch(IOException e2) {}

                        if (key != null && !(key.channel() instanceof ServerSocketChannel) && key.isValid())
                            key.cancel();
                    }
                }

                // Everything always handled
                selector.selectedKeys().clear();

                now=System.currentTimeMillis();
                _timeout.setNow(now);
                Task task = _timeout.expired();
                while (task!=null)
                {
                    if (task instanceof Runnable)
                        dispatch((Runnable)task);
                    task = _timeout.expired();
                }

                // Idle tick
                if (now-_idleTick>__IDLE_TICK)
                {
                    _idleTick=now;

                    final long idle_now=((_lowResourcesConnections>0 && selector.keys().size()>_lowResourcesConnections))
                        ?(now+_maxIdleTime-_lowResourcesMaxIdleTime)
                        :now;

                    dispatch(new Runnable()
                    {
                        public void run()
                        {
                            for (SelectChannelEndPoint endp:_endPoints.keySet())
                            {
                                endp.checkIdleTimestamp(idle_now);
                            }
                        }
                        public String toString() {return "Idle-"+super.toString();}
                    });

                }

                // Reset busy select monitor counts
                if (__MONITOR_PERIOD>0 && now>_monitorNext)
                {
                    _busySelects=0;
                    _pausing=false;
                    _monitorNext=now+__MONITOR_PERIOD;

                }
            }
            catch (ClosedSelectorException e) {}
            catch (CancelledKeyException e) {}
            finally {
                _selecting=null;
            }
        }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值