OpenStack Swift源码导读:业务整体架构和Proxy进程

移动开发 iOS OpenStack
OpenStack的源码分析在网上已经非常多了,针对各个部分的解读亦是非常详尽。这里我根据自己的理解把之前读过的Swift源码的一些要点记录一下,希望给需要的同学能带来一些帮助。

OpenStack的源码分析在网上已经非常多了,针对各个部分的解读亦是非常详尽。这里我根据自己的理解把之前读过的Swift源码的一些要点记录一下,希望给需要的同学能带来一些帮助。

一、Swift的整体框架图

Swift代码树

 

如上图,Swift的源码目录结构。其中proxy是前端的业务接入进程。account、container和object目录分别是账户、容器 和对象的业务处理逻辑进程。common目录是一些通用工具代码。common中比较重要的有:哈希环的处理逻辑。接下来会依次介绍各个进程的源码逻辑和 一些关键点机制。

各个业务进程或模块之间的逻辑关系可以参考《Openstack Swift简介》文中的架构图。

二、Proxy进程的业务处理

首先需要掌握基于PasteDeploy的堆栈式WSGI架构。根据PasteDeploy定义的各个层,可以很快理清配置文件定义的代码流程,从 middleware到server。找到最外层的middleware,即是业务的入口。对于proxy进程,可以简单给出业务时序图:

WSGI业务流程示意图

每一层的分工非常清晰,如在proxy进程默认配置文件中,最上层是做异常处理,所有的业务流程抛出的未处理的异常,在这里都将得到处理。

Proxy进程会分析请求的URI(account、container和object组成的资源路径)和请求方法(put、del等)来分析当前 请求的资源的具体类型,然后分贝找到控制该资源的controller,由controller来分发请求到具体的资源server。分发到原则是一致性 哈希环。一致性哈希环在系统初始化时由工具生成,在《Swift 和 Keystone单机安装总结》一文中有具体的操作步骤。

在《Openstack Swift简介》从理论上面介绍了具体的节点寻找过程。采用md5值加移位的方式来确定part,然后找到所有的虚拟节点。具体的代码为:

  1. container_partition, containers = self.app.container_ring.get_nodes(  
  2.  
  3.  self.account_name, self.container_name) 
  4. def get_nodes(self, account, container=None, obj=None):  
  5.  
  6.  """  
  7.  Get the partition and nodes  
  8. for an account/container/object.  
  9.  If a node is responsible  
  10. for more than one replica, it will  
  11.  only appear in the  
  12. output once. 
  13.  :param account: account name  
  14.  :param  
  15. container: container name  
  16.  :param obj: object name  
  17.  
  18.  :returns: a tuple of (partition, list of node dicts) 
  19.  Each node dict will have at least the following keys: 
  20.  ======  
  21. ===============================================================  
  22.  
  23.  id unique integer  
  24. identifier amongst devices  
  25.  weight a float of the  
  26. relative weight of this device as compared to  
  27.  
  28.  others;  
  29. this indicates how many partitions the builder will try  
  30.  
  31.  to assign  
  32. to this device  
  33.  zone integer indicating  
  34. which zone the device is in; a given  
  35.  
  36.  partition  
  37. will not be assigned to multiple devices within the  
  38.  
  39.  same zone  
  40.  
  41.  ip the ip address of the  
  42. device  
  43.  port the tcp port of the device  
  44.  
  45.  device the device's name on disk (sdb1, for  
  46. example)  
  47.  meta general use 'extra'  
  48. field; for example: the online date, the  
  49.  
  50.  hardware  
  51. description  
  52.  ======  
  53. ===============================================================  
  54.  
  55.  """  
  56.  part = self.get_part(account,  
  57. container, obj)  
  58.  return part,  
  59. self._get_part_nodes(part) 
  60. def get_part(self, account, container=None, obj=None):  
  61.   
  62. """  
  63.  Get the partition for an  
  64. account/container/object. 
  65.  :param account: account name  
  66.  :param  
  67. container: container name  
  68.  :param obj: object name  
  69.  
  70.  :returns: the partition number  
  71.  """  
  72.  
  73.  key = hash_path(account, container, obj, raw_digest=True)  
  74.  
  75.  if time() >; self._rtime:  
  76.  
  77.  self._reload()  
  78.  
  79.  part = struct.unpack_from('>;I', key)[0] >>  
  80. self._part_shift  
  81.  return part 
  82. def _get_part_nodes(self, part):  
  83.  part_nodes = []  
  84.  
  85.  seen_ids = set()  
  86.  for r2p2d in  
  87. self._replica2part2dev_id:  
  88.  if  
  89. part <; len(r2p2d):  
  90.  
  91.  dev_id =  
  92. r2p2d[part]  
  93.  
  94.  if dev_id  
  95. not in seen_ids:  
  96.  
  97.   
  98. part_nodes.append(self.devs[dev_id])  
  99.  
  100.   
  101. seen_ids.add(dev_id)  
  102.  return part_nodes 

