Azure SDK for Go 中的 Azure Core (azcore) 套件會實作在 SDK 中套用的數種模式:
- HTTP 管線流程,這是 SDK 用戶端連結庫所使用的基礎 HTTP 機制。
- 分頁(傳回集合的函式)。
- 長時間執行的作業 (LROs)。
分頁(返回集合的方式)
許多 Azure 服務都會傳回項目的集合。 由於項目數目可能很大,這些用戶端方法會傳回 Pager,這可讓您的應用程式一次處理一頁的結果。 這些類型會針對各種內容個別定義,但共用一般特性,例如 NextPage 方法。
例如,假設有一個 ListWidgets 方法會傳回 WidgetPager。 您接著會使用 WidgetPager ,如下所示:
func (c *WidgetClient) ListWidgets(options *ListWidgetOptions) WidgetPager {
// ...
}
pager := client.ListWidgets(options)
for pager.NextPage(ctx) {
for _, w := range pager.PageResponse().Widgets {
process(w)
}
}
if pager.Err() != nil {
// Handle error...
}
長時間執行的作業
Azure 上的某些作業可能需要很長的時間才能完成,從幾秒到幾天。 這類作業的範例包括將數據從來源 URL 複製到記憶體 Blob,或訓練 AI 模型來辨識表單。 這些 長時間執行的作業 (LRO) 不適合相對快速的要求和回應的標準 HTTP 流程。
依照慣例,啟動 LRO 的方法前面會加上 「Begin」,並傳回 Poller。 輪詢器用來定期輪詢服務,直到作業完成為止。
下列範例說明處理 LRO 的各種模式。 您也可以從 SDK 中的 poller.go 原始程式碼深入瞭解。
封鎖對 PollUntilDone 的呼叫
PollUntilDone 處理輪詢作業的整個過程,直到達到最終狀態為止。 然後,它會返回輪詢操作的最終 HTTP 回應,其中包含 respType 接口中承載的內容。
resp, err := client.BeginCreate(context.Background(), "blue_widget", nil)
if err != nil {
// Handle error...
}
w, err = resp.PollUntilDone(context.Background(), nil)
if err != nil {
// Handle error...
}
process(w)
自定義輪詢迴圈
Poll 會將輪詢要求傳送至輪詢端點,並傳回回應或錯誤。
resp, err := client.BeginCreate(context.Background(), "green_widget")
if err != nil {
// Handle error...
}
poller := resp.Poller
for {
resp, err := poller.Poll(context.Background())
if err != nil {
// Handle error...
}
if poller.Done() {
break
}
// Do other work while waiting.
}
w, err := poller.FinalResponse(ctx)
if err != nil {
// Handle error...
}
process(w)
從先前的操作繼續
從現有的Poller擷取並儲存繼續令牌。
若要繼續輪詢,可能需要在另一個進程或另一部計算機上,建立新的 PollerResponse 實例,然後通過呼叫其 Resume 方法來初始化它,並傳遞先前儲存的繼續令牌。
poller := resp.Poller
tk, err := poller.ResumeToken()
if err != nil {
// Handle error...
}
resp = WidgetPollerResponse()
// Resume takes the resume token as an argument.
err := resp.Resume(tk, ...)
if err != nil {
// Handle error...
}
for {
resp, err := poller.Poll(context.Background())
if err != nil {
// Handle error...
}
if poller.Done() {
break
}
// Do other work while waiting.
}
w, err := poller.FinalResponse(ctx)
if err != nil {
// Handle error...
}
process(w)
HTTP 管線流程
各種 SDK 用戶端透過 Azure 的 REST API 提供抽象概念,以啟用程式代碼完成和編譯時間類型安全性,因此您不需要透過 HTTP 處理較低層級的傳輸機制。 不過,您可以 自定義 傳輸機制(例如重試和記錄)。
SDK 會透過 HTTP 管線提出 HTTP 要求。 管線描述針對每個 HTTP 要求-回應來回行程所執行的步驟順序。
管線是由傳輸機制與任意多的策略所組成:
- 傳輸會將要求傳送至服務,並接收回應。
- 每個 策略 都會完成管線中的特定動作。
下圖說明管線的流程:
所有用戶端套件都會共用名為 azcore套件。 此套件會使用其已排序的原則集來建構 HTTP 管線,以確保所有用戶端套件的行為一致。
傳送 HTTP 要求時,所有原則都會按照新增至管線的順序執行,再將要求傳送至 HTTP 端點。 這些原則通常會新增要求標頭或記錄傳出 HTTP 要求。
在 Azure 服務回應之後,所有原則都會以反向順序執行,回應才會傳回您的程式代碼。 大部分的原則都會忽略回應,但記錄原則會記錄回應。 重試策略可能會重新發送請求,使您的應用程式在面對網路故障時更具韌性。
每個原則都會提供所需的要求或響應數據,以及執行原則的任何必要內容。 原則會使用指定的數據完成其作業,然後將控制權傳遞至管線中的下一個原則。
根據預設,每個用戶端套件都會建立一個設定為使用該特定 Azure 服務的管線。 您也可以定義自己的 自定義原則 ,並在建立用戶端時將它們插入 HTTP 管線中。
核心 HTTP 管線原則
核心套件提供三個屬於每個管線一部分的 HTTP 原則:
自定義 HTTP 管線原則
您可以定義自己的自定義原則,以新增核心套件內容以外的功能。 例如,若要查看您的應用程式如何處理網路或服務故障,您可以建立策略,在測試期間請求時故意引入故障。 或者,您可以建立模擬服務行為以進行測試的策略。
若要建立自定義 HTTP 原則,請使用實作 Do 介面的方法定義您自己的結構Policy:
- 您的政策的
Do方法應該視需要對傳入的policy.Request執行作業。 操作範例包括記錄、故障注入,或修改任何要求的URL、查詢參數或請求標頭。 - 方法
Do會呼叫要求的Next方法,將修改後的要求遞送至管線中的下一個策略。 -
Next回傳http.Response和錯誤。 您的原則可以執行任何必要的作業,例如記錄回應/錯誤。 - 您的原則必須將回應和錯誤傳回到管線中的上一個原則。
備註
策略必須是 goroutine-safe。 Goroutine 安全性可讓多個 goroutine 同時存取單一客戶端物件。 建立之後,原則通常會不可變。 這個不變性可確保 goroutine 是安全的。
自定義原則範本
下列程式代碼可作為定義自定義原則的起點。
type MyPolicy struct {
LogPrefix string
}
func (m *MyPolicy) Do(req *policy.Request) (*http.Response, error) {
// Mutate/process request.
start := time.Now()
// Forward the request to the next policy in the pipeline.
res, err := req.Next()
// Mutate/process response.
// Return the response & error back to the previous policy in the pipeline.
record := struct {
Policy string
URL string
Duration time.Duration
}{
Policy: "MyPolicy",
URL: req.Raw().URL.RequestURI(),
Duration: time.Duration(time.Since(start).Milliseconds()),
}
b, _ := json.Marshal(record)
log.Printf("%s %s\n", m.LogPrefix, b)
return res, err
}
func ListResourcesWithPolicy(subscriptionID string) error {
cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
return err
}
mp := &MyPolicy{
LogPrefix: "[MyPolicy]",
}
options := &arm.ConnectionOptions{}
options.PerCallPolicies = []policy.Policy{mp}
options.Retry = policy.RetryOptions{
RetryDelay: 20 * time.Millisecond,
}
con := arm.NewDefaultConnection(cred, options)
if err != nil {
return err
}
client := armresources.NewResourcesClient(con, subscriptionID)
pager := client.List(nil)
for pager.NextPage(context.Background()) {
if err := pager.Err(); err != nil {
log.Fatalf("failed to advance page: %v", err)
}
for _, r := range pager.PageResponse().ResourceListResult.Value {
printJSON(r)
}
}
return nil
}
自訂 HTTP 傳輸
傳輸會傳送 HTTP 要求,並傳回其回應/錯誤。 處理要求的第一個原則也是最後一個處理響應的原則,再將回應/錯誤傳回管線的原則(反向順序)。 管線中的最後一個原則會叫用傳輸。
根據預設,用戶端會共用 Go 的標準函式庫的http.Client。
您可以使用建立自訂原則的相同方式建立自定義具狀態或無狀態傳輸。 在具狀態的情況下,您會實作從 Do 介面繼承的方法。 在這兩種情況下,您的函式或 Do 方法都會再次接收 azcore.Request、傳回 azCore.Response,並以與原則相同的順序執行動作。
如何在叫用 Azure 作業時刪除 JSON 欄位
像 JSON-MERGE-PATCH 一類的作業會傳送 JSON null 以表示應該刪除某字段(連同其值):
{
"delete-me": null
}
此行為會與 SDK 的預設封送處理衝突,指定 omitempty 用來解決要排除的欄位與其零值之間的模棱兩可。
type Widget struct {
Name *string `json:",omitempty"`
Count *int `json:",omitempty"`
}
在上面的範例中,Name 和 Count 被定義為指向型別的指標,以區分遺漏值 (nil) 和零值 (0),兩者可能在語意上有所不同。
在 HTTP PATCH 作業中,任何具有 值的 nil 欄位都不會影響伺服器資源中的值。 更新 Widget 的 Count 欄位時,請指定 的新值 Count,保留 Name 為 nil。
若要滿足傳送 JSON null的需求,會使用 函 NullValue 式:
w := Widget{
Count: azcore.NullValue(0).(*int),
}
此程式代碼會設定 Count 為明確的 JSON null。 當要求傳送至伺服器時,會刪除資源的 Count 欄位。