2014.10.09

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">今天要完成:</span>

1. nova api是如何创建wsgi服务的?为什么一个.../v2/{tanent_id}/server/{server_id}的处理方法和.../v2/​{tenant_id}​/servers/​{server_id}​/metadata的处理方法就不一样呢?

2. nova api到底监听了几个端口,有几个wsgi服务?

现象:

[root@compute-57-09 admin]# ps -ef|grep nova
root     10298 10105  1 Sep09 pts/6    07:28:09 /usr/bin/python /usr/bin/nova-api
root     10333 10116  0 Sep09 pts/17   00:23:36 /usr/bin/python /usr/bin/nova-consoleauth --config-file /etc/nova/nova.conf
root     10338 10113  0 Sep09 pts/14   00:23:00 /usr/bin/python /usr/bin/nova-cert --config-file /etc/nova/nova.conf
root     10339 10114  0 Sep09 pts/15   00:30:05 /usr/bin/python /usr/bin/nova-scheduler --config-file /etc/nova/nova.conf
root     10344 10117  0 Sep09 pts/18   00:00:04 /usr/bin/python /usr/bin/nova-objectstore --config-file /etc/nova/nova.conf
root     10346 10115  0 Sep09 pts/16   00:16:25 /usr/bin/python /usr/bin/nova-novncproxy --config-file /etc/nova/nova.conf --web /opt/stack/noVNC
root     10347 10112  1 Sep09 pts/13   07:20:57 /usr/bin/python /usr/bin/nova-conductor --config-file /etc/nova/nova.conf
root     10628 10347  1 Sep09 pts/13   12:40:59 /usr/bin/python /usr/bin/nova-conductor --config-file /etc/nova/nova.conf
root     10629 10347  1 Sep09 pts/13   12:40:49 /usr/bin/python /usr/bin/nova-conductor --config-file /etc/nova/nova.conf
root     10630 10347  1 Sep09 pts/13   12:40:41 /usr/bin/python /usr/bin/nova-conductor --config-file /etc/nova/nova.conf
root     10631 10347  1 Sep09 pts/13   12:41:08 /usr/bin/python /usr/bin/nova-conductor --config-file /etc/nova/nova.conf
root     10632 10298  0 Sep09 pts/6    00:00:02 /usr/bin/python /usr/bin/nova-api
root     10633 10298  0 Sep09 pts/6    00:00:02 /usr/bin/python /usr/bin/nova-api
root     10634 10298  0 Sep09 pts/6    00:00:02 /usr/bin/python /usr/bin/nova-api
root     10635 10298  0 Sep09 pts/6    00:00:02 /usr/bin/python /usr/bin/nova-api
root     10658 10298  0 Sep09 pts/6    00:40:07 /usr/bin/python /usr/bin/nova-api
root     10659 10298  0 Sep09 pts/6    00:41:20 /usr/bin/python /usr/bin/nova-api
root     10660 10298  0 Sep09 pts/6    00:52:23 /usr/bin/python /usr/bin/nova-api
root     10661 10298  0 Sep09 pts/6    00:45:19 /usr/bin/python /usr/bin/nova-api
root     10666 10298  0 Sep09 pts/6    00:00:06 /usr/bin/python /usr/bin/nova-api
root     10667 10298  0 Sep09 pts/6    00:00:09 /usr/bin/python /usr/bin/nova-api
root     10668 10298  0 Sep09 pts/6    00:00:10 /usr/bin/python /usr/bin/nova-api
root     10669 10298  0 Sep09 pts/6    00:00:08 /usr/bin/python /usr/bin/nova-api
root     24157 10346  0 Oct08 pts/16   00:00:00 [nova-novncproxy] <defunct>
root     24499 17151  0 09:40 pts/0    00:00:00 grep nova
说明,nova-api很多,但是有一个父进程,就是最上面的那个,其他都是它派生出来的;

[root@compute-57-09 admin]# cat /usr/bin/nova-api
#!/usr/bin/python
# PBR Generated from u'console_scripts'
import sys

from nova.cmd.api import main

if __name__ == "__main__":
    sys.exit(main())

nova.cmd.api的代码如下:

def main():
    config.parse_args(sys.argv)
    logging.setup("nova")
    utils.monkey_patch()
    objects.register_all()

    gmr.TextGuruMeditation.setup_autorun(version)

    launcher = service.process_launcher()
    for api in CONF.enabled_apis:
        should_use_ssl = api in CONF.enabled_ssl_apis
        if api == 'ec2':
            server = service.WSGIService(api, use_ssl=should_use_ssl,
                                         max_url_len=16384)
        else:
            server = service.WSGIService(api, use_ssl=should_use_ssl)
        launcher.launch_service(server, workers=server.workers or 1)
    launcher.wait()
1)是否nova-api一启动就是那多么个进程?

应该是的,我停止nova-api服务,又启动服务;发现直接多了好多

