Firebase HostingをCircleCIからDeployする

とにかく簡単なので、プロトタイピングのときから初めておくといい。

fierebaseを使ったことがあるなら入っていると思うけど、firebase-tools を事前にインストールしておこう npm install -g firebase-tools

CIからDeployするためのDeployキーを取得する。

firebase login:ci って打つとCI用のDeployキーを払い出してくれる。

Waiting for authentication...

✔  Success! Use this token to login on a CI server:

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Example: firebase deploy --token "$FIREBASE_TOKEN"

払い出されたキーはCircleCIの環境変数に登録して、Deploy時に実行すればOKという感じ。 ほんとうに簡単。

ちなみにプロジェクトを指定してDeployしたい場合は、このように指定すればオッケー

 firebase deploy --project "myproject-xxxxx" --token "$FIREBASE_TOKEN"

FaradayでHTTPリクエストを並列で実行する方法

FaradayはRubyでHTTPリクエストを行うことができるライブラリだけど、 特に工夫もなしに利用すると1つ1つリクエストを順番に直列に実行するようになっている。

もしなにかしらの事情でAPIを並列に叩きたい事情というものもあるので、 その場合は以下の用に対応すればOK。 事前にtyphoeusというGemが必要なので、予めインストールしておこう。

github.com

require 'typhoeus'
require 'typhoeus/adapters/faraday'

response1, response2 = nil

conn = Faraday.new(:url => "http://example.com") do |faraday|
  faraday.adapter :typhoeus
end

conn.in_parallel do
  response1 = conn.get('/one')
  response2 = conn.get('/two')

  # these will return nil here since the
  # requests haven't been completed
  response1.body
  response2.body
end

# at this point the response information you expected should be fully available to you.
response1.body # response1.status, etc
response2.body

こちらにはもう少し詳しく記載されている。 github.com

さらに、Faraday別に使わなくてもtyphoeus単体で利用することも当然可能です。 Faraday別にいらなくね?という場合はこちらを。

github.com

で、効果の程は...?

一体どのくらい早くなるのかと、処理した結果の順番が保証されているかが気になるところなので、 雑に実際のAPIにリクエストして簡単に比較してみることにした。

業務で20個のAPIに対してRubyでHTTPリクエストをする必要が出たので、簡単にAPIを20個叩くようなサンプルを作って雑にベンチ取った。 実際のエンドポイントを叩いているのだけど、これは自分の持っているWebhook送信のデバッグ用のアプリケーションなので、お気になさらずに。

gist22ea9304be7d995147528f0e6713ba59

まとめ

  • 20個のHTTPリクエストは並列化したほうが早い
  • 並列処理の場合、処理結果の順番とかに変更がないか気になるところだけど、順番は保証されているようだった。

Webhookを作るときのデバッグに役に立つものを作った

その名もWebhookDebugger 実はRailsのアプリケーションで15分位でシュッと書いたものを単機能だったのを、素のRackアプリケーションにして書き直したものである。

github.com

以下に簡単にこのアプリケーションについての機能を簡単に説明しておく。

基本はオウム返しをするだけのアプリケーションだが...

ロジックはすごい簡単で、GETで送るクエリパラメータやPOSTのform-dataないしはjsonをレスポンスに含めて返してくれるものである。 それだけだとただのオウム返しAPIでWebhookを作るときに何も便利にはならない。 そこで一工夫を凝らしてみることでWebhookを作るときに便利にしてみた。

1. どのパス、どのHTTPメソッドを指定してもリクエストを受ける取ることができる。

request.binなどの外部サービスでも良いのだけど、これは受け取る先のURLパスが決まってしまっている。 どのパスに送信するかというのをユニットテストレベルではなく、ちゃんと目で確認したいときに重宝するだろう。

  curl -XPOST http://localhost:9292/hoge/fuga | jq
  {
      "path": "/hoge/fuga",
      "request_method": "POST",
      "status_code": 200
  }

  curl http://localhost:9292/nick | jq
  {
      "path": "/nick",
      "response_status_code": 200,
      "request_method": "GET",
      "status_code": 200
  }

2. 任意のHTTPステータスコードを返させることができる

パラメータに response_status_codeというものが存在し、これに対して任意のHTTPステータスコードを設定することができる。 コレにより、5xx系のエラーレスポンスの場合はX分後に送信キューに再送処理を積む。他にも4xx系だったら再送処理無しで異常終了にする。あとは5回以上エラーになったら再送をやめる。 といったことの確認が簡単に出来るようになる。 以下のコードの場合、レスポンスのHTTPステータスコードが必ず401になるようになる。

  curl -XPOST http://localhost:9292/hoge/fuga?response_status_code=401 | jq
  {
      "path": "/hoge/fuga",
      "response_time": 1000,
      "response_status_code": 401,
      "request_method": "POST",
      "status_code": 200
  }

3. レスポンスの待機時間を設定することができる

