Compartir por


Patrones de uso comunes en Azure SDK para Go

El paquete azure Core (azcore) de Azure SDK para Go implementa varios patrones que se aplican a lo largo del SDK:

Paginación (métodos que devuelven colecciones)

Muchos servicios de Azure devuelven colecciones de elementos. Dado que el número de elementos puede ser grande, estos métodos de cliente devuelven un pager, lo que permite que la aplicación procese una página de resultados a la vez. Estos tipos se definen individualmente para varios contextos, pero comparten características comunes, como un NextPage método.

Por ejemplo, supongamos que hay un ListWidgets método que devuelve un WidgetPager. A continuación, usaríais el WidgetPager como se muestra aquí:

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

Operaciones de larga duración

Algunas operaciones en Azure pueden tardar mucho tiempo en completarse, en cualquier lugar de unos segundos a unos días. Algunos ejemplos de estas operaciones incluyen copiar datos de una dirección URL de origen a un blob de almacenamiento o entrenar un modelo de IA para reconocer formularios. Estas operaciones de larga duración (LRO) son poco adecuadas para el flujo HTTP estándar de una solicitud y respuesta relativamente rápidas.

Por convención, los métodos que inician un LRO tienen el prefijo "Begin" y devuelven un poller. El Poller se utiliza para consultar periódicamente el servicio hasta que se complete la operación.

En los ejemplos siguientes se muestran varios patrones para controlar los LROs. También puede obtener más información en el código fuente poller.go en el SDK.

Bloqueo de la llamada a PollUntilDone

PollUntilDone controla todo el transcurso de una operación de procesamiento hasta que se alcanza un estado terminal. A continuación, devuelve la respuesta HTTP final de la operación de sondeo con el contenido de la carga en la interfaz 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)

Bucle de sondeo personalizado

Poll envía una solicitud de sondeo al endpoint de sondeo y devuelve la respuesta o un error.

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)

Reanudar una operación anterior

Extraiga y guarde el token de reanudación de un Poller existente.

Para reanudar el sondeo, quizás en otro proceso o en otro equipo, cree una nueva PollerResponse instancia y, a continuación, inicialícela llamando a su Resume método, pasando el token de reanudación guardado anteriormente.

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)

Flujo de canalización HTTP

Los distintos clientes del SDK ofrecen una capa de abstracción sobre la API REST de Azure, permitiendo que el código se complete de manera automática y garantizando la seguridad de tipos en tiempo de compilación, así es posible evitar lidiar con las mecánicas de transporte de bajo nivel a través de HTTP. Sin embargo, puede personalizar la mecánica de transporte (como reintentos y registro de eventos).

El SDK realiza solicitudes HTTP a través de una tubería HTTP. La pipeline describe la secuencia de pasos realizados para cada ciclo de solicitud y respuesta HTTP.

La canalización se compone de un módulo de transporte junto con un número indefinido de políticas.

  • El transporte envía la solicitud al servicio y recibe la respuesta.
  • Cada política completa una acción específica en la tubería.

En el diagrama siguiente se muestra el flujo de una canalización:

Diagrama que muestra el flujo de una canalización.

Todos los paquetes de cliente comparten un paquete core denominado azcore. Este paquete construye la canalización HTTP con su conjunto ordenado de directivas, lo que garantiza que todos los paquetes de cliente se comporten de forma coherente.

Cuando se envía una solicitud HTTP, todas las directivas se ejecutan en el orden en que se agregaron a la canalización antes de enviar la solicitud al punto de conexión HTTP. Estas directivas suelen agregar encabezados de solicitud o registrar la solicitud HTTP saliente.

Después de que el servicio de Azure responda, todas las directivas se ejecutan en orden inverso antes de que la respuesta regrese a tu código. La mayoría de las directivas omiten la respuesta, pero la directiva de registro registra la respuesta. La política de reintento podría volver a emitir la solicitud, lo que hace que tu aplicación sea más resistente a los errores de red.

