博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Odoo启动过程
阅读量:5273 次
发布时间:2019-06-14

本文共 20210 字,大约阅读时间需要 67 分钟。

openerp-server是启动Odoo服务器的第一步,其代码如下。

#!/usr/bin/env pythonimport openerpif __name__ == "__main__":    openerp.cli.main()# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

调用了openerp.cli.main()

openerp.cli.main

...import serverimport deployimport scaffoldimport startdef main():    args = sys.argv[1:]   # sys.argv:启动文件和配置文件的路径['D:/rhwl/odoo/odoo.py', '-c', 'D:\\rhwl\\local.conf']    # The only shared option is '--addons-path=' needed to discover additional    # commands from modules    if len(args) > 1 and args[0].startswith('--addons-path=') and not args[1].startswith("-"):        # parse only the addons-path, do not setup the logger...        tools.config._parse_config([args[0]])#设置路径        args = args[1:]    # Default legacy command    command = "server"    # Subcommand discovery    if len(args) and not args[0].startswith("-"):        logging.disable(logging.CRITICAL)        for m in module.get_modules():            m = 'openerp.addons.' + m            __import__(m)#导入模块            #try:            #except Exception, e:            #    raise            #    print e        logging.disable(logging.NOTSET)        command = args[0]        args = args[1:]    if command in commands:        o = commands[command]()        o.run(args)#运行server

openerp.cli.main() 通过tools.config._parse_config([args[0]])设计模块路径,通过最后一行o.run(args)运行server

openerp.cli.server

def main(args):    check_root_user()    openerp.tools.config.parse_config(args)    check_postgres_user()    report_configuration()    config = openerp.tools.config    if config["test_file"]:        config["test_enable"] = True    if config["translate_out"]:        export_translation()        sys.exit(0)    if config["translate_in"]:        import_translation()        sys.exit(0)    # This needs to be done now to ensure the use of the multiprocessing    # signaling mecanism for registries loaded with -d    if config['workers']:        openerp.multi_process = True    preload = []    if config['db_name']:        preload = config['db_name'].split(',')    stop = config["stop_after_init"]    setup_pid_file()    rc = openerp.service.server.start(preload=preload, stop=stop)    sys.exit(rc)class Server(Command):    """Start the odoo server (default command)"""    def run(self, args):        main(args)

通过openerp.service.server.start(preload=preload, stop=stop)启动Odoo服务器

openerp.service.server.start

def start(preload=None, stop=False):    """ Start the openerp http server and cron processor.    """    global server    load_server_wide_modules()    if openerp.evented:        server = GeventServer(openerp.service.wsgi_server.application)    elif config['workers']:        server = PreforkServer(openerp.service.wsgi_server.application)    else:        server = ThreadedServer(openerp.service.wsgi_server.application)    if config['auto_reload']:        autoreload = AutoReload(server)        autoreload.run()    rc = server.run(preload, stop)#在这里运行服务器    # like the legend of the phoenix, all ends with beginnings    if getattr(openerp, 'phoenix', False):        modules = []        if config['auto_reload']:            modules = autoreload.modules.keys()        _reexec(modules)    return rc if rc else 0

openerp.service.server.start启动opernerp服务器和cron进程

从代码中可以看到,odoo支持三种服务类型:

    • GeventServer
    • PreforkServer
    • ThreadedServer 
      其中,ThreadedServer为默认的服务器类型

Odoo服务器通过ThreadedServer.run()开始运行

ThreadedServer.run

def run(self, preload=None, stop=False):        """ Start the http server and the cron thread then wait for a signal.        The first SIGINT or SIGTERM signal will initiate a graceful shutdown while        a second one if any will force an immediate exit.        """        self.start(stop=stop)        rc = preload_registries(preload)        if stop:            self.stop()            return rc        # Wait for a first signal to be handled. (time.sleep will be interrupted        # by the signal handler.) The try/except is for the win32 case.        try:            while self.quit_signals_received == 0:                time.sleep(60)        except KeyboardInterrupt:            pass        self.stop()

ThreadedServer.start()

start()方法如下。

def start(self, stop=False):        _logger.debug("Setting signal handlers")        if os.name == 'posix':            signal.signal(signal.SIGINT, self.signal_handler)            signal.signal(signal.SIGTERM, self.signal_handler)            signal.signal(signal.SIGCHLD, self.signal_handler)            signal.signal(signal.SIGHUP, self.signal_handler)            signal.signal(signal.SIGQUIT, dumpstacks)        elif os.name == 'nt':            import win32api            win32api.SetConsoleCtrlHandler(lambda sig: self.signal_handler(sig, None), 1)        test_mode = config['test_enable'] or config['test_file']        if test_mode or (config['xmlrpc'] and not stop):            # some tests need the http deamon to be available...            self.http_spawn()        if not stop:            # only relevant if we are not in "--stop-after-init" mode            self.cron_spawn()

