源码版本: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. ThinkPHP - 6 - 学习笔记(2015.5.4)

    解决:OneThink 站点无法被友言uyan后台识别 打开友言uyan插件功能,但OneThink站点无法被友言uyan后台检测到.页面生成的uyan代码为: <!-- UY BEGIN -- ...

  2. LeetCode 48. Rotate Image (C++)

    题目: You are given an n x n 2D matrix representing an image. Rotate the image by 90 degrees (clockwis ...

  3. bond下改变网卡

    浪潮服务器打开控制台 用ip addr查看哪个网卡是绑定的,eth2和eth4是绑定状态 用mv命令,更改网卡名称 并将每个网卡里的信息更改 reboot,重启 ip addr查看,eth6和eth8 ...

  4. 详解Python闭包,装饰器及类装饰器

    在项目开发中,总会遇到在原代码的基础上添加额外的功能模块,原有的代码也许是很久以前所写,为了添加新功能的代码块,您一般还得重新熟悉源代码,稍微搞清楚一点它的逻辑,这无疑是一件特别头疼的事情.今天我们介 ...

  5. mysql中变量

    mysql中的变量: mysql中,有两种变量形式: 普通变量: 不带“@”符号: 定义形式: declare  变量名  类型名   [default  默认值]: //普通变量必须先这样定义 赋值 ...

  6. mysql 8 server windows 安装经验分享

    windows下安装一般分为文件/msi安装文件 本章我们说的是文件行的mysql server 安装 下载地址:https://dev.mysql.com/downloads/mysql/ 下载完后 ...

  7. 【bzoj5028】小Z的加油店 扩展裴蜀定理+差分+线段树

    题目描述 给出 $n$ 个瓶子和无限的水,每个瓶子有一定的容量.每次你可以将一个瓶子装满水,或将A瓶子内的水倒入B瓶子中直到A倒空或B倒满.$m$ 次操作,每次给 $[l,r]$ 内的瓶子容量增加 $ ...

  8. Java虚拟机的内存管理----垃圾收集器

    1.Serial收集器 优点,是简单而高效,单线程避免了线程交互的开销. 缺点,进行垃圾回收时需要Stop the world(暂停所有用户线程). 2.ParNew收集器 它是Serial收集器的多 ...

  9. 【明哥报错簿】可以访问jsp但是访问不到controller

    此工程wms-web-enterprise启动之后,jsp页面可以访问,但是进不了controller.后来发现wms-consumer无法打包编译,在仓库m2里面发现此consumer.jar包为完 ...

  10. Wifi密码破解实战

    原文链接地址:http://www.freebuf.com/articles/wireless/127261.html https://www.baidu.com/?tn=98012088_4_dg& ...