【OpenStack Liberty】cinder service模块启动流程(cinder-backup、cinder-scheduler、cinder-volume)

提示:本文基于openstack liberty版本源码进行梳理


前言

之前我们在源码准备工作中找到了程序入口。下面我们就四个模块的启动流程进行分析。启动流程大致相同。本文介绍cinder四个模块之中的三个:backup、scheduler、volume。这三个模块都是Service,cinder-api则是WSGIService,所以本文将这三个放在一起。

基本所有的openstack服务都依赖 evenlet 完成各种并发任务,它的进程可分为两类:

  1. WSGIService: 接收和处理 http 请求,依赖eventlet.wsgi的 wsgi server,处理 http 请求,比如cinder-api
  2. Service: 接收和处理 rpc 请求,如cinder-volume等

无论是 WSGIService还是 Service类型的进程,每当接收到一个请求(http 或 rpc),都会在线程池中分配一个协程处理该请求


一、cinder-backup&cinder-scheduler启动区别

没区别!!!
backup和scheduler的启动流程不能说是毫无相关,简直就是一模一样,上对比:

WX20230112-174030@2x.png

除了创建Service的binary参数不一样,其他不同就是改了个注释,换了个代码顺序。

惊不惊喜,意不意外😂

既然一样那本文以cinder-scheduler启动为例

二、cinder-scheduler启动流程

1.main方法

代码文件位置:cinder/cmd/scheduler.py

import eventlet
eventlet.monkey_patch()

import sys

from oslo_config import cfg
from oslo_log import log as logging
from oslo_reports import guru_meditation_report as gmr

from cinder import i18n
i18n.enable_lazy()

# Need to register global_opts
from cinder.common import config  # noqa
from cinder import objects
from cinder import service
from cinder import utils
from cinder import version


CONF = cfg.CONF


def main():
    objects.register_all()
    CONF(sys.argv[1:], project='cinder',
         version=version.version_string())
    logging.setup(CONF, "cinder")
    utils.monkey_patch()
    gmr.TextGuruMeditation.setup_autorun(version)
    server = service.Service.create(binary='cinder-scheduler')
    service.serve(server)
    service.wait()

这里主要关注的代码只有两行:

    # 创建service对象
    server = service.Service.create(binary='cinder-scheduler')
    # 加载service
    service.serve(server)

2.创建service对象

第一行创建了一个Service对象,调用Service.create方法去创建对象过程中主要做了以下几件事

  1. 根据传入的binary参数(这里是‘cinder-scheduler’),确定要加载的manager类名。类名为’cinder-'后面的内容,接上‘_manager’,如:‘scheduler_manager’
  2. 实例化Service对象
  3. 实例化过程中将manager对象导入(scheduler.manager)赋值给实例中的manager

以上代码可以在cinder.service.Service.create以及cinder.service.Service.__init__中看到:

		# cinder.service.Service.create(binary='cinder-scheduler')
        if not topic:
            topic = binary  # 此时topic也变成了cinder-scheduler
        if not manager:
            subtopic = topic.rpartition('cinder-')[2]  # ('', 'cinder-', 'scheduler'),此时subtopic为'scheduler'
            manager = CONF.get('%s_manager' % subtopic, None)  # manager = 'scheduler_manager'
        # ......省略无关代码
        service_obj = cls(host, binary, topic, manager,
                          report_interval=report_interval,
                          periodic_interval=periodic_interval,
                          periodic_fuzzy_delay=periodic_fuzzy_delay,
                          service_name=service_name)

        return service_obj


		# cinder.service.Service.__init__
		self.manager_class_name = manager
        manager_class = importutils.import_class(self.manager_class_name)
        manager_class = profiler.trace_cls("rpc")(manager_class)

        self.manager = manager_class(host=self.host,
                                     service_name=service_name,
                                     *args, **kwargs)

manger的初始化代码不再贴出,主要就是加载了sechduler_driver

3.加载service

	# 加载service
	service.serve(server)

这行代码加载并启动刚刚创建的Service对象,先来个总结:

  1. 使用Service Launcher.launch_service启动对象
  2. 实际是开一个线程执行Service.start方法

下面对主要代码进行跟踪:

	# cinder.service.serve
	_launcher = service.launch(CONF, server, workers=workers)

launch方法主要逻辑如下:

    # ......省略无关代码
    if workers is None or workers == 1:
    	# 由于workers传了None,走这里
        launcher = ServiceLauncher(conf, restart_method=restart_method)
    else:
        launcher = ProcessLauncher(conf, restart_method=restart_method)
    # 最终执行这个方法***
    launcher.launch_service(service, workers=workers)

    return launcher

launch_service逻辑:

    def launch_service(self, service, workers=1):
        # ......省略无关代码
        self.services.add(service)
    
    ...
    
    def add(self, service):
        """Add a service to a list and create a thread to run it.

        :param service: service to run
        """
        self.services.append(service)
        # 这里开启一个线程执行self.run_service
        self.tg.add_thread(self.run_service, service, self.done)

	...

	@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

        """
        try:
        	# 最终执行的是service的start方法
            service.start()
        except Exception:
            LOG.exception('Error starting thread.')
            raise SystemExit(1)
        else:
            done.wait()

根据代码跟踪知道,service的加载过程可以看作开启一个子线程执行service的start方法。后面看其他组件启动看到service.serve就可以直接去看service的start逻辑。

service的start方法:

    def start(self):
    
        ...
        # 调用manager的init_host,scheduler manager没有实现,空逻辑
        self.manager.init_host()
        
		...
		
        self.rpcserver = rpc.get_server(target, endpoints, serializer)
        self.rpcserver.start()
        
        self.manager.init_host_with_rpc()

4. 流程图

main Service Manager 创建service对象 构造方法中创建manager对象 构造方法根据配置文件scheduler_driver加载driver Manger创建完成 service对象创建完成 加载Service对象,启动线程执行Service.start()方法 创建启动rpc server并调用manager.init_host_with_rpc() main Service Manager

三、cinder-volume启动流程

cinder-volume启动流程与volume-scheduler大差不离,主要区别在于:先判断有没有配置backend,如果配置了多个,对于每个backend,都启动一个独立的cinder-volume进程;在没有配置backend的情况下启动流程与scheduler无异。

    if CONF.enabled_backends:
        for backend in CONF.enabled_backends:
            CONF.register_opt(host_opt, group=backend)
            backend_host = getattr(CONF, backend).backend_host
            host = "%s@%s" % (backend_host or CONF.host, backend)
            try:
                # 创建service
                server = service.Service.create(host=host,
                                                service_name=backend,
                                                binary='cinder-volume')
            except Exception:
                msg = _('Volume service %s failed to start.') % host
                LOG.exception(msg)
            else:
                session.dispose_engine()
                # 加载并启动service
                launcher.launch_service(server)
                service_started = True
    else:
    	# 没有配置backend,直接创建
        server = service.Service.create(binary='cinder-volume')
        launcher.launch_service(server)
        service_started = True

总结

以上就是cinde-scheduler的启动流程,放上主要代码及注释,有时候真的不知道怎么将这些代码思路组织成语言😭,憋字憋的难受,如有错误欢迎指教😘
后面有更好的想法再补充

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值