HerokuがErlang製のHTTP Proxyを公開しましたね。少し中身見てみたのですが、彼らもForkしたCowboyを使ってProxyを構築しているっぽい。他にも軽く読んでは見たのですが、Erlangも多少読めるようになっててびっくり。
https://github.com/heroku/vegur
話は少し変わって、[Elixir]Elixirで簡単なHTTP Proxyを書いているときに出会ったエラーのメモで、Supervisorのworkerごとに異なるportを待ち受けてそれぞれが独立したproxyとして動作する機構を書いてたのですが、PIDの衝突の発生で思った感じでできてませんでした。
でも、考えるほどにやっぱりできるはずだよなーと思いもう少しやってみました。それと、先ほどのVegurでもやっぱり異なるPortで待ち受ける処理が書いてあって、やっぱり実装間違ってるなと確信。
もう一度考え直してみると、今回は考えた通りに書いていったら期待通り動作することができました。
結局は、 Plug.Adapters.Cowboy.http に対して与えている ref の与え方が間違っていた、という情けないミスでした…
リポジトリ: https://github.com/KazuCocoa/http_proxy
前回の誤り
def start_link(proxy) do Plug.Adapters.Cowboy.http __MODULE__, [ref: proxy[:name]], port: proxy[:port].port end
今回
def start_link(proxy) do Plug.Adapters.Cowboy.http(__MODULE__, [], [port: proxy.port, ref: String.to_atom(module_name)]) end
これで、例えば以下のような実装を書いたときに、Supervisorで監視される各々のWorkerが異なるProcessとPortでリクエストを待つことができるようになります。 HttpProxy.Handle のモジュールに、先ほどのCowboyの起動が実装されていることを想定しています。
def init(:ok) do
import Supervisor.Spec
[%{port: 4000, ...}, %{port: 4001, ...}]
|> Enum.reduce([], fn proxy, acc ->
module_name = "HttpProxy.Handle#{proxy.port}"
[worker(HttpProxy.Handle, [[proxy, module_name]], [id: String.to_atom(module_name)]) | acc]
end)
|> supervise(strategy: :one_for_one)
end
修正時のコミット(ファイルの置き換えあり): https://github.com/KazuCocoa/http_proxy/commit/8b5368f15a2e149ac7069a33379e4c190cf93374
Elixir/Erlangさわりはじめて、大学の頃もでしたが、やっぱり私は分散系に対して技術的な興味を持っているのだなーと感じました。