stack      781 11718 14 09:48 pts/7    00:00:03 /usr/bin/python /usr/bin/nova-api
stack      790   781  0 09:48 pts/7    00:00:00 /usr/bin/python /usr/bin/nova-api
stack      791   781  0 09:48 pts/7    00:00:00 /usr/bin/python /usr/bin/nova-api
stack      792   781  0 09:48 pts/7    00:00:00 /usr/bin/python /usr/bin/nova-api
stack      793   781  0 09:48 pts/7    00:00:00 /usr/bin/python /usr/bin/nova-api
stack      797   781  0 09:48 pts/7    00:00:00 /usr/bin/python /usr/bin/nova-api
stack      798   781  0 09:48 pts/7    00:00:00 /usr/bin/python /usr/bin/nova-api
stack      799   781  0 09:48 pts/7    00:00:00 /usr/bin/python /usr/bin/nova-api
stack      800   781  0 09:48 pts/7    00:00:00 /usr/bin/python /usr/bin/nova-api
stack      807   781  0 09:48 pts/7    00:00:00 /usr/bin/python /usr/bin/nova-api
stack      808   781  0 09:48 pts/7    00:00:00 /usr/bin/python /usr/bin/nova-api
stack      809   781  0 09:48 pts/7    00:00:00 /usr/bin/python /usr/bin/nova-api
stack      810   781  0 09:48 pts/7    00:00:00 /usr/bin/python /usr/bin/nova-api
a)会不会执行一条nova命令就会多出来一个nova-api 呢?

通过实验并没有多出来一个!说明,每个nova-api都各司其职,执行不同的代码。

b)如何知道每个nova-api监听的端口?

[root@compute-57-09 admin]# ps -ef|grep nova
root      8960 17151  0 10:05 pts/0    00:00:00 grep nova
root     10298 10105  1 Sep09 pts/6    07:28:26 /usr/bin/python /usr/bin/nova-api
root     10333 10116  0 Sep09 pts/17   00:23:36 /usr/bin/python /usr/bin/nova-consoleauth --config-file /etc/nova/nova.conf
root     10338 10113  0 Sep09 pts/14   00:23:01 /usr/bin/python /usr/bin/nova-cert --config-file /etc/nova/nova.conf
root     10339 10114  0 Sep09 pts/15   00:30:06 /usr/bin/python /usr/bin/nova-scheduler --config-file /etc/nova/nova.conf
root     10344 10117  0 Sep09 pts/18   00:00:04 /usr/bin/python /usr/bin/nova-objectstore --config-file /etc/nova/nova.conf
root     10346 10115  0 Sep09 pts/16   00:16:26 /usr/bin/python /usr/bin/nova-novncproxy --config-file /etc/nova/nova.conf --web /opt/stack/noVNC
root     10347 10112  1 Sep09 pts/13   07:21:13 /usr/bin/python /usr/bin/nova-conductor --config-file /etc/nova/nova.conf
root     10628 10347  1 Sep09 pts/13   12:41:26 /usr/bin/python /usr/bin/nova-conductor --config-file /etc/nova/nova.conf
root     10629 10347  1 Sep09 pts/13   12:41:16 /usr/bin/python /usr/bin/nova-conductor --config-file /etc/nova/nova.conf
root     10630 10347  1 Sep09 pts/13   12:41:08 /usr/bin/python /usr/bin/nova-conductor --config-file /etc/nova/nova.conf
root     10631 10347  1 Sep09 pts/13   12:41:35 /usr/bin/python /usr/bin/nova-conductor --config-file /etc/nova/nova.conf
root     10632 10298  0 Sep09 pts/6    00:00:02 /usr/bin/python /usr/bin/nova-api
root     10633 10298  0 Sep09 pts/6    00:00:02 /usr/bin/python /usr/bin/nova-api
root     10634 10298  0 Sep09 pts/6    00:00:02 /usr/bin/python /usr/bin/nova-api
root     10635 10298  0 Sep09 pts/6    00:00:02 /usr/bin/python /usr/bin/nova-api
root     10658 10298  0 Sep09 pts/6    00:40:07 /usr/bin/python /usr/bin/nova-api
root     10659 10298  0 Sep09 pts/6    00:41:20 /usr/bin/python /usr/bin/nova-api
root     10660 10298  0 Sep09 pts/6    00:52:23 /usr/bin/python /usr/bin/nova-api
root     10661 10298  0 Sep09 pts/6    00:45:20 /usr/bin/python /usr/bin/nova-api
root     10666 10298  0 Sep09 pts/6    00:00:06 /usr/bin/python /usr/bin/nova-api
root     10667 10298  0 Sep09 pts/6    00:00:09 /usr/bin/python /usr/bin/nova-api
root     10668 10298  0 Sep09 pts/6    00:00:10 /usr/bin/python /usr/bin/nova-api
root     10669 10298  0 Sep09 pts/6    00:00:08 /usr/bin/python /usr/bin/nova-api
root     24157 10346  0 Oct08 pts/16   00:00:00 [nova-novncproxy] <defunct>

