源码版本:H版

一、前奏

nova api本身作为一个WSGI服务器,对外提供HTTP请求服务,对内调用nova的其他模块响应相应的HTTP请求。分为两大部分,一是服务器本身的启动与运行,一是加载的app,这个用来处理请求。

  目录结构如下:

首先,nova api是作为一个WSGI服务,肯定要查看它的启动过程,查看启动脚本/etc/init.d/openstack-nova-api(使用service命令实际在调用/etc/init.d文件夹下的脚本文件)。在/etc/init.d/openstack-nova-api文件中,start选项代表调用脚本/usr/bin/nova-api,如下:

/usr/bin/nova-api

import sys
from nova.cmd.api import main
if __name__ == "__main__":
  sys.exit(main())

接着,分析如下:

/nova/cmd/api.py (文件位置为/usr/lib/python2.6/site-packages,此处及下面均省略)

def main():
config.parse_args(sys.argv)
logging.setup("nova")
utils.monkey_patch() launcher = service.process_launcher()
for api in CONF.enabled_apis:
should_use_ssl = api in CONF.enabled_ssl_apis
if api == 'ec2':
"""为了与ec2兼容"""
server = service.WSGIService(api, use_ssl=should_use_ssl,
max_url_len=16384)
else:
server = service.WSGIService(api, use_ssl=should_use_ssl) 【1】
launcher.launch_service(server, workers=server.workers or 1)【2】
"""当前进程处于等待状态"""
  launcher.wait()

二、分析【1】处:主要是WSGIService对象的创建

/nova/service.py

WSGIService类:
def __init__(self, name, loader=None, use_ssl=False, max_url_len=None):
self.name = name
self.manager = self._get_manager()
self.loader = loader or wsgi.Loader()
self.app = self.loader.load_app(name)
self.host = getattr(CONF, '%s_listen' % name, "0.0.0.0")
self.port = getattr(CONF, '%s_listen_port' % name, 0)
self.workers = getattr(CONF, '%s_workers' % name, None)
self.use_ssl = use_ssl
"""封装了wsgi.py中的server"""
self.server = wsgi.Server(name,
self.app,
host=self.host,
port=self.port,
use_ssl=self.use_ssl,
max_url_len=max_url_len)
# Pull back actual port used
self.port = self.server.port
self.backdoor_port = None

/nova/wsgi.py

Loader类:
def __init__(self, config_path=None):
  """试图寻找api-paste.ini配置文件"""
  self.config_path = None   config_path = config_path or CONF.api_paste_config
  if not os.path.isabs(config_path):
    self.config_path = CONF.find_file(config_path)
  elif os.path.exists(config_path):
    self.config_path = config_path   if not self.config_path:
    raise exception.ConfigNotFound(path=config_path) def load_app(self, name):
  try:
    LOG.debug("Loading app %(name)s from %(path)s",
{'name': name, 'path': self.config_path})
    return deploy.loadapp("config:%s" % self.config_path, name=name)
  except LookupError as err:
    LOG.error(err)
    raise exception.PasteAppNotFound(name=name, path=self.config_path)

(此处关于app的具体加载过程见后文。)

三、分析【2】处:主要是WSGIService的启动

/nova/openstack/common/service.py

ProcessLauncher类:
def launch_service(self, service, workers=1):
  wrap = ServiceWrapper(service, workers)   LOG.info(_('Starting %d workers'), wrap.workers)
  """循环启动workers数目的子进程"""
  while self.running and len(wrap.children) < wrap.workers:
    self._start_child(wrap) def _start_child(self, wrap):
  ...
  """创建子进程"""
  pid = os.fork()
  if pid == 0:     status = 0
    try:
      """子进程进行处理"""
      self._child_process(wrap.service)
    except SignalExit as exc:
      signame = {signal.SIGTERM: 'SIGTERM',
signal.SIGINT: 'SIGINT'}[exc.signo]
      LOG.info(_('Caught %s, exiting'), signame)
      status = exc.code
    except SystemExit as exc:
      status = exc.code
    except BaseException:
      LOG.exception(_('Unhandled exception'))
      status = 2
    finally:
      """出现异常停掉服务"""
      wrap.service.stop()
    """子进程退出"""
    os._exit(status)   LOG.info(_('Started child %d'), pid)
  wrap.children.add(pid)
  self.children[pid] = wrap
  return pid def _child_process(self, service):
  ...
  launcher = Launcher()
  launcher.run_service(service)

/nova/openstack/common/service.py

Launcher类:
def __init__(self):
self._services = threadgroup.ThreadGroup()
self.backdoor_port = eventlet_backdoor.initialize_if_enabled() @staticmethod
def run_service(service):
service.start()
service.wait()

由于这里的service是WSGIService对象,如下:

/nova/service.py

WSGIService类:
def start(self):
if self.manager:
self.manager.init_host()
self.manager.pre_start_hook()
if self.backdoor_port is not None:
self.manager.backdoor_port = self.backdoor_port
self.server.start()
if self.manager:
self.manager.post_start_hook() def wait(self):
self.server.wait()

/nova/wsgi.py

