суббота, 1 мая 2021 г.

Клиент Go для Elasticsearch: пакет esapi

Пакет esapi обеспечивает доступ к API-интерфейсам Elasticsearch через типы данных языка программирования Go. Например, чтобы проиндексировать документ, вы вызываете соответствующий метод на клиенте:

res, err := client.Index(
  "my-index",
  strings.NewReader(`{"title":"Test"}`),
  client.Index.WithDocumentID("1"))
fmt.Println(res, err)

Пакет Go предоставляет тот же API, что и клиенты на других языках, обеспечивая единообразие взаимодействия с пользователем на разных языках программирования и облегчая общение в командах полиглотов. Следовательно, различные пространства имен API Elasticsearch доступны в качестве пространств имен на клиенте. Например, чтобы проверить работоспособность кластера, вы вызываете метод Cluster.Health() на клиенте; чтобы создать индекс, вы вызываете метод Indices.Create().

Метод возвращает esapi.Response и error. Ошибка возвращается всякий раз, когда запрос терпит неудачу; например, когда конечная точка недоступна или время ожидания запроса истекло. Тип esapi.Response - это легкая оболочка для *http.Response. Помимо отображения статуса ответа, заголовков и тела, он предоставляет несколько вспомогательных методов, таких как IsError(). Обратите внимание, что ответ 500 Internal Server Error по-прежнему является допустимым ответом, и поэтому в этом случае ошибка не возвращается - вызывающий код должен проверить статус ответа, чтобы правильно обработать ответ. Тип esapi.Response также реализует интерфейс fmt.Stringer, позволяющий печатать ответ во время разработки и отладки, как показано в примере выше.

Если вы присмотритесь, вы обнаружите, что вызов метода создает новый экземпляр структуры IndexRequest и вызывает его метод Do(). Вполне возможно создать экземпляр и вызвать метод самостоятельно - соответствующий код будет выглядеть так:

req := esapi.IndexRequest{
  Index:      "my-index",
  DocumentID: "1",
  Body:       strings.NewReader(`{"title":"Test"}`),
}
req.Do(context.Background(), client)

Оба варианта функционально эквивалентны. API-интерфейс, ориентированный на методы, имеет незначительные накладные расходы, но также имеет определенные преимущества.

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

Не менее важно, что он четко определяет, какие параметры API требуются (в приведенном выше примере это имя индекса и полезная нагрузка JSON), а какие - необязательные (идентификатор документа). Сравните с API Create(), который в сигнатуре метода дает понять, что требуется идентификатор документа:

$ go doc -short github.com/elastic/go-elasticsearch/v7/esapi.Create
type Create func(index string, id string, body io.Reader, o ...func(*CreateRequest)) (*Response, error)
...

Это особенно полезно для разработчиков, использующих автозавершение кода в своих IDE и редакторах.

Еще одно удобство, обеспечиваемое API-интерфейсом, ориентированным на методы, связано с параметрами, принимающими логические и числовые значения. Поскольку все типы имеют значение по умолчанию в Go, пакет не сможет определить, установлено ли значение false вызывающим кодом или это просто значение по умолчанию для типа bool; аналогичная проблема существует с типом int и значением 0. Это обычно решается в Go путем принятия указателя на значение в качестве аргумента, но это делает вызывающий код довольно многословным.

Следовательно, вместо того, чтобы объявлять переменную и передавать на нее указатель, вызывающий код может просто передать значение соответствующему методу, и он автоматически создаст указатель:

client.Reindex(
  strings.NewReader(`{...}`),
  client.Reindex.WithRequestsPerSecond(100),
  client.Reindex.WaitForCompletion(true),
)

Примечание. Пакет объявляет вспомогательные функции esapi.BoolPtr() и esapi.IntPtr(), чтобы упростить использование полей, принимающих указатель, при прямом использовании структур запроса.

С более чем 300 различными, постоянно развивающимися API-интерфейсами Elasticsearch практически невозможно управлять базой кода вручную - единственный устойчивый режим обслуживания - это полностью сгенерированная база кода. К счастью, репозиторий Elasticsearch содержит исчерпывающую спецификацию для каждого API в виде набора документов JSON. Это основной инструмент для обеспечения согласованности между клиентами и для того, чтобы идти в ногу с эволюцией API Elasticsearch.

Генератор Go, который является частью набора внутренних пакетов, переводит определение API из JSON в исходный код Go, генерирует отдельные файлы, форматирует их с помощью gofmt и создает файл с типом esapi.API с "картой" всех API, используя отражение в Go. Затем этот тип встраивается в клиент.

Как отмечалось выше, одной из задач клиента Elasticsearch является отправка и получение данных, по умолчанию это полезная нагрузка JSON. Клиент Go представляет тело запроса и ответа просто как io.Reader, оставляя все кодирование и декодирование вызывающему коду. Для такой реализации есть несколько причин. Прежде всего, нет формальной спецификации для чрезвычайно изменчивой полезной нагрузки API, и поэтому о генерации кода не может быть и речи.

Примечание: предпринимаются многочисленные попытки улучшить эту ситуацию с целью сделать возможным создание полностью сгенерированного API, например, результатов поиска, компонентов Query DSL и т. д.

Другая причина связана с производительностью и расширяемостью. Оставляя кодирование и декодирование вызывающему коду, между ним и клиентом существует четкая граница, что упрощает рассуждения о производительности. Опытным путем кодирование и декодирование JSON имеет наибольшее влияние на производительность клиента, обычно выше, чем стоимость передачи по сети. Еще с одной точки зрения, передав кодирование и декодирование вызывающему коду, можно легко использовать сторонние пакеты JSON, которые в большинстве случаев значительно превосходят стандартную библиотеку. Чтобы увидеть примеры и тесты для различных пакетов JSON смотрите папку _examples/encoding в репозитории.

Примечание. Чтобы упростить передачу пользовательских полезных данных в API, клиент предоставляет тип esutil.JSONReader.


Читайте также:


Комментариев нет:

Отправить комментарий