Padrões de uso comuns no SDK do Azure para Go
O pacote Azure Core (azcore
) no SDK do Azure para Go implementa vários padrões que são aplicados em todo o SDK:
- O fluxo de pipeline HTTP, que é o mecanismo HTTP subjacente usado pelas bibliotecas de cliente do SDK.
- Paginação (métodos que retornam coleções).
- Operações de longa duração (LROs).
Paginação (métodos que retornam coleções)
Muitos serviços do Azure retornam coleções de itens. Como o número de itens pode ser grande, esses métodos de cliente retornam um Pager, que permite que seu aplicativo processe uma página de resultados de cada vez. Esses tipos são definidos individualmente para vários contextos, mas compartilham características comuns, como um NextPage
método.
Por exemplo, suponha que há um ListWidgets
método que retorna um WidgetPager
arquivo . Em seguida, você usaria o WidgetPager
como mostrado aqui:
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...
}
Operações de longa duração
Algumas operações no Azure podem levar muito tempo para serem concluídas, de alguns segundos a alguns dias. Exemplos de tais operações incluem copiar dados de uma URL de origem para um blob de armazenamento ou treinar um modelo de IA para reconhecer formulários. Essas operações de longa duração (LROs) não se adequam ao fluxo HTTP padrão de uma solicitação e resposta relativamente rápidas.
Por convenção, os métodos que iniciam um LRO são prefixados com "Begin" e retornam um Poller. O Poller é usado para sondar periodicamente o serviço até que a operação termine.
Os exemplos a seguir ilustram vários padrões para lidar com LROs. Você também pode aprender mais com o código-fonte poller.go no SDK.
Bloquear chamada para PollUntilDone
PollUntilDone
lida com toda a extensão de uma operação de sondagem até que um estado terminal seja atingido. Em seguida, ele retorna a resposta HTTP final para a operação de sondagem com o conteúdo da carga útil na 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)
Loop de sondagem personalizado
Poll
envia uma solicitação de sondagem para o ponto de extremidade de sondagem e retorna a resposta ou um erro.
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)
Retomar de uma operação anterior
Extraia e salve o token de currículo de um Poller existente.
Para retomar a sondagem, talvez em outro processo ou em outro computador, crie uma nova PollerResponse
instância e, em seguida, inicialize-a chamando seu Resume
método, passando-lhe o token de currículo salvo 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)
Fluxo de pipeline HTTP
Os vários clientes SDK fornecem uma abstração sobre a API REST do Azure para habilitar o auto-completar de código e a segurança de tipo em tempo de compilação para que você não precise lidar com mecânicas de transporte de nível inferior por HTTP. No entanto, você pode personalizar a mecânica de transporte (como novas tentativas e registro).
O SDK faz solicitações HTTP por meio de um pipeline HTTP. O pipeline descreve a sequência de etapas executadas para cada viagem de ida e volta de solicitação-resposta HTTP.
O gasoduto é composto por um transporte juntamente com qualquer número de políticas:
- O transporte envia o pedido ao serviço e recebe a resposta.
- Cada política conclui uma ação específica no pipeline.
O diagrama a seguir ilustra o fluxo de um pipeline:
Todos os pacotes de cliente compartilham um pacote principal chamado azcore
. Este pacote constrói o pipeline HTTP com seu conjunto ordenado de políticas, garantindo que todos os pacotes de cliente se comportem de forma consistente.
Quando uma solicitação HTTP é enviada, todas as políticas são executadas na ordem em que foram adicionadas ao pipeline antes que a solicitação seja enviada ao ponto de extremidade HTTP. Essas políticas normalmente adicionam cabeçalhos de solicitação ou registram a solicitação HTTP de saída.
Depois que o serviço do Azure responde, todas as políticas são executadas na ordem inversa antes que a resposta retorne ao seu código. A maioria das políticas ignora a resposta, mas a política de registro registra a resposta. A política de repetição pode reemitir a solicitação, tornando seu aplicativo mais resiliente a falhas de rede.
Cada política é fornecida com os dados de solicitação ou resposta necessários, juntamente com qualquer contexto necessário para executar a política. A política conclui sua operação com os dados fornecidos e, em seguida, passa o controle para a próxima política no pipeline.
Por padrão, cada pacote de cliente cria um pipeline configurado para funcionar com esse serviço específico do Azure. Você também pode definir suas próprias políticas personalizadas e inseri-las no pipeline HTTP ao criar um cliente.
Principais políticas de pipeline HTTP
O pacote Core fornece três políticas HTTP que fazem parte de cada pipeline:
Políticas de pipeline HTTP personalizadas
Você pode definir sua própria política personalizada para adicionar recursos além do conteúdo do pacote principal. Por exemplo, para ver como seu aplicativo lida com falhas de rede ou serviço, você pode criar uma política que injete falhas quando solicitações são feitas durante o teste. Ou você pode criar uma política que zomba do comportamento de um serviço para teste.
Para criar uma política HTTP personalizada, defina sua própria estrutura com um Do
método que implementa a Policy
interface:
- O método da
Do
sua política deve executar operações conforme necessário no arquivopolicy.Request
. Exemplos de operações incluem registro, injeção de uma falha ou modificação de qualquer URL da solicitação, parâmetros de consulta ou cabeçalhos de solicitação. - O
Do
método encaminha a solicitação (modificada) para a próxima política no pipeline chamando o método daNext
solicitação. Next
retorna ohttp.Response
e um erro. Sua política pode executar qualquer operação necessária, como registrar a resposta/erro.- Sua política deve retornar uma resposta e um erro à política anterior no pipeline.
Nota
As políticas devem ser seguras para a rotina. A segurança Goroutine permite que vários goroutines acessem um único objeto cliente simultaneamente. É comum que uma política seja imutável depois de criada. Esta imutabilidade garante que a rotina é segura.
Modelo de política personalizado
O código a seguir pode ser usado como ponto de partida para definir uma política 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
Um transporte envia uma solicitação HTTP e retorna sua resposta/erro. A primeira política a lidar com a solicitação também é a última política que lida com a resposta antes de retornar a resposta/erro de volta às políticas do pipeline (na ordem inversa). A última política em preparação invoca o transporte.
Por padrão, os clientes usam o compartilhado http.Client
da biblioteca padrão do Go.
Você cria um transporte com ou sem monitoração de estado personalizado da mesma forma que cria uma política personalizada. No caso stateful, você implementa o Do
método herdado da interface do Transporter . Em ambos os casos, sua função ou Do
método recebe novamente um azcore.Request
, retorna um azCore.Response
e executa ações na mesma ordem de uma política.
Como excluir um campo JSON quando você invoca uma operação do Azure
Operações como JSON-MERGE-PATCH
enviar um JSON null
para indicar que um campo deve ser excluído (junto com seu valor):
{
"delete-me": null
}
Esse comportamento entra em conflito com o marshaling padrão do SDK que especifica omitempty
como uma maneira de resolver a ambiguidade entre um campo a ser excluído e seu valor zero.
type Widget struct {
Name *string `json:",omitempty"`
Count *int `json:",omitempty"`
}
No exemplo anterior, Name
e Count
são definidos como ponteiro para tipo para desambiguar entre um valor ausente (nil
) e um valor zero (0), que pode ter diferenças semânticas.
Em uma operação HTTP PATCH, quaisquer campos com o valor nil
não afetam o valor no recurso do servidor. Ao atualizar o campo de Count
um Widget, especifique o novo valor para Count
, deixando Name
como nil
.
Para cumprir o requisito de envio de um JSON null
, a NullValue
função é usada:
w := Widget{
Count: azcore.NullValue(0).(*int),
}
Este código é definido Count
como um JSON null
explícito. Quando a solicitação é enviada ao servidor, o campo do Count
recurso é excluído.