Server类:
def start(self):
...
wsgi_kwargs = {
'func': eventlet.wsgi.server,
'sock': self._socket,
'site': self.app,
'protocol': self._protocol,
'custom_pool': self._pool,
'log': self._wsgi_logger,
'log_format': CONF.wsgi_log_format
} if self._max_url_len:
wsgi_kwargs['url_length_limit'] = self._max_url_len
"""创建一个green thread,运行func函数,在其中传入后面的参数。此处为调用eventlet.wsgi.server函数,传入sock=self._socket等参数到server函数中。其中eventlet.wsgi.server函数负责启动一个wsgi服务器程序接受客户端发来的请求,
可以参考:http://eventlet.net/doc/modules/wsgi.html?highlight=wsgi.server#eventlet.wsgi.server"""
self._server = eventlet.spawn(**wsgi_kwargs) def wait(self):
try:
"""green thread阻塞等待"""
self._server.wait()
except greenlet.GreenletExit:
LOG.info(_("WSGI server has stopped."))

总结:

我们可以通过pstree命令查看系统中进程的子进程数,发现nova-api进程中有一个主进程,其子进程数目为一个ec2,一个metadata,有workers数目的osapi_compute(其中workers可以通过/etc/nova/nova.conf中osapi_compute_workers选项设置)。然后通过ps –Lf查看每个进程的线程数目,发现其均为单线程。由此可以看出,整个server的创建和启动过程就是,主进程产生若干的子进程,子进程使用green thread启动wsgi server并等待服务。

参考文档:

http://www.choudan.net/2013/12/09/OpenStack-WSGI-APP%E5%AD%A6%E4%B9%A0.html

http://developer.openstack.org/api-ref-compute-v2.html(nova的API文档)

nova-api源码分析(WSGI server的创建及启动)的更多相关文章

  1. spark 源码分析之四 -- TaskScheduler的创建和启动过程

    在 spark 源码分析之二 -- SparkContext 的初始化过程 中,第 14 步 和 16 步分别描述了 TaskScheduler的 初始化 和 启动过程. 话分两头,先说 TaskSc ...

  2. kube-scheduler源码分析(1)-初始化与启动分析

    kube-scheduler源码分析(1)-初始化与启动分析 kube-scheduler简介 kube-scheduler组件是kubernetes中的核心组件之一,主要负责pod资源对象的调度工作 ...

  3. dubbo源码分析3-service bean的创建与发布

    dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...

  4. vscode源码分析【七】主进程启动消息通信服务

    第一篇: vscode源码分析[一]从源码运行vscode 第二篇:vscode源码分析[二]程序的启动逻辑,第一个窗口是如何创建的 第三篇:vscode源码分析[三]程序的启动逻辑,性能问题的追踪 ...

  5. vscode源码分析【三】程序的启动逻辑,性能问题的追踪

    第一篇: vscode源码分析[一]从源码运行vscode 第二篇:vscode源码分析[二]程序的启动逻辑,第一个窗口是如何创建的 启动追踪 代码文件:src\main.js 如果指定了特定的启动参 ...

  6. Spring源码分析之Bean的创建过程详解

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...

  7. Apache Kafka源码分析 – Broker Server

    1. Kafka.scala 在Kafka的main入口中startup KafkaServerStartable, 而KafkaServerStartable这是对KafkaServer的封装 1: ...

  8. Kafka源码分析(三) - Server端 - 消息存储

    系列文章目录 https://zhuanlan.zhihu.com/p/367683572 目录 系列文章目录 一. 业务模型 1.1 概念梳理 1.2 文件分析 1.2.1 数据目录 1.2.2 . ...

  9. MyCat源码分析系列之——配置信息和启动流程

    更多MyCat源码分析,请戳MyCat源码分析系列 MyCat配置信息 除了一些默认的配置参数,大多数的MyCat配置信息是通过读取若干.xml/.properties文件获取的,主要包括: 1)se ...

随机推荐

  1. Centos 7 zabbix 实战应用

    实际需求:公司已经有了100台服务器,现在需要使用zabbix全部监控起来. 先出个方案(规划) 常规监控:cpu,内存,磁盘,网卡 问题:怎样快速添加100台机器         方法1:使用克隆的 ...

  2. 《JavaScript设计模式与开发实践》——第3章 闭包和高阶函数

    闭包 变量的作用域和生存周期密切相关 高阶函数 函数可以作为参数被传递 函数可以作为返回值输出

  3. Improving the Safety, Scalability, and Efficiency of Network Function State Transfers

    Improving the Safety, Scalability, and Efficiency of Network Function State Transfers 来源:ACM SIGCOMM ...

  4. JAVA第三次笔记

  5. influxdb 命令

    写入数据: curl -X POST -d '[{"name":"foo","columns":["val"],&quo ...

  6. sleep() 与 wait()的比较

    1.这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类. sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用了b的sleep方法,实际上还 ...

  7. Python2 获取docx/doc文件内容

    整体思路: 下载文件并修改后缀为zip文件,解压zip文件,所要获取的内容在固定的文件夹下:work/temp/word/document.xml 所用包,全部是python自带,不需要额外下载安装. ...

  8. php缩略图

    /*引入文件Easyphpthumbnail.class.php 引用地址:http://www.itdaodan.com/article-detail-id-252.html */   class ...

  9. javascript之彻底理解prototype

    prototype很简单, 就是提供一种引用的机制. let BaseObject = { toString(){ return this.str; }, }; let Object1 = {str: ...

  10. Java 面试前的基础准备 - 01

    使用这个在线网页编辑真的是不习惯,还是 windows live writer 好. 下面列一个清单用于最近的面试:( 清单是网上down的 ) static,final,transient 等关键字 ...