17.odoo入门——初探后台启动过程(四)

来源:互联网 发布:linux搭建telnet服务 编辑:程序博客网 时间:2024/06/07 01:50

第17天——————————

因为odoo服务器有三种模式——prefork, gevented, threaded,首先得大致了解一下这三种MPM(Multi-Processing Module,多进程处理模块)模式的特点,可以参考一篇关于apache服务器的博文:    http://blog.jobbole.com/91920/

读了这篇博文之后大致了解了这三种模式的区别和优缺点。

接下来看到我们的源代码,由于GeventServerPreforkServerThreadedServer三个类都继承于CommonServer类,其源代码如下:

class CommonServer(object):    def __init__(self, app):        # TODO Change the xmlrpc_* options to http_*        self.app = app  #传入的参数是 odoo.service.wsgi_server.application ,先搁置着        # config        self.interface = config['xmlrpc_interface'] or '0.0.0.0' #在我的配置文件中为空        self.port = config['xmlrpc_port’] #在配置文件中可以查看到,xmlrpc_port = 8069        # runtime        self.pid = os.getpid()    def close_socket(self, sock):  #反正就是关闭套接字啰        """ Closes a socket instance cleanly        :param sock: the network socket to close        :type sock: socket.socket        """        try:            sock.shutdown(socket.SHUT_RDWR)        except socket.error, e:            if e.errno == errno.EBADF:                # Werkzeug > 0.9.6 closes the socket itself (see commit                # https://github.com/mitsuhiko/werkzeug/commit/4d8ca089)                return            # On OSX, socket shutdowns both sides if any side closes it            # causing an error 57 'Socket is not connected' on shutdown            # of the other side (or something), see            # http://bugs.python.org/issue4397            # note: stdlib fixed test, not behavior            if e.errno != errno.ENOTCONN or platform.system() not in ['Darwin', 'Windows']:                raise        sock.close()

由于我的配置中使用的是threaded,那么看到ThreadedServer类:

首先这个类里面有比较多的函数,这里不贴一整段代码,而是一个函数一个函数去查看源码,首先要了解signal handler的概念,其实,要看懂几个常用的结束信号,可以参考:

http://blog.csdn.net/yikai2009/article/details/8643818  linux下的几个信号。 而cron是linux下的一个定时执行工具,大概是可以定时分配cpu给每个线程吧——自行百度吧

linux下的spawn,参考——http://blog.csdn.net/ysdaniel/article/details/7059511

Import threading #这个模块中导入了这个库,python内置的多线程处理的库

类的构造函数:

def __init__(self, app):    super(ThreadedServer, self).__init__(app)    self.main_thread_id = threading.currentThread().ident    # Variable keeping track of the number of calls to the signal handler defined    # below. This variable is monitored by ``quit_on_signals()``.    self.quit_signals_received = 0 #统计收到的退出信号    #self.socket = None    self.httpd = None

信号处理函数:

def signal_handler(self, sig, frame): #应该就是收到2次结束信号就退出    if sig in [signal.SIGINT, signal.SIGTERM]:        # shutdown on kill -INT or -TERM        self.quit_signals_received += 1        if self.quit_signals_received > 1:            # logging.shutdown was already called at this point.            sys.stderr.write("Forced shutdown.\n")            os._exit(0)    elif sig == signal.SIGHUP:        # restart on kill -HUP        odoo.phoenix = True        self.quit_signals_received += 1

再挑顺序看到ThreadedServer类的start函数:

def start(self, stop=False):    _logger.debug("Setting signal handlers")  #就是这里了,控制台每次启动都会出的一条语句来自这里    if os.name == 'posix':  #对linux定义几个声明        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)        signal.signal(signal.SIGUSR1, log_ormcache_stats)    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下的这个函数:

def http_spawn(self):    t = threading.Thread(target=self.http_thread, name="odoo.service.httpd") #参考 http://python.jobbole.com/81546/#参数target是一个可调用对象(也称为活动[activity]),在线程启动后执行;    t.setDaemon(True)    t.start()    _logger.info('HTTP service (werkzeug) running on %s:%s', self.interface, self.port)#每次启动都会看到的这句话,原来输出是来自于这里
再看到http_thread这个东西,它来自于ThreadedServer类下的这个函数:

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()

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)….若干函数

这个暂时不太了解werkzeug.serving.ThreadedWSGIServer,但是大致知道应该是个处理httpweb服务器程序,那么到此我们就大概了解了ThreadedServer类的启动和运行过程了。这里接触到的一个知识点就是,ThreadedServer类通过接受信号来知道自己是否被kill——这也算是进程间的通讯吧。

看到启动过程中控制台的最后一句输出:

INFO ? odoo.service.server: HTTP service (werkzeug) running on 0.0.0.0:8069
那么也就是说 http 服务器是跟ThreadedServer类下的http_thread,也就是 http_thread这个函数啦。

由自己输出调试可知,接下来执行了

def cron_spawn(self):    """ Start the above runner function in a daemon thread.    The thread is a typical daemon thread: it will never quit and must be    terminated when the main process exits - with no consequence (the processing    threads it spawns are not marked daemon).    """    # Force call to strptime just before starting the cron thread    # to prevent time.strptime AttributeError within the thread.    # See: http://bugs.python.org/issue7980    datetime.datetime.strptime('2012-01-01', '%Y-%m-%d')    for i in range(odoo.tools.config['max_cron_threads']):        def target():            self.cron_thread(i)        #print "one”#这是我自己的输出调试        t = threading.Thread(target=target, name="odoo.service.cron.cron%d" % i)        t.setDaemon(True)        t.start()        _logger.debug("cron%d started!" % i)

这里是根据配置文件中的参数max_cron_threads开启了若干个线程,线程启动后运行的便是cron_thread函数,看到这个函数源码:

def cron_thread(self, number):    while True:        time.sleep(SLEEP_INTERVAL + number)     # Steve Reich timing style        registries = odoo.modules.registry.Registry.registries        _logger.debug('cron%d polling for jobs', number)        for db_name, registry in registries.iteritems():            while registry.ready:                try:                    acquired = odoo.addons.base.ir.ir_cron.ir_cron._acquire_job(db_name)                    if not acquired:                        break                except Exception:                    _logger.warning('cron%d encountered an Exception:', number, exc_info=True)                    break

大概就是先睡眠一定的时间,睡眠完成后变成就绪状态,就可以等待工作的到来而获得cpu.不过从我的输出调试来看,在我不进行任何操作的时候,多个线程轮流切换,但是也没有进行执行任何有意义的行动,因为acquired总是空,而且目前我也还没找到odoo.addons.base.ir.ir_cron.ir_cron._acquire_job,那么这个问题暂且搁置

下一步要探讨的地方应该是 werkzeug.serving.ThreadedWSGIServer 这样的一个http服务器   和  多线程具体执行的工作, 以及在我们打开8069这个网页的时候后台都做了些什么工作。

原创粉丝点击