您的位置:首页 > 其它

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

2017-08-07 10:44 489 查看
第17天——————————

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

接下来看到我们的源代码,由于GeventServer,PreforkServer,ThreadedServer三个类都继承于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,但是大致知道应该是个处理http的web服务器程序,那么到此我们就大概了解了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这个网页的时候后台都做了些什么工作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: