これは Chatwork Advent Calendar 2020 / Scala Advent Calendar 2020 10日目 の記事になります。
こんにちは。サーバーサイド開発部の Scala プロダクトを開発運用する部署でマネージャーをしている、 hayasshi です。
Chatwork は Scala を採用すると決めてから、約 6 年経ちました。
その中で、失敗もしながら、少しずつ Scala のシステム領域を広げてきました。
今回と次回の二記事にて、この 6 年で開発し、いま実際に稼働運用されている、 Chatwork の Scala プロダクトの紹介と、それを普段どのように開発運用しているかについて、書きたいと思います。
Scala プロダクトの紹介
今回は Chatwork の Scala プロダクトについてご紹介します。
特に下記の項目についてそれぞれ記載したいと思います。
- どのような役割を持ったシステムか
- 利用されている技術(ミドルウェア、ライブラリ)
プロダクトによっては構成図も交えながらご説明したいと思います。
Chatwork の Scala プロダクトは、そのほとんどが Kubernetes 上でコンテナとして動作しています。
Kubernetes に関する記事も別途ありますので、今回はその説明は割愛します。
また Chatwork では、プロジェクトにコードネームをつける慣習があり、プロダクトもその愛称で呼ばれることが多いので、ここでは社内でよく呼ばれる愛称でご紹介します。
Falcon
どのような役割を持ったシステムか
Falcon は、Chatwork にスケーラブルなメッセージの読み書きを提供する、メッセージシステムです。
メッセージ投稿や読み出しの際、事前条件(権限やチャットルームへの参加有無など)チェックをしたあとで、メッセージを永続化するために呼び出します。
呼び出しは HTTP ベースでおこない、書き込み(投稿や編集)リクエストが受け付けられ実際に永続化されるまでは非同期に処理されます。
※ チャットログエクスポート機能のためのコンポーネントもいくつかありますが、今回は省略しています
書き込みリクエストを、イベントとして Kafka に一旦キューイングし、それをベースに処理することで、スケーラビリティとイベントベースなシステムの柔軟性を獲得することができました。
このあたりのことは、Chatwork でも何度か発信をしていますので、より詳細を知りたい方は是非下記の資料もお読みいただければと思います。
- History of Falcon, the way to production release
- ChatWorkの新メッセージングシステムを支える技術
- 3週間で出来たEvent SourcingでCQRSなアーキテクチャ上でのHBaseのupgrade
利用されている技術(ミドルウェア、ライブラリ)
ミドルウェア:HBase、Kafka
ライブラリ:Akka、AkkaHTTP、AkkaStreams、KafkaStreams、Circeなど
Webhook
どのような役割を持ったシステムか
Chatwork で発生したイベントを、設定された条件に従い、外部へ HTTP リクエストを用いて通知する、いわゆる Outgoing WebHook をおこなうシステムになります。
(社内では Webhook と一語で表現する事が多いです)
Falcon で扱えるようになったメッセージイベントを筆頭に、Kafka を利用したストリーム処理を中心にしたシステム構成になっています。
Kafka の at least once delivery の特性を活かし、極力ロストしないようにする代わりに、重複して実行される可能性があるというのは、達成すべき非機能要件の観点から WebHook を取り扱うシステムと非常に相性が良いと感じています。
利用されている技術(ミドルウェア、ライブラリ)
ミドルウェア:Kafka、AWS Aurora
ライブラリ:AkkaStreams、Alpakka Kafka、AkkaHTTP、ScalikeJDBC、Scalaz、Circeなど
Astraea
どのような役割を持ったシステムか
RFC6749 に準拠した認可APIサーバーを、フルスクラッチで構築したものになります。
Webhook と同時期に構築されたもので、サードパーティのサービス提供者が、ユーザーの同意のもと、安全に Chatwork API にアクセスするためのトークンを発行する仕組みを担います。
RFC6749 を準拠する認可処理のモデリングをおこない、堅牢に構築されています。
Astraea をつかった Chatwork の OAuth に関するブログもいくつかありますので、詳細を知りたい方は下記もご覧ください。
Chatwork Creator's Note > Category > OAuth
利用されている技術(ミドルウェア、ライブラリ)
ミドルウェア:Amazon Aurora、Amazon ElastiCache、Amazon SQS、Amazon S3
ライブラリ:AkkaHTTP、AkkaStreams、Slick、Cats、Circeなど
Synapse
どのような役割を持ったシステムか
外部サービスと Chatwork API の間に立ち、それぞれのインターフェースを吸収して接続する、連携のためのハブ・アプリケーションです。
例えば Chatwork では、IFTTT の Service を提供していますが、IFTTT 側の要求するインターフェースを Synapse に実装し、Chatwork API に連携しています。
このような「他のサービスの都合による実装」を、リソースサーバーから分離、疎結合にすることで、コアプロダクトの肥大化やコアドメインへの集中を維持する目的で作成しました。
利用されている技術(ミドルウェア、ライブラリ)
ミドルウェア:Amazon ElastiCache、Amazon S3
ライブラリ:AkkaHTTP(server + client)、Cats、Circeなど
Reaction
どのような役割を持ったシステムか
その名の通り、リアクションデータを永続化するためのシステムです。
Falcon と同様、事前条件(権限やチャットルームへの参加有無など)チェックをしたあとで、呼び出されます。
これまでの HTTP リクエストを受け付けるアプリケーションでは、Akka を主軸にした実装でしたが、このプロダクトでは、HTTP ルーティングこそ AkkaHTTP ですが、そこから先は Monix を用いたモナドベースでのユースケース実装をしています。
当時の開発プロジェクトでおこなわれたこと(PoC、技術選定、負荷試験など)を紹介した資料もありますので、別途詳細を見たい方は、下記も合わせてご覧ください。
Chatworkでリアクション機能をリリースした話
利用されている技術(ミドルウェア、ライブラリ)
ミドルウェア:Amazon DynamoDB
ライブラリ:AkkaHTTP、Monix(Task)、Cats、Circeなど
Biryani
どのような役割を持ったシステムか
メッセージ検索インデックスを作成するアプリケーションです。
Falcon のメッセージイベントを、Amazon Elasticsearch Service にインデックスを作成するストリーム処理をおこないます。
構文解析こそ Elasticsearch にお任せしていますが、メッセージの言語判定はアプリケーション内部でおこなっています。
Chatwork の複雑になっていた検索インデックス作成処理を見直し、時間短縮、コスト削減をおこなったスゴイやつです。
そのプロジェクトの詳細は、当時のプロジェクトメンバーがブログにまとめていますので、下記も合わせてご覧ください。
Chatwork Creator's Note > Category > Elasticsearch
利用されている技術(ミドルウェア、ライブラリ)
ミドルウェア:Kafka、Amazon Elasticsearch Service
ライブラリ:AkkaStreams、Alpakka Kafka、Elasticsearch Client、Circeなど
LinkPreview
どのような役割を持ったシステムか
Chatwork のタイムラインに表示されている URL プレビューの情報を取得するためのものです。
Chatwork は読み込みの要求が大きい(read heavy)ので、キャッシュをうまく取り扱い、かつ柔軟にスケールしやすい単機能なものということで、CloudFront + AWS Lambda を採用した、サーバーサイド開発部 Scala 初のサーバーレスアプリケーションになりました。
実は、JVM のスピンアップの遅さに対応するため、GraalVM Native Image ツールをつかい、ネイティブアプリケーションとして AWS Lambda で動かしています。
その他にも、URL 先に多大な負荷を与えないためのスロットリングの仕組みや、同じ URL に対する同時実行数の抑制をおこなう仕組みなどを実装し、システムをコントロールしています。
利用されている技術(ミドルウェア、ライブラリ)
インフラ:Amazon CloudFront、Amazon API Gateway、AWS Lambda
ミドルウェア:Amazon DynamoDB
ライブラリ:AWS SDK、Akka、scalaj-http など
Pegasus
どのような役割を持ったシステムか
Falcon にしろ Reaction にしろ、これまでの Chatwork のコアドメイン(チャット)に関する機能は、Scala プロダクトとしては永続化層として扱われてきました。
現行システム側のドメインロジックを通った上で、Scala プロダクトが呼び出されるというようなパターンです。
このままでは Scala プロダクトにロジックがたまらず、新規機能開発や改善をおこなう際に、かならず現行システムのメンバーが必要になってしまいます。
このプロダクトは、そうせざるを得なかった問題に対して、回避も含めた対応をおこない、Scala プロダクト単独で機能追加を可能にすることを目的に開発されました。
直接リクエストを受け、ビジネスロジックを書くことができるようになったことで、例えば Reaction など、本来 Chatwork のコアドメインとしてまとまっておくべき機能を統合するなどし、システム全体をシンプルにしていく取り組みもおこなえるようになりました。
利用されている技術(ミドルウェア、ライブラリ)
ミドルウェア:Amazon Aurora、Amazon DynamoDB、Amazon SQS など
ライブラリ:AkkaHTTP、Slick、Circe など
以上が、現在稼働している Scala プロダクトの紹介になります。
さいごに
私自身、入社 5 年が経ち、振り返ると様々な開発をおこなってきました。
最初の方は開発することに意識が先行し、開発したプロダクトの運用について十分に検討ができていなかったり、チームが維持できなかったりと度々問題課題と対峙してきました。
最近はようやく、開発と運用を一緒におこなっていくチームができ、これまでの反省を活かしながらシステムやアーキテクチャの設計をおこなっています。
このプロダクト紹介を見て、「技術的な詳細が知りたい!」「こうなった経緯や課題問題を深く聞きたい!」「コードネームの由来を聞いてみたい!」と思われた方、ぜひぜひカジュアルにお話させていただければと思いますので、下記からお問い合わせください!
次回は、このチームでおこなっている開発について書きたいと思います。