
⏰ 🔥 Un proxy TCP per simulare condizioni di rete e di sistema per test di caos e resilienza
Toxiproxy è un framework per simulare condizioni di rete. È realizzato specificamente per funzionare in ambienti di test, CI e sviluppo, supportando la manomissione deterministica delle connessioni, ma con il supporto per il caos randomizzato e la personalizzazione. Toxiproxy è lo strumento di cui hai bisogno per dimostrare con i test che la tua applicazione non ha singoli punti di fallimento. Lo stiamo usando con successo in tutti gli ambienti di sviluppo e test presso Shopify da ottobre 2014. Consulta il nostro [post sul blog][blog] sulla resilienza per maggiori informazioni.
L'utilizzo di Toxiproxy è composto da due parti. Un proxy TCP scritto in Go (contenuto in questo repository) e un client che comunica con il proxy tramite HTTP. Configuri la tua applicazione in modo che tutte le connessioni di test passino attraverso Toxiproxy e puoi quindi manipolarne lo stato tramite HTTP. Consulta Utilizzo di seguito per scoprire come configurare il tuo progetto.
Ad esempio, per aggiungere 1000ms di latenza alla risposta di MySQL dal client Ruby:
Toxiproxy[:mysql_master].downstream(:latency, latency: 1000).apply do
Shop.first # this takes at least 1s
end
Per abbattere tutte le istanze Redis:
Toxiproxy[/redis/].down do
Shop.first # this will throw an exception
end
Mentre gli esempi in questo README sono attualmente in Ruby, nulla ti impedisce di creare un client in qualsiasi altro linguaggio (vedi Clients).
Quelli esistenti che abbiamo trovato non fornivano il tipo di API dinamica di cui
avevamo bisogno per i test di integrazione e unitari. Strumenti Linux come nc e
simili non sono multipiattaforma e richiedono i privilegi di root, il che li rende
problematici negli ambienti di test, sviluppo e CI.
Vediamo un esempio con un'applicazione Rails. Nota che Toxiproxy non è in alcun modo legato a Ruby, è stato semplicemente il nostro primo caso d'uso. Puoi vedere l'esempio completo su sirupsen/toxiproxy-rails-example. Per iniziare subito, vai direttamente alla sezione Utilizzo.
Per il nostro popolare blog, per qualche motivo stiamo memorizzando i tag dei nostri post in
Redis e i post stessi in MySQL. Potremmo avere una classe Post che
include alcuni metodi per manipolare i tag in un insieme Redis:
class Post < ActiveRecord::Base
# Return an Array of all the tags.
def tags
TagRedis.smembers(tag_key)
end
# Add a tag to the post.
def add_tag(tag)
TagRedis.sadd(tag_key, tag)
end
# Remove a tag from the post.
def remove_tag(tag)
TagRedis.srem(tag_key, tag)
end
# Return the key in Redis for the set of tags for the post.
def tag_key
"post:tags:#{self.id}"
end
end
Abbiamo deciso che generare un errore durante la scrittura nel tag data store (aggiunta/rimozione) vada bene. Tuttavia, se il tag data store è inattivo, dovremmo essere in grado di visualizzare il post senza tag. Potremmo semplicemente intercettare Redis::CannotConnectError attorno alla chiamata Redis SMEMBERS nel metodo tags. Usiamo Toxiproxy per verificarlo.
Poiché abbiamo già installato Toxiproxy ed è in esecuzione sulla nostra macchina, possiamo passare al passaggio 2. È qui che dobbiamo assicurarci che Toxiproxy abbia una mappatura per i tag Redis. A config/boot.rb (prima che venga stabilita qualsiasi connessione) aggiungiamo:
require 'toxiproxy'
Toxiproxy.populate([
{
name: "toxiproxy_test_redis_tags",
listen: "127.0.0.1:22222",
upstream: "127.0.0.1:6379"
}
])
Poi in config/environments/test.rb impostiamo TagRedis come client Redis
che si connette a Redis tramite Toxiproxy aggiungendo questa riga:
TagRedis = Redis.new(port: 22222)
Tutte le chiamate nell'ambiente di test ora passano attraverso Toxiproxy. Ciò significa che possiamo aggiungere uno unit test in cui simuliamo un guasto:
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
Il test fallisce con Redis::CannotConnectError. Perfetto! Toxiproxy ha abbattuto
Redis con successo per la durata della closure. Sistemiamo il metodo tags
per renderlo resiliente:
def tags
TagRedis.smembers(tag_key)
rescue Redis::CannotConnectError
[]
end
I test passano! Ora abbiamo uno unit test che dimostra che il recupero dei tag quando Redis è inattivo restituisce un array vuoto, invece di lanciare un'eccezione. Per una copertura completa dovresti anche scrivere un integration test che racchiude il recupero dell'intera pagina del blog post quando Redis è inattivo.
L'applicazione di esempio completa si trova su sirupsen/toxiproxy-rails-example.
Configurare un progetto per utilizzare Toxiproxy consiste in tre passaggi:
Linux
Vedi Releases per i binari
e i pacchetti di sistema più recenti per la tua architettura.
Ubuntu
$ wget -O toxiproxy-2.13.0.deb https://github.com/Shopify/toxiproxy/releases/download/v2.13.0/toxiproxy_2.13.0_linux_amd64.deb
$ sudo dpkg -i toxiproxy-2.13.0.deb
$ sudo service toxiproxy start
OS X
Con Homebrew:
$ brew tap shopify/shopify
$ brew install toxiproxy