在默认参数下,将执行self.http_spawn()

ThreadedServer.http_spawn

def http_thread(self):        def app(e, s):            return self.app(e, s)        self.httpd = ThreadedWSGIServerReloadable(self.interface, self.port, app)        self.httpd.serve_forever()    def http_spawn(self):        t = threading.Thread(target=self.http_thread, name="openerp.service.httpd")        t.setDaemon(True)        t.start()        _logger.info('HTTP service (werkzeug) running on %s:%s', self.interface, self.port)

 

http_spawn启动命令为openerp.service.httpd的线程,并在控制台输入'HTTP service (werkzeug) running...的提示信息。

ThreadedWSGIServerReloadable

http_thread线程中,将调用ThreadedWSGIServerReloadable

class ThreadedWSGIServerReloadable(LoggingBaseWSGIServerMixIn, werkzeug.serving.ThreadedWSGIServer):    """ werkzeug Threaded WSGI Server patched to allow reusing a listen socket    given by the environement, this is used by autoreload to keep the listen    socket open when a reload happens.    """    def __init__(self, host, port, app):        super(ThreadedWSGIServerReloadable, self).__init__(host, port, app,                                                           handler=RequestHandler)    def server_bind(self):        envfd = os.environ.get('LISTEN_FDS')        if envfd and os.environ.get('LISTEN_PID') == str(os.getpid()):            self.reload_socket = True            self.socket = socket.fromfd(int(envfd), socket.AF_INET, socket.SOCK_STREAM)            # should we os.close(int(envfd)) ? it seem python duplicate the fd.        else:            self.reload_socket = False            super(ThreadedWSGIServerReloadable, self).server_bind()    def server_activate(self):        if not self.reload_socket:            super(ThreadedWSGIServerReloadable, self).server_activate()

ThreadedWSGIServerReloadablewerkzeug.serving.ThreadedWSGIServer继承而来,作为参数的app为openerp.service.server.start传递的openerp.service.wsgi_server.application()函数

openerp.service.wsgi_server.application

def application(environ, start_response):    if config['proxy_mode'] and 'HTTP_X_FORWARDED_HOST' in environ:        return werkzeug.contrib.fixers.ProxyFix(application_unproxied)(environ, start_response)    else:        return application_unproxied(environ, start_response)

默认参数的情况下,将调用application_unproxied()

openerp.service.wsgi_server.application_unproxied

def application_unproxied(environ, start_response):    """ WSGI entry point."""    # cleanup db/uid trackers - they're set at HTTP dispatch in    # web.session.OpenERPSession.send() and at RPC dispatch in    # openerp.service.web_services.objects_proxy.dispatch().    # /!\ The cleanup cannot be done at the end of this `application`    # method because werkzeug still produces relevant logging afterwards     if hasattr(threading.current_thread(), 'uid'):        del threading.current_thread().uid    if hasattr(threading.current_thread(), 'dbname'):        del threading.current_thread().dbname    with openerp.api.Environment.manage():        # Try all handlers until one returns some result (i.e. not None).        wsgi_handlers = [wsgi_xmlrpc]        wsgi_handlers += module_handlers        for handler in wsgi_handlers:            result = handler(environ, start_response)            if result is None:                continue            return result    # We never returned from the loop.    response = 'No handler found.\n'    start_response('404 Not Found', [('Content-Type', 'text/plain'), ('Content-Length', str(len(response)))])    return [response]

application_unproxied函数查找合适的回调函数对HTTP访问进行处理。回调函数来源于两个部分: 

- wsgi_xmlrpc 
- module_handlers 
其中,module_handlers是WSGI回调函数列表。系统在执行import openerp的过程中已将Root类添加到了WSGI回调表中。代码在openerp.http

# register main wsgi handlerroot = Root()openerp.service.wsgi_server.register_wsgi_handler(root)

 

openerp.http.Root

Root类是 OpenERP Web客户端的WSGI应用,处理HTTP请求的接口为__call__()

def __call__(self, environ, start_response):        """ Handle a WSGI request        """        if not self._loaded:            self._loaded = True            self.load_addons()        return self.dispatch(environ, start_response)

可见,dispatch()是处理HTTP请求的核心方法