[root@compute-57-09 admin]# netstat -anlp  | grep 10298
tcp        0      0 0.0.0.0:8773                0.0.0.0:*                   LISTEN      10298/python        
tcp        0      0 0.0.0.0:8774                0.0.0.0:*                   LISTEN      10298/python        
tcp        0      0 0.0.0.0:8775                0.0.0.0:*                   LISTEN      10298/python 
看看8773 8774 8775都是干什么的?

网上说的很清楚:Nova-api 8773 (for EC2 API) 8774 (for openstack API) 8775 (metadata port) cinder 8776

[root@compute-57-09 admin]# netstat -anp  | grep 10632
[root@compute-57-09 admin]# netstat -anp  | grep 10633
[root@compute-57-09 admin]# netstat -anp  | grep 10634
[root@compute-57-09 admin]# netstat -anp  | grep 10635
[root@compute-57-09 admin]# netstat -anp  | grep 10658
tcp        0      0 192.168.39.30:8774          172.16.207.68:60072         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:55861         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:56512         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:52688         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:60042         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:51513         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:51742         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:56662         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:52576         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:51940         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:52694         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:53572         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:56622         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:58098         192.168.39.30:5672          ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:56688         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:52813         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:55874         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:56692         ESTABLISHED 10658/python        
tcp        1      0 192.168.39.30:43471         192.168.39.190:3306         CLOSE_WAIT  10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:56652         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:52792         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:52554         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:52786         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          192.168.39.30:35358         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:53564         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:51726         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:56491         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:56479         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:55990         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:56473         ESTABLISHED 10658/python        
tcp        1      0 192.168.39.30:43470         192.168.39.190:3306         CLOSE_WAIT  10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:53630         ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:51495         ESTABLISHED 10658/python        
tcp        1      0 192.168.39.30:43473         192.168.39.190:3306         CLOSE_WAIT  10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:55680         ESTABLISHED 10658/python        
tcp        1      0 192.168.39.30:43476         192.168.39.190:3306         CLOSE_WAIT  10658/python        
tcp        1      0 192.168.39.30:43472         192.168.39.190:3306         CLOSE_WAIT  10658/python        
tcp        0      0 192.168.39.30:57190         192.168.39.30:5672          ESTABLISHED 10658/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:56527         ESTABLISHED 10658/python        
[root@compute-57-09 admin]# netstat -anp  | grep 10659
tcp        0      0 192.168.39.30:8774          172.16.207.68:60795         ESTABLISHED 10659/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:63898         ESTABLISHED 10659/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:51432         ESTABLISHED 10659/python        
tcp        0      0 192.168.39.30:57477         192.168.39.30:5672          ESTABLISHED 10659/python        
tcp        1      0 192.168.39.30:43450         192.168.39.190:3306         CLOSE_WAIT  10659/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:53701         ESTABLISHED 10659/python        
tcp        1      0 192.168.39.30:43451         192.168.39.190:3306         CLOSE_WAIT  10659/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:60117         ESTABLISHED 10659/python        
tcp        0      0 192.168.39.30:45147         192.168.39.30:5672          ESTABLISHED 10659/python        
tcp        1      0 192.168.39.30:43448         192.168.39.190:3306         CLOSE_WAIT  10659/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:51474         ESTABLISHED 10659/python        
tcp        1      0 192.168.39.30:43449         192.168.39.190:3306         CLOSE_WAIT  10659/python        
tcp        0      0 192.168.39.30:57226         192.168.39.30:5672          ESTABLISHED 10659/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:60879         ESTABLISHED 10659/python        
tcp        1      0 192.168.39.30:43455         192.168.39.190:3306         CLOSE_WAIT  10659/python        
[root@compute-57-09 admin]# netstat -anp  | grep 10660
tcp        0      0 192.168.39.30:8774          172.16.207.68:53982         ESTABLISHED 10660/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:51424         ESTABLISHED 10660/python        
tcp        0      0 192.168.39.30:50736         192.168.39.30:5672          ESTABLISHED 10660/python        
tcp        0      0 192.168.39.30:57846         192.168.39.30:5672          ESTABLISHED 10660/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:52178         ESTABLISHED 10660/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:62032         ESTABLISHED 10660/python        
tcp        0      0 192.168.39.30:57245         192.168.39.30:5672          ESTABLISHED 10660/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:52220         ESTABLISHED 10660/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:52414         ESTABLISHED 10660/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:60074         ESTABLISHED 10660/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:52234         ESTABLISHED 10660/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:52402         ESTABLISHED 10660/python        
[root@compute-57-09 admin]# netstat -anp  | grep 10661
tcp        0      0 192.168.39.30:8774          172.16.207.68:53641         ESTABLISHED 10661/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:60888         ESTABLISHED 10661/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:60957         ESTABLISHED 10661/python        
tcp        1      0 192.168.39.30:43465         192.168.39.190:3306         CLOSE_WAIT  10661/python        
tcp        1      0 192.168.39.30:43461         192.168.39.190:3306         CLOSE_WAIT  10661/python        
tcp        0      0 192.168.39.30:47976         192.168.39.30:5672          ESTABLISHED 10661/python        
tcp        0      0 192.168.39.30:57672         192.168.39.30:5672          ESTABLISHED 10661/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:51946         ESTABLISHED 10661/python        
tcp        1      0 192.168.39.30:43459         192.168.39.190:3306         CLOSE_WAIT  10661/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:56618         ESTABLISHED 10661/python        
tcp        1      0 192.168.39.30:43460         192.168.39.190:3306         CLOSE_WAIT  10661/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:63924         ESTABLISHED 10661/python        
tcp        0      0 192.168.39.30:57233         192.168.39.30:5672          ESTABLISHED 10661/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:60964         ESTABLISHED 10661/python        
tcp        1      0 192.168.39.30:43462         192.168.39.190:3306         CLOSE_WAIT  10661/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:55826         ESTABLISHED 10661/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:60803         ESTABLISHED 10661/python        
tcp        0      0 192.168.39.30:8774          172.16.207.68:60633         ESTABLISHED 10661/python        
[root@compute-57-09 admin]# netstat -anp  | grep 10667
tcp        0      0 192.168.39.30:58746         192.168.39.30:5672          ESTABLISHED 10667/python        
tcp        0      0 192.168.39.30:58744         192.168.39.30:5672          ESTABLISHED 10667/python        
[root@compute-57-09 admin]# netstat -anp  | grep 10666
tcp        0      0 192.168.39.30:58553         192.168.39.30:5672          ESTABLISHED 10666/python        
tcp        0      0 192.168.39.30:58551         192.168.39.30:5672          ESTABLISHED 10666/python        
[root@compute-57-09 admin]# netstat -anp  | grep 10668
tcp        0      0 192.168.39.30:55172         192.168.39.30:5672          ESTABLISHED 10668/python        
tcp        0      0 192.168.39.30:55171         192.168.39.30:5672          ESTABLISHED 10668/python        
[root@compute-57-09 admin]# netstat -anp  | grep 10669
tcp        0      0 192.168.39.30:55117         192.168.39.30:5672          ESTABLISHED 10669/python        
tcp        0      0 192.168.39.30:55119         192.168.39.30:5672          ESTABLISHED 10669/python