Cada directiva se proporciona con los datos de solicitud o respuesta necesarios, junto con cualquier contexto necesario para ejecutar la directiva. La directiva completa su operación con los datos especificados y, a continuación, pasa el control a la siguiente directiva de la canalización.

De forma predeterminada, cada paquete de cliente crea una canalización configurada para trabajar con ese servicio específico de Azure. También puede definir sus propias directivas personalizadas e insertarlas en la canalización HTTP al crear un cliente.

Directivas de canalización HTTP principales

El paquete Core proporciona tres directivas HTTP que forman parte de cada canalización:

Políticas de canalización HTTP personalizadas

Puede definir su propia directiva personalizada para agregar funcionalidades más allá del contenido del paquete Core. Por ejemplo, para ver cómo la aplicación trata los errores de red o servicio, podría crear una directiva que inserte un error cuando se realicen solicitudes durante las pruebas. O bien, podría crear una directiva que simula el comportamiento de un servicio para las pruebas.

Para crear una directiva HTTP personalizada, defina su propia estructura con un Do método que implemente la Policy interfaz :

  1. El método de la Do política debe realizar operaciones según sea necesario en el policy.Request entrante. Entre los ejemplos de operaciones se incluyen el registro, la inserción de un error o la modificación de cualquiera de las direcciones URL de la solicitud, los parámetros de consulta o los encabezados de solicitud.
  2. El método Do reenvía la solicitud (modificada) a la siguiente directiva de la canalización llamando al método Next de la solicitud.
  3. Next devuelve http.Response y un error. La política puede realizar cualquier operación necesaria, como registrar la respuesta o el error.
  4. Su política debe devolver una respuesta y un error a la política anterior en la canalización.

Nota:

Las políticas deben ser seguras para goroutine. La seguridad de Goroutine permite que varias goroutines accedan simultáneamente a un único objeto de cliente. Es habitual que una directiva sea inmutable después de la creación. Esta inmutabilidad garantiza que la goroutina sea segura.

Plantilla de directiva personalizada

El código siguiente se puede usar como punto de partida para definir una directiva personalizada.

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
}

Transporte HTTP personalizado

Un transporte envía una solicitud HTTP y devuelve su respuesta o error. La primera directiva para controlar la solicitud es también la última directiva que controla la respuesta antes de devolver la respuesta o el error a las directivas de la canalización (en orden inverso). La última política de la tubería invoca el transporte.

De forma predeterminada, los clientes usan la http.Client biblioteca estándar compartida de Go.

Cree un transporte con estado o sin estado personalizado igual que crea una directiva personalizada. En el caso con estado, se implementa el método Do heredado de la interfaz Transporter. En ambos casos, su función o el método Do recibe de nuevo un azcore.Request, devuelve un azCore.Response y realiza acciones en el mismo orden que una política.

Eliminación de un campo JSON al invocar una operación de Azure

Operaciones como JSON-MERGE-PATCH enviar un JSON null para indicar que se debe eliminar un campo (junto con su valor):

{
    "delete-me": null
}

Este comportamiento entra en conflicto con la serialización predeterminada del SDK que especifica omitempty como una manera de resolver la ambigüedad entre un campo que se va a excluir y su valor cero.

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

En el ejemplo anterior, Name y Count se definen como puntero a tipo para desambiguar entre un valor que falta (nil) y un valor cero (0), que puede tener diferencias semánticas.

En una operación HTTP PATCH, los campos con el valor nil no afectan al valor en el recurso del servidor. Al actualizar el campo de Count un widget, especifique el nuevo valor para Count, dejando Name como nil.

Para cumplir el requisito de enviar un JSON null, se usa la NullValue función :

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

Este código establece Count en un JSON nullexplícito. Cuando se envía la solicitud al servidor, se elimina el campo del Count recurso.

Consulte también