Jetty学习【三】 Handler

本文介绍了Jetty中的Handler概念,包括其在产生HTTP响应、检查和修改请求以及调用其他Handler中的作用。接着,讨论了Jetty提供的不同Handler类型,并详细阐述了Handler的调用路径和具体执行步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、什么是Handler

先来看看javadoc中对Handler的描述

/* ------------------------------------------------------------ */
/** A Jetty Server Handler.
 * 
 * A Handler instance is required by a {@link Server} to handle incoming
 * HTTP requests.  A Handler may: <ul>
 * <li>Completely generate the HTTP Response</li>
 * <li>Examine/modify the request and call another Handler (see {@link HandlerWrapper}).
 * <li>Pass the request to one or more other Handlers (see {@link HandlerCollection}).
 * </ul>
 * 
 * Handlers are passed the servlet API request and response object, but are 
 * not Servlets.  The servlet container is implemented by handlers for 
 * context, security, session and servlet that modify the request object 
 * before passing it to the next stage of handling.
 * 
 */
public interface Handler extends LifeCycle, Destroyable

也就是说,当一个HTTP请求到来的时候,需要Handler进行处理,而且必须要有一个Handler否则消息无法处理。Handler主要有以下几个功能:

1)产生HTTP Response

2)检查和修改request,并且调用其他Handler。之前已经介绍过,HandlerWrapper可以将多个Handler连接起来使用。

3)将request传递给其他的一个或者多个Handler。

现在知道了Handler的大致功能,接下来看看Jetty中实现了哪些Handler可以供开发者使用。

2、现有的Handler



在来回顾下之前的两篇文章中已经提到的Handler,

1)DefaultHandler,用于返回默认的错误页面404
2)ContextHandler,可以设置web应用的context
3)WebAppContext,可以设置web应用的context,用于加载和处理已有的web工程。
4)ServletContextHandler,可以设置web的context,同时可以添加servlet用于处理http request。
5)ResourceHandler,用于加载本地资源并且显示
之外还有HandlerWrapper,HandlerCollection,ContextHandlerCollection,HandlerList。

3、Handler调用路径

首先可以确认的是Server中的handle是调用Handler的入口,那么我们以这个函数为线索一步一步找出Handler是如何被调用的。
1)看一下handle函数是如何被调用的



2)查看handleRequest是如何被调用的


3)查看handle是如何被调用的


4)查看handle是如何被调用的


可以看出是在一个线程中被调用的,那么找出这个线程,


5)查看_handler是如何被调用的



6)查看dispatch是如何被调用的


7)查看schedule是如何被调用的



8)查看doSelect是如何被调用的


是在一个线程中被调用,查看这个线程



而这个函数所在的类是SelectorManager,在上一篇文章中讲到server启动时,会从_beans中读取各个bean,然后调用bean的start()函数。而其中有一个bean的类型

是SelectChannelConnector$ConnectorSelectorManager,我们来看一个这个类的定义,

    private final class ConnectorSelectorManager extends SelectorManager

它的父类正好是SelectorManager,再来看一下SelectManager的定义,
/* ------------------------------------------------------------ */
/**
 * The Selector Manager manages and number of SelectSets to allow
 * NIO scheduling to scale to large numbers of connections.
 * <p>
 */
public abstract class SelectorManager extends AbstractLifeCycle implements Dumpable

AbstractLifeCycle是生命周期的抽象,其中正好有start()函数,该函数会调用doStart(),而这个doStart()就是上面截图中的doStart()函数。将文章2中服务器启动部分和上面的步骤倒过来连起来,就可以看到handler完整的调用路径。

服务器启动 -> 步骤8 -> 步骤7 ->步骤6 -> 步骤5 ...... -> 步骤1.

4、Handler调用详细步骤


首先看看doStart()函数的工作流程,
protected void doStart() throws Exception {
		// 初始化_selectSet,SelectSet是对Selector的封装
		_selectSet = new SelectSet[_selectSets];
		for (int i = 0; i < _selectSet.length; i++)
			_selectSet[i] = new SelectSet(i);

		super.doStart();

		// start a thread to Select
		// 根据设置的selector数目_selectSets,启动_selectSets个线程
		for (int i = 0; i < getSelectSets(); i++) {
			// 匿名类中需要使用final
			final int id = i;
			// 使用dispatch函数分发线程,并且获取返回值
			boolean selecting = dispatch(new Runnable() {
				public void run() {
					String name = Thread.currentThread().getName();
					int priority = Thread.currentThread().getPriority();
					try {
						SelectSet[] sets = _selectSet;
						if (sets == null)
							return;
						// 获取第i个selector
						SelectSet set = sets[id];

						Thread.currentThread().setName(name + " Selector" + id);
						if (getSelectorPriorityDelta() != 0)
							Thread.currentThread().setPriority(
									Thread.currentThread().getPriority()
											+ getSelectorPriorityDelta());
						LOG.debug("Starting {} on {}", Thread.currentThread(),
								this);
						// 线程主内容:selector不停的doSelect()
						while (isRunning()) {
							try {
								set.doSelect();
							} catch (IOException e) {
								LOG.ignore(e);
							} catch (Exception e) {
								LOG.warn(e);
							}
						}
					} finally {
						LOG.debug("Stopped {} on {}", Thread.currentThread(),
								this);
						Thread.currentThread().setName(name);
						if (getSelectorPriorityDelta() != 0)
							Thread.currentThread().setPriority(priority);
					}
				}

			});

			if (!selecting)
				throw new IllegalStateException("!Selecting");
		}
	}