def dispatch(self, environ, start_response):        """        Performs the actual WSGI dispatching for the application.        """        try:            httprequest = werkzeug.wrappers.Request(environ)            httprequest.app = self            explicit_session = self.setup_session(httprequest)            self.setup_db(httprequest)            self.setup_lang(httprequest)            request = self.get_request(httprequest)            def _dispatch_nodb():                try:                    func, arguments = self.nodb_routing_map.bind_to_environ(request.httprequest.environ).match()                except werkzeug.exceptions.HTTPException, e:                    return request._handle_exception(e)                request.set_handler(func, arguments, "none")                result = request.dispatch()                return result            with request:                db = request.session.db                if db:                    openerp.modules.registry.RegistryManager.check_registry_signaling(db)                    try:                        with openerp.tools.mute_logger('openerp.sql_db'):                            ir_http = request.registry['ir.http']                    except (AttributeError, psycopg2.OperationalError):                        # psycopg2 error or attribute error while constructing                        # the registry. That means the database probably does                        # not exists anymore or the code doesnt match the db.                        # Log the user out and fall back to nodb                        request.session.logout()                        result = _dispatch_nodb()                    else:                        result = ir_http._dispatch()                        openerp.modules.registry.RegistryManager.signal_caches_change(db)                else:                    result = _dispatch_nodb()                response = self.get_response(httprequest, result, explicit_session)            return response(environ, start_response)        except werkzeug.exceptions.HTTPException, e:            return e(environ, start_response)

dispatch将通过setup_session()方法恢复或者创建sessionsession保存在openerp.tools.config.session_dir所指明的目录下。 

dispatch()中,通过 httprequest = werkzeug.wrappers.Request(environ)得到WSGIRequest对象,并对werkzeug.wrappers.Request类通过HttpRequest类进行包装。 
通过bind_to_environ函数,得到当前Http请求对应的回调函数。并将回调函数通过request.set_handler(func, arguments, "none")传递给request。紧接着通过ir_http = request.registry['ir.http']得到一个ir_http对象

Odoo 模块加载

request.registry

request.registry定义如下:

class WebRequest(object):    ...    @property    def registry(self):        """        The registry to the database linked to this request. Can be ``None``        if the current request uses the ``none`` authentication.        .. deprecated:: 8.0            use :attr:`.env`        """        return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None

RegistryManager.get()从指定的名称中返回一个registry对象,其定义如下。

@classmethod    def get(cls, db_name, force_demo=False, status=None, update_module=False):        """ Return a registry for a given database name."""        with cls.lock():            try:                return cls.registries[db_name]            except KeyError:                return cls.new(db_name, force_demo, status,                               update_module)            finally:                # set db tracker - cleaned up at the WSGI                # dispatching phase in openerp.service.wsgi_server.application                threading.current_thread().dbname = db_name

新建Registry的代码如下,此时应当为加载一个新的数据库。

@classmethod    def new(cls, db_name, force_demo=False, status=None,            update_module=False):        """ Create and return a new registry for a given database name.        The (possibly) previous registry for that database name is discarded.        """        import openerp.modules        with cls.lock():            with openerp.api.Environment.manage():                registry = Registry(db_name)                # Initializing a registry will call general code which will in                # turn call registries.get (this object) to obtain the registry                # being initialized. Make it available in the registries                # dictionary then remove it if an exception is raised.                cls.delete(db_name)                cls.registries[db_name] = registry                try:                    with registry.cursor() as cr:                        seq_registry, seq_cache = Registry.setup_multi_process_signaling(cr)                        registry.base_registry_signaling_sequence = seq_registry                        registry.base_cache_signaling_sequence = seq_cache                    # This should be a method on Registry                    openerp.modules.load_modules(registry._db, force_demo, status, update_module)                except Exception:                    del cls.registries[db_name]                    raise                # load_modules() above can replace the registry by calling                # indirectly new() again (when modules have to be uninstalled).                # Yeah, crazy.                registry = cls.registries[db_name]                cr = registry.cursor()                try:                    registry.do_parent_store(cr)                    cr.commit()                finally:                    cr.close()        registry.ready = True        if update_module:            # only in case of update, otherwise we'll have an infinite reload loop!            cls.signal_registry_change(db_name)        return registry

openerp.modules.load_modules

加载模块

