问题导读
1.keystone-all.py的作用是什么?
2.Python应用程序使用WSGI(Web Server Gateway Interface)协议来做什么?
!在调试keystone遇到问题,evenlet线程出错,解决办法参考: http://adam.younglogic.com/2012/12/keystone-and-eclipse-pydev/ ,主要是在调试keystone-all时增加启动参数,–standard-threads解决,其实里面也有说明,在用pydev调试是需要将monkeypatch_thread设置为False。
!原因:cannot switch to a different thread
keystone-all.py启动keystone服务,所以从keystone-all来入手分析。
首先将keystone源码目录下的模块路径进行设置,将目录配置在系统目录下。
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(__file__),
os.pardir,
os.pardir))
if os.path.exists(os.path.join(possible_topdir,
'keystone',
'__init__.py')):
sys.path.insert(0, possible_topdir) 复制代码
再引入目录下的其他模块
from paste import deploy
import pbr.version
from keystone.openstack.common import gettextutils
gettextutils.install('keystone', lazy=True) 复制代码
引入paste.deploy模块,这个模块官方的解释是,Paste Deployment is a system for finding and configuring WSGI applications and servers.意思是这个模块是一个用于发现和配置WSGI应用或者服务的系统。官方网址是:paste.deploy模块官方网址
WSGI(Web Server Gateway Interface)是一个协议,Python应用程序或框架和Web服务器之间的一种接口,我总结的通俗说法就是将Python应用程序的某些接口转变成Web上的接口可以通过http服务来进行调用。官方网址是:PEP 333,python2.7 wsgi官方网址
pbr模块是用于安装时配置的一个模块,官方解释是:PBR is a library that injects some useful and sensible default behaviors into your setuptools run.意思是PBR是一个库,用于注入一些有用的和敏感的基本配置信息到setuptools.setuptools是一个工具用于下载,编译,安装,升级,卸载python的包文件。
pbr的官方网址是:pbr官方网址
setuptools的官方网址是:setuptools官方网址
gexttextutil是一个运行后加载的模块,用于识别用户的提交的语言,帮助返回错误信息时使用用户语言。
from keystone.common import environment
from keystone.common import utils
from keystone import config
from keystone.openstack.common import importutils
CONF = config.CONF 复制代码
environment模块用于配置环境变量,用于配置keystone的web服务是使用eventlet库里的htttp模块还是python库下http模块。
utils模块提供一些Openstack工具类。
config模块,是一个包装模块,用于包装keystone.common下的config模块。包装的概念就是对一个已经存在的对象增加,删除,修改其内部属性和功能。顾名思义,就是为服务提供配置信息的模块。
importutils模块,帮助导入其他类,对象,模块的一个模块。
下面就是启动keystone服务的主体程序,首先读取配置文件keystone.conf和其他配置文件,再配置log打印信息。
配置paste_config对象和上文提到的paste.deploy模块对应为其配置文件,后面就是开启服务的主体。
if __name__ == '__main__':
dev_conf = os.path.join(possible_topdir,
'etc',
'keystone.conf')
config_files = None
if os.path.exists(dev_conf):
config_files = [dev_conf]
CONF(project='keystone',
version=pbr.version.VersionInfo('keystone').version_string(),
default_config_files=config_files)
config.setup_logging(CONF, product_name='keystone')
# Log the options used when starting if we're in debug mode...
if CONF.debug:
CONF.log_opt_values(logging.getLogger(CONF.prog), logging.DEBUG)
paste_config = config.find_paste_config()
monkeypatch_thread = not CONF.standard_threads
pydev_debug_url = utils.setup_remote_pydev_debug()
if pydev_debug_url:
# in order to work around errors caused by monkey patching we have to
# set the thread to False. An explanation is here:
# http://lists.openstack.org/pipermail/openstack-dev/2012-August/
# 000794.html
monkeypatch_thread = False
environment.use_eventlet(monkeypatch_thread)
servers = []
servers.append(create_server(paste_config,
'admin',
CONF.bind_host,
int(CONF.admin_port)))
servers.append(create_server(paste_config,
'main',
CONF.bind_host,
int(CONF.public_port)))
serve(*servers) 复制代码
通过eventlet模块启动配置服务器,完成eventlet环境的配置,并启动服务器。eventlet是一个第三方库,官方说明提供并发网络服务器的功能,官方网址:eventlet官方网址
environment.use_eventlet(monkeypatch_thread) 复制代码
在environment模块下代码
@configure_once('eventlet')
def use_eventlet(monkeypatch_thread=None):
global httplib, subprocess, Server
# This must be set before the initial import of eventlet because if
# dnspython is present in your environment then eventlet monkeypatches
# socket.getaddrinfo() with an implementation which doesn't work for IPv6.
os.environ['EVENTLET_NO_GREENDNS'] = 'yes'
import eventlet
from eventlet.green import httplib as _httplib
from eventlet.green import subprocess as _subprocess
from keystone.common.environment import eventlet_server
if monkeypatch_thread is None:
monkeypatch_thread = not os.getenv('STANDARD_THREADS')
# Raise the default from 8192 to accommodate large tokens
eventlet.wsgi.MAX_HEADER_LINE = 16384
# NOTE(ldbragst): Explicitly declare what should be monkey patched and
# what shouldn't. Doing this allows for more readable code when
# understanding Eventlet in Keystone. The following is a complete list
# of what is monkey patched instead of passing all=False and then passing
# module=True to monkey patch a specific module.
eventlet.patcher.monkey_patch(os=False, select=True, socket=True,
thread=monkeypatch_thread, time=True,
psycopg=False, MySQLdb=False)
Server = eventlet_server.Server
httplib = _httplib
subprocess = _subprocess 复制代码
完成环境配置后创建服务器,启动服务。
create_server函数完成deploy模块配置信息的读取,通过loadapp加载WSGI服务器配置选项,再通过environment启动配置过的文件Server,并设置ssl协议,配置证书,保证服务器的安全传输。
serve函数启动信号监听机制=(SIGINT中断),并start服务器,导入notifier模块,给keystone提供notify的服务(事件通知服务,回调函数?),最后使服务wait(),等待全部操作完成后,提供服务。
servers = []
servers.append(create_server(paste_config,
'admin',
CONF.bind_host,
int(CONF.admin_port)))
servers.append(create_server(paste_config,
'main',
CONF.bind_host,
int(CONF.public_port)))
serve(*servers) 复制代码
def create_server(conf, name, host, port):
app = deploy.loadapp('config:%s' % conf, name=name)
server = environment.Server(app, host=host, port=port)
if CONF.ssl.enable:
server.set_ssl(CONF.ssl.certfile, CONF.ssl.keyfile,
CONF.ssl.ca_certs, CONF.ssl.cert_required)
return server
def sigint_handler(signal, frame):
"""Exits at SIGINT signal."""
logging.debug('SIGINT received, stopping servers.')
sys.exit(0)
def serve(*servers):
signal.signal(signal.SIGINT, sigint_handler)
for server in servers:
server.start()
# notify calling process we are ready to serve
if CONF.onready:
try:
notifier = importutils.import_module(CONF.onready)
notifier.notify()
except ImportError:
try:
utils.check_output(CONF.onready.split())
except Exception:
logging.exception('Failed to execute onready command')
for server in servers:
server.wait() 复制代码
Server类实现,通过eventlet模块开启两个并发的网络服务器。
class Server(object):
"""Server class to manage multiple WSGI sockets and applications."""
def __init__(self, application, host=None, port=None, threads=1000):
self.application = application
self.host = host or '0.0.0.0'
self.port = port or 0
self.pool = eventlet.GreenPool(threads)
self.socket_info = {}
self.greenthread = None
self.do_ssl = False
self.cert_required = False
def start(self, key=None, backlog=128):
"""Run a WSGI server with the given application."""
LOG.info(_('Starting %(arg0)s on %(host)s:%(port)s') %
{'arg0': sys.argv[0],
'host': self.host,
'port': self.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
info = socket.getaddrinfo(self.host,
self.port,
socket.AF_UNSPEC,
socket.SOCK_STREAM)[0]
_socket = eventlet.listen(info[-1],
family=info[0],
backlog=backlog)
if key:
self.socket_info[key] = _socket.getsockname()
# SSL is enabled
if self.do_ssl:
if self.cert_required:
cert_reqs = ssl.CERT_REQUIRED
else:
cert_reqs = ssl.CERT_NONE
sslsocket = eventlet.wrap_ssl(_socket, certfile=self.certfile,
keyfile=self.keyfile,
server_side=True,
cert_reqs=cert_reqs,
ca_certs=self.ca_certs)
_socket = sslsocket
self.greenthread = self.pool.spawn(self._run,
self.application,
_socket)
def set_ssl(self, certfile, keyfile=None, ca_certs=None,
cert_required=True):
self.certfile = certfile
self.keyfile = keyfile
self.ca_certs = ca_certs
self.cert_required = cert_required
self.do_ssl = True
def kill(self):
if self.greenthread:
self.greenthread.kill()
def wait(self):
"""Wait until all servers have completed running."""
try:
self.pool.waitall()
except KeyboardInterrupt:
pass
except greenlet.GreenletExit:
pass
def _run(self, application, socket):
"""Start a WSGI server in a new green thread."""
log = logging.getLogger('eventlet.wsgi.server')
try:
eventlet.wsgi.server(socket, application, custom_pool=self.pool,
log=logging.WritableLogger(log))
except Exception:
LOG.exception(_('Server error'))
raise 复制代码
存在不明确的地方:
1.eventlet网络服务器的实现
2.paste.deploy机制的实现
3.notify机制的实现
相关文章
Openstack之keystone源代码分析1--WSGI接口流程分析
http://www.aboutyun.com/thread-10137-1-1.html
Openstack之keystone源代码分析2--Controller->Manager->Driver
http://www.aboutyun.com/thread-10138-1-1.html
[openstack][G版]keystone源码学习
http://www.aboutyun.com/thread-10136-1-1.html
bluefire1991