RoomClip 開発者ブログ

外部サービスを利用した機能実装の際に注意しないといけないこと。

こんにちは。Tunnelにてエンジニアをしてます阿南です。
最近は、WebやAPIまわりを中心にやっております。

RoomClipの技術ブログのリリース早々に自分の順番が回ってきたので、拙筆ではありますが、技術ブログを書かせていただきます。



RoomClipではユーザーは投稿した自分の写真に含まれる商品検索して、アイテムタグとしてその写真にタグ付けすることができる機能があります。

この商品検索の際、各ショップが提供しているAPIを利用して商品検索を行っているわけなのですが、外部から提供されているサービスを利用する時って、自分が実装した部分の範疇にないところで問題が起きたり、サービスの定める仕様に左右されたりと何かと問題があるんですよね。

このアイテム検索の機能拡張でも同様で、直面したのは以下の問題でした。

  • 外部のサービスのシステム障害やメンテナンスモードなどにより、自社のサービス(アイテム検索機能)の一部が利用できなくなるケース

  • 外部のサービスが定めるリクエストの制限数に達してしまい、一定時間リクエストが受理されないようなケース

このようなケースの解決策として、サーキット・ブレーカーなるアイデアがあるらしく、それを元にしてアーキテクチャを構成することで、外部APIを利用した際に発生した上記のような問題を、回避・解決した話をしたいと思います。

サーキット・ブレーカーとは?


簡単にまとめますと

  • クライアントとサーバーの間に入り、クライアント→サーバーへのリクエストや、サーバー→クライアントへのレスポンスを監視。

  • 障害など発生し、一時的に利用ができなくなってしまったサービス(今回の件なら、外部API)を自社のシステムから切り離すことで、自社のサービス全体を停止せずに運用するための構成。

のことを言うそうです。

主に以下のような挙動をしています。

  1. クライアントから外部サーバーへのリクエストや、外部サーバーからクライアントへのレスポンスはサーキット・ブレーカーを介して行なわれる。(closed state)

  2. 外部サーバーに対して、リクエストを送る際に、リクエストに失敗してしまうと、サーキット・ブレーカーが落ちる。(open state)

  3. ブレーカーが落ちている間は、クライアントからのすべてのリクエストが、外部サーバーからのレスポンスを待つことなく、サーキット・ブレーカーがすぐにリクエスト失敗のレスポンスをクライアントに返す。

  4. 一定時間経過後に、サーキット・ブレーカーは外部サーバーへリクエストが可能な状態になる。(half-open state)

商品検索APIを利用するような今回のケースを例にしてみますと、

  1. 平常時(closed state)

平常時(closed state)

  1. リクエスト失敗(この時点で、ブレーカーが落ちる)

リクエスト失敗

  1. ブレーカーが落ちている間(open state)

ブレーカーが落ちている間(open state)

  1. 一定時間経過(half-open state)

一定時間経過(half-open state)


のような構成となり、4.の時点で外部のサーバーへのリクエストが成功すれば、1.のclosed stateに戻り、失敗すれば、3.のopen stateに戻ります。

回避・解決方法


今回のケースにおいてサーキット・ブレーカーが、監視するのは以下の2つです。

  1. 各外部サーバーに障害が発生していないかの監視

    • 使用不可サーバーの判別が常について状態にする目的
    • 障害発覚時点で自動的にブレーカーを落とす(open state)
  2. 各外部APIの上限リクエスト数が超えないかを監視

    • 各外部APIの秒間リクエスト数の上限を超えないようにする目的
    • リクエスト制限を超えた時点で自動的にブレーカーを落とす(open state)

また2つの役割を1つのクラスが担うのでなく、別々のクラスがそれぞれの役割を担うような実装してます

"外部サーバーに障害が発生していないかの監視"について

  1. ユーザーからのリクエストとは別に定期的に外部のサーバーへのリクエストを送信。

  2. レスポンス内容を判断(400,500番系のエラー検出や200番で返ってきたときのレスポンス内容で判断)し、外部サーバー側での障害が見られるようなら、各外部サーバーごとにブレーカーを落とす。

  3. その後即座にhalf-openになり、ユーザーからのリクエストとは別に定期的にリクエストを送信し続けるが、ユーザーからのリクエストに対しては、ブレーカーが落ちている状態(open state)になっている。


以下のようなイメージです。
ユーザーからのリクエストに対してはopen stateであるので、

open state

のようにユーザーからは見えていますが、実際はhalf-open stateになっています。

half-open state


また外部のサーバーごとにブレーカーが落ちたり元に戻ったりしますが、この外部のサーバーごとのブレーカーの状態(すなわち外部サーバーがリクエストを受理できるかどうかの状態)はMySQLで管理しています。

"各外部APIの上限リクエスト数が超えないかを監視"について

RoomClipで利用している外部の商品検索APIのリクエスト制限は、だいたい秒間1~10リクエストでした。
ですので、各APIのごとに1秒間で何回のリクエストがあるのかをカウントするようにします。

