Vanliga användningsmönster i Azure SDK för Go
Azure Core-paketet (azcore
) i Azure SDK för Go implementerar flera mönster som tillämpas i hela SDK:t:
- HTTP-pipelineflödet, som är den underliggande HTTP-mekanismen som används av SDK:s klientbibliotek.
- Sidnumrering (metoder som returnerar samlingar).
- Långvariga åtgärder (LRO:er).
Sidnumrering (metoder som returnerar samlingar)
Många Azure-tjänster returnerar samlingar med objekt. Eftersom antalet objekt kan vara stort returnerar dessa klientmetoder en pager, vilket gör att appen kan bearbeta en sida med resultat i taget. Dessa typer definieras individuellt för olika kontexter men har gemensamma egenskaper, till exempel en NextPage
metod.
Anta till exempel att det finns en ListWidgets
metod som returnerar en WidgetPager
. Du använder sedan som WidgetPager
du ser här:
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...
}
Tidskrävande åtgärder
Vissa åtgärder i Azure kan ta lång tid att slutföra, allt från några sekunder till några dagar. Exempel på sådana åtgärder är att kopiera data från en käll-URL till en lagringsblob eller träna en AI-modell för att identifiera formulär. Dessa långvariga åtgärder (LRO: er) är dåligt lämpade för standard-HTTP-flödet för en relativt snabb begäran och ett relativt snabbt svar.
Enligt konvention är metoder som startar en LRO prefix med "Begin" och returnerar en Poller. Poller används för att regelbundet avsöka tjänsten tills åtgärden har slutförts.
Följande exempel illustrerar olika mönster för hantering av LRI:er. Du kan också lära dig mer från källkoden poller.go i SDK:et.
Blockera anrop till PollUntilDone
PollUntilDone
hanterar hela intervallet för en avsökningsåtgärd tills ett terminaltillstånd har nåtts. Den returnerar sedan det slutliga HTTP-svaret för avsökningsåtgärden med innehållet i nyttolasten i respType
gränssnittet.
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)
Anpassad avsökningsloop
Poll
skickar en avsökningsbegäran till avsökningsslutpunkten och returnerar svaret eller ett fel.
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)
Återuppta från en tidigare åtgärd
Extrahera och spara meritförteckningstoken från en befintlig Poller.
Om du vill återuppta avsökningen, kanske i en annan process eller på en annan dator, skapar du en ny PollerResponse
instans och initierar den genom att anropa dess Resume
metod och skicka den den tidigare sparade återställningstoken.
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-pipelineflöde
De olika SDK-klienterna tillhandahåller en abstraktion över azures REST API för att aktivera kodkomplettering och kompilering av tidstypsäkerhet så att du inte behöver hantera transportmekanik på lägre nivå via HTTP. Du kan dock anpassa transportmekaniken (till exempel återförsök och loggning).
SDK:t gör HTTP-begäranden via en HTTP-pipeline. Pipelinen beskriver sekvensen med steg som utförs för varje http-begärandesvar tur och retur.
Pipelinen består av en transport tillsammans med valfritt antal principer:
- Transporten skickar begäran till tjänsten och tar emot svaret.
- Varje princip slutför en specifik åtgärd i pipelinen.
Följande diagram illustrerar flödet i en pipeline:
Alla klientpaket delar ett Core-paket med namnet azcore
. Det här paketet konstruerar HTTP-pipelinen med sin ordnade uppsättning principer, vilket säkerställer att alla klientpaket fungerar konsekvent.
När en HTTP-begäran skickas körs alla principer i den ordning de lades till i pipelinen innan begäran skickas till HTTP-slutpunkten. Dessa principer lägger vanligtvis till begärandehuvuden eller loggar den utgående HTTP-begäran.
När Azure-tjänsten har svarat körs alla principer i omvänd ordning innan svaret återgår till koden. De flesta principer ignorerar svaret, men loggningsprincipen registrerar svaret. Återförsöksprincipen kan skicka begäran igen, vilket gör din app mer motståndskraftig mot nätverksfel.
Varje princip tillhandahålls med nödvändiga begärande- eller svarsdata, tillsammans med alla nödvändiga kontexter för att köra principen. Principen slutför sin åtgärd med angivna data och skickar sedan kontroll till nästa princip i pipelinen.
Som standard skapar varje klientpaket en pipeline som är konfigurerad för att fungera med den specifika Azure-tjänsten. Du kan också definiera dina egna anpassade principer och infoga dem i HTTP-pipelinen när du skapar en klient.
Grundläggande HTTP-pipelineprinciper
Core-paketet innehåller tre HTTP-principer som ingår i varje pipeline:
Anpassade HTTP-pipelineprinciper
Du kan definiera din egen anpassade princip för att lägga till funktioner utöver innehållet i Core-paketet. Om du till exempel vill se hur din app hanterar nätverks- eller tjänstfel kan du skapa en princip som matar in fel när begäranden görs under testningen. Eller så kan du skapa en princip som hånar en tjänsts beteende för testning.
Om du vill skapa en anpassad HTTP-princip definierar du din egen struktur med en Do
metod som implementerar Policy
gränssnittet:
- Principens
Do
metod bör utföra åtgärder efter behov på inkommandepolicy.Request
. Exempel på åtgärder är loggning, inmatning av ett fel eller ändring av någon av begärans URL,frågeparametrar eller begärandehuvuden. - Metoden
Do
vidarebefordrar (ändrad) begäran till nästa princip i pipelinen genom att anropa metoden för begäranNext
. Next
http.Response
returnerar felet och . Din princip kan utföra alla nödvändiga åtgärder, som att logga svaret/felet.- Principen måste returnera ett svar och ett fel tillbaka till den tidigare principen i pipelinen.
Kommentar
Politiken måste vara goroutine-säker. Goroutine-säkerhet gör att flera goroutines kan komma åt ett enda klientobjekt samtidigt. Det är vanligt att en princip är oföränderlig när den har skapats. Denna oföränderlighet säkerställer att goroutinen är säker.
Mall för anpassad princip
Följande kod kan användas som utgångspunkt för att definiera en anpassad princip.
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
}
Anpassad HTTP-transport
En transport skickar en HTTP-begäran och returnerar svaret/felet. Den första principen som hanterar begäran är också den sista principen som hanterar svaret innan svaret/felet returneras till pipelinens principer (i omvänd ordning). Den sista principen i pipelinen anropar transporten.
Som standard använder klienterna det delade http.Client
från Gos standardbibliotek.
Du skapar en anpassad tillståndskänslig eller tillståndslös transport på samma sätt som du skapar en anpassad princip. I det tillståndskänsliga fallet implementerar du metoden Do
som ärvts från transportörgränssnittet . I båda fallen tar funktionen eller Do
metoden återigen emot en azcore.Request
, returnerar en azCore.Response
och utför åtgärder i samma ordning som en princip.
Ta bort ett JSON-fält när du anropar en Azure-åtgärd
Åtgärder som JSON-MERGE-PATCH
att skicka en JSON null
för att ange att ett fält ska tas bort (tillsammans med dess värde):
{
"delete-me": null
}
Det här beteendet står i konflikt med SDK:ts standardmarsering som anger omitempty
som ett sätt att lösa tvetydigheten mellan ett fält som ska undantas och dess nollvärde.
type Widget struct {
Name *string `json:",omitempty"`
Count *int `json:",omitempty"`
}
I föregående exempel Name
definieras och Count
som pekare-till-typ för att skilja mellan ett saknat värde (nil
) och ett nollvärde (0), vilket kan ha semantiska skillnader.
I en HTTP PATCH-åtgärd påverkar inte fält med värdet nil
värdet i serverns resurs. När du uppdaterar en widgets Count
fält anger du det nya värdet för Count
och lämnar Name
som nil
.
För att uppfylla kravet på att skicka en JSON null
NullValue
används funktionen:
w := Widget{
Count: azcore.NullValue(0).(*int),
}
Den här koden anger Count
till en explicit JSON null
. När begäran skickas till servern tas resursens Count
fält bort.