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

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

一、Swift的整体框架图

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

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

二、Proxy进程的业务处理

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

每一层的分工非常清晰,如在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. self.account_name,self.container_name)
  3. defget_nodes(self,account,container=None,obj=None):
  4. """
  5. Getthepartitionandnodes
  6. foranaccount/container/object.
  7. Ifanodeisresponsible
  8. formorethanonereplica,itwill
  9. onlyappearinthe
  10. outputonce.
  11. :paramaccount:accountname
  12. :param
  13. container:containername
  14. :paramobj:objectname
  15. :returns:atupleof(partition,listofnodedicts)
  16. Eachnodedictwillhaveatleastthefollowingkeys:
  17. ======
  18. ===============================================================
  19. iduniqueinteger
  20. identifieramongstdevices
  21. weightafloatofthe
  22. relativeweightofthisdeviceascomparedto
  23. others;
  24. thisindicateshowmanypartitionsthebuilderwilltry
  25. toassign
  26. tothisdevice
  27. zoneintegerindicating
  28. whichzonethedeviceisin;agiven
  29. partition
  30. willnotbeassignedtomultipledeviceswithinthe
  31. samezone
  32. iptheipaddressofthe
  33. device
  34. portthetcpportofthedevice
  35. devicethedevice'snameondisk(sdb1,for
  36. example)
  37. metageneraluse'extra'
  38. field;forexample:theonlinedate,the
  39. hardware
  40. description
  41. ======
  42. ===============================================================
  43. """
  44. part=self.get_part(account,
  45. container,obj)
  46. returnpart,
  47. self._get_part_nodes(part)
  48. defget_part(self,obj=None):
  49. """
  50. Getthepartitionforan
  51. account/container/object.
  52. :paramaccount:accountname
  53. :param
  54. container:containername
  55. :paramobj:objectname
  56. :returns:thepartitionnumber
  57. """
  58. key=hash_path(account,container,obj,raw_digest=True)
  59. iftime()>;self._rtime:
  60. self._reload()
  61. part=struct.unpack_from('>;I',key)[0]>>
  62. self._part_shift
  63. returnpart
  64. def_get_part_nodes(self,part):
  65. part_nodes=[]
  66. seen_ids=set()
  67. forr2p2din
  68. self._replica2part2dev_id:
  69. if
  70. part<;len(r2p2d):
  71. dev_id=
  72. r2p2d[part]
  73. ifdev_id
  74. notinseen_ids:
  75. part_nodes.append(self.devs[dev_id])
  76. seen_ids.add(dev_id)
  77. returnpart_nodes

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

  
  
  • defmake_requests(self,req,ring,part,method,path,headers,
  • query_string=''):
  • """
  • Sendsan
  • HTTPrequesttomultiplenodesandaggregatestheresults.
  • Itattemptstheprimarynodesconcurrently,theniterates
  • overthe
  • handoffnodesasneeded.
  • :paramreq:arequestsentbytheclient
  • :paramring:theringusedforfindingbackendservers
  • :parampart:thepartitionnumber
  • :parammethod:themethodtosendtothebackend
  • :param
  • path:thepathtosendtothebackend
  • (fullpathendsupbeing/<$device>/<$part>/<$path>)
  • :paramheaders:alistofdicts,whereeachdict
  • representsone
  • backendrequestthatshouldbemade.
  • :paramquery_string:
  • optionalquerystringtosendtothebackend
  • :returns:a
  • swob.Responseobject
  • """
  • start_nodes=ring.get_part_nodes(part)
  • nodes=
  • GreenthreadSafeIterator(self.app.iter_nodes(ring,part))
  • pile=GreenAsyncPile(len(start_nodes))
  • forheadin
  • headers:
  • pile.spawn(self._make_request,nodes,
  • head,query_string,self.app.logger.thread_locals)
  • response=[]
  • statuses=[]
  • for
  • respinpile:
  • ifnotresp:
  • continue
  • response.append(resp)
  • statuses.append(resp[0])
  • ifself.have_quorum(statuses,
  • len(start_nodes)):
  • break
  • #giveanypendingrequests*some*chancetofinish
  • pile.waitall(self.app.post_quorum_timeout)
  • whilelen(response)<;len(start_nodes):
  • response.append((HTTP_SERVICE_UNAVAILABLE,'','',''))
  • statuses,reasons,resp_headers,bodies=zip(*response)
  • returnself.best_response(req,statuses,bodies,
  • '%s%s'%(self.server_type,req.method),
  • headers=resp_headers)


  • 原文链接:http://mobile.51cto.com/hot-448613.htm

    相关文章

    软件简介:蓝湖辅助工具,减少移动端开发中控件属性的复制和粘...
    现实生活中,我们听到的声音都是时间连续的,我们称为这种信...
    前言最近在B站上看到一个漂亮的仙女姐姐跳舞视频,循环看了亿...
    【Android App】实战项目之仿抖音的短视频分享App(附源码和...
    前言这一篇博客应该是我花时间最多的一次了,从2022年1月底至...
    因为我既对接过session、cookie,也对接过JWT,今年因为工作...