Поделиться через


Распространенные варианты использования в пакете Azure SDK для Go

Основной пакет Azure (azcore) в Azure SDK для Go позволяет реализовать несколько шаблонов, которые применяются во всем пакете SDK:

разбиение на страницы (методы, которые возвращают коллекции);

Многие службы Azure возвращают коллекции элементов. Так как количество элементов может быть огромным, эти методы клиента возвращают значение Пейджер, что позволяет приложению обрабатывать одну страницу результатов за раз. Эти типы определяются по отдельности для разных контекстов, но имеют общие характеристики, такие как метод NextPage.

Например, предположим, что используется метод ListWidgets, который возвращает WidgetPager. Потом вы используете WidgetPager как показано здесь:

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

Длительные операции

Выполнение некоторых операций в Azure может быть длительным — от нескольких секунд до нескольких дней. К таким операциям относятся копирование данных из исходного URL-адреса в BLOB-объект службы хранилища и обучение модели искусственного интеллекта для распознавания форм. Эти длительные операции (LROS) плохо подходят для стандартного потока HTTP относительно быстрого запроса и ответа.

По соглашению методы, запускающие LRO, имеют префикс Begin и возвращают значение Модуль опроса. Модуль опроса используется для периодического опроса службы до завершения операции.

В приведенных ниже примерах показаны различные шаблоны для обработки LRO. Кроме того, дополнительные сведения можно получить из исходного кода poller.go в пакете SDK.

Блокирующий вызов PollUntilDone

PollUntilDone обрабатывает весь диапазон операции опроса до тех пор, пока не будет достигнуто конечное состояние. Затем он возвращает окончательный HTTP-ответ для операции опроса с содержимым полезных данных в интерфейсе 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)

Настраиваемый цикл опроса

Poll отправляет запрос опроса к конечной точке опроса и возвращает соответствующие ответ или ошибку.

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)

Возобновление предыдущей операции

Извлеките и сохраните его токен возобновления из существующего модуля опроса.

Чтобы возобновить опрос (возможно, в другом процессе или компьютере), создайте новый экземпляр PollerResponse, а затем инициализируйте его, вызвав соответствующий метод Resume, передав ему ранее сохраненный токен возобновления.

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

Различные клиенты пакета SDK предоставляют абстракцию по REST API Azure, чтобы обеспечить завершение кода и безопасность типов во время компиляции, поэтому вам не нужно иметь дело с механикой транспорта более низкого уровня по протоколу HTTP. Однако можно настроить механизм транспорта (например, повторные попытки и ведение журнала).

Пакет SDK позволяет выполнять HTTP-запросы через конвейер HTTP. Этот конвейер позволяет описать последовательность действий, выполняемых для каждого кругового пути "запрос и ответ" HTTP.

Конвейер состоит из элементов транспортировки и определенного количества политик:

  • При транспортировке запрос отправляется в службу, а также пересылается ответ на запрос.
  • В рамках каждой политики выполняется определенное действие в конвейере.

На следующей диаграмме показан поток конвейера:

Диаграмма, на которой показан поток конвейера.

Все пакеты клиентов совместно используют основной пакет с именем azcore. Этот пакет позволяет создать конвейер HTTP с упорядоченным набором политик, что обеспечивает согласованную работу всех пакетов клиентов.

При отправке HTTP-запроса все политики выполняются в том порядке, в котором они были добавлены в конвейер перед отправкой запроса к конечной точке HTTP. Эти политики позволяют добавить заголовки запросов или регистрировать исходящие HTTP-запросы.

После ответа службы Azure все политики выполняются в обратном порядке, прежде чем ответ возвратится в ваш код. Большинство политик игнорируют ответ, но политика ведения журнала записывает ответ. Политика повторных попыток может повторно выполнить запрос, что делает приложение более устойчивым к сбоям сети.

Для каждой политики предоставляются необходимые данные запроса или ответа, а также любой необходимый контекст для выполнения политики. На их основе политика может выполнить требуемые действия с данными и передать управление следующей политике в конвейере.

По умолчанию каждый пакет клиента создает конвейер, настроенный для работы с этой конкретной службой Azure. Вы также можете определить собственные настраиваемые политики и вставить в конвейер HTTP при создании клиента.

Политики конвейера HTTP основного пакета

Основной пакет предоставляет три указанные ниже политики HTTP, которые являются частью каждого конвейера.

Настраиваемые политики в конвейере HTTP

Вы можете определить собственную настраиваемую политику для добавления возможностей за пределами содержимого пакета Core. Например, чтобы увидеть, как ваше приложение справляется со сбоями сети или служб, можно создать политику, которая добавляет ошибку при выполнении запросов во время тестирования. Кроме того, можно создать политику, которая позволяет имитировать поведение службы для тестирования.

Чтобы создать настраиваемую политику HTTP, определите собственную структуру с помощью метода Do, используя интерфейс Policy:

  1. Метод Do политики должен выполнять операции, как это необходимо для входящего policy.Request. К примерам операций относятся ведение журнала, добавление сбоя или изменение любых URL-адресов, параметров или заголовков запроса.
  2. Метод Do переадресовывает (измененный) запрос в следующую политику в конвейере, вызвав метод запроса Next.
  3. Next возвращает http.Response и ошибку. Политика может выполнять любые необходимые операции, например регистрировать ответ или ошибку.
  4. Политика должна вернуть ответ и ошибку в предыдущую политику в конвейере.

Примечание.

Политики должны обеспечивать безопасность использования гоурутин. Политики должны обеспечивать безопасность использования гоурутин, так как это позволяет нескольким гоурутинам одновременно получать доступ к одному клиентскому объекту. Как правило, после создания политика будет неизменной. Эта неизменность гарантирует безопасность гоурутин.

Пользовательский шаблон политики

Следующий код можно использовать в качестве отправной точки для определения пользовательской политики.

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
}

Настраиваемая транспортировка HTTP

При транспортировке отправляется HTTP-запрос и возвращаются соответствующие ответ или ошибка. Первая политика обработки запроса также является последней политикой, которая обрабатывает ответ перед возвратом ответа или ошибки обратно в политики конвейера (в обратном порядке). Последняя политика в конвейере вызывает транспорт.

По умолчанию клиенты используют общий элемент http.Client из стандартной библиотеки Go.

Настраиваемая транспортировка с отслеживанием или без отслеживания состояния создается точно так же, как создается настраиваемая политика. В случае с отслеживанием состояния вы реализуете метод Do, унаследованный из интерфейса Transporter. В обоих случаях функция или метод Do снова получают azcore.Request и возвращают azCore.Response, а действия выполняются в том же порядке, что и для политики.

Удаление поля JSON при вызове операции Azure

Такие операции, как JSON-MERGE-PATCH отправляют null JSON, чтобы указать на необходимость удаления поля (вместе с соответствующим значением):

{
    "delete-me": null
}

Это поведение не соответствует маршалингу по умолчанию в рамках пакета SDK, который определяет omitempty как средство для устранения неоднозначности между полем, которое должно быть исключено, и его нулевым значением.

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

В предыдущем примере Name и Count определены как указатель на тип для указания неоднозначности между отсутствующим значением (nil) и нулевым значением (0), которые могут иметь семантические различия.

В операции HTTP PATCH все поля со значением не влияют на значение nil в ресурсе сервера. При обновлении поля Count мини-приложения укажите новое значение для Count, оставив для Name значение nil.

Чтобы выполнить требование по отправке null JSON, используется функция NullValue:

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

Этот код позволяет установить для Count явное значение null JSON. Когда запрос отправляется серверу, поле ресурса Count удаляется.

См. также