
⏰ 🔥 TCP-прокси для имитации сетевых и системных условий с целью тестирования хаоса и отказоустойчивости
Toxiproxy — это фреймворк для моделирования сетевых условий. Он создан специально для работы в средах тестирования, CI и разработки, поддерживая детерминированное вмешательство в соединения, но также с поддержкой случайного хаоса и настройки. Toxiproxy — это инструмент, который нужен вам, чтобы с помощью тестов доказать, что ваше приложение не имеет единых точек отказа. Мы успешно используем его во всех средах разработки и тестирования в Shopify с октября 2014 года. Смотрите наш [пост в блоге][blog] об отказоустойчивости для получения дополнительной информации.
Использование Toxiproxy состоит из двух частей. TCP-прокси, написанный на Go (именно он содержится в этом репозитории), и клиент, взаимодействующий с прокси по HTTP. Вы настраиваете своё приложение так, чтобы все тестовые соединения проходили через Toxiproxy и затем можете управлять их состоянием через HTTP. Смотрите Использование ниже о том, как настроить ваш проект.
Например, чтобы добавить 1000 мс задержки к ответу MySQL из клиента Ruby:```ruby Toxiproxy[:mysql_master].downstream(:latency, latency: 1000).apply do Shop.first # this takes at least 1s end
Чтобы остановить все экземпляры Redis:```ruby
Toxiproxy[/redis/].down do
Shop.first # this will throw an exception
end
Хотя примеры в этом README в настоящее время написаны на Ruby, ничто не мешает вам создать клиент на любом другом языке (см. Клиенты).
Существующие решения, которые мы нашли, не предоставляли такого динамического API, который был нужен нам для интеграционного и модульного тестирования. Инструменты Linux вроде nc и подобные не кроссплатформенны и требуют прав root, что делает их проблематичными в тестовых средах, средах разработки и CI.
Давайте разберём пример с приложением на Rails. Обратите внимание, что Toxiproxy никак не привязан к Ruby — это просто наш первый случай использования. Полный пример можно посмотреть на sirupsen/toxiproxy-rails-example. Чтобы сразу приступить к делу, перейдите к разделу Использование.
Для нашего популярного блога мы по какой-то причине храним теги записей в
Redis, а сами записи — в MySQL. У нас может быть класс Post, который
содержит несколько методов для работы с тегами в множестве Redis:```ruby
class Post < ActiveRecord::Base
def tags TagRedis.smembers(tag_key) end
def add_tag(tag) TagRedis.sadd(tag_key, tag) end
def remove_tag(tag) TagRedis.srem(tag_key, tag) end
def tag_key "post:tags:#{self.id}" end end
Мы решили, что возникновение ошибки при записи в хранилище тегов
(добавление/удаление) допустимо. Однако, если хранилище тегов недоступно, мы
должны иметь возможность видеть пост без тегов. Мы можем просто перехватить
`Redis::CannotConnectError` вокруг вызова Redis `SMEMBERS` в методе `tags`.
Давайте используем Toxiproxy, чтобы проверить это.
Поскольку мы уже установили Toxiproxy и он запущен на нашей машине, мы можем
перейти к шагу 2. Здесь нам нужно убедиться, что Toxiproxy имеет сопоставление
для тегов Redis. В `config/boot.rb` (до установления любого соединения) мы
добавляем:```ruby
require 'toxiproxy'
Toxiproxy.populate([
{
name: "toxiproxy_test_redis_tags",
listen: "127.0.0.1:22222",
upstream: "127.0.0.1:6379"
}
])
Затем в config/environments/test.rb мы задаём TagRedis как Redis-клиент, который подключается к Redis через Toxiproxy, добавив эту строку:```ruby
TagRedis = Redis.new(port: 22222)
Все вызовы в тестовой среде теперь проходят через Toxiproxy. Это означает, что мы можем
добавить модульный тест, в котором мы имитируем сбой:```ruby
test "should return empty array when tag redis is down when listing tags" do
@post.add_tag "mammals"
# Take down all Redises in Toxiproxy
Toxiproxy[/redis/].down do
assert_equal [], @post.tags
end
end
Тест завершается ошибкой Redis::CannotConnectError. Отлично! Toxiproxy успешно отключил Redis на время выполнения замыкания. Давайте сделаем метод tags устойчивым к сбоям:```ruby
def tags
TagRedis.smembers(tag_key)
rescue Redis::CannotConnectError
[]
end
Тесты проходят! Теперь у нас есть модульный тест, который доказывает, что при недоступном Redis
получение тегов возвращает пустой массив, а не выбрасывает исключение. Для полного
покрытия следует также написать интеграционный тест, который охватывает получение
всей страницы блога, когда Redis недоступен.
Полный пример приложения находится по адресу
[sirupsen/toxiproxy-rails-example](https://github.com/sirupsen/toxiproxy-rails-example).
## Использование
Настройка проекта для использования Toxiproxy состоит из трёх шагов:
1. Установка Toxiproxy
2. Заполнение Toxiproxy
3. Использование Toxiproxy
### 1. Установка Toxiproxy
**Linux**
Обратитесь к [`Releases`](https://github.com/Shopify/toxiproxy/releases) за последними
бинарными файлами и системными пакетами для вашей архитектуры.
**Ubuntu**```bash
$ wget -O toxiproxy-2.1.4.deb https://github.com/Shopify/toxiproxy/releases/download/v2.1.4/toxiproxy_2.1.4_amd64.deb
$ sudo dpkg -i toxiproxy-2.1.4.deb
$ sudo service toxiproxy start
OS X
С помощью Homebrew:```bash $ brew tap shopify/shopify $ brew install toxiproxy
Или с помощью [MacPorts](https://www.macports.org/):```bash
$ port install toxiproxy
Windows