Delen via


Algemene gebruikspatronen in Azure SDK voor Go

Het Azure Core-pakket (azcore) in Azure SDK voor Go implementeert verschillende patronen die 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, waardoor uw app één pagina met resultaten tegelijk kan verwerken. Deze typen worden afzonderlijk gedefinieerd voor verschillende contexten, maar delen algemene kenmerken, zoals een NextPage methode.

Stel dat er een methode is die een ListWidgetsWidgetPager. Vervolgens gebruikt u de WidgetPager afbeelding zoals 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...
}

Langlopende bewerkingen

Het kan lang duren voordat sommige bewerkingen in Azure zijn voltooid, vanaf een paar seconden tot een paar 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 (LRO's) zijn slecht geschikt voor de standaard HTTP-stroom van een relatief snelle aanvraag en reactie.

Volgens de conventie worden methoden die een LRO starten voorafgegaan door 'Begin' en retourneren ze een Poller. De Poller wordt gebruikt om de service periodiek te peilen totdat de bewerking is voltooid.

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

Oproep naar PollUntilDone blokkeren

PollUntilDone verwerkt het volledige bereik van een polling-bewerking totdat een terminalstatus is bereikt. Vervolgens wordt het uiteindelijke HTTP-antwoord voor de polling-bewerking geretourneerd met de inhoud van de nettolading in de respType interface.

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)

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 vanaf een vorige bewerking

Pak het cv-token uit een bestaande poller en sla het op.

Als u polling wilt hervatten, misschien in een ander proces of op een andere computer, maakt u een nieuw PollerResponse exemplaar en initialiseert u deze door de methode aan te roepen Resume , waarbij het eerder opgeslagen cv-token wordt doorgegeven.

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 SDK-clients bieden een abstractie over de REST API van Azure om de beveiliging van code-voltooiing en compileertijd in te schakelen, zodat u niet te maken hebt met transportmechanica op lager niveau via HTTP. U kunt echter de transportmechanica aanpassen (zoals nieuwe pogingen en logboekregistratie).

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

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

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

In het volgende diagram ziet u de stroom van een pijplijn:

Diagram met de stroom van een pijplijn.

Alle clientpakketten delen een Core-pakket met de naam azcore. 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. Deze beleidsregels voegen doorgaans aanvraagheaders toe of registreren de uitgaande HTTP-aanvraag.

Nadat de Azure-service reageert, worden alle beleidsregels uitgevoerd in de omgekeerde volgorde 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 verzenden, waardoor uw app toleranter wordt voor netwerkfouten.

Elk beleid wordt geleverd met de benodigde aanvraag- of antwoordgegevens, samen met de benodigde context voor het uitvoeren van het beleid. Het beleid voltooit de bewerking met de opgegeven gegevens en geeft vervolgens de controle 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.

Kernbeleid voor HTTP-pijplijnen

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

Aangepast HTTP-pijplijnbeleid

U kunt uw eigen aangepaste beleid definiëren om mogelijkheden toe te voegen buiten de inhoud van het Core-pakket. Als u bijvoorbeeld wilt zien hoe uw app omgaat met netwerk- of servicefouten, kunt u een beleid maken dat fouten injecteert wanneer er aanvragen worden gedaan tijdens het testen. U kunt ook een beleid maken waarmee het gedrag van een service wordt gesimuleerd voor het testen.

Als u een aangepast HTTP-beleid wilt maken, definieert u uw eigen structuur met een Do methode waarmee de Policy interface wordt 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 de URL van de aanvraag, queryparameters of aanvraagheaders.
  2. De Do methode stuurt de (gewijzigde) aanvraag door naar het volgende beleid in de pijplijn door de methode van Next de aanvraag aan te roepen.
  3. Next retourneert de http.Response en een fout. Uw beleid kan elke benodigde bewerking uitvoeren, 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 goroutineveiligheid kunnen meerdere goroutines gelijktijdig toegang krijgen tot één clientobject. Het is gebruikelijk dat een beleid onveranderbaar is nadat het is gemaakt. Deze onveranderbaarheid zorgt ervoor dat de goroutine veilig is.

Aangepaste beleidssjabloon

De volgende code kan worden gebruikt als uitgangspunt om een aangepast beleid te definiëren.

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 eerste beleid voor het afhandelen van de aanvraag is ook het laatste beleid dat het antwoord afhandelt voordat het antwoord/de fout wordt geretourneerd naar het beleid van de pijplijn (in omgekeerde volgorde). Het laatste beleid in de pijplijn roept het transport aan.

Clients gebruiken standaard de standaardbibliotheek http.Client van Go.

U maakt een aangepast stateful of staatloos transport op dezelfde manier als u een aangepast beleid maakt. In het stateful geval implementeert u de Do methode die is overgenomen van de Transporter-interface . In beide gevallen ontvangt uw functie of Do methode opnieuw een azcore.Request, retourneert een azCore.Responseen voert acties uit in dezelfde volgorde als een beleid.

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

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

{
    "delete-me": null
}

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

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

In het voorgaande voorbeeld Name worden Count de aanwijzer-naar-type gedefinieerd om onderscheid te maken tussen een ontbrekende waarde (nil) en een nulwaarde (0), die semantische verschillen kunnen hebben.

In een HTTP PATCH-bewerking hebben velden met de waarde nil geen invloed op de waarde in de bron van de server. Wanneer u het veld van Count een widget bijwerkt, geeft u de nieuwe waarde op voor Count, waarbij u weggaat Name als nil.

De functie wordt gebruikt om te voldoen aan de vereiste voor het NullValue verzenden van een JSONnull:

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

Deze code wordt ingesteld Count op een expliciete JSON null. Wanneer de aanvraag naar de server wordt verzonden, wordt het veld van Count de resource verwijderd.

Zie ook