天天看點

CDN - API 類型問題排查

作者:張醫博

淺談

在調用阿裡雲 CDN openAPI,經常會出現各種各樣的問題排查起來沒有好的思路不好分析,今天說下基本的排查思路。

分析

不管調什麼 CDN openAPI,無論是控制台還是 SDK 或者使用者的腳本,出現問題都可以按照如下思路排查。

搜集資訊

1) 調用 CDN openAPI 一定會傳回一個阿裡雲的 requestID ,這個值是用來排查使用者完整的請求參數和傳回結果的憑證,如果用戶端調用 API 傳回結果不是預期或者請求失敗,都會傳回,務必保留。

2)如果沒有 requestID 傳回的情況下,需要使用者提供

  • 調用 openAPI 的名稱;
  • 傳回的異常結果全部截圖;
  • 調用的方式是 API 還是 SDK;
  • 什麼語言版本的。

問題分析

擷取 requestID 的情況。

1)先查 雲台,requestID 的請求過程記錄;

一站式查詢-》 ECS 全鍊路日志 -〉 輸入 requestID -》調整日志 查詢。

關注下列的資訊,檢視詳細的傳回錯誤内容,以及錯誤資訊、API 名稱;

CDN - API 類型問題排查

2)搜尋 API.文檔

搜尋對應的 API ,然後檢視 API 請求是否有特殊說明,舉例說明

類似如下是 API 調用文檔,我們先要明白 API 是做什麼的?然後了解使用上的限制?以及調用傳參的限制?

CDN - 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 帶寬怎麼區分,等等。

或者控制體台報錯的問題,通路控制異常問題,所有此類問題都可以按照如下排查;

CDN - API 類型問題排查

1) 首先浏覽器打開 F12。開發者模式。

2) 找到你要浏覽的頁面或者你點選的功能子產品,觸發調用。

3)然後分析你觸發調用請求的 URL 是什麼,大緻都可看出來的,并不複雜。比如我是查域名的流量,那就是 DescribeDomainBpsData.json 接口,也就是 CDN 的 openAPI ,因為即便是控制台也是調用後端 API 操作的。

4)然後看浏覽器對應的響應頭資訊,将這些頭和 API 對應的文檔 傳回參數說明對比就知道什麼含義了。

CDN - API 類型問題排查

某使用者回報調用直播導播台的 DescribeCasters 功能,并沒有傳回 CasterTemplate 字段。

首先找到這個 API 的解釋說明

https://help.aliyun.com/document_detail/60261.html?spm=5176.10695662.1996646101.searchclickresult.1f352a00rEBEV7
CDN - API 類型問題排查

通過這個 API 告訴了使用者兩個條件:

1)導播台分辨率配置,付費類型為預付費時必輸。那也就是非預付費的不一定會輸出。

2)使用者是否在直播的播流域名上配置了,轉碼如果沒有配置轉碼的話,一樣不會輸出 CasterTemplate 字段;