Web server启动过程的关键代码如下几句:
self.pool = eventlet.GreenPool(1)
self._server = self._service.pool.spawn(self._service._run,self._application,dup_sock)
def _run(self, application, socket):
"""Start a WSGI server in a new green thread."""
eventlet.wsgi.server(socket, application,
max_size=self.num_threads,
log=LOG,
keepalive=CONF.wsgi_keep_alive,
log_format=CONF.wsgi_log_format,
socket_timeout=self.client_socket_timeout)
这几句代码的含义是:
1 创建一个协程池
2 协程的启动函数就是_run
3 _run函数的本质是创建一个符号WSGI规范的Web Server
4 这个Web Server的WSGI Application就是传入的参数application
5 这个Web Server绑定的Socket就是传入的参数socket。
下面介绍这两个参数
一 Socket
对于一个Web Server来说,它对外首先体现的就是Server IP和Server 端口号。从编程角度来说,这两者都体现在Socket中,也就是前文所说的传入参数socket,这个传入的socket,其构造方法是如下函数:
def _get_socket(self, host, port, backlog):
bind_addr = (host, 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
try:
info = socket.getaddrinfo(bind_addr[0],
bind_addr[1],
socket.AF_UNSPEC,
socket.SOCK_STREAM)[0]
family = info[0]
bind_addr = info[-1]
except Exception:
LOG.exception(_LE("Unable to listen on %(host)s:%(port)s"),
{'host': host, 'port': port})
sys.exit(1)
sock = None
retry_until = time.time() + CONF.retry_until_window
while not sock and time.time() < retry_until:
try:
sock = eventlet.listen(bind_addr,
backlog=backlog,
family=family)
except socket.error as err:
with excutils.save_and_reraise_exception() as ctxt:
if err.errno == errno.EADDRINUSE:
ctxt.reraise = False
eventlet.sleep(0.1)
if not sock:
raise RuntimeError(_("Could not bind to %(host)s:%(port)s "
"after trying for %(time)d seconds") %
{'host': host,
'port': port,
'time': CONF.retry_until_window})
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# sockets can hang around forever without keepalive
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
# This option isn't available in the OS X version of eventlet
if hasattr(socket, 'TCP_KEEPIDLE'):
sock.setsockopt(socket.IPPROTO_TCP,
socket.TCP_KEEPIDLE,
CONF.tcp_keepidle)
return sock
代码本身就是Socket编程。需要强调的一点是host、port这两个参数,它们是在配置文件(etc/neutron.conf)中配置的,比如:
#etc/neutron.conf
#绑定web server的IP地址
bind_host=0.0.0.0
#绑定到web Server的端口号
bind_port=9696
这个配置文件表明,Web Server的IP地址是本机(bind_host=0.0.0.0),web Server的端口号是9696(bind_port=9696)。
二 Application
对于一个符合WSGI规范的Web Server而言,除了server IP和端口外,最重要的参数就是WSGI Application了,因为WSGI Application才是真正处理HTTP请求的实体。_run函数的参数application,在Neutron启动的过程中,是靠如下函数加载的:
def _run_wsgi(app_name):
app = config.load_paste_app(app_name)
短短一句话,涉及Neutron Server的RESTful API发布与处理,以及Neutron Plugins的加载。