作者:張醫博
淺談
在調用阿裡雲 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 字段;