反复出现:5672,rabbit_port = 5672

                  3306,mysql

ec2的接口没人访问,还有metadata的接口没人访问,我在虚机中访问169.254.169.254,这样应该可以建立8775连接

[root@BC-EC-85-50 ~]# netstat -anp|grep 5188
tcp        0      0 192.168.85.50:44063         192.168.85.50:5672          ESTABLISHED 5188/python         
tcp        1      0 192.168.85.50:52381         192.168.85.50:3306          CLOSE_WAIT  5188/python         
tcp        0      0 192.168.85.50:44064         192.168.85.50:5672          ESTABLISHED 5188/python         
tcp        0      0 192.168.85.50:8774          192.168.85.50:40955         ESTABLISHED 5188/python         
tcp        0      0 192.168.85.50:8776          192.168.85.50:51881         ESTABLISHED 25175/python        
tcp        0      0 ::ffff:192.168.85.50:51881  ::ffff:192.168.85.50:8776   ESTABLISHED 3821/java 

通过在虚机中不断的

curl 169.254.169.254:80/openstack/latest/meta_data.json
在metadata server所在的机器上确实能够看到访问8775的连接:

tcp        0      0 192.168.85.50:34924         192.168.85.50:8775          TIME_WAIT   -                   
tcp        0      0 192.168.85.50:34461         192.168.85.50:8775          TIME_WAIT   -                   
tcp        0      0 192.168.85.50:34980         192.168.85.50:8775          TIME_WAIT   -                   
tcp        0      0 192.168.85.50:34664         192.168.85.50:8775          TIME_WAIT   -                   
tcp        0      0 192.168.85.50:34578         192.168.85.50:8775          TIME_WAIT   -    
可以大胆推测下,这个就是neutron-metadata-agent向nova meta_data server请求数据所建立的socket连接

C.nova-api不一定要监听端口,监听端口的nova-api只有一个,那就是所有nova-api的父进程,他负责监听端口,具体的通信交给它的孩子们。

D.一个python程序是否能监听多个端口?

应该可以,创建好几个socket,并监听就行

E.子nova-api每一个有什么作用呢?

貌似是nova-api代码中有好几个os_fork(),这样就创建了好几个新的进程,那么具体是干什么的呢?还得分析下代码,而且应该还能调试,good

代码中,首先启动ec2 server:

日志:

2014-10-09 11:01:51.482 INFO nova.openstack.common.service [-] Starting 4 workers
2014-10-09 11:01:51.486 INFO nova.openstack.common.service [-] Started child 5595
2014-10-09 11:01:51.492 INFO nova.openstack.common.service [-] Started child 5597
2014-10-09 11:01:51.498 INFO nova.openstack.common.service [-] Started child 5601
2014-10-09 11:01:51.503 INFO nova.openstack.common.service [-] Started child 5602

这个debug信息显示出,ec2有4个工作进程,大胆推断,一共有13个nova-api,因此,metadata,openstack,ec2,各有4个工作进程,我想应该是这样的

