
⏰ 🔥 Un proxy TCP para simular condiciones de red y del sistema para pruebas de caos y resiliencia
Toxiproxy es un framework para simular condiciones de red. Está hecho específicamente para funcionar en entornos de pruebas, CI y desarrollo, con manipulación determinista de las conexiones, pero también con soporte para caos aleatorio y personalización. Toxiproxy es la herramienta que necesitas para demostrar con pruebas que tu aplicación no tiene puntos únicos de fallo. Lo hemos estado utilizando con éxito en todos los entornos de desarrollo y pruebas de Shopify desde octubre de 2014. Consulta nuestra [entrada de blog][blog] sobre resiliencia para más información.
El uso de Toxiproxy consta de dos partes. Un proxy TCP escrito en Go (lo que contiene este repositorio) y un cliente que se comunica con el proxy a través de HTTP. Configuras tu aplicación para que todas las conexiones de prueba pasen por Toxiproxy y luego puedes manipular su estado mediante HTTP. Consulta Uso a continuación para configurar tu proyecto.
Por ejemplo, para añadir 1000ms de latencia a la respuesta de MySQL desde el cliente Ruby:```ruby Toxiproxy[:mysql_master].downstream(:latency, latency: 1000).apply do Shop.first # this takes at least 1s end
Para derribar todas las instancias de Redis:```ruby
Toxiproxy[/redis/].down do
Shop.first # this will throw an exception
end
Mientras que los ejemplos en este README están actualmente en Ruby, nada te impide crear un cliente en cualquier otro lenguaje (consulta Clientes).
Los proxies existentes que encontramos no proporcionaban el tipo de API dinámica que necesitábamos para
pruebas de integración y unitarias. Las herramientas de Linux como nc y demás no son
multiplataforma y requieren privilegios de root, lo que las hace problemáticas en entornos de prueba,
desarrollo e integración continua (CI).
Veamos un ejemplo con una aplicación Rails. Ten en cuenta que Toxiproxy no está ligado de ninguna manera a Ruby, solo ha sido nuestro primer caso de uso. Puedes ver el ejemplo completo en sirupsen/toxiproxy-rails-example. Para empezar de inmediato, salta a Uso.
Para nuestro popular blog, por alguna razón estamos almacenando las etiquetas de nuestras publicaciones en
Redis y las publicaciones en sí en MySQL. Podríamos tener una clase Post que
incluya algunos métodos para manipular etiquetas en un conjunto de 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
Hemos decidido que provocar un error al escribir en el almacén de datos de
etiquetas (añadir/eliminar) es aceptable. Sin embargo, si el almacén de datos
de etiquetas está caído, deberíamos poder ver la publicación sin etiquetas.
Podríamos simplemente rescatar el `Redis::CannotConnectError` alrededor de la
llamada `SMEMBERS` de Redis en el método `tags`. Usemos Toxiproxy para probar eso.
Como ya hemos instalado Toxiproxy y se está ejecutando en nuestra máquina, podemos
saltar al paso 2. Aquí es donde necesitamos asegurarnos de que Toxiproxy tenga una asignación para
las etiquetas de Redis. En `config/boot.rb` (antes de que se realice cualquier conexión) añadimos:```ruby
require 'toxiproxy'
Toxiproxy.populate([
{
name: "toxiproxy_test_redis_tags",
listen: "127.0.0.1:22222",
upstream: "127.0.0.1:6379"
}
])
Luego, en config/environments/test.rb, configuramos TagRedis para que sea un cliente de Redis
que se conecta a Redis a través de Toxiproxy añadiendo esta línea:```ruby
TagRedis = Redis.new(port: 22222)
Todas las llamadas en el entorno de pruebas ahora pasan a través de Toxiproxy. Eso significa que podemos añadir una prueba unitaria donde simulamos un fallo:```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
The test fails with Redis::CannotConnectError. ¡Perfecto! Toxiproxy apagó
Redis correctamente durante la duración del bloque. Arreglemos el método tags
para que sea resiliente:```ruby
def tags
TagRedis.smembers(tag_key)
rescue Redis::CannotConnectError
[]
end
¡Las pruebas pasan! Ahora tenemos una prueba unitaria que demuestra que obtener las etiquetas cuando Redis está caído devuelve un array vacío, en lugar de lanzar una excepción. Para una cobertura completa, también deberías escribir una prueba de integración que abarque la obtención de la página completa del blog cuando Redis está caído.
La aplicación de ejemplo completa está en
[sirupsen/toxiproxy-rails-example](https://github.com/sirupsen/toxiproxy-rails-example).
## Uso
Configurar un proyecto para usar Toxiproxy consta de tres pasos:
1. Instalar Toxiproxy
2. Poblar Toxiproxy
3. Usar Toxiproxy
### 1. Instalación de Toxiproxy
**Linux**
Consulte los [`Releases`](https://github.com/Shopify/toxiproxy/releases) para obtener los binarios y paquetes de sistema más recientes para su arquitectura.
**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