Algemene gebruikspatronen in Azure SDK voor Go

Het pakket Azure Core ( azcore ) in Azure SDK voor Go implementeert verschillende patronen die overal in de SDK worden toegepast:

Paginering (methoden die verzamelingen retourneren)

Veel Azure-services retourneren verzamelingen items. Omdat het aantal items groot kan zijn, retourneren deze clientmethoden een Pager, waarmee uw app één pagina met resultaten tegelijk kan verwerken. Deze typen worden afzonderlijk gedefinieerd voor verschillende contexten, maar delen gemeenschappelijke kenmerken, zoals een NextPage methode.

Stel bijvoorbeeld dat er een methode is ListWidgets die een retourneert. WidgetPager Vervolgens gebruikt u de zoals WidgetPager hier wordt weergegeven:

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...
}

Zie het SDK-bronbestand zz_generated_pagers.go voor een voorbeeld van een Pager-implementatie.

Langlopende bewerkingen

Sommige bewerkingen in Azure kunnen lang duren, van een paar seconden tot enkele dagen. Voorbeelden van dergelijke bewerkingen zijn het kopiëren van gegevens van een bron-URL naar een opslagblob of het trainen van een AI-model om formulieren te herkennen. Deze langlopende bewerkingen (LRE's) zijn niet geschikt voor de standaard HTTP-stroom van een relatief snelle aanvraag en reactie.

Methoden die een LRO starten, worden volgens de conventies vooraf laten gaan door 'Begin' en retourneren een Poller. Poller wordt gebruikt om de service periodiek te peilen totdat de bewerking is uitgevoerd.

In de volgende voorbeelden ziet u verschillende patronen voor het afhandelen van LRO's. U kunt ook meer informatie vinden in de broncode poller.go in de SDK.

Aanroep naar PollUntilDone blokkeren

PollUntilDone verwerkt de hele periode van een pollingbewerking totdat een terminaltoestand is bereikt. Vervolgens wordt het uiteindelijke HTTP-antwoord voor de pollingbewerking met de inhoud van de nettolading in de respType opgegeven interface retourneert.

resp, err := client.BeginCreate(context.Background(), "blue_widget", nil)

if err != nil {
    // Handle error...
}

// Second argument is the polling interval if the endpoint doesn't send a Retry-After header.
w, err = resp.PollUntilDone(context.Background(), 5*time.Second)

if err != nil {
    // Handle error...
}

process(w)

Aangepaste poll-lus

Poll verzendt een polling-aanvraag naar het polling-eindpunt en retourneert het antwoord of een fout.

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)

Hervatten na een vorige bewerking

Extraheren en opslaan van het hervatten van het token uit een bestaande Poller.

Als u polling wilt hervatten, mogelijk in een ander proces of op een andere computer, maakt u een nieuw exemplaar en initialiseert u het door de methode aan te roepen en het eerder opgeslagen PollerResponseResume hervat-token door te geven.

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-pijplijnstroom

De verschillende clients bieden een abstractie via de HTTP-API van een Azure-service om de code te voltooien en de veiligheid van het type compilatietijd mogelijk te maken. U hebt dus geen te maken met transportmechanica op lager niveau. Maar u kunt de transportmechanica aanpassen (zoals nieuwe proberen en logboekregistratie).

De SDK doet HTTP-aanvragen via een HTTP-pijplijn. De pijplijn beschrijft de volgorde van de stappen die worden uitgevoerd voor elke retour met HTTP-aanvraag-antwoord.

De pijplijn bestaat uit een transport samen met een aantal beleidsregels:

  • Het transport verzendt de aanvraag naar de service en ontvangt het antwoord.
  • Elk beleid voltooit een specifieke actie in de pijplijn.

Dit diagram illustreert de stroom van een pijplijn:

Diagram met de stroom van een pijplijn.

Alle clientpakketten delen een Core-pakket met de naam . Met dit pakket wordt de HTTP-pijplijn samengesteld met de geordende set beleidsregels, zodat alle clientpakketten consistent werken.

  • Wanneer een HTTP-aanvraag wordt verzonden, worden alle beleidsregels uitgevoerd in de volgorde waarin ze zijn toegevoegd aan de pijplijn voordat de aanvraag naar het HTTP-eindpunt wordt verzonden. Dit beleid voegt meestal aanvraagheaders toe of logboeken voor de uitgaande HTTP-aanvraag.
  • Nadat de Azure-service heeft gereageerd, worden alle beleidsregels in omgekeerde volgorde uitgevoerd voordat het antwoord terugkeert naar uw code. De meeste beleidsregels negeren het antwoord, maar het logboekregistratiebeleid registreert het antwoord. Het beleid voor opnieuw proberen kan de aanvraag opnieuw uitgeven, waardoor uw app beter bestand is tegen netwerkfouten.