<span style="color: rgb(84, 84, 84); font-family: arial, sans-serif;font-size:12px; line-height: 18.2000007629395px;">再看日志,说明,每个进程都会启动一个wsgi的服务,去进行服务</span>
2014-10-09 11:01:51.604 INFO nova.ec2.wsgi.server [-] (5595) wsgi starting up on http://0.0.0.0:8773/
2014-10-09 11:01:51.614 INFO nova.ec2.wsgi.server [-] (5597) wsgi starting up on http://0.0.0.0:8773/
2014-10-09 11:01:51.625 INFO nova.ec2.wsgi.server [-] (5602) wsgi starting up on http://0.0.0.0:8773/
2014-10-09 11:01:51.623 INFO nova.ec2.wsgi.server [-] (5601) wsgi starting up on http://0.0.0.0:8773/

果然被言重:openstack的api也创建4个工作进程

2014-10-09 11:13:11.161 INFO nova.wsgi [-] osapi_compute listening on 0.0.0.0:8774
2014-10-09 11:13:11.161 INFO nova.openstack.common.service [-] Starting 4 workers
2014-10-09 11:13:11.164 INFO nova.openstack.common.service [-] Started child 6457
2014-10-09 11:13:11.168 INFO nova.openstack.common.service [-] Started child 6458
2014-10-09 11:13:11.170 INFO nova.osapi_compute.wsgi.server [-] (6457) wsgi starting up on http://0.0.0.0:8774/
2014-10-09 11:13:11.172 INFO nova.openstack.common.service [-] Started child 6459
2014-10-09 11:13:11.178 INFO nova.osapi_compute.wsgi.server [-] (6459) wsgi starting up on http://0.0.0.0:8774/
2014-10-09 11:13:11.179 INFO nova.osapi_compute.wsgi.server [-] (6458) wsgi starting up on http://0.0.0.0:8774/
2014-10-09 11:13:11.175 INFO nova.openstack.common.service [-] Started child 6460
2014-10-09 11:13:11.180 INFO nova.osapi_compute.wsgi.server [-] (6460) wsgi starting up on http://0.0.0.0:8774/

metadata api也创建4个工作进程

2014-10-09 11:13:11.220 INFO nova.wsgi [-] metadata listening on 0.0.0.0:8775
2014-10-09 11:13:11.221 INFO nova.openstack.common.service [-] Starting 4 workers
2014-10-09 11:13:11.224 INFO nova.openstack.common.service [-] Started child 6463
2014-10-09 11:13:11.227 INFO nova.openstack.common.service [-] Started child 6464
2014-10-09 11:13:11.231 INFO nova.openstack.common.service [-] Started child 6465
2014-10-09 11:13:11.238 INFO nova.metadata.wsgi.server [-] (6464) wsgi starting up on http://0.0.0.0:8775/
2014-10-09 11:13:11.239 INFO nova.metadata.wsgi.server [-] (6463) wsgi starting up on http://0.0.0.0:8775/
2014-10-09 11:13:11.235 INFO nova.openstack.common.service [-] Started child 6466

看代码:

def main():
    config.parse_args(sys.argv)
    logging.setup("nova")
    utils.monkey_patch()
    objects.register_all()

    gmr.TextGuruMeditation.setup_autorun(version)

    launcher = service.process_launcher()
    for api in CONF.enabled_apis:
        should_use_ssl = api in CONF.enabled_ssl_apis
        if api == 'ec2':
            server = service.WSGIService(api, use_ssl=should_use_ssl,
                                         max_url_len=16384)
        else:
            server = service.WSGIService(api, use_ssl=should_use_ssl)
        launcher.launch_service(server, workers=server.workers or 1)
    launcher.wait()
launcher是service.process_launcher()返回的,因此launcher.launch_service也是执行的/nova/openstack/common/service.py中的launch_serivice()

    def launch_service(self, service, workers=1):
        #ServiceWrapper不知道是干什么的。。。
<span style="white-space:pre">	</span>wrap = ServiceWrapper(service, workers)
<span style="white-space:pre">	</span>#wrap.workers = 4
        LOG.info(_LI('Starting %d workers'), wrap.workers)
        #循环4次,执行4次!start_child
<span style="white-space:pre">	</span>while self.running and len(wrap.children) < wrap.workers:
            self._start_child(wrap)
看下_start_child(wrap)的代码:

    def _start_child(self, wrap):
        if len(wrap.forktimes) > wrap.workers:
            # Limit ourselves to one process a second (over the period of
            # number of workers * 1 second). This will allow workers to
            # start up quickly but ensure we don't fork off children that
            # die instantly too quickly.
            if time.time() - wrap.forktimes[0] < wrap.workers:
                LOG.info(_LI('Forking too fast, sleeping'))
                time.sleep(1)

            wrap.forktimes.pop(0)

        wrap.forktimes.append(time.time())
<span style="white-space:pre">	</span>#创建子进程了
        pid = os.fork()
        #pid==0 子进程
