资讯专栏INFORMATION COLUMN

Jetty : Embedded Server 启动流程 - 2

phpmatt / 1013人阅读

摘要:前言本文接着启动流程往下讲,上回说到调用方法,开始接收和处理请求,默认使用的子类,所以我们重点来看看的具体实现类层次,我们上文提到过,这里有两个新面孔抽象实现类上文讲过方法最终会调用方法,使用模板方法模式在方法中实现了的基本流程子类方法,

前言

本文接着 Jetty : Embedded Server 启动流程 - 1往下讲,上回说到 Server.start 调用 Connector.start 方法,开始接收和处理请求,Server 默认使用 Connector 的子类 SelectChannelConnector,所以我们重点来看看 SelectChannelConnector 的具体实现

类层次
AbstractLifeCycle
    AggregateLifeCycle
        AbstractConnector
            AbstractNIOConnector
                SelectChannelConnector

AbstractLifeCycle,AggregateLifeCycle 我们上文提到过,这里有两个新面孔:

AbstractConnector:Connector 抽象实现类

AbstractNIOConnector

AbstractConnector

上文讲过 Server.start 方法最终会调用 Connector.start 方法,AbstractConnector 使用《模板方法》模式在 doStart 方法中实现了 start 的基本流程

@Override
protected void doStart() throws Exception {
    if (_server == null) {
        throw new IllegalStateException("No server");
    }
    
    // 子类 override open 方法,打开 server socket
    open();

    // 如果没有指定 ThreadPool,默认使用 Server 的 ThreadPool
    if (_threadPool == null) {
        _threadPool = _server.getThreadPool();
        addBean(_threadPool, false);
    }
    ...
    synchronized (this) {
        _acceptorThreads = new Thread[getAcceptors()];
        for (int i = 0; i < _acceptorThreads.length; i++) {
            // 启动 acceptor 线程监听客户端连接
            if (!_threadPool.dispatch(new Acceptor(i))) {
                throw new IllegalStateException("!accepting");
            }
            ...
        }
    }
    ...
}

几个关键点:

子类通过 override(覆盖)open 方法初始化 server socket

通过 setAcceptors 方法可以设置 acceptor 线程数量

如果没有特殊指定,acceptor 线程 和 请求处理线程在(Server)同一个线程池里头

Acceptor 类(实现了 Runnable 接口)实现了具体的 accept 方法

Acceptor

Acceptor 类是 AbstractConnector 类的内部类,它在 Run 方法里头调用 AbstractConnector.accept 方法接收客户端连接

private class Acceptor implements Runnable {
    public void run() {
        Thread current = Thread.currentThread();
        ...
        accept(_acceptor);
        ...
    }
}

accept 方法是个抽象方法,由 AbstractConnector 的子类提供具体实现

SelectChannelConnector

我们重点关注和启动流程相关的三个方法:open,doStart,accept 以及一个类 SelectorManager

open

上文提到 AbstractConnector 在 start 方法中调用 open 方法,子类 override open 方法打开 server socket

    public void open() throws IOException {
        synchronized(this) {
            if (_acceptChannel == null) {
                // Create a new server socket
                _acceptChannel = ServerSocketChannel.open();
                // Set to blocking mode,阻塞接收连接请求
                _acceptChannel.configureBlocking(true);

                // Bind the server socket to the local host and port
                _acceptChannel.socket().setReuseAddress(getReuseAddress());
                InetSocketAddress addr = getHost()==null ?
                    new InetSocketAddress(getPort()) :
                    new InetSocketAddress(getHost(), getPort());
                _acceptChannel.socket().bind(addr,getAcceptQueueSize());

                _localPort=_acceptChannel.socket().getLocalPort();
                if (_localPort<=0)
                    throw new IOException("Server channel not bound");

                addBean(_acceptChannel);
            }
        }
    }
accept
@Override
public void accept(int acceptorID) throws IOException {
    ServerSocketChannel server;
    synchronized(this) {
        server = _acceptChannel;
    }

    if (server!=null && server.isOpen() && _manager.isStarted()) {
        // 获取客户端 SocketChannel
        SocketChannel channel = server.accept();
        // 设置非阻塞模式(NIO)
        channel.configureBlocking(false);
        Socket socket = channel.socket();
        configure(socket);
        // 将 channel 注册到 SelectorManager(见下文)
        _manager.register(channel);
    }
}

通过 server.accept 获取到客户端 SocketChannel,并将它注册到 _manager(SelectorManager),这个 _manager 是啥?

SelectorManager

The Selector Manager manages and number of SelectSets to allow NIO Scheduling to scale to large numbers of connections
注意 SelectChannelConnector 在构造方法里将 _manager 作为 managed bean 添加到 bean registry 里,这样在 SelectChannelConnector 启动(start 方法被调用)的时候 SelectManager 也会跟着启动(参考上文)

