Дизайн клієнта etcd
Дизайн клієнта etcd
Gyuho Lee (github.com/gyuho, Amazon Web Services, Inc.), Joe Betz (github.com/jpbetz, Google Inc.)
Вступ
Сервер etcd довів свою надійність за роки тестування з інʼєкціями відмов. Більшість складної логіки застосунків вже обробляється сервером etcd та його сховищами даних (наприклад, членство в кластері є прозорим для клієнтів, з пересиланням пропозицій на лідера на рівні Raft). Хоча компоненти сервера є правильними, його поєднання з клієнтом вимагає іншого набору складних протоколів для забезпечення його правильності та високої доступності в умовах збоїв. Ідеально, сервер etcd надає один логічний вигляд кластера з багатьох фізичних машин, а клієнт реалізує автоматичне перемикання між репліками. Цей документ описує архітектурні рішення клієнта та деталі їх реалізації.
Глосарій
clientv3: Офіційний Go клієнт для API etcd v3.
clientv3-grpc1.0: Офіційна реалізація клієнта з grpc-go v1.0.x, яка використовується в останній версії etcd v3.1.
clientv3-grpc1.7: Офіційна реалізація клієнта з grpc-go v1.7.x, яка використовується в останній версії etcd v3.2 та v3.3.
clientv3-grpc1.23: Офіційна реалізація клієнта з grpc-go v1.23.x, яка використовується в останній версії etcd v3.4.
Balancer: Балансувальник навантаження клієнта etcd, який реалізує механізм повторних спроб та перемикання. Клієнт etcd повинен автоматично балансувати навантаження між кількома точками доступу.
Endpoints: Список точок доступу сервера etcd, до яких можуть підключатися клієнти. Зазвичай це 3 або 5 URL-адрес клієнтів кластера etcd.
Pinned endpoint: При налаштуванні з кількома точками доступу, балансувальник клієнта <= v3.3 вибирає лише одну точку доступу для встановлення TCP-зʼєднання, щоб зберегти загальну кількість відкритих зʼєднань з кластером etcd. У v3.4 балансувальник використовує кругове чергування закріплених точок доступу для кожного запиту, таким чином рівномірно розподіляючи навантаження.
Client Connection: TCP-зʼєднання, яке було встановлено з сервером etcd через gRPC Dial.
Sub Connection: Інтерфейс gRPC SubConn. Кожне суб-зʼєднання містить список адрес. Балансувальник створює SubConn зі списку розвʼязаних адрес. gRPC ClientConn може відповідати кільком SubConn (наприклад, example.com розвʼязується в 10.10.10.1 та 10.10.10.2 двох суб-зʼєднань). Балансувальник etcd v3.4 використовує внутрішній розвʼязувач для встановлення одного суб-зʼєднання для кожної точки доступу.
Transient disconnect: Коли сервер gRPC повертає статус помилки code Unavailable.
Вимоги до клієнта
Правильність. Запити можуть зазнати невдачі у разі збоїв сервера. Однак, це ніколи не порушує гарантії узгодженості: глобальні властивості впорядкування, ніколи не записує пошкоджені дані, семантика “не більше одного разу” для змінних операцій, спостереження ніколи не спостерігає часткові події тощо.
Життєздатність. Сервери можуть зазнати невдачі або короткочасно відключитися. Клієнти повинні продовжувати роботу в будь-якому випадку. Клієнти не повинні зависати в очікуванні повернення сервера з офлайну, якщо не налаштовані на це. Ідеально, клієнти виявляють недоступні сервери за допомогою HTTP/2 ping та перемикаються на інші вузли з чіткими повідомленнями про помилки.
Ефективність. Клієнти повинні працювати ефективно з мінімальними ресурсами: попередні TCP-зʼєднання повинні бути відповідним чином закриті після перемикання точки доступу. Механізм перемикання повинен ефективно передбачати наступну репліку для підключення, без марних повторних спроб на невдалих вузлах.
Переносимість. Офіційний клієнт повинен бути чітко задокументований, а його реалізація повинна бути застосовною до інших мов програмування. Обробка помилок між різними мовами програмування повинна бути узгодженою. Оскільки etcd повністю підтримує gRPC, реалізація повинна бути тісно повʼязана з довгостроковими цілями дизайну gRPC (наприклад, політика повторних спроб повинна бути сумісною з повторними спробами gRPC). Оновлення між двома версіями клієнта повинні бути безперервними.
Огляд клієнта
Клієнт etcd реалізує наступні компоненти:
- балансувальник, який встановлює gRPC-зʼєднання з кластером etcd,
- API клієнт, який надсилає RPC-запити на сервер etcd, та
- обробник помилок, який вирішує, чи повторювати невдалий запит або перемикати точки доступу.
Мови можуть відрізнятися у способі встановлення початкового зʼєднання (наприклад, налаштування TLS), способі кодування та надсилання повідомлень Protocol Buffer на сервер, у способі обробки потокових RPC тощо. Однак, помилки, повернуті сервером etcd, будуть однаковими. Так само повинна бути обробка помилок та політика повторних спроб.
Наприклад, сервер etcd може повернути "rpc error: code = Unavailable desc = etcdserver: request timed out", що є тимчасовою помилкою, яка очікує повторних спроб. Або повернути rpc error: code = InvalidArgument desc = etcdserver: key is not provided, що означає, що запит був недійсним і не повинен повторюватися. Go клієнт може розбирати помилки за допомогою google.golang.org/grpc/status.FromError, а Java клієнт за допомогою io.grpc.Status.fromThrowable.
clientv3-grpc1.0: Огляд балансувальника
clientv3-grpc1.0 підтримує кілька TCP-зʼєднань при налаштуванні з кількома точками доступу etcd. Потім вибирає одну адресу і використовує її для надсилання всіх клієнтських запитів. Закріплена адреса підтримується до закриття обʼєкта клієнта (див. Рисунок 1). Коли клієнт отримує помилку, він випадковим чином вибирає іншу адресу і повторює спробу.

