`

Glance源码架构探秘(二)

阅读更多

    上一篇文章,介绍了Glance服务的对外启动接口/bin/glance-api,其中最重要的部分就是通过server = eventlet.wsgi.Server()生成了一个http server,并通过server.start()启动了一个WSGI程序。

    WSGI(web server gateway interface)web服务器网关接口。简单来说,WSGI的作用就是将client发给web server的请求转发给实际处理这个请求的程序。

    WSGI在python中官方的参考实现wsgiref,#module-wsgiref。

 from wsgiref.simple_server import make_server 
 # Every WSGI application must have an application object - a callable 
 # object that accepts two arguments. For that purpose, we're going to 
 # use a function (note that you're not limited to a function, you can 
 # use a class for example). The first argument passed to the function 
 # is a dictionary containing CGI-style envrironment variables and the 
 # second variable is the callable object (see PEP 333). 
 def hello_world_app(environ, start_response): 
     status = '200 OK' 
     # HTTP Status 
     headers = [('Content-type', 'text/plain')] 
     # HTTP Headers 
     start_response(status, headers) 
     # The returned object is going to be printed return ["Hello World"] 
     httpd = make_server('', 8000, hello_world_app) 
     print "Serving on port 8000..." 
     # Serve until process is killed ()      

 参考流程大概就是server中定义一个start_response()函数,返回值为一个write()函数,用来返回给client的响应。application函数要实现两个接口参数,environ和start_response(),前者就是服务器server传递过来的request请求,application控制后者将程序的返回值发回给web server。

    下面分析OpenStack中wsgi接口的实现,/glance/common/wsgi.py

class Server(object): 
     """Server class to manage multiple WSGI sockets and applications.""" 
     def __init__(self, threads=1000): 
         self.threads = threads 
         self.children = [] 
         self.running = True def 
         start(self, application, default_port): 
         """ Run a WSGI server with the given application.
          : param application: The application to be run in the WSGI server 
         :param default_port: Port to bind to if none is specified in conf """ 
         def kill_children(*args): 
             """Kills the entire process group.""" 
             sel(_('SIGTERM or SIGINT received')) 
             signal.signal(signal.SIGTERM, signal.SIG_IGN) 
             signal.signal(signal.SIGINT, signal.SIG_IGN) 
             self.running = False 
             os.killpg(0, signal.SIGTERM) 
             def hup(*args): 
                 """ Shuts down the server, but allows running requests to complete """ 
                 sel(_('SIGHUP received')) 
                 signal.signal(signal.SIGHUP, signal.SIG_IGN) 
                 self.running = False 
                 self.application = application 
                 self.sock = get_socket(default_port) 
                 os.umask(027) 
                 # ensure files are created with the correct privileges 
                 self.logger = os_logging.getLogger('eventlet.wsgi.server')
                 if CONF.workers == 0: 
                     # Useful for profiling, test, debug etc. 
                     self.pool = self.create_pool() 
                     self.pool.spawn_n(self._single_run, self.application, self.sock) 
                     return 
                 else:  
                     sel(_("Starting %d workers") % CONF.workers) 
                     signal.signal(signal.SIGTERM, kill_children) 
                     signal.signal(signal.SIGINT, kill_children) 
                     signal.signal(signal.SIGHUP, hup) 
                     while len(self.children) < CONF.workers: 
                         self.run_child() 
                     def create_pool(self): 
                         eventlet.patcher.monkey_patch(all=False, socket=True) 
                         return eventlet.GreenPool(size=self.threads) 
                     def wait_on_children(self): 
                         while self.running: 
                             try: 
                                 pid, status = os.wait() 
                                 if os.WIFEXITED(status) or os.WIFSIGNALED(status): 
                                     sel(_('Removing dead child %s') % pid) 
                                     self.children.remove(pid) 
                                     if os.WIFEXITED(status) and os.WEXITSTATUS(status) != 0: 
                                         self.logger.error(_('Not respawning child %d, cannot ' 'recover from termination') % pid) 
                                         if not self.children: 
                                             sel( _('All workers have terminated. Exiting')) 
                                             self.running = False else 
                                             self.run_child() 
                             except OSError, err: 
                                 if err.errno not in (errno.EINTR, errno.ECHILD): 
                                     raise 
                             except KeyboardInterrupt:
                                  sel(_('Caught keyboard interrupt. Exiting.')) 
                                  break 
                              eventlet.greenio.shutdown_safe(self.sock) 
                              self.sock.close() 
                              self.logger.debug(_('Exited')) 
                              def wait(self): 
                                  """Wait until all servers have completed running.""" 
                                  try: 
                                      if self.children: 
                                          self.wait_on_children() 
                                      else: 
                                          self.pool.waitall() 
                                  except KeyboardInterrupt: 
                                      pass 
                                  def run_child(self): 
                                      pid = os.fork() 
                                      if pid == 0: 
                                          signal.signal(signal.SIGHUP, signal.SIG_DFL) 
                                          signal.signal(signal.SIGTERM, signal.SIG_DFL) 
                                          # ignore the interrupt signal to avoid a race whereby 
                                          # a child worker receives the signal before the parent 
                                          # and is respawned unneccessarily as a result 
                                          signal.signal(signal.SIGINT, signal.SIG_IGN) 
                                          self.run_server() sel(_('Child %d exiting normally') % os.getpid()) 
                                          # self.pool.waitall() has been called by run_server, so 
                                          # its safe to exit here 
                                          sys.exit(0) 
                                      else: 
                                          sel(_('Started child %s') % pid) 
                                          self.children.append(pid) 
                                          def run_server(self): 
                                              """Run a WSGI server.""" 
                                              if cfg.CONF.pydev_worker_debug_host: 
                                                  utils.setup_remote_pydev_debug(cfg.CONF.pydev_worker_debug_host, cfg.CONF.pydev_worker_debug_port) 
                                                  eventlet.wsgi.HttpProtocol.default_request_version = "HTTP/1.0" 
                                                  try: 
                                                      eventlet.hubs.use_hub('poll') 
                                                  except Exception: 
                                                      msg = _("eventlet 'poll' hub is not available on this platform") 
                                                      raise  exception.WorkerCreationFailure(reason=msg) 
                                                     self.pool = self.create_pool() 
                                                  try: 
                                                      eventlet.wsgi.server(self.sock, self.application, log=WritableLogger(self.logger), custom_pool=self.pool) 
                                                  except socket.error, err: 
                                                      if err[0] != errno.EINVAL: 
                                                          raise self.pool.waitall()

 大家可能看了代码之后很乱,前面许多行是设置log写入程序,打开conf读取ip,port等信息,最重要的内容是代码最后几行:

self.pool = self.create_pool()

eventlet.wsgi.server(self.sock, self.application, log = WritableLogger(self.logger), custom_pool=self.pool)

刚才提到了server和application之间的通讯接口WSGI,现在讲讲server。OpenStack并没有使用Python标准库中的BaseHTTPServer,而是使用了在网络并发等领域处理效率非常优异的eventlet库。

     eventlet提供了一套API以实现“协程”(coroutines)。所谓的“协程”可以简单地看做是“假线程”,他可以实现线程的非阻塞异步IO调用的功能,但是每个协程都有自己独立的堆栈,这和线程之间公用堆栈是有区别的。eventlet会维护一个协程“池”,用来存放所有创建的协程。但是不同于线程,协程同时只能有一个实例在运行,其他的协程要运行,必须等待当前协程显式地被挂起。不同于线程的执行顺序随机,协程的执行时按调用顺序的。

    OpenStack的服务,没有使用市面上常见的web server的原因,大概就是其处理并发无非就是使用多线程或IO复用等。然而,当多客户端并发访问时,OpenStack内部的一些共享资源,并不能十分安全地利用互斥锁等方法进行线程共享资源的互斥。为了防止并发出现资源死锁,简化架构设计流程,采用“协程”是个非常不错的选择。并且,线程间的切换需要大量的时间和空间的开销,而协程可以有效地避免这个问题。

import eventlet 
def handle(client): 
    while True: 
        c = client.recv(1) 
        if not c: 
            break 
        client.sendall(c) 
        server = eventlet.listen(('0.0.0.0', 6000)) 
        pool = eventlet.GreenPool(10000) 
        while True: 
            new_sock, address = server.accept() 
            pool.spawn_n(handle, new_sock)

 上面是eventlet一个简单服务器端的示例,首先用eventlet.GreenPool(1000)生成一个最大容量为1000的“协程”缓冲池,server.accept()等待,服务器端server端收到一个客户端的连接请求,就用pool.spawn_n()启动一个“协程”进行处理响应。

    回到glance中,OpenStack将python原版的CGIHTTPServer进行“绿化”,提供了eventlet.wsgi.server进行http的响应,其内部实现结构和作用和上面的代码相似,同样都是来一个http请求,就会启动一个协程server进行响应。参数custom_pool就是我们上面刚刚申请的GreenPool协程池。参数self.application为WSGI程序入口。这样我们就成功运行了一个WSGI服务程序。

分享到:
评论

相关推荐

    paraview-glance-源码.rar

    《Paraview-Glance源码解析》 Paraview-Glance是ParaView项目的一个分支,专注于提供轻量级的、基于Web的可视化界面,让用户能够远程访问和交互式地查看大型科学数据。源码分析是理解软件工作原理、进行定制化开发...

    glance 1.1

    下面将详细讨论Glance 1.1版本中的核心功能、设计原则以及它在OpenStack架构中的角色。 1. **核心功能**: - **镜像注册**:Glance 1.1允许用户上传和注册各种类型的虚拟机镜像,如VMDK、QCOW2或RAW格式,以便后续...

    Glance安装配置简介.docx

    Glance是OpenStack云平台中的一个关键组件,主要负责镜像服务,为虚拟机实例提供镜像的存储、检索和管理功能。以下是对Glance安装配置的详细说明: 1. **创建Glance数据库及授权** 在安装Glance之前,需要在MySQL...

    openstack其中四大组件源码

    通过深入学习这四大组件的源码,开发者不仅可以了解OpenStack的基本架构和工作原理,还能对云存储、计算和身份管理有更深入的理解。此外,源码分析对于定制化开发、性能优化以及故障排查都具有重要意义。对于希望...

    云计算Glance 管理

    云计算Glance管理 云计算Glance管理是云计算领域中的一种重要的镜像管理服务。Glance是OpenStack云计算平台中的一部分,负责管理和维护虚拟机镜像。Glance提供了镜像的注册、存储、检索和删除等功能,确保了云计算...

    OpenStack之镜像服务(Glance)安装

    7. **配置后端存储系统**:在Glance的配置文件中,需要指定镜像的存储位置,如`[glance_store]`部分,设置`stores`、`default_store`和`filesystem_store_datadir`,这告诉Glance在哪里存储实际的镜像文件。...

    Glance监控命令在HP UX上的使用

    ### Glance监控命令在HP-UX上的使用 #### Glance工具概述 Glance是一个功能强大的实时监控工具,专为HP-UX系统设计。它能够提供丰富的系统信息,并以图形模式和文本模式显示,帮助用户更好地理解和管理系统的资源...

    20-理解 Glance1

    Glance的架构包含两个主要的服务进程:glance-api和glance-registry。glance-api对外提供REST API接口,处理用户的请求,如果是元数据操作,它会将请求转发给glance-registry;如果是镜像文件的存取,它会根据配置的...

    Python-Glance是一个用于替代tophtop的跨平台监控工具

    **Python-Glance:跨平台监控工具的创新选择** 在现代IT环境中,系统监控是确保服务稳定性和性能的关键环节。传统的命令行工具如`top`和`htop`虽然功能强大,但它们的交互方式受限于终端界面,无法提供丰富的可视化...

    OpenStack-glance服务-glance-api.conf配置文件

    OpenStack-glance服务-glance-api.conf配置文件,在配置OpenStack的glance服务中,配置文件glance-api.conf需要进行部分修改,进而来适应各种服务,该文件为修改完成的glance-api.conf文件内容。

    004.Glance详解1

    二、Glance搭建 安装和配置Glance通常涉及以下步骤: 1. 安装Glance服务及其依赖包。 2. 配置Glance服务,包括指定数据库连接、存储后端以及认证机制等。 3. 初始化数据库,创建必要的表结构。 4. 启动Glance服务...

    glance镜像发布

    openstack项目中关于glance的应用与注意事项

    5.2 glance的安装和配置1

    1. 源码加载`admin`用户的环境变量: ``` source /root/admin-openrc ``` 2. 创建Glance用户,指定其属于`default`域,与`service`项目关联,赋予`admin`角色: ``` openstack user create --domain default -...

    源码安装openstack软件包

    在"源码安装openstack软件包"的过程中,我们需要对OpenStack的架构和组件有深入理解,并熟悉Linux操作系统、编译工具以及依赖管理。 首先,OpenStack的组件包括Nova(计算)、Glance(镜像服务)、Cinder(块存储)...

    glance命令使用

    #### 二、基本使用 ##### 启动命令 ```bash glance ``` 默认情况下,`glance`每5秒刷新一次屏幕,展示系统资源和活动进程的基本信息。为了获取更详细的性能数据,可以通过命令行参数来定制显示内容。 #### 三、...

    Openstack映像管理服务Glance.zip

    Glance 项目为 Openstack 提供了用来发现、注册和获取虚拟机映像的服务,提供 RESTful API 用来查询 VM 映像元数据。 标签:Glance

    Glance_监控命令在HP_UX_上的使用

    Glance_监控命令在HP_UX_上的使用

    云计算实验2 安装镜像服务glance.doc

    - 理解Glance在OpenStack架构中的作用及其工作原理。 - 掌握如何通过Glance创建、管理以及检索虚拟机镜像。 #### 实验准备 为了顺利进行实验,学生需要准备以下工具和环境: - **虚拟机软件**:VMware Workstation...

    OpenStack的架构详解

    #### 二、OpenStack的核心组件 OpenStack项目主要包括以下几个核心组件: 1. **OpenStack Compute (Nova)**:作为OpenStack的核心组件之一,Nova负责处理计算实例的生命周期管理,包括实例的启动、调度、监控及...

Global site tag (gtag.js) - Google Analytics