public class SelectChannelConnector extends AbstractNIOConnector {
    private final SelectorManager _manager = new ConnectorSelectorManager();

    public SelectChannelConnector() {
        _manager.setMaxIdleTime(getMaxIdleTime());
        addBean(_manager, true);
        ...
    }
}

SelectManager doStart 方法,这里只保留方法主要逻辑

@Override
protected void doStart() throws Exception {
    _selectSet = new SelectSet[_selectSets];
    for (int i = 0; i < _select.length; i++) {
        _selectSet[i] = new SelectSet(i);
    }
    super.doStart();
    // start a thread to select
    for (int i = 0; i < getSelectSets(); i++) {
        final int id = i;
        // 提交 select runnable 到线程池
        boolean selecting = dispatch(new Runnalbe() {
            public void run() {
                ...
                LOG.debug("Starting {} on {}", Thread.currentThread(), this);
                while (isRunning()) {
                    try {
                        // 对注册的 channel 进行多路选择(select)
                        set.doSelect();
                    } catch (...) {
                        ...
                    }
                }
            }
        });
    }
}

doStart 方法启动 _selectSet 个线程监听 channel select 事件,我们回过头来看 SelectManager 的 register 方法

public void register(SocketChannel acceptChannel) {
    int s=_set++;
    if (s<0) {
        s=-s;
    }
    s=s%_selectSets;
    SelectSet set=_selectSet[s];
    set.addChange(acceptChannel);
    set.wakeup();
}

acceptChannel 被均匀分配(addChange)给 SelectSet

总结

到目前为止我们总结一下:

Server 启动 N 个 Accept 线程接收客户端连接

Server 启动 N 个 Select 线程对 SocketChannel 进行多路IO选择,每个线程执行 SelectSet 的 doSelect 方法

Server 接收到客户端连接后将 SocketChannel 注册到 SelectorManager

SelectorManager 将注册的 SocketChannel 均匀分配给各个 SelectSet,同时唤醒 Select 线程迎接新的客户端请求

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/71015.html

相关文章

  • Jetty : Embedded Server 启动流程 - 1

    前言 本文基于 Jetty 8.1.x 版本简单介绍 Jetty Embedded Server 核心概念,线程模型,启动流程。以下代码片段摘自 Jetty 源代码 中的 example-jetty-embedded 模块的 OneServletContext.java public class OneServletContext { public static void main(Str...

    everfly 评论0 收藏0
  • Spring MVC实现Spring Security,Spring Stomp websocket

    摘要:使用框架各个组件实现一个在线聊天网页,当有用户连接,服务器监听到用户连接会使用推送最新用户列表,有用户断开刷新在线列表,实时推送用户聊天信息。根据请求头是否等于判断是否是。 使用Spring框架各个组件实现一个在线聊天网页,当有用户连接WebSocket,服务器监听到用户连接会使用Stomp推送最新用户列表,有用户断开刷新在线列表,实时推送用户聊天信息。引入Jetty服务器,直接嵌入整...

    shuibo 评论0 收藏0
  • 《Spring Boot 编程思想 - 核心篇》勘误汇总

    摘要:如果您在阅读编程思想核心篇或示例练习的过程中发现了其中错误或提出建议,请将内容提交至勘误汇,小马哥将勘误或建议内容汇总到此,修正后的内容将在后续的书籍发行中体现,并刊登勘误贡献者。笔者水平有限,行文的过程中错误无法避免,为此深表歉意。 如果您在阅读《Spring Boot 编程思想 - 核心篇》或示例练习的过程中发现了其中错误或提出建议,请将内容提交至【勘误汇】,小马哥将勘误或建议内容...

    trilever 评论0 收藏0
  • Spring Boot 2 快速教程:WebFlux 快速入门(二)

    摘要:响应式编程是基于异步和事件驱动的非阻塞程序,只是垂直通过在内启动少量线程扩展,而不是水平通过集群扩展。三特性常用的生产的特性如下响应式编程模型适用性内嵌容器组件还有对日志消息测试及扩展等支持。 摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 02:WebFlux 快速入门实践 文章工程: JDK...

    gaara 评论0 收藏0
  • ActiveMQ+ZooKeeper 伪集群整合

    摘要:前言本案例使用的是伪集群方式,即在一台主机上部署个服务端口不同个服务端口不同。要想保证负载均衡得再结合部署方案,配置网络连接器。编码时,端消费者通过协议来连接集群。只需使用进行配置即可,默认端口为。 前言 本案例使用的是伪集群方式,即在一台主机上部署3个activemq服务(端口不同)+3个zookeeper服务(端口不同)。 真集群部署请看:ActiveMQ+ZooKeeper集群整...

    Honwhy 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<