clientv3-grpc1.0: Обмеження балансувальника
clientv3-grpc1.0 відкриття кількох TCP-зʼєднань може забезпечити швидше перемикання балансувальника, але вимагає більше ресурсів. Балансувальник не розуміє стану справності вузла або членства в кластері. Тому можливо, що балансувальник застрягне з одним невдалим або розділеним вузлом.
clientv3-grpc1.7: Огляд балансувальника
clientv3-grpc1.7 підтримує лише одне TCP-зʼєднання з обраним сервером etcd. При наданні кількох точок доступу кластера, клієнт спочатку намагається підʼєднатися до всіх. Як тільки одне зʼєднання встановлено, балансувальник закріплює адресу, закриваючи інші (див. Рисунок 2). Закріплена адреса підтримується до закриття обʼєкта клієнта. Помилка, від сервера або мережевого збою клієнта, надсилається обробнику помилок клієнта (див. Рисунок 3).


Обробник помилок клієнта приймає помилку від сервера gRPC і вирішує, чи повторювати спробу на тій самій точці доступу, чи перемикатися на інші адреси, на основі коду та повідомлення про помилку (див. Рисунок 4 та Рисунок 5).


Потокові RPC, такі як Watch та KeepAlive, часто запитуються без тайм-аутів. Замість цього клієнт може надсилати періодичні HTTP/2 ping для перевірки стану закріпленої точки доступу; якщо сервер не відповідає на ping, балансувальник перемикається на інші точки доступу (див. Рисунок 6).

