Share via


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:

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) lämpar sig inte bra 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 det respType angivna 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 klienterna tillhandahåller en abstraktion över en Azure-tjänsts HTTP-API för att möjliggöra kodkomplettering och kompilering av tidstypsäkerhet. Så du behöver inte ta itu med transportmekanik på lägre nivå. Men du kan 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.

Det här diagrammet illustrerar flödet i en pipeline:

Diagram that shows the flow of a 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 utfärda 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 vad som ingår 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:

  1. Principens Do metod bör utföra åtgärder efter behov på inkommande policy.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.
  2. Metoden Do vidarebefordrar (ändrad) begäran till nästa princip i pipelinen genom att anropa metoden för begäran Next .
  3. Nexthttp.Response returnerar felet och . Din princip kan utföra alla nödvändiga åtgärder, som att logga svaret/felet.
  4. 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.

I följande avsnitt visas hur du definierar en anpassad princip.

Principmall

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. Transporten anropas av den sista principen i pipelinen. Det är den första principen som hanterar svaret innan svaret/felet returneras till pipelinens principer (i omvänd ordning).

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.Responseoch 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 vars värde är nil värdet i serverns resurs. När du uppdaterar en widgets Count fält anger du det nya värdet för Countoch lämnar Name som nil.

För att uppfylla kravet på att skicka en JSON nullNullValue 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.

Se även