<span style="line-height: 18.2000007629395px; font-family: arial, sans-serif;"><span style="white-space:pre">	</span>      if pid == 0:</span>
            launcher = self._child_process(wrap.service)
            while True:
                self._child_process_handle_signal()
                status, signo = self._child_wait_for_exit_or_signal(launcher)
                if not _is_sighup_and_daemon(signo):
                    break
                launcher.restart()

            os._exit(status)

        LOG.info(_LI('Started child %d'), pid)

        wrap.children.add(pid)
        self.children[pid] = wrap

        return pid
上面最重要的是
launcher = self._child_process(wrap.service)
   def _child_process(self, service):
        self._child_process_handle_signal()

        # Reopen the eventlet hub to make sure we don't share an epoll
        # fd with parent and/or siblings, which would be bad
        eventlet.hubs.use_hub()

        # Close write to ensure only parent has it open
        os.close(self.writepipe)
        # Create greenthread to watch for parent to close pipe
        eventlet.spawn_n(self._pipe_watcher)

        # Reseed random number generator
        random.seed()
<span style="white-space:pre">	</span>#下面的应该比较重要
        launcher = Launcher()
        launcher.launch_service(service)
        return launcher
跟进launcher.launch_service(service)

    def launch_service(self, service):
        """Load and start the given service.

        :param service: The service you would like to start.
        :returns: None

        """
        service.backdoor_port = self.backdoor_port
        self.services.add(service)

跟进self.services.add(service)

    def add(self, service):
        self.services.append(service)
        self.tg.add_thread(self.run_service, service, self.done)

跟进self.tg.add_thread(self.run_service, service, self.done)


   def add_thread(self, callback, *args, **kwargs):
        gt = self.pool.spawn(callback, *args, **kwargs)
        th = Thread(gt, self)
        self.threads.append(th)
        return th

这里的callback就是run_service,但是我对于Thread(),对于pool.spawn都不熟,但是,原理上应该是创建一个线程执行run_service,service作为run_server的参数也一同传进去了,metadata,openstack,ec2对应的service应该是不同的。

    @staticmethod
    def run_service(service, done):
        """Service start wrapper.

        :param service: service to run
        :param done: event to wait on until a shutdown is triggered
        :returns: None

        """
        service.start()
        systemd.notify_once()
        done.wait()
最终,果然进入了这个函数,调用的是service.start() nova/service.py

跟进service.start()

    def start(self):
        """Start serving this service using loaded configuration.

        Also, retrieve updated port number in case '0' was passed in, which
        indicates a random port should be used.

        :returns: None

        """
        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()
跟进 self.server.start()     nova/wsgi.py

    def start(self):
        """Start serving a WSGI application.

        :returns: None
        """
        # The server socket object will be closed after server exits,
        # but the underlying file descriptor will remain open, and will
        # give bad file descriptor error. So duplicating the socket object,
        # to keep file descriptor usable.

        dup_socket = self._socket.dup()
        if self._use_ssl:
            try:
                ca_file = CONF.ssl_ca_file
                cert_file = CONF.ssl_cert_file
                key_file = CONF.ssl_key_file

                if cert_file and not os.path.exists(cert_file):
                    raise RuntimeError(
                          _("Unable to find cert_file : %s") % cert_file)

                if ca_file and not os.path.exists(ca_file):
                    raise RuntimeError(
                          _("Unable to find ca_file : %s") % ca_file)

                if key_file and not os.path.exists(key_file):
                    raise RuntimeError(
                          _("Unable to find key_file : %s") % key_file)

                if self._use_ssl and (not cert_file or not key_file):
                    raise RuntimeError(
                          _("When running server in SSL mode, you must "
                            "specify both a cert_file and key_file "
                            "option value in your configuration file"))
                ssl_kwargs = {
                    'server_side': True,
                    'certfile': cert_file,
                    'keyfile': key_file,
                    'cert_reqs': ssl.CERT_NONE,
                }

                if CONF.ssl_ca_file:
                    ssl_kwargs['ca_certs'] = ca_file
                    ssl_kwargs['cert_reqs'] = ssl.CERT_REQUIRED

                dup_socket = eventlet.wrap_ssl(dup_socket,
                                               **ssl_kwargs)

                dup_socket.setsockopt(socket.SOL_SOCKET,
                                      socket.SO_REUSEADDR, 1)
                # sockets can hang around forever without keepalive
                dup_socket.setsockopt(socket.SOL_SOCKET,
                                      socket.SO_KEEPALIVE, 1)

                # This option isn't available in the OS X version of eventlet
                if hasattr(socket, 'TCP_KEEPIDLE'):
                    dup_socket.setsockopt(socket.IPPROTO_TCP,
                                    socket.TCP_KEEPIDLE,
                                    CONF.tcp_keepidle)

            except Exception:
                with excutils.save_and_reraise_exception():
                    LOG.error(_("Failed to start %(name)s on %(host)s"
                                ":%(port)s with SSL support") % self.__dict__)

        wsgi_kwargs = {
            'func': eventlet.wsgi.server,
            'sock': dup_socket,
            'site': self.app,
            'protocol': self._protocol,
            'custom_pool': self._pool,
            'log': self._wsgi_logger,
            'log_format': CONF.wsgi_log_format,
            'debug': False
            }

        if self._max_url_len:
            wsgi_kwargs['url_length_limit'] = self._max_url_len

        self._server = eventlet.spawn(**wsgi_kwargs)
