マイクロサービスアーキテクチャを読んだ

マイクロサービスアーキテクチャを読んだ。

マイクロサービスアーキテクチャ
Sam Newman
オライリージャパン
売り上げランキング: 5,636

所感としては、カバー範囲が意外と広範囲だったのと、日本で流行っているものとUSで流行っているものの違いのおかげか、知らないプロダクトや技法などについて書いてあって勉強になった。 パラパラと読んでしまったので、もう1度読んで深掘りしたいかもしれない。

以下、ハイライトしてた文章を引用。すでに自分が知ってたり、考えたことがあったものについてはハイライトしてない。

  • いろいろな意味で、マイクロサービスに分解したい既存のコードベースがある方が、最初からマイクロサービスに取り組むよりはるかに簡単です。
  • 組織に存在する境界づけられたコンテキストについて考える際、共有するデータの観点ではなく、そのコンテキストが残りのドメインに提供する機能について考えるべきです。
  • 1つのマイクロサービス内ではDRYを破らないけれども、すべてのサービスにわたるDRYの違反には寛大に対処します。
  • 私が気に入っており、適切に機能しているのを見たことがあるモデルは、図4-10に示すようにこのようなバックエンドの仕様を特定のユーザインタフェースやアプリケーションに制限する方法です。このパターンは BFF: Backends for Frontends と呼ばれることもあります。
    • 補足: APIゲートウェイについて語っていて、デバイスの種別ごとに(モバイル、ウェブなど)ゲートウェイを作るのが良いと言っている
    • そもそもとしてゲートウェイが必要なのは、複数のAPIリクエストを1つに束ねるなどしたいため
  • BFFには、特定のユーザエクスペリエンスの提供に特化した振る舞いだけを追加すべきです。
    • バックエンドが使うさまざまな機能のビジネスロジックは、サービス自体の中にとどまるべきです。
  • ファサードサービスを使って基盤となる大きく恐ろしいCRM(Salesforceのような)を隠す
    • 基盤となるCRMを移行できるようにしておく
  • 合成監視
    • 補足: ピタゴラスイッチ的なシステムの end-to-end な監視。テストジョブをエンキューして期待通りに最後まで処理が行くか
  • フィーチャーチームとは、小規模なチームが一連の機能開発を推進し、たとえコンポーネント(さらにはサービス)境界を超えても、必要なすべての機能を実装するという考え方です。
    • 複数の異なるチームにまたがる変更を調整するという課題を避けられます
    • しかし、サービス管理者の役割はずっと複雑です。
  • たとえ制御できたとしても、ほとんど制御できないキャッシュが1つあります。それはユーザのブラウザのキャッシュです。
    • 補足: Expires: Never を食わせてしまい、URLを変えるしかなかった事案が紹介されていた
  • メッセージ病院 (配達不能、失敗したメッセージの送り先となるキュー) を実装しなければなりませんでした。また、そのメッセージを閲覧し、必要に応じてリトライするUIも作成しました。
  • サーキットブレーカー: 下流リソースへの特定の数のリクエストが失敗すると、サーキットブレーカーが落ちます。サーキットブレーカーが落ちている間は、すべてのリクエストがすぐに失敗します。特定の時間の経過後、クライアントはリクエスト送信して下流サービスが復旧しているかを確認し、十分に健全なレスポンスを得たら、サーキットブレーカーをリセットします。
  • swaggerでサービスの文書化
  • Consumer Driven Contract
  • Suro
  • Riemann - Distributed Monitoring System

mini editor in ruby

I wrote a mini editor in ruby, kiro. kiro is actually a ruby port of antirez/kilo. kilo is a very interesting project for me because it is a small editor implementation written in only about 1K lines of codes. I've learned how to write an editor with this work.