Elk beleid wordt geleverd met de benodigde aanvraag- of antwoordgegevens, samen met eventuele benodigde context voor het uitvoeren van het beleid. Het beleid voltooit de bewerking met de opgegeven gegevens en geeft vervolgens het besturingselement door aan het volgende beleid in de pijplijn.

Standaard maakt elk clientpakket een pijplijn die is geconfigureerd voor gebruik met die specifieke Azure-service. U kunt ook uw eigen aangepaste beleidsregels definiëren en deze invoegen in de HTTP-pijplijn wanneer u een client maakt.

Beleidsregels voor HTTP-kernpijplijnen

Het Core-pakket biedt drie HTTP-beleidsregels die deel uitmaken van elke pijplijn:

Aangepast beleid voor HTTP-pijplijnen

U kunt uw eigen aangepaste beleid definiëren om mogelijkheden toe te voegen die verder gaan dan wat is opgenomen in het Core-pakket. Als u bijvoorbeeld wilt zien hoe uw app omgaat met netwerk- of servicefouten, kunt u een beleid maken dat fouten veroorzaakt wanneer er tijdens het testen aanvragen worden gedaan. U kunt ook een beleid maken dat het gedrag van een service bespott voor testen.

Als u een aangepast HTTP-beleid wilt maken, definieert u uw eigen structuur met Do een methode waarmee de interface wordt Policy geïmplementeerd:

  1. De -methode van Do uw beleid moet zo nodig bewerkingen uitvoeren op de binnenkomende policy.Request . Voorbeelden van bewerkingen zijn logboekregistratie, het injecteren van een fout of het wijzigen van een van de aanvraag-URL's, queryparameters of aanvraagheaders.
  2. Met Do de methode wordt de aanvraag (gewijzigd) doorgestuurd naar het volgende beleid in de pijplijn door de methode van de aanvraag aan te Next roepen.
  3. Next retourneert http.Response de en een fout. Met uw beleid kan elke noodzakelijke bewerking worden uitgevoerd, zoals het vastleggen van het antwoord/de fout.
  4. Uw beleid moet een antwoord en een fout retourneren naar het vorige beleid in de pijplijn.

Notitie

Beleidsregels moeten goroutine-safe zijn. Met goroutine-veiligheid hebben meerdere goroutines gelijktijdig toegang tot één clientobject. Het is gebruikelijk dat een beleid onveranderbaar is nadat het is gemaakt. Deze onveranderbaarheid zorgt ervoor dat de goroutine veilig is.

In de volgende sectie wordt gedemonstreerd hoe u een aangepast beleid definieert.

Beleidssjabloon

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
}

Aangepast HTTP-transport

Een transport verzendt een HTTP-aanvraag en retourneert de reactie/fout. Het transport wordt aangeroepen door het laatste beleid in de pijplijn. Dit is het eerste beleid dat het antwoord af handelen voordat het antwoord/de fout wordt teruggedraaid naar het beleid van de pijplijn (in omgekeerde volgorde).

Clients gebruiken standaard de gedeelde uit http.Client de standaardbibliotheek van Go.

U maakt een aangepaste stateful of staatloze transport op dezelfde manier als u een aangepast beleid maakt. In stateful gevallen implementeert u de methode Do die is overgenomen van de Do In beide gevallen ontvangt uw functie of methode opnieuw een , retourneert een en voert acties uit in dezelfde Doazcore.Request volgorde als een azCore.Response beleid.

Een JSON-veld verwijderen wanneer u een Azure-bewerking aanroept

Bewerkingen zoals JSON-MERGE-PATCH het verzenden van een JSON om aan te geven dat een veld moet worden verwijderd null (samen met de waarde):

{
    "delete-me": null
}

Dit gedrag conflicteert met het standaardgedrag van de SDK dat aangeeft als een manier om de dubbelzinnigheid op te lossen tussen een veld dat moet worden uitgesloten en de omitempty nulwaarde.

type Widget struct {
    Name *string `json:",omitempty"`
    Count *int `json:",omitempty"`
}

In het voorgaande voorbeeld worden en gedefinieerd als pointer-to-type om onderscheid te maken tussen een ontbrekende waarde ( ) en een NameCount nul-waarde (0), die mogelijk semantische verschillen nil hebben.

In een HTTP PATCH-bewerking heeft elk veld waarvan de waarde is geen invloed op de nil waarde in de resource van de server. Wanneer u het veld van een widget Count bijwerkt, geeft u de nieuwe waarde voor Count op en laat u deze staan op Namenil .

Om te voldoen aan de vereiste voor het verzenden van een null JSON, wordt de functie NullValue gebruikt:

w := Widget{
    Count: azcore.NullValue(0).(*int),
}

Met deze code wordt Count een expliciete JSON-code null gebruikt. Wanneer de aanvraag naar de server wordt verzonden, wordt het veld van de resource Count verwijderd.

Zie ook