Openstack L版本的neutron server 启动流程分析(二)

上次分析到了函数_eventlet_wsgi_server,函数在neutron/server/wsgi_eventlet.py中

def _eventlet_wsgi_server():
    pool = eventlet.GreenPool()

    neutron_api = service.serve_wsgi(service.NeutronApiService)
    api_thread = pool.spawn(neutron_api.wait)

    try:
        neutron_rpc = service.serve_rpc()
    except NotImplementedError:
        LOG.info(_LI("RPC was already started in parent process by "
                     "plugin."))
    else:
        rpc_thread = pool.spawn(neutron_rpc.wait)

        plugin_workers = service.start_plugin_workers()
        for worker in plugin_workers:
            pool.spawn(worker.wait)

        # api and rpc should die together.  When one dies, kill the other.
        rpc_thread.link(lambda gt: api_thread.kill())
        api_thread.link(lambda gt: rpc_thread.kill())

    pool.waitall()

该函数中需要特别关注的是两个函数service.serve_wsgi和service.serve_rpc,这两个函数分别完成了wsgi和rpc的初始化。

先看下server.serve_wsgi函数,该函数是在neutron.server.py中实现的,代码如下

class NeutronApiService(WsgiService):
    """Class for neutron-api service."""

    @classmethod
    def create(cls, app_name='neutron'):

        # Setup logging early, supplying both the CLI options and the
        # configuration mapping from the config file
        # We only update the conf dict for the verbose and debug
        # flags. Everything else must be set up in the conf file...
        # Log the options used when starting if we're in debug mode...

        config.setup_logging()
        service = cls(app_name)
        return service


def serve_wsgi(cls):

    try:
        service = cls.create()
        service.start()
    except Exception:
        with excutils.save_and_reraise_exception():
            LOG.exception(_LE('Unrecoverable error: please check log '
                              'for details.'))

    return service

从调用关系上看serve_wsgi()中的cls就是NeutronApiService。在上面的代码中也列出了类NeutronApiService的实现情况。

在上面的代码中我们看到,NeutronApiService继承了类WsgiService,而且还增加了一个create方法。在create方法中,设置了logging,也就是日志的配置,然后就调用了
WsgiService的构造函数,讲app_name作为参数传入到了WsgiService的构造函数中,通过上下文代码我们看到,app_name的名字为"Neutron"。下面看下WsgiService类的实现。

class WsgiService(object):
    """Base class for WSGI based services.

    For each api you define, you must also define these flags:
    :<api>_listen: The address on which to listen
    :<api>_listen_port: The port on which to listen

    """

    def __init__(self, app_name):
        self.app_name = app_name
        self.wsgi_app = None

    def start(self):
        self.wsgi_app = _run_wsgi(self.app_name)

    def wait(self):
        self.wsgi_app.wait()


#注意加粗的函数_run_wsgi,继续跟踪这个函数

def _run_wsgi(app_name):
    app = config.load_paste_app(app_name)//这是从配置文件/usr/share/neutron/api-paste.ini加载的,后面会单独介绍加载
    if not app:
        LOG.error(_LE('No known API applications configured.'))
        return
    server = wsgi.Server("Neutron")
    server.start(app, cfg.CONF.bind_port, cfg.CONF.bind_host,
                 workers=_get_api_workers())
    LOG.info(_LI("Neutron service started, listening on %(host)s:%(port)s"),
             {'host': cfg.CONF.bind_host, 'port': cfg.CONF.bind_port})
    return server
#这个函数有完成几处功能,第一
#app = config.load_paste_app(app_name)
#加载配置文件/usr/share/neutron/api-paste.ini

#第二是启动wsgi server,看下代码

#server = wsgi.Server("Neutron")
#server.start(app, cfg.CONF.bind_port, cfg.CONF.bind_host,
#                 workers=_get_api_workers())
#下面是server.start函数





class Server(object):
    """Server class to manage multiple WSGI sockets and applications."""

    def __init__(self, name, num_threads=1000, disable_ssl=False):
        # Raise the default from 8192 to accommodate large tokens
        eventlet.wsgi.MAX_HEADER_LINE = CONF.max_header_line
        self.num_threads = num_threads
        self.disable_ssl = disable_ssl
        # Pool for a greenthread in which wsgi server will be running    
        ... .....
    def start(self, application, port, host='0.0.0.0', workers=0):
        """Run a WSGI server with the given application."""
        self._host = host
        self._port = port
        backlog = CONF.backlog

        self._socket = self._get_socket(self._host,
                                        self._port,
                                        backlog=backlog)

        self._launch(application, workers)
