服务启动流程分析
本文通过 nova-api 服务启动的代码执行流程,具体了解一下 nova-api 服务是如何使用 Eventlet 框架的。
首先是 nova-api 的启动脚本
|
|
ProcessLauncher类和 WSGIService类的初始化方法如下。前者用来启动并管理 wsgi服务,后者用来从 api-paste.ini 的配置中启动 nova-api 服务。
|
|
launcher.launch_server(server, workers=server.workers or 1)
真正开始启动 nova-api 服务线程。self._start_child()
方法启动一个子进程,子进程调用 self._child_process()
方法,实例化一个 Launcher 对象来启动真正的 nova-api server服务。
|
|
Launcher 的 run_server()
方法中的 server.start()
中的 server 为 nova.wsgi.Server。
|
|
接下来我们看下 eventlet.spawn()
方法。
|
|
首先获取全局单例对象 hub,以 hub.greenlet 为父亲线程创建一个绿色线程 g。将绿色线程 g 放入 hub 的待调度执行队列中。方法最后返回绿色线程对象 g。
再看下绿色线程 g(eventlet.wsgi.server()方法) 的内容:使用传入参数初始化一个 Server 对象,以及一个 GreenPool 对象。传入的sock阻塞accept循环等待客户端连接,每次有客户端连接,绿色线程池都分配一个绿色线程处理这个连接的请求。该绿色线程传入的执行方法为 pool.spawn_n(serv.process_request, client_socket)
。
|
|
每当一个绿色线程的任务执行完成,或者因为IO操作被挂起,绿色线程的执行权就被 switch 到绿色线程池 pool 中所有绿色线程的父亲线程 g.greenlet
来执行。g.greenlet
即是 hub 对象初始化时指定的 greenlet.greenlet(self.run)
,即是Hub对象中的 self.run()
方法。该方法作为调度中心,指定下一个该由哪个绿色线程开始执行。
|
|
小结
nova-api 启动服务时,会根据 nova.conf 中配置的 workers 数量启动指定数量的子进程。每个子进程初始化一个以 hub 对象的self.greenlet为父亲线程的绿色线程池,以及一个监听 nova-api服务器和指定端口号的 socket 服务。每当有客户端请求该socket服务,绿色线程池都会生成一个绿色线程处理该请求。当有多个请求到达时,同一时间只有一个绿色线程处于工作状态,其他绿色线程等待。当工作的绿色线程完成工作或者被IO任务阻塞时,线程的执行被切换到绿色线程池中所有绿色线程的父亲线程,即 hub 对象的self.greenlet执行 (即hub对象的self.run()方法)。父亲线程会根据其他绿色线程的就绪状态,选择出接下来可以执行的绿色线程,并切换到该绿色线程执行。