aliyunsdkcore源码
一、目录结构
aliyunsdkcore
--acs_exception # 异常相关
--__init__.py
--error_code.py # 定义了error的id名
--error_msg.py # 定义了部分和error_code对应的描述信息
--error_type.py # 定义了四种错误类型:clint、server、throttling、unknown
--exceptions.py # 定义了ClintException、ServerException
--auth # 认证相关
--http # http连接相关
--__init__.py
--format_type.py # 定义可接受文档类型(xml、json、raw)及相关方法
--http_request.py # 定义HttpRequest
--http_response.py # 定义HttpResponse
--method_type.py # 定义了6种基本请求方法
--protocol_type.py # 定义http和https
--profile # 解析产品及endpoint的文件
--utils # 扩展的工具和方法
--__init__.py
--parameter_helper.py # 定义了获取uuid,时间等方法
--__init__.py
--client.py # 定义了ACSclient类
--endpoints.xml
--request.py # 定义了Acs请求类
二、源码简析
1、异常部分
aliyunsdkcore/acs_exception目录下主要定义了基本的Exception的ID、提示信息、类型和两个异常类(ClintException、ServerException)。
以下是ClintException的源码:
class ClientException(Exception):
def __init__(self, code, msg):
Exception.__init__(self)
self.__error_type = error_type.ERROR_TYPE_CLIENT # 设置为error_type中的ERROR_TYPE_CLIENT类型
self.message = msg # 初始化错误信息
self.error_code = code # 初始化错误代码
# 重写__str__(), x.__str__() <==> str(x)
def __str__(self):
return "%s %s" % (
self.error_code,
self.message,
)
# 定义属性设置、获取方法
def set_error_code(self, code):
self.error_code = code
def set_error_msg(self, msg):
self.message = msg
def get_error_type(self):
return self.__error_type
def get_error_code(self):
return self.error_code
def get_error_msg(self):
return self.message
ServerException类比ClientException多了http状态码、request_id等属性及相应方法,不做赘述。
2、Request.py
设置默认请求类型
_default_protocol_type = protocol_type.HTTP
def set_default_protocol_type(user_protocol_type):
global _default_protocol_type
# 设置默认请求类型,如果用户提供的参数不是http或https就抛出异常
if user_protocol_type == protocol_type.HTTP or user_protocol_type == protocol_type.HTTPS:
_default_protocol_type = user_protocol_type
else:
raise exceptions.ClientException(
error_code.SDK_INVALID_PARAMS,
"Invalid 'protocol_type', should be 'http' or 'https'"
)
请求对象
aliyunsdkcore/request.py中定义了四个类:
- AcsRequest——抽象类,封装了通用的属性及方法
- RpcRequest——继承AcsRequest,Rpc风格的请求对象
- RoaRequest——继承AcsRequest,Roa风格的请求对象
- OssRequest——继承AcsRequest,Oss风格的请求对象
- CommonRequest——继承了RpcRequest和RoaRequest
笔者阅码有限,目前只知道阿里云的aliyunsdkecs模块的Request对象基本继承自RpcRequest,aliyunsdkros模块的Request对象则继承自RoaRequest,这一点在两种产品的API风格中就可窥见一斑,至于OssRequest和CommonRequest还没见到过,若读者大大知道,还望不吝赐教,哈哈!
AcsRequest
__ init __(...)
AcsRequest 的__ init __(...) 的源码。
class AcsRequest:
"""
Acs request base class. This class wraps up common parameters for a request.
"""
__metaclass__ = abc.ABCMeta
def __init__(self, product, version=None,
action_name=None,
location_service_code=None,
location_endpoint_type='openAPI',
accept_format=None,
protocol_type=None,
method=None):
self._version = version # 版本
self._product = product # 产品名
self._action_name = action_name # 操作名
self._protocol_type = protocol_type # 协议类型
if self._protocol_type is None:
self._protocol_type = _default_protocol_type
self._accept_format = accept_format
self._params = {} # 参数 dict
self._method = method # 请求方法
self._header = {} # 请求头
self._body_params = {} # body体参数
self._uri_pattern = None# uri模板
self._uri_params = None # uri需要的参数
self._content = None # 内容
self._location_service_code = location_service_code
self._location_endpoint_type = location_endpoint_type
self.add_header('x-sdk-invoke-type', 'normal')
还有一些相关属性的设置、获取方法,以及三个抽象方法:
@abc.abstractmethod
def get_style(self):
pass
@abc.abstractmethod
def get_url(self, region_id, ak, secret):
pass
@abc.abstractmethod
def get_signed_header(self, region_id, ak, secret):
pass
3、client.py
此处定义了一个AcsClient类,__ init __(...)如下:
def __init__(
self,
ak=None, # 用户key
secret=None, # 用户secret
region_id="cn-hangzhou",# 地区id
auto_retry=True, # 连接失败自动重连
max_retry_time=3, # 最多重连次数
user_agent=None, # 用户代理
port=80, # 连接端口
timeout=DEFAULT_SDK_CONNECTION_TIMEOUT_IN_SECONDS, # 超时:默认10秒
public_key_id=None,
private_key=None,
session_period=3600, # session过期时间
credential=None, # 证书
debug=False): # 是否处于debug模式
self.__max_retry_num = max_retry_time
self.__auto_retry = auto_retry
self.__ak = ak
self.__secret = secret
self.__region_id = region_id
self.__user_agent = user_agent
self._port = port
self._location_service = LocationService(self, timeout=timeout)
self._timeout = timeout
# if true, do_action() will throw a ClientException that contains URL
self._url_test_flag = False
credential = {
'ak': ak,
'secret': secret,
'public_key_id': public_key_id,
'private_key': private_key,
'session_period': session_period,
'credential': credential,
}
self._signer = SignerFactory.get_signer(credential, region_id, self.implementation_of_do_action, debug)
get_response()
常用的一个方法是get_response()
def get_response(self, acs_request):
return self.implementation_of_do_action(acs_request) # 调用implementation_of_do_action()
def implementation_of_do_action(self, request, signer=None):
# 判断是否是AcsRequest,不是则抛出异常
if not isinstance(request, AcsRequest):
raise ClientException(
error_code.SDK_INVALID_REQUEST,
error_msg.get_msg('SDK_INVALID_REQUEST'))
# add core version
core_version = __import__('aliyunsdkcore').__version__
request.add_header('x-sdk-core-version', core_version)
# 如果是CommonRequest,则Requeste标记成相应风格
if isinstance(request, CommonRequest):
request.trans_to_acs_request()
# 设置接收端并创建Response
endpoint = self._resolve_endpoint(request)
http_response = self._make_http_response(endpoint, request, signer)
if self._url_test_flag:
raise ClientException("URLTestFlagIsSet", http_response.get_url())
# Do the actual network thing 请求
try:
status, headers, body = http_response.get_response_object() # 调用get_response_object
return status, headers, body
except IOError as e:
raise ClientException(
error_code.SDK_SERVER_UNREACHABLE,
error_msg.get_msg('SDK_SERVER_UNREACHABLE') + ': ' + str(e))
4、http
HTTPResponse
get_response_object()
上文中的get_response_object()是aliyunsdkcorehttphttp_response.py中HttpResponse的方法
def get_response_object(self):
# 根据是否提供ssl证书,调用使用相应协议的方法
if self.get_ssl_enabled():
return self.get_https_response_object()
else:
return self.get_http_response_object()
# http
def get_http_response(self):
# 端口 未指定则默认80
if self.__port is None or self.__port == "":
self.__port = 80
try:
# 创建HTTPConnection对象
self.__connection = httplib.HTTPConnection(
self.get_host(), self.__port, timeout=self._timeout)
# 连接
self.__connection.connect()
# 请求
self.__connection.request(
method=self.get_method(),
url=self.get_url(),
body=self.get_body(),
headers=self.get_headers())
# 接收
response = self.__connection.getresponse()
# 返回status,headers,body
return response.status, response.getheaders(), response.read()
finally:
# 关闭连接
self.__close_connection()
https则默认使用443端口,httplib.HTTPSConnection()创建连接对象。
do_action_with_exception()
另外常用的是do_action_with_exception(),是已经废弃的do_action()的升级版,自动解析response。
def do_action_with_exception(self, acs_request):
# set server response format as json, because thie function will
# parse the response so which format doesn't matter
acs_request.set_accept_format('JSON')
status, headers, body = self.implementation_of_do_action(acs_request)
request_id = None
# 尝试获取request
try:
body_obj = json.loads(body)
request_id = body_obj.get('RequestId')
except ValueError or TypeError or AttributeError:
# in case the response body is not a json string, return the raw
# data instead
pass
# 判断请求状态码,如果请求错误,指定请求ID、错误代码、错误信息,抛出异常
if status < httplib.OK or status >= httplib.MULTIPLE_CHOICES:
server_error_code, server_error_message = self._parse_error_info_from_response_body(
body)
raise ServerException(
server_error_code,
server_error_message,
http_status=status,
request_id=request_id)
return body
_parse_error_info_from_response_body()
AcsRequest还提供了一个解析错误信息的类方法:
@staticmethod
def _parse_error_info_from_response_body(response_body):
# 以json格式解析
try:
body_obj = json.loads(response_body)
if 'Code' in body_obj and 'Message' in body_obj:
return body_obj['Code'], body_obj['Message']
else:
return (
error_code.SDK_UNKNOWN_SERVER_ERROR,
error_msg.get_msg('SDK_UNKNOWN_SERVER_ERROR'))
# 不能解析则设置错误信息类型为SDK_UNKNOWN_SERVER_ERROR 未知错误
except ValueError:
# failed to parse body as json format
return (error_code.SDK_UNKNOWN_SERVER_ERROR,
error_msg.get_msg('SDK_UNKNOWN_SERVER_ERROR'))
以上大致就是简单的一些分析,其中auth部分和profile部分分别提供认证和请求目标点等服务,也不做具体分析了。
仅作学习之余的笔记,如有误点还望大神宥谅则个,并在评论区留下正解,不胜感激。