API Consul

24 minute read

Это перевод статьи "An introduction to Consul key/value store API in Golang".

К сожалению, статья совсем не такая подробная и интересная как хотелось бы. Реквестирую больше экшена в статьях про consul и другие полезные go-тулзовины.

Введение

Consul это инструмент для обнаружения сервисов(service discovery) и простое хранилище пар ключ-значение, которое удобно использовать для конфигурации. Большинство свой функциональности consul предоставляет как простое RESTful HTTP API. Это позволяет писать клиенты к этому API на самых различных языках. В этом посте мы рассмотрим как работать с Go пакетом для Consul API, в частности, c хранилищем пар ключ-значение(далее просто k/v).

Для запуска примеров из статьи вам нужен компилятор Go и установленный consul. После этого нужно установить пакет для работы с consul:

go get github.com/hashicorp/consul/api

Теперь мы можем его импортировать, указав(для удобства и читаемоcти) псевдоним consulapi.

import (
    consulapi "github.com/hashicorp/consul/api"
)

Экземпляр клиента

Для создания клиента нужно вызвать функцию NewClient и передать ей объект Config как аргумент. Самый простой способ создать экземпляр Config, это вызвать функцию DefaultConfig и изменить необходимые атрибуты, например Address, Scheme, Datacenter и т.д.

config := consulapi.DefaultConfig()
config.Address = "192.168.1.2:8500"
consul, err := consulapi.NewClient(config)

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

Давайте рассмотрим, какие атрибуты в объекте Config доступны.

Address

Это строка, в которой указан адрес consul сервера в формате HOST:PORT, например "192.168.1.2:8500". Его можно указать через переменную окружения CONSUL_HTTP_ADDR. Дефолтное значение атрибута 127.0.0.1:8500 и это будет работать для большинства наших примеров, если вы не надумаете изменить адрес consul сервера.

Scheme

Этот атрибут должен быть строкой, указывающей какая схема используется в URI consul сервера. Значение может быть http or https. Дефолтное значение http. Для управления этим значением есть две булевые переменные окружения: CONSUL_HTTP_SSL и CONSUL_HTTP_SSL_VERIFY. Если установить CONSUL_HTTP_SSL как true, то будет использоваться https схема, иначе http. Если CONSUL_HTTP_SSL_VERIFY установить false, то не будет SSL верификации.

Datacenter

Определяет какой датацентр нужно использовать. Если атрибут не указан, то используется дефолтный датацентр.

HttpClient

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

HttpAuth

Этот атрибут должен быть указателем на структуру HttpBasicAuth, в которой указаны значения Username и Password в виде строк для HTTP Basic Authentication. Например вот так:

config := consulapi.DefaultConfig()
config.HttpAuth = &consulapi.HttpBasicAuth{Username: "guest", Password: "secret"}
consul, err := consulapi.NewClient(config)

Также, значение HttpAuth можно установить с помощью переменной окружения CONSUL_HTTP_AUTH. В эту переменную нужно записать строку с указанием пользователя, пароля и двоеточием в качестве разделителя. Если указна строка без двоеточия, то это будет обрабатываться как указание пользователя с пустым паролем. Например:

export CONSUL_HTTP_AUTH=guest:secret
export CONSUL_HTTP_AUTH=guest

WaitTime

Этот параметр определяет, как долго можно блокировать Watch. Если ничего не указанно, то используется дефолтное значение.

Token

Атрибут используется для задания в реквесте специального ACL токена, который будет использоваться вместо токена агента по умолчанию.

Используя методы Client вы можете получить доступ к различным возможностям API consul'а. В рамках данной статьи, нас интересует работа c k/v хранилищем.

k/v хранилище

Чтобы получить объект, с помощь которого мы сможем работать с k/v хранилищем, нам нужно вызвать метод KV у экземпляра клиента.

kv := consul.KV()

В нашем распоряжении есть множество методов для CRUD операций и работой с k/v хранилищем. Структура KVPair используется для представления одной записи.

type KVPair struct {
    Key         string
    CreateIndex uint64
    ModifyIndex uint64
    LockIndex   uint64
    Flags       uint64
    Value       []byte
    Session     string
}
  • Key это ключ, как правило с разделительными слешами. Например sites/1/domain.
  • CreateIndex это номер индекса, указывающий, когда ключ был создан.
  • ModifyIndex это номер индекса, указывающий, когда ключ был изменен.
  • LockIndex это номер индекса, указывающий, когда была взята блокировка этого ключа.
  • Flag может использоваться в рамках приложения, туда можно записывать дополнительную информацию.
  • Value это само значение в виде байт-массива с максимальным значением 512 килобайт.
  • Session можно записывать после создания сессии.

