Nova Service
Service
nova/service.py
整个服务创建的过程,由一个进程变出多个子进程,再到运行起来,一个 server 贯穿始终,成为主线。这里的 server 是一个 Service 对象,名字容易让人与 api 里的 server 混淆。
以 nova-scheduler 这个服务为例,它是从 nova/cmd/scheduler.py (以前是 bin/nova-scheduler) 启动整个 nova-scheduler 服务。它通过 nova/service.py 的类方法 create 创建了一个 topic 为 nova.scheduler.rpcapi 的 service,即后面会提到的 server。在创建时,还显式提供了一个 ‘binary’ 参数,它只是一个以 ‘nova-‘ 开头的字符串,用于给其它参数赋上默认的参数,比如 binary 为 ‘nova-scheduler’,如果没有提供 manager 参数,那么就会默认去 CONF 里找名为 xxx_manager 的类,把它 import 作为 manager。Service 的 create 的全部参数如下:
host # defaults to CONF.host
binary # defaults to basename of executable
topic # defaults to bin_name - 'nova-' part
manager # defaults to CONF.<topic>_manager
report_interval # defaults to CONF.report_interval
periodic_enable # defaults to CONF.periodic_enable
periodic_fuzzy_delay # defaults to CONF.periodic_fuzzy_delay
periodic_interval_max # if set, the max time to wait between runs
一个 Service 通过一个 manager 监听某个 topic 的 queue 来实现 rpc。Service 会通过 manager 运行定时任务(periodical tasks),并向数据库的 service 表报告当前状态。
一个 Service 创建好后,需要让它运行起来。于是执行 nova/service.py 最后面的 serve(xxx) 方法,它会新生成一个 ProcessLauncher 对象(这个类也是在 service.py 中定义的),用来为这个 service 管理进程。然后,调用这个 ProcessLauncher 的 launch_server(server, workers=<int>) 函数(注意是 ProcessLauncher 类里面的 launch_server 方法),启动 <int> 个 worker(子进程),父进程只起管理子进程的功能,子进程的信息全部存在父进程所在的 wrap 里,子进程是真正干活的。子进程会执行 _child_process(server) 方法。
在 _child_process(server) 方法中,把子进程传进去。在对进程信号和 eventlet 进行开启。然后创建一个 Launcher 对象,把子进程的 server 扔进去,通过 Launcher 的 run_server 静态方法,把 server.start() 并让它 server.wait()。直到运行结束或出错抛出异常。
到这里,一个 service 就运行起来了。
在 nova/service.py 的其它类中,充满了各种利用进程信号量的处理。
signal.signal(xxx,XXX) 方法可以将系统对 xxx 事件的处理方法改为 XXX。
# 几个常用信号:
SIGINT 终止进程 中断进程 (control+c)
SIGTERM 终止进程 软件终止信号 (比较友好,如果进程不能中断,可以忽略这个信号)
SIGKILL 终止进程 杀死进程 (这个信号不能被忽略,让进程立刻停止)
SIGALRM 闹钟信号
SIGCHLD 一个子进程结束后自动向父进程发送SIGCHLD信号。
# 两种标准信号处理方法:
signal.SIG_DFL 按系统默认的对该信号处理的方法进行处理。
signal.SIG_IGN 忽略该信号。
关于 signal 的详细用法,参考:
http://docs.python.org/2/library/signal.html
http://blog.csdn.net/liangguohuan/article/details/7099978
关于 service 的 worker 的创建(_start_child 方法),首先确保子进程不会被太快地 fork 出来,保证一秒一个。在 pid = os.fork() 这句执行时,子进程就被创建出来了,子进程在此时的 pid 变量值会被赋为 0,父进程的 pid 变量的值为子进程的系统真实 pid。
接下来,父进程和子进程都开始执行下一句代码,但是两者是异步的。所以接下来的一句 if pid == 0: 就会把父子进程区别对待,if 里的代码只会对子进程起作用,来防止子进程循环创建子进程。
最后,其实可以细看一下 nova/service.py 里 Service 类的 start() 方法,它将通过 manager 创建 dispathcer,用它创建出 consumer。并创建出定时任务。
WSGI Service
nova-api service 跟上面的 Service 不同,它是通过 nova.service.WSGIService 类起来的。
它依次把 nova.conf 里的 enabled_apis = ec2, osapi_compute, metadata 模块载入。
载入的时候,在 WSGIService 里,会尝试载入名为 xxx_manager 的 class。
WSGIService nova-api 如何起来
nova.cmd.api -> main() 启动 api='osapi_compute' 的 WSGIService
由于不存在 osapi_compute_manager 这样的类,所以 self.manager 为 None。
所以就在 __init__ 里初始化了
self.server = wsgi.Server(...)
然后直接在 start 时执行 self.server.start()
在 wsgi.Server 这个对象 __init__ 的时候,
self._socket = eventlet.listen(bind_addr, family, backlog=backlog)
向系统申请了指定 port 的 socket 资源。其中 backlog 是 The maximum number of queued connections.
wsgi.Server.start() 时,经过对 socket 的一些列准备(复制一份、设置 SO_KEEPALIVE、wrap ssl …),
最后用 eventlet.spawn(func, *args, **kwargs) 把服务起起来。
而这里的:
func就是eventlet.wsgi.server,这个方法就是 eventlet 根据 sock、site、custom_pool、log_format 等参数来起 http server。
参数里:
app是上面 Service 传进来的self.loader.load_app(name),Return the paste URLMap wrapped WSGI application.protocol默认是eventlet.wsgi.HttpProtocolbacklog默认是128custom_pool是eventlet.GreenPool(self.pool_size)
comments powered by Disqus