Azure SDK for Go 中的常見使用模式
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 原則,請使用實作 Policy
介面的方法定義您自己的結構Do
:
- 原則的
Do
方法應該視需要對傳入policy.Request
執行作業。 作業範例包括記錄、插入失敗或修改任何要求的URL、查詢參數或要求標頭。 - 方法
Do
會呼叫要求的Next
方法,將 (modified) 要求轉送至管線中的下一個原則。 Next
會傳http.Response
回和錯誤。 您的原則可以執行任何必要的作業,例如記錄回應/錯誤。- 您的原則必須傳回回應,並將錯誤傳回管線中的上一個原則。
注意
原則必須是 goroutine 安全。 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
作繼承自 Transporter 介面的方法 。 在這兩種情況下,您的函式或 Do
方法都會再次接收 azcore.Request
、傳回 azCore.Response
,並以與原則相同的順序執行動作。
如何在叫用 Azure 作業時刪除 JSON 欄位
傳送 JSON null
之類的JSON-MERGE-PATCH
作業,以指出應該刪除欄位(連同其值):
{
"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
欄位。