#openerp.modules.load_modules(registry._db, force_demo, status, update_module)def load_modules(db, force_demo=False, status=None, update_module=False):    ...        # STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps)         graph = openerp.modules.graph.Graph()        graph.add_module(cr, 'base', force)        ...        loaded_modules, processed_modules = load_module_graph(cr, graph, status, perform_checks=update_module, report=report)                # STEP 2: Mark other modules to be loaded/updated        ...        # STEP 3: Load marked modules (skipping base which was done in STEP 1)        # IMPORTANT: this is done in two parts, first loading all installed or        #            partially installed modules (i.e. installed/to upgrade), to        #            offer a consistent system to the second part: installing        #            newly selected modules.        #            We include the modules 'to remove' in the first step, because        #            they are part of the "currently installed" modules. They will        #            be dropped in STEP 6 later, before restarting the loading        #            process.        # IMPORTANT 2: We have to loop here until all relevant modules have been        #              processed, because in some rare cases the dependencies have        #              changed, and modules that depend on an uninstalled module        #              will not be processed on the first pass.        #              It's especially useful for migrations.            ...            if update_module:                processed_modules += load_marked_modules(cr, graph,                    ['to install'], force, status, report,                    loaded_modules, update_module)                      ...        # STEP 4: Finish and cleanup installations        ...        # STEP 5: Cleanup menus         # Remove menu items that are not referenced by any of other        # (child) menu item, ir_values, or ir_model_data.        # TODO: This code could be a method of ir_ui_menu. Remove menu without actions of children        ...        # STEP 6: Uninstall modules to remove        # Remove records referenced from ir_model_data for modules to be        # removed (and removed the references from ir_model_data).           ...        # STEP 7: verify custom views on every model        ...        # STEP 8: call _register_hook on every model        ...        # STEP 9: Run the post-install tests      ...

opernerp.modules.load_module_graph()

for index, package in enumerate(graph):        module_name = package.name        module_id = package.id        ...        load_openerp_module(package.name)        new_install = package.installed_version is None        if new_install:            py_module = sys.modules['openerp.addons.%s' % (module_name,)]            pre_init = package.info.get('pre_init_hook')            if pre_init:                getattr(py_module, pre_init)(cr)        models = registry.load(cr, package)#加载模块        loaded_modules.append(package.name)        if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):            registry.setup_models(cr, partial=True)            init_module_models(cr, package.name, models)        # Can't put this line out of the loop: ir.module.module will be        # registered by init_module_models() above.        modobj = registry['ir.module.module']       ...

openerp.modules.load_openerp_module()

def load_openerp_module(module_name):    global loaded    if module_name in loaded:        return    initialize_sys_path()    try:        mod_path = get_module_path(module_name)        __import__('openerp.addons.' + module_name)               info = load_information_from_description_file(module_name)        if info['post_load']:            getattr(sys.modules['openerp.addons.' + module_name], info['post_load'])()    except Exception, e:        msg = "Couldn't load module %s" % (module_name)        _logger.critical(msg)        _logger.critical(e)        raise    else:        loaded.append(module_name)

openerp.modules.init_module_models()

def init_module_models(cr, module_name, obj_list):    _logger.info('module %s: creating or updating database tables', module_name)    todo = []    for obj in obj_list:        result = obj._auto_init(cr, {
'module': module_name}) ... cr.commit()

通过obj._auto_end(cr, {'module': module_name})初始化数据库表

def _auto_init(self, cr, context=None):    ...        self._field_create(cr, context=context)#添加ir_model、ir_model_fields和ir_model_data数据项        create = not self._table_exist(cr)        ...             self._create_table(cr)                ...

openerp.modules.registry.load()

def load(self, cr, module):        ...        from .. import models        models_to_load = [] # need to preserve loading order        lazy_property.reset_all(self)        for cls in models.MetaModel.module_to_models.get(module.name, []):            model = cls._build_model(self, cr)#实例化模块            if model._name not in models_to_load:                               models_to_load.append(model._name)        return [self.models[m] for m in models_to_load]

openrp.Models.build_model()

openerp.modules.registry将自身作为pool传递给_build_model

@classmethod    def _build_model(cls, pool, cr):               ...        # instantiate the model, and initialize it        model = object.__new__(cls)        model.__init__(pool, cr)        return model

 

转载于:https://www.cnblogs.com/dancesir/p/7008967.html

你可能感兴趣的文章
mysql启动过程
查看>>
2017前端面试题总结
查看>>
Http GetPost网络请求
查看>>
SWIFT国际资金清算系统
查看>>
Sping注解:注解和含义
查看>>
站立会议第四天
查看>>
如何快速掌握一门技术
查看>>
利用AMPScript获取Uber用户数据的访问权限
查看>>
vagrant 同时设置多个同步目录
查看>>
python接口自动化28-requests-html爬虫框架
查看>>
生成随机数的模板
查看>>
Mysql 数据库操作
查看>>
转:linux终端常用快捷键
查看>>
UVa 11059 最大乘积
查看>>
数组分割问题求两个子数组的和差值的小
查看>>
composer 报 zlib_decode(): data error
查看>>
hdu 3938 并查集
查看>>
《深入分析Java Web技术内幕》读书笔记之JVM内存管理
查看>>
python之GIL release (I/O open(file) socket time.sleep)
查看>>
软件开发与模型
查看>>