clientv3-grpc1.7: Обмеження балансувальника
clientv3-grpc1.7 балансувальник надсилає HTTP/2 keepalives для виявлення відключень від потокових запитів. Це простий механізм ping сервера gRPC і не враховує членство в кластері, тому не може виявити мережеві розділення. Оскільки розділений сервер gRPC все ще може відповідати на ping клієнта, балансувальник може застрягти з розділеним вузлом. Ідеально, keepalive ping виявляє розділення і запускає перемикання точки доступу до тайм-ауту запиту (див. etcd#8673 та Рисунок 7).

clientv3-grpc1.7 балансувальник підтримує список несправних точок доступу. Відключені адреси додаються до списку “unhealthy” і вважаються недоступними до закінчення часу очікування, який жорстко закодований як тайм-аут зʼєднання зі стандартним значенням 5 секунд. Балансувальник може мати хибні позитивні результати щодо того, які точки доступу є несправними. Наприклад, точка доступу A може повернутися відразу після того, як була внесена до чорного списку, але все ще буде недоступною протягом наступних 5 секунд (див. Рисунок 8).
clientv3-grpc1.0 страждав від тих самих проблем.

Upstream gRPC Go вже перейшов на новий інтерфейс балансувальника. Наприклад, реалізація балансувальника clientv3-grpc1.7 використовує новий балансувальник gRPC і намагається бути сумісною зі старими поведінками балансувальника. Хоча його сумісність була підтримана досить добре, клієнт etcd все ще страждав від тонких змін поведінки. Крім того, розробники gRPC рекомендують не покладатися на старий інтерфейс балансувальника. Загалом, щоб отримати кращу підтримку від upstream, найкраще бути в синхронізації з останніми випусками gRPC. І нові функції, такі як політика повторних спроб, можуть не бути перенесені на гілку gRPC 1.7. Тому як сервер, так і клієнт etcd повинні перейти на останні версії gRPC.
clientv3-grpc1.23: Огляд балансувальника
clientv3-grpc1.7 настільки тісно повʼязаний зі старим інтерфейсом gRPC, що кожне оновлення залежності gRPC порушувало поведінку клієнта. Більшість зусиль з розробки та налагодження були присвячені виправленню цих змін поведінки клієнта. В результаті його реалізація стала надто складною з поганими припущеннями щодо підключень сервера.
Основна мета clientv3-grpc1.23 — спростити логіку перемикання балансувальника; замість підтримки списку несправних точок доступу, який може бути застарілим, просто використовувати кругове чергування до наступної точки доступу, коли клієнт відключається від поточної точки доступу. Він не припускає стану точки доступу. Таким чином, більше не потрібне складне відстеження стану (див. Рисунок 8 та вище). Оновлення до clientv3-grpc1.23 не повинно викликати проблем; всі зміни були внутрішніми, зберігаючи всі зворотні сумісності.
Внутрішньо, при наданні кількох кінцевих точок, clientv3-grpc1.23 створює кілька суб-зʼєднань (одне суб-зʼєднання для кожної точки доступу), тоді як clientv3-grpc1.7 створює лише одне зʼєднання з закріпленою точкою доступу (див. Рисунок 9). Наприклад, у кластері з 5 вузлів, балансувальник clientv3-grpc1.23 вимагатиме 5 TCP-зʼєднань, тоді як clientv3-grpc1.7 вимагатиме лише одне. Зберігаючи пул TCP-зʼєднань, clientv3-grpc1.23 може споживати більше ресурсів, але забезпечує більш гнучкий балансувальник навантаження з кращою продуктивністю перемикання. Стандартна політика балансування — кругове чергування, але її можна легко розширити для підтримки інших типів балансувальників (наприклад, сила двох, вибір лідера тощо). clientv3-grpc1.23 використовує групу розвʼязувачів gRPC та реалізує політику вибору балансувальника, щоб делегувати складну роботу балансування upstream gRPC. З іншого боку, clientv3-grpc1.7 вручну обробляє кожне зʼєднання gRPC та перемикання балансувальника, що ускладнює реалізацію. clientv3-grpc1.23 реалізує повторні спроби в ланцюжку перехоплювачів gRPC, який автоматично обробляє внутрішні помилки gRPC та дозволяє більш досконалі політики повторних спроб, такі як резервне копіювання, тоді як clientv3-grpc1.7 вручну інтерпретує помилки gRPC для повторів.

clientv3-grpc1.23: Обмеження балансувальника
Покращення можуть бути зроблені шляхом кешування статусу кожної точки доступу. Наприклад, балансувальник може попередньо пінгувати кожен сервер, щоб підтримувати список справних кандидатів, і використовувати цю інформацію під час кругового чергування. Або при відключенні балансувальник може пріоритизувати справні точки доступу. Це може ускладнити реалізацію балансувальника, тому це можна вирішити в наступних версіях.
Пінг keepalive на стороні клієнта все ще не враховує мережеві розділення. Потоковий запит може застрягти з розділеним вузлом. Потрібно реалізувати розширену службу перевірки стану, щоб зрозуміти членство в кластері (див. etcd#8673 для отримання додаткової інформації).

Наразі логіка повторних спроб обробляється вручну як перехоплювач. Це можна спростити за допомогою офіційних повторних спроб gRPC.
Відгук
Чи це було корисним?
Раді чути! Будь ласка, повідомте нам, як ми можемо зробити краще.
Дуже шкода це чути. Будь ласка, повідомте нам, як ми можемо зробити краще.