浅谈
在调用阿里云 CDN openAPI,经常会出现各种各样的问题排查起来没有好的思路不好分析,今天说下基本的排查思路。
分析
不管调什么 CDN openAPI,无论是控制台还是 SDK 或者用户的脚本,出现问题都可以按照如下思路排查。
搜集信息
1) 调用 CDN openAPI 一定会返回一个阿里云的 requestID ,这个值是用来排查用户完整的请求参数和返回结果的凭证,如果客户端调用 API 返回结果不是预期或者请求失败,都会返回,务必保留。
2)如果没有 requestID 返回的情况下,需要用户提供
- 调用 openAPI 的名称;
- 返回的异常结果全部截图;
- 调用的方式是 API 还是 SDK;
- 什么语言版本的。
问题分析
获取 requestID 的情况。
1)先查 云台,requestID 的请求过程记录;
一站式查询-》 ECS 全链路日志 -〉 输入 requestID -》调整日志 查询。
关注下列的信息,查看详细的返回错误内容,以及错误信息、API 名称;
2)搜索 API.文档
搜索对应的 API ,然后查看 API 请求是否有特殊说明,举例说明
类似如下是 API 调用文档,我们先要明白 API 是做什么的?然后了解使用上的限制?以及调用传参的限制?
对比用户的参数使用是否都是正确的,如果确保都是正确再向下排查。
3)调用测试
当文档和 requestID 都查不出来后,我们只能调用 openAPI 亲自测试一遍,这里提供了下载的测试脚本,可以参考。测试是请使用客户端的 AccesskeyID 和 SecretKey 测试,这样贴近用户环境测试结果更有效。
- 两个脚本放在一个目录下运行
aliyun.ini 脚本
[Credentials]
accesskeyid = <ak>
accesskeysecret = <sk>
can.py 生成鉴权 URL 脚本
#!/usr/bin/python
# -*- coding:utf-8 -*-
import sys,os
import urllib, urllib2
import base64
import hmac
import hashlib
from hashlib import sha1
import time
import uuid
import json
from optparse import OptionParser
import ConfigParser
import traceback
access_key_id = '';
access_key_secret = '';
cdn_server_address = 'https://cdn.aliyuncs.com'
CONFIGFILE = os.getcwd() + '/aliyun.ini'
CONFIGSECTION = 'Credentials'
cmdlist = '''
接口说明请参照pdf文档
'''
def percent_encode(str):
res = urllib.quote(str.decode(sys.stdin.encoding).encode('utf8'), '')
res = res.replace('+', '%20')
res = res.replace('*', '%2A')
res = res.replace('%7E', '~')
return res
def compute_signature(parameters, access_key_secret):
sortedParameters = sorted(parameters.items(), key=lambda parameters: parameters[0])
canonicalizedQueryString = ''
for (k,v) in sortedParameters:
canonicalizedQueryString += '&' + percent_encode(k) + '=' + percent_encode(v)
stringToSign = 'GET&%2F&' + percent_encode(canonicalizedQueryString[1:])
h = hmac.new(access_key_secret + "&", stringToSign, sha1)
signature = base64.encodestring(h.digest()).strip()
return signature
def compose_url(user_params):
timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
parameters = { \
'Format' : 'JSON', \
'Version' : '2014-11-11', \
'AccessKeyId' : access_key_id, \
'SignatureVersion' : '1.0', \
'SignatureMethod' : 'HMAC-SHA1', \
'SignatureNonce' : str(uuid.uuid1()), \
'TimeStamp' : timestamp, \
}
for key in user_params.keys():
parameters[key] = user_params[key]
signature = compute_signature(parameters, access_key_secret)
parameters['Signature'] = signature
url = cdn_server_address + "/?" + urllib.urlencode(parameters)
return url
def make_request(user_params, quiet=False):
url = compose_url(user_params)
print url
def configure_accesskeypair(args, options):
if options.accesskeyid is None or options.accesskeysecret is None:
print("config miss parameters, use --id=[accesskeyid] --secret=[accesskeysecret]")
sys.exit(1)
config = ConfigParser.RawConfigParser()
config.add_section(CONFIGSECTION)
config.set(CONFIGSECTION, 'accesskeyid', options.accesskeyid)
config.set(CONFIGSECTION, 'accesskeysecret', options.accesskeysecret)
cfgfile = open(CONFIGFILE, 'w+')
config.write(cfgfile)
cfgfile.close()
def setup_credentials():
config = ConfigParser.ConfigParser()
try:
config.read(CONFIGFILE)
global access_key_id
global access_key_secret
access_key_id = config.get(CONFIGSECTION, 'accesskeyid')
access_key_secret = config.get(CONFIGSECTION, 'accesskeysecret')
except Exception, e:
print traceback.format_exc()
print("can't get access key pair, use config --id=[accesskeyid] --secret=[accesskeysecret] to setup")
sys.exit(1)
if __name__ == '__main__':
parser = OptionParser("%s Action=action Param1=Value1 Param2=Value2\n" % sys.argv[0])
parser.add_option("-i", "--id", dest="accesskeyid", help="specify access key id")
parser.add_option("-s", "--secret", dest="accesskeysecret", help="specify access key secret")
(options, args) = parser.parse_args()
if len(args) < 1:
parser.print_help()
sys.exit(0)
if args[0] == 'help':
print cmdlist
sys.exit(0)
if args[0] != 'config':
setup_credentials()
else: #it's a configure id/secret command
configure_accesskeypair(args, options)
sys.exit(0)
user_params = {}
idx = 1
if not sys.argv[1].lower().startswith('action='):
user_params['action'] = sys.argv[1]
idx = 2
for arg in sys.argv[idx:]:
try:
key, value = arg.split('=')
user_params[key.strip()] = value
except ValueError, e:
print(e.read().strip())
raise SystemExit(e)
make_request(user_params)
4) 测试分析
如果发现按照 API 文档,生成的 API 调用 URL 没有问题,说明用户使用的 API 代码可能存在问题,或者传的参数和我们不一样。可以用生成的正常 URL 和异常的 URL 进行比对。
案例:
案例
有用户问控制台的各项指标什么含义,峰值 QPS 是哪个,总的 QPS 是哪个? HTTPS 的带宽和 HTTP 带宽怎么区分,等等。
或者控制体台报错的问题,访问控制异常问题,所有此类问题都可以按照如下排查;
分析
1) 首先浏览器打开 F12。开发者模式。
2) 找到你要浏览的页面或者你点击的功能模块,触发调用。
3)然后分析你触发调用请求的 URL 是什么,大致都可看出来的,并不复杂。比如我是查域名的流量,那就是 DescribeDomainBpsData.json 接口,也就是 CDN 的 openAPI ,因为即便是控制台也是调用后端 API 操作的。
4)然后看浏览器对应的响应头信息,将这些头和 API 对应的文档 返回参数说明对比就知道什么含义了。
案例
某用户反馈调用直播导播台的 DescribeCasters 功能,并没有返回 CasterTemplate 字段。
分析
首先找到这个 API 的解释说明
https://help.aliyun.com/document_detail/60261.html?spm=5176.10695662.1996646101.searchclickresult.1f352a00rEBEV7
通过这个 API 告诉了用户两个条件:
1)导播台分辨率配置,付费类型为预付费时必输。那也就是非预付费的不一定会输出。
2)用户是否在直播的播流域名上配置了,转码如果没有配置转码的话,一样不会输出 CasterTemplate 字段;