In brief, what kilo was doing is:

  • Open STDIN in raw mode (ref. termios(3) - cfmakeraw, IO#raw is available in ruby)
  • Get window size using ioctl(1, TIOCGWINSZ, &ws) (io/console is available in ruby)
    • If it fails, write "\x1b[999C\x1b[999B" to STDOUT to go to right/bottom margin,
    • then get cursor position by writing "\x1b[6n" as it reports current position to STDIN
  • loop
    • refresh screen
    • process keypress
  • process keypress
    • Block (wait) until the user types anything
    • Store a character into data structure if the user types something
      • Sometimes append, sometimes insert, sometimes split one row to two rows (ENTER), delete
      • Syntax highlights characters in each row at here
  • refresh screen
    • POINT: Construct a buffer and write to STDOUT in a batch to avoid flickering effects
    • Append each line of syntax highlited characters into a buffer
      • sometimes cutoff characters if raw length overflows the window size
    • Pad ~ until bottom
    • Append status bar to the buffer
    • Then, write the buffer to STDOUT
  • Save
    • Dump the data structure into a file
  • Load
    • Construct the data structure from a file

My kiro codes are not rubyish, and not supporting syntax highlights yet. I may brush up if I have time and willingness.

CloudFrontをかますとキャッシュなしのAPIコールでも速くなるようだ

Slack 社の Secured API Acceleration with Engineers from Amazon CloudFront and Slack という資料を読んでいたら、Slack社のようなグルーバル企業において「CloudFront をはさんだらキャッシュしないAPIアクセスでも速くなった :D」 と書いてあった。しかし、CloudFront (例えばjp)から ELB (例えばus) までは依然として大きな latency があるわけで、本当に速くなるのか懐疑的だったので自分でも試してみた。

このスライドの11ページに速くなった理由は5つ書いてあり、以下のとおり。

  • (1) CloudFront Latency Based Routing
  • (2) TCP/IP Optimizations for the Network Path
  • (3) Keep-Alive Connections to reduce RTT
  • (4) AWS Backbone Network
  • (5) SSL/TLS Optimizations

(1) CloudFront Latency Based Routing については、日本のエッジロケーションを自動選択してくれるんだから、まぁそうだよね、という感じで、 (5) SSL/TLS Optimizations についても、距離が近くなればSSL確立まで速くなるのは、まぁわかる、という感じ。

(4) AWS Backbone Network については、なにかあるんだろうが、実際速くなるんだろうか、という気持ちで、 (2) TCP/IP Optimizations for the Network Path と (3) Keep-Alive Connections to reduce RTT については、何を指しているのかがスライドには書いてなくて、わからん、という感じ。試してみたほうが早いな、と。

やったこと

  • us-east-1 に t2.nano で Amazon Linux なインスタンスを立て、nginx-1.8.1-3.27.amzn1.x86_64 を yum install して空の index.html を返すようにしておく(素の設定のまま)
  • us-east-1 に ELB を立てて作ったインスタンスを追加しておく (ELB を作るのは、CloudFront が ELB か S3 しかオリジンに設定できないため)
  • CloudFront を立てて、ELB をオリジンに設定。キャッシュを一切しないように設定しておく

この状況で日本のオフィスから以下の ruby スクリプトを実行して、ELBを指定した場合とCloudFrontを指定した場合の時間を計測した。

エッジロケーションまでの距離が近くなればSSL確立の時間が短くなるのはわかっているので、あえてhttpsではなくhttpで計測して、(4) AWS Backbone Network にどれぐらいの効果があるのかを検証した。

require 'net/http'
fqdn = 'xxxxxx'

started = Time.now.to_f
# no keeepalive
10.times do
  Net::HTTP.get fqdn, '/index.html'
end
puts "#{(Time.now.to_f - started) / 10.0} sec / req"

結果

  • ELB: 0.405 sec / req
  • CloudFront: 0.267 sec / req

確かに速くなった。

まとめ

確かにキャッシュなしでも (4) AWS Backbone Network の効果で、1 HTTP リクエストにおける RTT が事実上 2/3 程度に短縮されるようだ。さらにSSL確立も速くなるので実用としてはもっと差がでるだろう。 ちょうど us リージョンに立てているけれど、日本からもアクセスされる API サーバがあるので、使ってみようかな、という気持ち。

ところで、

  • (2) TCP/IP Optimizations for the Network Path
  • (3) Keep-Alive Connections to reduce RTT

については依然として何を言っているのかわからないけど、CloudFront <=> ELB 間は keepalive 接続してたりするのだろうか。スライド書いた人に聞いたほうが早いかな。

追記

rebuildfm fastly の miyagawa さんに twitter で教えて頂いたが、fastly など他のCDNでも、エッジロケーション <=> オリジン間のネットワーク最適化は行っているそうで業界的には DSA と呼ばれる技術体系とのこと。TCP multiplexing や HTTP keep-alive したりしているようだ。参考:速度比較

(蛇足) ap-northeast-1 の EC2、ELB に対して日本のオフィスから CloudFront 経由でアクセスした場合はどう?と聞かれたので、念のため実際に計測した結果を書いておく。物理的な距離はどちらもたいして変わらないので、結果もたいして変わらないだろうと予想。むしろ、CloudFront の1段が増えるので遅くなる可能性もありそう。

結果 

  • EC2直接: 0.030 sec / req
  • ELB: 0.063 sec / req
  • CloudFront: 0.064 sec / req

予想通り、たいして変わらなかった。

(補足) ブコメを見て補足。CloudFront を利用して us リージョンの EC2 インスタンスにアクセスするのに比べ、当然 jp リージョンの EC2 インスタンスにアクセスするほうが(日本のクライアントからは)圧倒的に速いので誤解のなきよう。

A Ruby and Fluentd committer working at DeNA. 記事本文および記事中のコード片は引用および特記あるものを除いてすべて修正BSDライセンスとします。 #ruby #fluentd #growthforecast #haikanko #yohoushi #specinfra #serverspec #focuslight
はてぶ人気エントリー

Google