主要是根据配置的selector的数目_selectSets,初始化_selectSets个selector,然后针对每个selector启动一个线程,用于监听事件。这些线程中都有一个循环,方便selector不停地doSelect。

然后进入doSelect函数,
		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 ChannelAndAttachment) {
							// finish accepting/connecting this connection
							final ChannelAndAttachment asc = (ChannelAndAttachment) change;
							final SelectableChannel channel = asc._channel;
							ch = channel;
							final Object att = asc._attachment;

							if ((channel instanceof SocketChannel)
									&& ((SocketChannel) channel).isConnected()) {
								key = channel.register(selector,
										SelectionKey.OP_READ, att);
								SelectChannelEndPoint endpoint = createEndPoint(
										(SocketChannel) channel, key);
								key.attach(endpoint);
								endpoint.schedule();
							} else if (channel.isOpen()) {
								key = channel.register(selector,
										SelectionKey.OP_CONNECT, att);
							}
						} else if (change instanceof SocketChannel) {
							// Newly registered channel
							final SocketChannel channel = (SocketChannel) change;
							ch = channel;
							key = channel.register(selector,
									SelectionKey.OP_READ, null);
							SelectChannelEndPoint endpoint = createEndPoint(
									channel, key);
							key.attach(endpoint);
							endpoint.schedule();
						} 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) {
						LOG.ignore(e);
					} catch (Throwable e) {
						if (isRunning())
							LOG.warn(e);
						else
							LOG.debug(e);

						try {
							if (ch != null)
								ch.close();
						} catch (IOException e2) {
							LOG.debug(e2);
						}
					}
				}

				// 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();
					}

					// 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;

				// Look for things to do
				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();
						if (att instanceof SelectChannelEndPoint) {
							if (key.isReadable() || key.isWritable())
								((SelectChannelEndPoint) att).schedule();
						} 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) {
								connectionFailed(channel, e, att);
							} finally {
								if (connected) {
									key.interestOps(SelectionKey.OP_READ);
									SelectChannelEndPoint endpoint = createEndPoint(
											channel, key);
									key.attach(endpoint);
									endpoint.schedule();
								} else {
									key.cancel();
								}
							}
						} 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) {
						LOG.ignore(e);
					} catch (Exception e) {
						if (isRunning())
							LOG.warn(e);
						else
							LOG.ignore(e);

						try {
							if (channel != null)
								channel.close();
						} catch (IOException e2) {
							LOG.debug(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) {
				if (isRunning())
					LOG.warn(e);
				else
					LOG.ignore(e);
			} catch (CancelledKeyException e) {
				LOG.ignore(e);
			} finally {
				_selecting = null;
			}
		}

doSelect()函数十分的长,这里暂时不做解释,只需要知道它是从channel中获取selectkey,然后调用SelectChannelEndPoint的schedule方法进行处理。关于连接会在之后的文章中详细介绍。

可以看到多处如下代码:

endpoint.schedule()
进入这个函数,
    /* ------------------------------------------------------------ */
    /** Called by selectSet to schedule handling
     *
     */
    public void schedule()
    {
        synchronized (this)
        {
            // If there is no key, then do nothing
            if (_key == null || !_key.isValid())
            {
                _readBlocked=false;
                _writeBlocked=false;
                this.notifyAll();
                return;
            }

            // If there are threads dispatched reading and writing
            if (_readBlocked || _writeBlocked)
            {
                // assert _dispatched;
                if (_readBlocked && _key.isReadable())
                    _readBlocked=false;
                if (_writeBlocked && _key.isWritable())
                    _writeBlocked=false;

                // wake them up is as good as a dispatched.
                this.notifyAll();

                // we are not interested in further selecting
                _key.interestOps(0);
                if (_state<STATE_DISPATCHED)
                    updateKey();
                return;
            }

            // Remove writeable op
            if ((_key.readyOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE && (_key.interestOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE)
            {
                // Remove writeable op
                _interestOps = _key.interestOps() & ~SelectionKey.OP_WRITE;
                _key.interestOps(_interestOps);
                _writable = true; // Once writable is in ops, only removed with dispatch.
            }

            // If dispatched, then deregister interest
            if (_state>=STATE_DISPATCHED)
                _key.interestOps(0);
            else
            {
                // other wise do the dispatch
            	// 在这里会进入到调用handler的地方
                dispatch();
                if (_state>=STATE_DISPATCHED && !_selectSet.getManager().isDeferringInterestedOps0())
                {
                    _key.interestOps(0);
                }
            }
        }
    }

点击进入dispatch()函数,
    /* ------------------------------------------------------------ */
    public void dispatch()
    {
        synchronized(this)
        {
            if (_state<=STATE_UNDISPATCHED)
            {
                if (_onIdle)
                    _state = STATE_NEEDS_DISPATCH;
                else
                {
                    _state = STATE_DISPATCHED;
                    boolean dispatched = _manager.dispatch(_handler);
                    if(!dispatched)
                    {
                        _state = STATE_NEEDS_DISPATCH;
                        LOG.warn("Dispatched Failed! "+this+" to "+_manager);
                        updateKey();
                    }
                }
            }
        }
    }

_manager.dispatch(_handler)就是调用_handler的真正地方。而_handler就是在之前多次提到的存放到server中的handler。

在之后涉及到的就是jetty种的线程池的工作原理,这里暂时不做介绍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值