然后根据quorum原则来决定当前请求至少需要几个节点成功即可返回。如NWR分别为322,则至少需要2个节点写成功,才能确保此次写成功。体现在公用的make_request方法中:

  1. def make_requests(self, req, ring, part, method, path, headers,  
  2.   
  3. query_string=''):  
  4.  """  
  5.  Sends an  
  6. HTTP request to multiple nodes and aggregates the results.  
  7.  
  8.  It attempts the primary nodes concurrently, then iterates  
  9. over the  
  10.  handoff nodes as needed. 
  11.  :param req: a request sent by the client  
  12.  
  13.  :param ring: the ring used for finding backend servers  
  14.  
  15.  :param part: the partition number  
  16.   
  17. :param method: the method to send to the backend  
  18.  :param  
  19. path: the path to send to the backend  
  20.  
  21.   
  22. (full path ends up being /<$device>/<$part>/<$path>)  
  23.  
  24.  :param headers: a list of dicts, where each dict  
  25. represents one  
  26.  
  27.   
  28. backend request that should be made.  
  29.  :param query_string:  
  30. optional query string to send to the backend  
  31.  :returns: a  
  32. swob.Response object  
  33.  """  
  34.   
  35. start_nodes = ring.get_part_nodes(part)  
  36.  nodes =  
  37. GreenthreadSafeIterator(self.app.iter_nodes(ring, part))  
  38.   
  39. pile = GreenAsyncPile(len(start_nodes))  
  40.  for head in  
  41. headers:  
  42.   
  43. pile.spawn(self._make_request, nodes, part, method, path,  
  44.  
  45.   
  46. head, query_string, self.app.logger.thread_locals)  
  47.   
  48. response = []  
  49.  statuses = []  
  50.  for  
  51. resp in pile:  
  52.  if not resp:  
  53.  
  54.  continue  
  55.  
  56.  response.append(resp)  
  57.  
  58.  statuses.append(resp[0])  
  59.  
  60.  if self.have_quorum(statuses,  
  61. len(start_nodes)):  
  62.  
  63.  break  
  64.  
  65.  # give any pending requests *some* chance to finish  
  66.  
  67.  pile.waitall(self.app.post_quorum_timeout)  
  68.  
  69.  while len(response) <; len(start_nodes):  
  70.  
  71.   
  72. response.append((HTTP_SERVICE_UNAVAILABLE, ''''''))  
  73.   
  74. statuses, reasons, resp_headers, bodies = zip(*response)  
  75.   
  76. return self.best_response(req, statuses, reasons, bodies,  
  77.  
  78.   
  79. '%s %s' % (self.server_type, req.method),  
  80.  
  81.   
  82. headers=resp_headers)
责任编辑:闫佳明 来源: http://shentar.me
相关推荐

2012-05-17 13:47:37

OpenStack架构

2012-07-12 16:00:32

OpenStackSwift架构

2016-11-25 13:14:50

Flume架构源码

2022-03-18 15:55:15

鸿蒙操作系统架构

2017-07-17 11:52:54

jQuery源码分析前端框架类库

2022-12-05 08:41:39

Redis调试环境源码

2011-08-01 09:15:25

惠普OpenStack云端建构

2015-01-12 14:55:36

2013-07-26 09:16:13

SwiftOpenStackSwiftStack

2011-08-01 13:32:22

惠普OpenStack云端架构

2013-04-18 09:29:03

OpenStack云管理平台Folsom平台

2018-05-17 22:45:05

2021-02-06 13:28:21

鸿蒙HarmonyOS应用开发

2016-11-04 21:46:46

UnderscoreJavascript

2009-06-24 14:25:13

JSF整体架构

2016-04-11 14:24:08

用户画像技术架构数据分析

2013-12-10 09:57:35

Openstack S开源云存储Openstack

2014-11-27 13:29:29

OpenStackSwift开源

2013-07-09 09:16:37

OpenStack企业业务模式私有云

2022-06-06 08:02:21

ahooks架构hooks
点赞
收藏

51CTO技术栈公众号