以下のように動作します。

  1. 1秒で揮発するように設定したRedisが各APIごとに、ユーザーからの叩かれたリクエスト回数をカウントした結果を保持し、カウンターが上限数に達したら、外部サーバーへリクエストを送る前に、APIごとにブレーカーが落ちる。

  2. 数秒(2~3秒程度)待機し、half-openに移行。再度同様のリクエストを送信する。

カウンターが上限に達していた場合

Redisを1秒で揮発するように設定しておくことで1秒経つと、その前の1秒間に送られたリクエスト数がリセットされます。

ちょうどリクエストの上限に引っかかってしまったら数秒待機となってしまうので、商品検索の結果のレスポンスが遅れ得てしまいますが、リクエスト制限に引っかかって検索結果が返ってこないような自体になるよりはよいのではないでしょうか。


こんな感じで僕の初の技術ブログ執筆は終わりですが、 やっぱりなにかを書くのって難しいですね。

今後もTunnelの諸先輩エンジニアの元でたくさん学ばせていただきまして、もっと面白い記事が書ければなと思います。

それでは。


参考


この記事を書いた人:アナン トモロウ

Tunnel株式会社でエンジニアしております。 大学院生(仮)です。 2016年3月からTunnelにて修行の日々を送っております。 エンジニア歴は1年くらいです。 最近はWebやったり、API書いたりしてます。


GoogleSpreadSheetで回る記事入稿システムを構築する

こんにちは、Tunnelで エンジニアをしている仲本です。

日頃はサーバー周り、インフラ周りのおじさんとして頑張って生きております。

今回はGoogleSpreadSheetを活用した記事入稿システムを紹介させて頂きます。

弊社ではRoomClip magというWEBマガジンがございまして、 今ではお陰様でLINEの公式メディアアカウントになるほどの市民権を得ています。

運営は少人数の編集部とライターさん達で回っており、 記事の入稿はGoogleSpreadSheet上で管理・校正された原稿が本システムによって入稿されます。

今回はその辺りの仕組み周りをご紹介させて頂きたいと思います。

入稿までの流れ

以下のようにGoogleSpreadSheetのデータをWordpressまで届けます。

構成

入稿用GAS

構成(GAS) ここでは、セルに構築された原稿やタイトルなどの情報を取得し、APIサーバーに送信しています。 コード自体はクロージャである程度キレイにまとめたかったのですが、これを書いた時は初めてだったのでベタ書きでごめんなさい。 セルの内容をそのままHTTPでPOSTしているだけなのですが、一応post_tokenを付けていたずらを防ぐようにしています。

入稿用API

構成(API) 次にGASからのリクエストを受取るためのAPI部分のコードになります。 そのままWordpressに投げず、仕様や設計の変更に対する柔軟性とか含めてここで1クッション置いています。 XMLRPCの中身を作る処理は本エントリーに貼り付けるには重たすぎるので割愛するとして、このように投げてあげます。 ちなみにCodeIgniterをxmlrpcライブラリを使った書き方になります。

Wordpress

構成(Wordpress) ここでは特に書くコードはありませんが、Wordpressのxmlrpcは攻撃を受ける事が稀にあるのでこのようにEC2からしか叩けないようにしておきましょう。

これでSpreadSheetから記事入稿が出来るようになりました。 ただ、記事は色々な時間にアップされるため、公開機能を分離してコントロールする必要があります。

公開機能のGAS

構成(GAS) その際、弊社では記事の一覧を別のスプレッドシートで管理していて、そこから公開ステータスや公開日を変更できるようになっています。 そこで使われているGASはこのようになっています。 セルの値変更イベントをトリガに動作させるようにしています。 例えば、「公開」「非公開」などをスプレッドシート上でプルダウンにしておけば、運用スタッフ目線でもわかりやすいと思います。

画像変換GAS

構成(GAS) 記事はアップ出来たとはいえ、取り扱っている画像コンテンツの管理はまだ楽になっていません。 弊社のコンテンツは画像で、画像一枚一枚をIDで管理しています。 なので、画像のURL生成などを考えるとIDを元にファイル名などを生成出来た方が便利なわけです。 そのまんまな内容なのですが、画像IDを入れるとファイル名が返ってくるように作っています。 これで、画像の管理はIDで行い、URLなどの出力は関数を通して自動化するという事ができるようになりました。

コンテンツのURL生成システム

構成(rclone) 次は、自分達で調達したり作成した画像ファイルについてです。 ID管理されているわけではなく、これらはGoogleDriveで管理されています。 これらがそのまんま画像URLになればいいなあという話で、弊社ではGoogleDriveとS3を同期しています。 同期にはrcloneというソフトウェアをサーバー上でくるくると回しています。 構築は適当にAmazonLinuxを立ち上げて以下の手順で構築しました。

さいごに

このようにWordpressの管理画面を使わずにほぼGoogleSpreadSheetだけで記事入稿の運用が可能になるため、生産性も向上しました。

今後も増え続ける入稿数に対応できるように、工夫を重ねられる漢を目指していきたいと思います。 それではごきげんよう!


この記事を書いた人:仲本

Tunnel株式会社のエンジニア サーバーサイド・インフラ周りを中心に担当。 味のあるオッサンを目指して頑張っている。 夜は酒場に居る事が多い。