当前位置: 首页 > 科技观察

OpenStackSwift源码指南:整体业务架构及Proxy流程

时间:2023-03-15 16:48:13 科技观察

OpenStack源码分析网上已经很多了,每一部分的解读也很详细。在此我根据自己的理解记录下之前阅读的Swift源码的一些要点,希望能给有需要的同学带来一些帮助。1、Swift的整体框架如上图所示,以及Swift的源码目录结构。其中,proxy为前端业务接入流程。账号、容器、对象目录分别是账号、容器、对象的业务处理逻辑流程。common目录是一些常用的工具代码。比较重要的共同点是:哈希环的处理逻辑。下面依次介绍各个流程的源码逻辑和一些关键点机制。各个业务流程或者模块之间的逻辑关系,可以参考本文的架构图。2、Proxy进程的业务处理首先需要掌握基于PasteDeploy的栈式WSGI架构。根据PasteDeploy定义的每一层,可以快速梳理出配置文件定义的代码流,从中间件到服务端。找到最外层的中间件,也就是业务的入口。对于代理流程,可以简单给出一个业务时序图:每一层的分工都很明确。比如在proxy进程的默认配置文件中,最上层是异常处理。所有业务流程抛出的未处理异常都会在这里处理。Proxy进程会分析请求的URI(由account、container、object组成的资源路径)和请求方法(put、del等),分析出当前请求资源的具体类型,然后分贝找到controller控制资源的,controller会把Request分发给特定的资源服务器。分配原则是一致的哈希环。一致性哈希环是在系统初始化时由工具生成的,具体操作步骤在《Swift 和 Keystone单机安装总结》一文中有。在《Openstack Swift简介》中,从理论上介绍了具体的节点查找过程。用md5值加shift的方法确定部分,然后找到所有虚拟节点。具体的代码为:container_partition,containers=self.app.container_ring.get_nodes(self.account_name,self.container_name)defget_nodes(self,account,container=None,obj=None):"""Getthepartitionandnodesforanaccount/container/object.Ifanodeisresponsibleformorethanonereplica,它只会出现在输出中一次。:paramaccount:accountname:paramcontainer:containername:paramobj:objectname:returns:atupleof(partition,listofnodedicts)每个nodedict至少有以下键:===================================================================id唯一整数标识符在设备中的唯一整数标识符weight浮点数该设备的相对重量与其他设备相比;这表明构建器将尝试分配给该设备的许多分区zone整数指示设备所在的区域;给定的分区将不会分配给同一区域内的多个设备'snameondisk(sdb1,forexample)metageneraluse'extra'字段;例如:theonlinedate,硬件描述==========================================================================="""part=self.get_part(account,container,obj)returnpart,self._get_part_nodes(part)defget_part(self,account,container=None,obj=None):"""Getthepartitionforanaccount/container/object.:paramaccount:accountname:paramcontainer:containername:paramobj:objectname:returns:thepartitionnumber"""key=hash_path(account,容器,obj,raw_digest=True)iftime()>;self._rtime:self._reload()part=struct.unpack_from('>;I',key)[0]>>self._part_shiftreturnpartdef_get_part_nodes(self,part):part_nodes=[]seen_ids=set()forr2p2dinself._replica2part2dev_id:ifpart<;len(r2p2d):dev_id=r2p2d[part]ifdev_idnotinseen_ids:part_nodes.append(self.devs[dev_id])seen_ids.add(dev_id)returnpart_nodes和然后根据quorum原则判断当前请求至少有几个节点可以返回成功,比如NWR为322,至少需要有两个节点写入成功才能保证写入成功成功的。体现在公共的make_request方法中:defmake_requests(self,req,ring,part,method,path,headers,query_string=''):"""向多个节点发送HTTP请求并聚合结果。它同时尝试主节点,然后根据需要迭代切换节点。分区号:参数方法:发送到后端的方法:参数路径:发送到后端的路径(完整路径结束于/<$设备>/<$部分>/<$路径>):参数标题:字典列表,其中每个字典代表一个应该进行的后端请求。:参数查询字符串:可选的查询字符串发送到后端:返回:aswob.Responseobject“ring”=“”start_nodes.get_part_nodes(part)nodes=GreenthreadSafeIterator(self.app.iter_nodes(ring,part))pile=GreenAsyncPile(len(start_nodes))forheadinheaders:pile.spawn(self._make_request,nodes,part,method,path,head,query_string,self.app.logger.thread_locals)response=[]statuses=[]forrespinpile:ifnotresp:continueresponse.append(resp)statuses.append(resp[0])ifself.have_quorum(statuses,len(start_nodes)):break#giveanypendingrequests*some*chancetofinishpile.waitall(self.app.post_quorum_timeout)whilelen(response))<;len(start_nodes):response.append((HTTP_SERVICE_UNAVAILABLE,'','',''))statuses,reasons,resp_headers,bodies=zip(*response)returnsself.best_response(req,statuses,reasons,bodies,'%s%s'%(self.server_type,req.method),headers=resp_headers)