パラメータに response_timeを設定することで、任意のミリ秒分だけ、レスポンスを返す時間を遅らせることができる。(waitで雑に設定しているだけなので、きっかり1秒とかではなく、ベストエフォートです。) コレによりサーバレスポンスに時間がかかってしまった場合、一旦失敗として扱って送信キューに再送処理を積む。といったことの確認が簡単になった。

  curl -XPOST http://localhost:9292/hoge/fuga?response_time=1000 | jq
 {
      "path": "/hoge/fuga",
      "response_time": 1000,
      "request_method": "POST",
      "status_code": 200
  }

以上の3つがあることで、 オウム返しによる、レスポンスでの送信内容にチェック、 エラー時のWebhookの再送機能やレスポンス待機時間によるエラーハンドリングなどが実現可能になった。

Webhook送信元のアプリケーションがdocker-composeで管理されている場合、Webhookの送信先として、このアプリケーションのコンテナを定義しておくのが良いと思う。

なかなかWebhook送信先のアプリケーションの実装について知識がないと、 こういうテストがなかなか難しかったり、送信先のアプリケーションの完成を待たないとちゃんとテストできないとかがありがちなのだけど、 このAPIを利用することでエラーケースのテストや再送のテストが用意になって、効率がグッと向上した。

どういったわけか、業務上Webhookを作る機会が何故か多かったし、 これからもWebhook処理も書く必要はかならずあるので、このAPIは今後も重宝することだろう。

Sinatraアプリを素のRackアプリケーションに書き換えた

Rackとは

Webサーバ/Webアプリケーションフレームワーク間のインタフェースの役割を果たすライブラリで、 Rackを利用してフレームワークやアプリケーションのインターフェース部分を実装することで、 Webサーバを変更したり、逆にWebサーバを変更しないでもRackでインタフェースを実装されたフレームに置き換えることができる。

Rubyだと、WEBrickやUnicron、PumaにPassengerといろいろなアプリケーションサーバがあるけど、 多少挙動に違いがあるにせよ、RailsSinatraといった性質の異なるフレームワークが殆どのアプリケーションサーバで稼働するのは Rackでインタフェース部分を実装されているからである。

他の言語

むしろRubyのRackは後発で、Pythonで既にあった、WSGIに影響されて実装されたと言われている。 PerlではPSGIがそれに相当する。*1

Rackについてぼんやりと理解していたけど、 このあたりに乗っているリンクを眺めて更に理解を深めることができたので、興味があると見てみると良いでしょう。

github.com

アプリケーションを実際に書き換えてみた。

Rackってそう言えばちゃんと触ったこと無いな〜と思ったので、とりあえず触ってみることに。

なんかちょうどいい感じのSinatraアプリケーションのogp_parse_apiが転がっていたので、 今回はコレをSinatraから素のRackアプリケーションに書き換えて見ることにする。 ogp_parse_apiについては、過去のブログに機能的には詳しく書いているので、よかったら見てくれ。

webuilder240.hatenablog.com

というわけで簡単に作ったアプリケーションをRackだけで書き換えてみた。 ルーティングするものも1つしか無いし、機能が全く無いのであっさりできた。

これに取り組んだ大きなモチベーションとしては、技術的なアドバイスをくれる人がいるのだけど、 その人がとある案件の対処法にRackでアプリケーション書いて対応しようとした。 という話を聞いたのがきっかけで、Rubyエンジニアで2年弱やってきているのに、 素のRackアプリケーションを書いたことがないのは、 Rubyエンジニアとしてモグリなんじゃないのかと思えて焦りを感じてるし、素振りがてらやってみた。

good-bye sinatra by webuilder240 · Pull Request #1 · webuilder240/ogp_parse_api · GitHub

書き換えてみてよかったこと

  • Sinatraの依存から開放された。
    • 他のGemに依存しているので、まあ誤差だと思うけど。
    • それ目線で言うならDockerイメージをalpineベースにしたり、nokogiriに依存するGemをなくす方が先。
  • Rackのことがなんとなくわかった、ちょっとは仲良くなれた。
    • 本当に単機能であればRackで書くのもありかなと思いました。
  • 今後nodejsとかやngx_mrubyに置き換えるのも見据えるようなアプリケーションの場合、それよりも敷居が低い素のRack実装は、調べることも少なくある程度はパフォーマンス稼げるし取り掛かりとしていいのかなと。
  • 速度は未計測...

まとめ

まぁ本当に素振りでやったのでただの自己満足です。 ただ、こういう適当に遊べる環境を持っておくのは大事だと思いました。

*1:AuthorはRebuild.fmでおなじみの宮川さんです。

javascript経由で<script>タグを呼び出して実行する方法

これやりたい経緯

静的ファイル上でユーザーエージェントを判別して、広告タグを出し分けたいというのを実現するための方法と一つとしてです。 本当は配信先の広告タグスクリプトでよしなにやっててほしいんですが、 そういうわけにも行かないときはやっぱりあるので...

サンプルコード

jsfiddleにでも置きたいと思っていたのだけど、jsfiddleでは複数ファイルのスクリプトの実行をサポートしていないように見えたので、 とりあえずgistにでも置いておく。 まぁ、結果から言うと普通に出来た。

load dynamic execute script tag

まとめ

ただ、配信先の広告タグをコレでよしなに出し分けれたとしても問題があるかもしれないし、 収益に関わる案件なので、導入は慎重に行きたい。