Тип KVPairs это ничто иное как список ссылок на объекты KVPair:

type KVPairs []*KVPair

QueryMeta содержит некоторую дополнительную информацию по выполненному запросу:

type QueryMeta struct {
    // LastIndex. Это поле может использоваться аналогично 
    // WaitIndex для предотвращения блокировки запроса
    LastIndex uint64

    // Время последнего взаимодействия с лидером
    // для данного сервера(обработавшего запрос)
    LastContact time.Duration

    // Известен ли лидер
    KnownLeader bool

    // Время выполнения запроса
    RequestTime time.Duration
}

QueryOptions используется для указания дополнительных параметров при запросе:

type QueryOptions struct {
    // Обеспечивает перезаписывание датацетром DC
    // DC, указанного в Config.
    Datacenter string

    // AllowStale позволяет любому серверу(кроме лидера) consult 
    // обрабатывать запросы на чтение. Это уменьшает задержки и
    // увеличивает пропускную способность.
    AllowStale bool

    // RequireConsistent увеличивает достоверность данных, 
    // отдаваемых клиенту. Это дороже по производительности, 
    // но предотвращает отдачу старых данных клиенту.
    RequireConsistent bool

    // WaitIndex используется для блокирующих запросов.
    // Ожидаем истечение таймаута или следующего
    // доступного индекса.
    WaitIndex uint64

    // WaitTime используется для указания времени ожидания.
    // По умолчанию берется значение из конфига, 
    // но мы можем его поменять.
    WaitTime time.Duration

    // Token используется для указания ACL в запросе,
    // который перетирает дефолтный токен агента.
    Token string
}

Put

Сигнатура метода выглядит так:

func (k *KV) Put(p *KVPair, q *WriteOptions) (*WriteMeta, error)

А вот так этим методом можно пользоваться:

d := &consulapi.KVPair{Key: "sites/1/domain", Value: []byte("example.com")}
kv.Acquire(d, nil)

Get

Сигнатура метода

func (k *KV) Get(key string, q *QueryOptions) (*KVPair, *QueryMeta, error)

И его использования:

kvp, qm, error := kv.Get("sites/1/domain", nil)
if err != nil {
    fmt.Println(err)
} else {
    fmt.Println(string(kvp.Value))
}

Delete

Сигнатура метода Delete:

func (k *KV) Delete(key string, w *WriteOptions) (*WriteMeta, error)

И пример как им пользоваться:

wm, err := kv.Delete("sites/1/domain", nil)
if err != nil {
    fmt.Println(err)
}

Keys

Метод для получения списка ключей:

func (k *KV) Keys(prefix, separator string, q *QueryOptions) ([]string, *QueryMeta, error)

List

Получение пачки значений по префиксу ключа:

func (k *KV) List(prefix string, q *QueryOptions) (KVPairs, *QueryMeta, error)

DeleteTree

Метод для удаления набора ключей с одинаковым префиксом:

func (k *KV) DeleteTree(prefix string, w *WriteOptions) (*WriteMeta, error)

И пример использования:

wm, err := kv.DeleteTree("sites", nil)
if err != nil {
    fmt.Println(err)
}

Набор примитивов для более сложных операций

Существует несколько примитивов для продвинутых операций, таких как комплексная синхронизация и выбор лидера. Перечислим несколько методов:

  • Acquire используется для операций приобретения(acquisition). Можно применять к Key, Flags, Value и Session. В случае удачи возвращает true, иначе false.
  • CAS используется для операций "проверить и установить"(Check-And-Set). Работает с Key, ModifyIndex, Flags и Value. В случае удачи возвращает true, иначе false.
  • DeleteCAS эта операция аналогична CAS. Работает с Key и ModifyIndex. В случае удачи возвращает true, иначе false.
  • Release используется для лока релизной операции. Работает с Key, Flags, Value и Session. В случае удачи возвращает true, иначе false.

Заключение

В этой статья мы рассмотрели API k/v хранилище consul'a и его использование в рамках пакета. У consul'a еще много других возможностей, кроме k/v хранилища. И очень круто, что пакет для работы API написан самими разработчиками consul.