On friday I started reading up on lyft’s recently-announced envoy proxy/communication bus/general awesome piece of kit.

Envoy can be used in many different ways:

  • As a reverse proxy in front of some service
  • As a service-to-service communication provider (including service discovery)
  • As a local proxy to some remote service

This last configuration piqued my interest, as I have been interested in solutions for doing circuit breaking ever since I learned of netflix’s hysterix library.

But retrofitting a circuit breaking onto an existing and rather diverse architecture can be a challenge. So, could envoy act as a low-level component that allowed circuit breaking, request resilience and service discovery to be slowly introduced into an existing architecture without much effort?

To try it out, I built the enviado gem. Enviado can be used to quickly start an envoy service along side a normal ruby application, and then make requests using envoy’s many features.

I’ve also developed a small example testing some of envoy’s resilience features:

require 'faraday'
require 'pry'
require 'rack'
require 'enviado'

REQUESTS=100
PORT=8080

def start_web_server
  Thread.new do
    Rack::Handler::WEBrick.run(
      proc { |env|
        if rand <= 0.5
          [200, {"Content-Type" => "text/html"}, ["Hello, World!\n"]]
        else
          [500, {"Content-Type" => "text/html"}, ["I am a small potato\n"]]
        end
      },
      Host: '127.0.0.1',
      Port: PORT,
      Logger: WEBrick::Log.new('/dev/null'),
      AccessLog: [],
    )
  end

  sleep 1
end

def perform_requests(url)
  connection = Faraday.new(url: url)

  codes = Hash.new { |h, k| h[k] = 0 }

  REQUESTS.times do
    response = connection.get
    codes[response.status] += 1
  end

  codes
end

def main
  puts "Starting web server..."
  start_web_server

  puts "Doing #{REQUESTS} requests WITHOUT enviado..."
  puts "Results: #{perform_requests('http://localhost:8080')}"

  puts "Starting enviado"
  Enviado.start(config_path: 'envoy_service_to_service_simple.json')

  puts "Doing #{REQUESTS} requests WITH enviado..."
  puts "Results: #{perform_requests('http://localhost:9211')}"
end

main

And the resulting sample output:

Starting web server...
Doing 100 requests WITHOUT enviado...
Results: {500=>44, 200=>56}
Starting enviado
[2016-11-19 13:14:58.039][211][warning][main] initializing epoch 0 (hot restart version=3.2490504)
[2016-11-19 13:14:58.041][211][warning][main] all clusters initialized. starting workers
[2016-11-19 13:14:58.041][211][warning][main] starting main dispatch loop
Doing 100 requests WITH enviado...
Results: {200=>100}

This was just a quick hack, so feedback is very welcome. It currently ships only with an envoy binary for linux, but you can either run it via docker or PRs that add binaries for other platforms are very welcome!

P.s.: If you want to play with envoy, but prefer elixir, the awesome Tiago Sousa has got you covered with elixado. Kudos to him!

Addendum: If you’re interested on learning more about hysterix, I really recommend the Joker conf talk below by Tomasz Nurkiewicz: