感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!
如果转载,请保留作者信息。
博客地址:http://blog.csdn.net/gaoxingnengjisuan
邮箱地址:dong.liu@siat.ac.cn
PS:最近没有登录博客,很多朋友的留言没有看见,这里道歉!还有就是本人较少上QQ,可以邮件交流。
概述:
上一篇博客中我们说过,请求信息到达swift-proxy服务之后,会确定获取具体的控制器(ObjectController、ContainerController、AccountController),接下来就是调用/swift/proxy/controllers/account.py或/swift/proxy/controllers/container.py或/swift/proxy/controllers/obj.py下面的PUT,POST,DELETE,GET,HEAD等方法;然后再在具体的方法中实现到具体存储服务(Object-server、Container-server、Account-server)的连接,继而调用其下具体的PUT,POST,DELETE,GET,HEAD等方法来进行请求req的实现;
这篇博客主要关注swift-proxy与swift-account服务中PUT,POST,DELETE,GET,HEAD等方法的对应调用实现;
源码解析部分(代码中较重要的部分已经进行了相关的注释):
GETorHEAD
/swift/proxy/controllers/account.py----class AccountController(Controller)----def GETorHEAD
def GETorHEAD(self, req):
"""
通过HTTP处理GET/HEAD请求;
"""
if len(self.account_name) > MAX_ACCOUNT_NAME_LENGTH:
resp = HTTPBadRequest(request=req)
resp.body = 'Account name length of %d longer than %d' % (len(self.account_name), MAX_ACCOUNT_NAME_LENGTH)
return resp
# get_nodes:为object获取分区号和所有副本节点信息;
partition, nodes = self.app.account_ring.get_nodes(self.account_name)
# GETorHEAD_base:HTTP GET或HEAD的基本处理;
# 返回swob.Response对象;
resp = self.GETorHEAD_base(
req, _('Account'), self.app.account_ring, partition,
req.swift_entity_path.rstrip('/'))
if resp.status_int == HTTP_NOT_FOUND:
if resp.headers.get('X-Account-Status', '').lower() == 'deleted':
resp.status = HTTP_GONE
elif self.app.account_autocreate:
resp = account_listing_response(self.account_name, req,
get_listing_content_type(req))
if req.environ.get('swift_owner'):
self.add_acls_from_sys_metadata(resp)
else:
for header in self.app.swift_owner_headers:
resp.headers.pop(header, None)
return resp
1.为object获取分区号和所有副本节点信息;2.HTTP GET或HEAD的基本处理,实现发送请求到远程对象服务上并调用具体方法来处理请求信息;
注:这里的具体实现就不详细解析了;
/swift/account/server.py----class AccountController(object)----def HEAD
def HEAD(self, req):
"""
处理HTTP协议的HEAD请求;
HEAD请求返回account的基本信息,并以key-value的形式保存在HTTPHEAD中返回;
"""
drive, part, account = split_and_validate_path(req, 3)
out_content_type = get_listing_content_type(req)
# mount_check是是否进行mount检查;
# 如果进行mount检查,并且检查结果没有挂载,则引发http 507错误,提示磁盘没有足够存储空间;
if self.mount_check and not check_mount(self.root, drive):
return HTTPInsufficientStorage(drive=drive, request=req)
# _get_account_broker是一个内部方法,功能是返回一个AccountBroker的实例,用于代理对sqlite数据库的操作;
broker = self._get_account_broker(drive, part, account,
pending_timeout=0.1,
stale_reads_ok=True)
if broker.is_deleted():
return self._deleted_response(broker, req, HTTPNotFound)
# get_info:为账户获取全局数据,获得account的基本信息;
# 返回包括account, created_at, put_timestamp, delete_timestamp, container_count, object_count, bytes_used, hash, id等值的字典;
info = broker.get_info()
headers = {
'X-Account-Container-Count': info['container_count'],
'X-Account-Object-Count': info['object_count'],
'X-Account-Bytes-Used': info['bytes_used'],
'X-Timestamp': info['created_at'],
'X-PUT-Timestamp': info['put_timestamp']}
headers.update((key, value)
for key, (value, timestamp) in
broker.metadata.iteritems() if value != '')
headers['Content-Type'] = out_content_type
return HTTPNoContent(request=req, headers=headers, charset='utf-8')
注:HEAD请求返回account的基本信息(元数据信息),并以key-value的形式保存在HTTPHEAD中返回;
/swift/account/server.py----class AccountController(object)----def GET
def GET(self, req):
"""
处理HTTP协议的GET请求;
GET同HEAD一样,都是请求返回account的基本信息,并以key-value的形式保存在HTTPHEAD当中;
不同之处在于GET方法中获取了指定account下的container列表,存储在body中,同HTTPHEAD一同返回;
"""
drive, part, account = split_and_validate_path(req, 3)
prefix = get_param(req, 'prefix')
delimiter = get_param(req, 'delimiter')
if delimiter and (len(delimiter) > 1 or ord(delimiter) > 254):
# delimiters can be made more flexible later
return HTTPPreconditionFailed(body='Bad delimiter')
limit = ACCOUNT_LISTING_LIMIT
given_limit = get_param(req, 'limit')
if given_limit and given_limit.isdigit():
limit = int(given_limit)
if limit > ACCOUNT_LISTING_LIMIT:
return HTTPPreconditionFailed(request=req, body='Maximum limit is %d' % ACCOUNT_LISTING_LIMIT)
marker = get_param(req, 'marker', '')
end_marker = get_param(req, 'end_marker')
out_content_type = get_listing_content_type(req)
# mount_check是是否进行mount检查;
# 如果进行mount检查,并且检查结果没有挂载,则引发http 507错误,提示磁盘没有足够存储空间;
if self.mount_check and not check_mount(self.root, drive):
return HTTPInsufficientStorage(drive=drive, request=req)
# _get_account_broker是一个内部方法,功能是返回一个AccountBroker的实例,用于代理对sqlite数据库的操作;
broker = self._get_account_broker(drive, part, account,
pending_timeout=0.1,
stale_reads_ok=True)
if broker.is_deleted():
return self._deleted_response(broker, req, HTTPNotFound)
# 获取指定account下的容器列表(包括具体容器的相关信息);
return account_listing_response(account, req, out_content_type, broker,
limit, marker, end_marker, prefix,
delimiter)
来看方法account_listing_response的具体实现:
def account_listing_response(account, req, response_content_type, broker=None,
limit='', marker='', end_marker='', prefix='',
delimiter=''):
"""
获取指定account下的容器列表(包括具体容器的相关信息);
"""
if broker is None:
broker = FakeAccountBroker()
info = broker.get_info()
resp_headers = {
'X-Account-Container-Count': info['container_count'],
'X-Account-Object-Count': info['object_count'],
'X-Account-Bytes-Used': info['bytes_used'],
'X-Timestamp': info['created_at'],
'X-PUT-Timestamp': info['put_timestamp']}
resp_headers.update((key, value)
for key, (value, timestamp) in
broker.metadata.iteritems() if value != '')
# 获取容器列表,每个容器信息包括(name, object_count, bytes_used, 0);
account_list = broker.list_containers_iter(limit, marker, end_marker,
prefix, delimiter)
if response_content_type == 'application/json':
data = []
for (name, object_count, bytes_used, is_subdir) in account_list:
if is_subdir:
data.append({'subdir': name})
else:
data.append({'name': name, 'count': object_count,
'bytes': bytes_used})
account_list = json.dumps(data)
elif response_content_type.endswith('/xml'):
output_list = ['<?xml version="1.0" encoding="UTF-8"?>',
'<account name=%s>' % saxutils.quoteattr(account)]
for (name, object_count, bytes_used, is_subdir) in account_list:
if is_subdir:
output_list.append(
'<subdir name=%s />' % saxutils.quoteattr(name))
else:
item = '<container><name>%s</name><count>%s</count>' \
'<bytes>%s</bytes></container>' % \
(saxutils.escape(name), object_count, bytes_used)
output_list.append(item)
output_list.append('</account>')
account_list = '\n'.join(output_list)
else:
if not account_list:
resp = HTTPNoContent(request=req, headers=resp_headers)
resp.content_type = response_content_type
resp.charset = 'utf-8'
return resp
account_list = '\n'.join(r[0] for r in account_list) + '\n'
ret = HTTPOk(body=account_list, request=req, headers=resp_headers)
ret.content_type = response_content_type
ret.charset = 'utf-8'
return ret
下一篇博客将继续swift-proxy与swift-account的分析工作。