Golangで画像プロキシサーバをつくった

Graidという画像プロキシサーバをつくりました。
外のサーバから画像を取得してリサイズしたりして返す、ということをするサーバです。

Golangでなんかミドルウェア的なものが作ってみたかったのでやってみました。

Graidについてのスライド

エンジニアが集まってお昼ご飯を食べながら技術的なネタを話すテックランチという会が社内で定期的に開催されているので、Graidについて話してきました。

Graid // Speaker Deck

機能

http://localhost:8080/path/to/image.jpg:w400:h300:q80

例えば8080ポートで起動させてURLにアクセスすると、

  • オリジンの画像サーバ(設定ファイルに設定する)の/path/to/image.jpgを取ってきて
  • 横400px、縦300pxにリサイズして画質80%に加工して
  • レスポンスを返す

という処理をします。:w400:h300:q80の部分が画像加工のクエリ、その中の:wとか:hが処理内容を決定するオペレータです。つなげて複数することができます。

オペレータ

リサイズの他に、今のところ以下のオペレータがあります。

オペレータ 説明
:w 画像の横幅を指定してリサイズ。(:hを指定しない場合は横幅に合わせてリサイズ) :w400
:h 画像の縦幅を指定してリサイズ。(:wを指定しない場合は縦幅に合わせてリサイズ) :h300
:q jpegの品質(1〜100) :q80
:c クロップ。2点の座標をカンマ区切りで指定する。 :c40,10,220,240
:grayscale 白黒フィルター。 :grayscale1
:sepia セピアフィルター。(1〜100) :sepia100
:contrast コントラストの調整。 :contrast50
:brightness 明るさの調整。 :brightness-50
:saturation 彩度の調整。 :saturation80
:colorize カラーフィルター。 :colorize240,50,100
:colorbalance カラーバランスフィルター。 :colorbalance20,-20,0

画像処理

いまのところjpegだけ対応しています。

画像処理部分はgiftに渡す仕組みになっています。簡単に加工ができて便利です。 最初はリサイズとクロップができればいいなぐらいに考えていたのですが、giftがとても便利なので他のオペレータもつくってみました。

フィルターを組み合わせてインスタグラムみたいなオペレータがつくれそうですね。

ImageMagickのラッパーであるgographics/imagickを使うかImageMagickのconvertをキックするか、迷ってライブラリを探していたのですが、 fawick/speedtest-resizeをみてgiftを知りました。各ライブラリで計測した画像リサイズ速度が載っています。

キャッシュ

外部から取得して加工した画像はクエリごとにキャッシュして、次のアクセスから速くレスポンスを返します。キャッシュの保存先はファイルかRedisです。設定ファイルで指定することができます。

また、クエリのURLに?nocache=1をつけるとキャッシュを無視してもう一度リモートから画像を取得します。

設定ファイル

graid/graid.toml at master · violetyk/graid · GitHubが設定ファイルです。起動するポート、ワーカープールサイズ、キャッシュ、オリジンサーバの指定などを設定します。

TOMLで記述します。TOMLはGolang製静的サイトジェネレータのHugoで使われています。階層構造や配列がかけるiniファイルのようなフォーマットで、よさげです。各種言語で実装されたパーサやエディタのシンタックスハイライトもそろっています。

今後

daemonといいつつまだデーモンとして実装できていないのでなのとかしたいです。

あとは、

  • jpeg以外の画像フォーマットへ対応
  • オペレータの指定をコロンではなくてGETパラメータにするモードの作成
  • タイムアウトの指定ができるように
  • HTTPクライアントの同一ホストへの最大コネクション数の指定
  • 各処理のベンチマークする仕組み

を実装しようかと思っています。