最重要的是:self._server = eventlet.spawn(**wsgi_kwargs),但是wsgi_kwargs的参数非常重要;给出ec2的参数:

 wsgi_kwargs = {
            'func': eventlet.wsgi.server,   <code object server at 0x255c378, file "/usr/lib/python2.6/site-packages/eventlet/wsgi.py", line 689>
            'sock': dup_socket,             <eventlet.greenio.GreenSocket object at 0x3950e50>
            'site': self.app,               URLMap: {(None, '/services/Cloud'): <nova.api.ec2.FaultWrapper object at 0x39506d0>}
            'protocol': self._protocol,     eventlet.wsgi.HttpProtocol
            'custom_pool': self._pool,      <eventlet.greenpool.GreenPool object at 0x2d55990>
            'log': self._wsgi_logger,       <nova.openstack.common.log.WritableLogger object at 0x2d55e10>
            'log_format': CONF.wsgi_log_format,     '%(client_ip)s "%(request_line)s" status: %(status_code)s len: %(body_length)s time: %(wall_seconds).7f'
            'debug': False
            }
看到这里的时候,必须要对eventlet进行一个彻底的分析,否则下面的代码是无法理解的,我们得从最基本的学起:

eventlet (http://www.choudan.net/2013/08/18/OpenStack-eventlet%E5%88%86%E6%9E%90(%E4%B8%80).html)

1. eventlet 由greenlet和select,poll,epoll组成,先来看看greenlet
2. greenlet我理解就是只能同一时间只有一个被执行的线程,具体它是什么不重要,而它能干什么才重要,例子:
import greenlet

def test1(n):
    print "test1:",n
    gr2.switch(32)
    print "test1: over"

def test2(n):
    print "test2:",n
    gr1.switch(23)
    print "test2: over"


greenlet = greenlet.greenlet
current = greenlet.getcurrent()
gr1 = greenlet(test1,current)
gr2 = greenlet(test2,current)
gr1.switch(2)

当你创建一个greenlet,它得到一个初始化过的空堆栈;当你第一次切换到它,他会启动指定的函数,然后切换跳出greenlet。当最终栈底 函数结束时,greenlet的堆栈又编程空的了,而greenlet也就死掉了。greenlet也会因为一个未捕捉的异常死掉。(http://gashero.yeax.com/?p=112)

这句话是精华,说明什么?执行

gr1 = greenlet(test1,current)
gr1得到了一个空堆栈,当执行到
gr1.switch(2)

他所指定的函数将被启动,大胆的猜测,执行到这句话时,执行将跳转到

def test1(n):

输出test1:2

执行

<pre name="code" class="python" style="color: rgb(84, 84, 84); line-height: 18.2000007629395px;">    gr2.switch(32)

 会跳转到 

def test2(n):
输出:

test2:32

执行

    gr1.switch(23)
这句话是最重要的,它又调回test1刚才被打断的地方,也就是将执行
 print "test1: over"
执行完了到哪去呢?不会再跳到test2了,只会退出!直接返回
gr1.switch(2)
然后退出,这个过程也很重要要理解

同理:再看一个修改的例子:

import greenlet

def test1(n):
    print "test1:",n
    gr2.switch(32)
    print "test1: over"

def test2(n):
    print "test2:",n
    #gr1.switch(23)
    print "test2: over"

greenlet = greenlet.greenlet
current = greenlet.getcurrent()
gr1 = greenlet(test1,current)
gr2 = greenlet(test2,current)
gr1.switch(2)

输出是:

test1: 2
test2: 32
test2: over

3.所谓的父greenlet

父greenlet是当greenlet死掉时,继续原来的位置执行

每一个创建的greenlet,都有一个自己的父greenlet(显示/隐式都行)它的作用就是当子greenlet执行完成后,能回到原来的调用点继续执行
上面的例子中,似乎gr1,gr2的父greenlet是同一个?这个是这样的么??
4.greenlet的三个重要函数:

greenlet(run=None,parent=None)

创建一个greenlet对象,而不执行。run是执行回调,而parent是父greenlet,缺省是当前greenlet。

greenlet.getcurrent()

返回当前greenlet,也就是谁在调用这个函数。

greenlet.GreenletExit

这个特定的异常不会波及到父greenlet,它用于干掉一个greenlet。

5.几个greenlet之间的切换:
import greenlet

def test1(x,y):
    z=gr2.switch(x+y)
    print z

def test2(u):
    print u
    gr1.switch(42)

greenlet = greenlet.greenlet
#current = greenlet.getcurrent()
gr1=greenlet(test1)
gr2=greenlet(test2)
gr1.switch("hello"," world")
注意一点,gr1.switch(42)将切换回test1的
z=gr2.switch(x+y)
z将被赋值为42,其他的复杂的内容我也说不清楚,例子就是这样,输出:
hello world
42

green thread

那么在eventlet中对greenlet进行了简单的封装,就成了GreenThread,并且上面的程序还会引来一个问题,如果我们想要写一个协程,那到底该如何来控制函数的执行过程了,如果协程多了,控制岂不是很复杂了。带着这个问题来看eventlet的实现。( http://www.choudan.net/2013/08/18/OpenStack-eventlet%E5%88%86%E6%9E%90(%E4%B8%80).html
1.我想说,下面的我也没看过,到目前为止,我认为greenlet还是比较好理解的,反正就那样呗,不过控制需要自己写,有一个疑问,同一时间只有一个协程能够被执行,为什么还要用协程呢?我在一个函数中,安排好执行的顺序不就完了么?为啥这么麻烦?
2.再精简一点:A函数要利用B函数做一些操作,很简单,调用B不就行了么?
A call B,B执行完了,一定是返回A
但是,对于greenlet,A switch to B,此时,和A就没一毛钱关系了,B执行完了,也就完了,A下面的代码将无法执行。实在想不到greenlet的应用场景,还得看别人的例子:
我试图说清楚:
def process_commands(*args):
    while True:
        line=''
        while not line.endswith('n'):
            line+=read_next_char()
        if line=='quitn':
            print "are you sure?"
            if read_next_char()!="y":
                continue    #忽略指令
        process_commands(line)
这是个命令行分析处理的代码,read_next_char()可以获得用户的下一个输入,cli中应该很简单
但是对于GUI程序,怎么处理呢?可以这样,当用户每次输入一个字母时,会触发一个按键的事件,在按键事件处理函数中,能获得这个字母。但请记住,GUI是消息驱动的,也就是说,不是函数
解决方法1
def process_commands(*args):
主动调用
line+=read_next_char()
获得字母,而是按键被触发之后,进入相应的处理函数,将这个字母主动告诉process_commands,如果不用greenlet,我们先思考下,我们将怎样做?
def key_event()
得到字母
将字母写到缓存中,有个标志位i表示写到第几个了
如果写的太快了,也要阻塞
def read_next_char()
也有个标志位j,从第j个缓存中读取字母,j++(要判断是否能够读即j<i)
不能读,则阻塞
因此,需要两个线程,他俩有非常明确的同步关系,但是因为线程执行的时候没有办法约束,只能使用全局变量,或者是其他的锁的技术来保证他俩的工作顺序(实际上我的解决办法是个异步的办法)

解决方法2
不要异步处理,用同步,即只要有按键,触发一个消息,阻塞在 read_next_char()的代码立刻执行,将这个字母获得到,然后依然阻塞,等待用户输入

解决方法3:
看看greenlet的解决办法,greenlet有个最厉害的地方,(普通函数是调用者调用被调用者,被调用者处于一种被动的状态,只能等着被调用)而greenlet模糊了这种调用关系,试想:
<pre name="code" class="python" style="color: rgb(51, 51, 51); text-align: justify;">def process_commands(*args):
 需要字母的输入时,它就切换到key_event(), 它自己被挂起了,如果没有用户输入,key_event()将挂起,有了之后,立刻切换到process_commands(),并且通过gr1.switch(参数)参数可以讲字母传回去,得到字母后,期待下一个字母,将再次切换到key_event(),可以说,完美的解决了这个问题。 
贴出代码( http://www.choudan.net/2013/08/18/OpenStack-eventlet%E5%88%86%E6%9E%90(%E4%B8%80).html
def event_keydown(key):
    g_processor.switch(key)

def read_next_char():
    g_self=greenlet.getcurrent()
    next_char=g_self.parent.switch()    #跳到上一层(main)的greenlet,等待下一次按键
    return next_char

g_processor=greenlet(process_commands)
g_processor.switch(*args)
gui.mainloop()
这段代码简短而精妙啊!
g_self.parent.switch()切换到哪了?这是最大的难点,
g_self=greenlet.getcurrent()
这个是在已经被切换的基础上执行的,因此已经是第二级了,通过调用g_self.parent,switch()能回到最上层的
g_processor.switch(*args)
之后继续执行,如果还看不懂,自己试验一下

如果这么说,这个greenlet确实有点用处,我明白了些,不知道你呢?

接下来看看green thread到底是被封装成什么样子了
(明天再看吧)

我又有个问题,照spawn的说法来讲,就是创建一个green thread,去执行func函数
This launches a greenthread to call  func . Spawning off multiple greenthreads gets work done in parallel. The return value from  spawn  is a  greenthread.GreenThread  object, which can be used to retrieve the return value of  func . See  spawn  for more details. 

"Spawning off multiple greenthreads gets work done in parallel"这句话有点小疑惑,不就是创建一个greenthread去执行func么?如果我在程序中,两次调用spawn,会出现什么样的情况?
(今天先到这里)








2)CONF.enabled_apis有哪些?







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值