self._get_socket函数代码如下

 def _get_socket(self, host, port, backlog):
        bind_addr = (host, port)
        # TODO(dims): eventlet's green dns/socket module does not actually
        # support IPv6 in getaddrinfo(). We need to get around this in the
        # future or monitor upstream for a fix
        try:
            info = socket.getaddrinfo(bind_addr[0],
                                      bind_addr[1],
                                      socket.AF_UNSPEC,
                                      socket.SOCK_STREAM)[0]
            family = info[0]
            bind_addr = info[-1]
        except Exception:
            LOG.exception(_LE("Unable to listen on %(host)s:%(port)s"),
                          {'host': host, 'port': port})
            sys.exit(1)

        sock = None
        retry_until = time.time() + CONF.retry_until_window
        while not sock and time.time() < retry_until:
            try:
                sock = eventlet.listen(bind_addr,
                                       backlog=backlog,
                                       family=family)
            except socket.error as err:
                with excutils.save_and_reraise_exception() as ctxt:
                    if err.errno == errno.EADDRINUSE:
                        ctxt.reraise = False
                        eventlet.sleep(0.1)
        if not sock:
            raise RuntimeError(_("Could not bind to %(host)s:%(port)s "
                               "after trying for %(time)d seconds") %
                               {'host': host,
                                'port': port,
                                'time': CONF.retry_until_window})
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # sockets can hang around forever without keepalive
        sock.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'):
            sock.setsockopt(socket.IPPROTO_TCP,
                            socket.TCP_KEEPIDLE,
                            CONF.tcp_keepidle)

        return sock
该函数调用eventlet库,设置监听端口设置监听端口的属性等。
start中调用的另一个重要的函数是_lauch函数,self._launch函数在下面列出来
   def _launch(self, application, workers=0):
        #application是前面传来的app,也就是从文件/usr/share/neutron/api-paste.ini
        #加载的一些内容
       service = WorkerService(self, application, self.disable_ssl)
        if workers < 1:
            # The API service should run in the current process.
            self._server = service
            # Dump the initial option values
            cfg.CONF.log_opt_values(LOG, logging.DEBUG)
            service.start()
            systemd.notify_once()
        else:
            # dispose the whole pool before os.fork, otherwise there will
            # be shared DB connections in child processes which may cause
            # DB errors.
            api.dispose()
            # The API service runs in a number of child processes.
            # Minimize the cost of checking for child exit by extending the
            # wait interval past the default of 0.01s.
            self._server = common_service.ProcessLauncher(cfg.CONF,
                                                          wait_interval=1.0)
            self._server.launch_service(service, workers=workers)
主意加粗标红的部分。函数WorkerService也是在neutron.wsgi中实现的,看下这个类是如何实现的

class WorkerService(worker.NeutronWorker):
    """Wraps a worker to be handled by ProcessLauncher"""
    def __init__(self, service, application, disable_ssl=False):
        self._service = service
        self._application = application
        self._disable_ssl = disable_ssl
        self._server = None

    def start(self):
        super(WorkerService, self).start()
        # When api worker is stopped it kills the eventlet wsgi server which
        # internally closes the wsgi server socket object. This server socket
        # object becomes not usable which leads to "Bad file descriptor"
        # errors on service restart.
        # Duplicate a socket object to keep a file descriptor usable.
        dup_sock = self._service._socket.dup()
        if CONF.use_ssl and not self._disable_ssl:
            dup_sock = self._service.wrap_ssl(dup_sock)
        self._server = self._service.pool.spawn(self._service._run,
                                                self._application,
                                                dup_sock)
    def wait(self):
        if isinstance(self._server, eventlet.greenthread.GreenThread):
            self._server.wait()

    def stop(self):
        if isinstance(self._server, eventlet.greenthread.GreenThread):
            self._server.kill()
            self._server = None
一鼓作气,在看下类worker.NeutronWorker
class NeutronWorker(service.ServiceBase):
    """Partial implementation of the ServiceBase ABC

    Subclasses will still need to add the other abstractmethods defined in
    service.ServiceBase. See oslo_service for more details.

    If a plugin needs to handle synchornization with the Neutron database and
    do this only once instead of in every API worker, for instance, it would
    define a NeutronWorker class and the plugin would have get_workers return
    an array of NeutronWorker instnaces. For example:
        class MyPlugin(...):
            def get_workers(self):
                return [MyPluginWorker()]

        class MyPluginWorker(NeutronWorker):
            def start(self):
                super(MyPluginWorker, self).start()
                do_sync()
    """
    def start(self):
        registry.notify(resources.PROCESS, events.AFTER_CREATE, self.start)
先分析到这里



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

庐山老僧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值