До сих пор мы были озабочены первой обязанностью клиента: раскрытием API-интерфейсов Elasticsearch как типов на языке программирования Go. Давайте переключимся на компонент, отвечающий за отправку и получение данных по сети: пакет estransport.
Основным типом, предоставляемым пакетом, является estransport.Client, который реализует estransport.Interface. Он определяет единственный метод Perform(), который принимает *http.Request и возвращает *http.Response:
$ go doc -short github.com/elastic/go-elasticsearch/v7/estransport.Interface
type Interface interface {
Perform(*http.Request) (*http.Response, error)
}
Реализация этого метода по умолчанию является сердцем транспортного компонента, имеющего дело не только с отправкой и получением данных, но и с управлением пулом соединений, повторными запросами, хранением клиентских метрик, журналированием операций и т. д. Давайте посмотрим поближе.
Прежде чем клиент сможет отправить запрос в кластер, он сначала должен знать, куда его отправить. Для локальной разработки это довольно просто: вы просто сохраняете значение по умолчанию (http://localhost:9200) или настраиваете клиент с одним адресом. В этом случае используется "единичный" пул соединений, который возвращает только одно соединение. Это также относится к использованию Elasticsearch Service в Elastic Cloud, которое предоставляет только одну конечную точку для кластера, поскольку имеет собственную логику балансировки нагрузки. И то же самое, естественно, применимо к любому кластеру за балансировщиком нагрузки или прокси.
Однако локальные производственные кластеры работают с несколькими узлами, и было бы неоптимально отправлять запросы только на один узел, что делает его узким местом. Вот почему пакет экспортирует интерфейс estransport.ConnectionPool, который позволяет выбрать соединение из списка:
$ go doc -short github.com/elastic/go-elasticsearch/v7/estransport.ConnectionPool
type ConnectionPool interface {
Next() (*Connection, error) // Next возвращает следующее доступное соединение.
OnSuccess(*Connection) error // OnSuccess сообщает, что соединение было успешным.
OnFailure(*Connection) error // OnFailure сообщает, что соединение не удалось.
URLs() []*url.URL // URLs возвращает список URL-адресов доступных подключений.
}
...
Всякий раз, когда клиент настроен с несколькими адресами кластера, используется реализация пула соединений "статуса", которая хранит список исправных и неработоспособных соединений с механизмом проверки того, стало ли неработоспособное соединение снова работоспособным. В соответствии с общей расширяемостью клиента настраиваемая реализация пула соединений может быть передана в конфигурации при необходимости.
Метод Next() пула соединений "статуса" делегирует еще один интерфейс: estransport.Selector, с реализацией по умолчанию циклического селектора, который обычно является наиболее эффективным способом распределения нагрузки между узлами кластера. Опять же, пользовательская реализация селектора может быть передана в конфигурации, когда в сложных топологиях сети требуется более сложный механизм выбора соединения.
Например, упрощенная реализация селектора "hostname" может выглядеть так:
func (s *HostnameSelector) Select(conns []*estransport.Connection) (*estransport.Connection, error) {
// Блокировка доступа снята
var filteredConns []*estransport.Connection
for _, c := range conns {
if strings.Contains(c.URL.String(), "es1") {
filteredConns = append(filteredConns, c)
}
}
if len(filteredConns) > 0 {
s.current = (s.current + 1) % len(filteredConns)
return filteredConns[s.current], nil
}
return nil, errors.New("No connection with hostname [es1] available")
}
Однако настраиваемый селектор более полезен с другой функцией клиента: способностью обнаруживать узлы в кластере, в просторечии известной как "сниффинг". Он использует Nodes Info API для получения информации об узлах в кластере и динамически обновляет конфигурацию клиента в зависимости от состояния кластера. Это позволяет вам, например, указывать клиенту только на координирующие узлы кластера и позволяет ему автоматически обнаруживать данные или принимать узлы.
Клиент Go предоставляет метод DiscoverNodes() для выполнения операции вручную, а также параметры конфигурации DiscoverNodesInterval и DiscoverNodesOnStart для выполнения операции при инициализации клиента и через определенные промежутки времени. Помимо получения списка узлов и создания динамической конфигурации клиента, он также хранит метаданные, прикрепленные к узлам, такие как роли или атрибуты.
Еще одна полезная функция транспортного компонента - это возможность повторить неудачный запрос, что особенно важно в распределенной системе, такой как Elasticsearch. По умолчанию он повторяет запрос до трех раз при получении сетевой ошибки или ответа HTTP с кодом состояния 502, 503 или 504. Количество попыток повторения и коды ответа HTTP настраиваются вместе с дополнительной задержкой отсрочки. Эту функцию можно вообще отключить.
Читайте также:
- Клиент Go для Elasticsearch
- Клиент Go для Elasticsearch: пакет esapi
- Клиент Go для Elasticsearch: конфигурация и кастомизация
- Клиент Go для Elasticsearch: кодирование и декодирование полезной нагрузки JSON
- Клиент Go для Elasticsearch: массовая индексация
Комментариев нет:
Отправить комментарий