Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

カフカとルビー、Sidekiqラブストーリー

成長を続けるオールインワンAPMとして、AppSignalがトラフィックの増加に対応できるようにするために多くの時間を費やしています。通常、私たちはそれをどのように行うかについて話しません。私たちのブログには、Rubyの内部で素晴らしいことや、Elixirでクレイジーなことをしていることについての記事がたくさんありますが、AppSignalを動かしている理由については書かれていません。

ただし今回は、過去数年間に行ったスタックの大きな変更の一部を共有したいので、毎月送信される2桁の数十億のリクエストを(簡単に)処理できます。リアルタイムで。そのため、今日はスケーリングの経験を利用して、独自のスタックについて話し合い、その方法を支援しています。

標準のレール設定からより多くのカスタムパーツへ

AppSignalは、かなり標準的なRailsセットアップとして始まりました。バックグラウンドで処理するSidekiqジョブを作成するAPIエンドポイントを介してデータを収集するRailsアプリを使用しました。

しばらくして、Rails APIをRackミドルウェアに置き換えて速度を少し上げ、その後、これをGoWebサーバーに置き換えてSidekiq互換のジョブをRedisにプッシュしました。

アプリの状態と増分/更新

この設定は長い間うまく機能していましたが、データベースがデータベースに対して実行されるクエリの量に追いつかないという問題が発生し始めました。この時点で、すでに数百億のリクエストを処理していました。これの主な理由は、各Sidekiqプロセスが、正しいカウンターをインクリメントして適切なドキュメントを更新するために、データベースからアプリ全体の状態を取得する必要があることでした。

データのローカルキャッシュを使用してこれをいくらか軽減することはできますが、セットアップのラウンドロビンの性質により、ペイロードがどのサーバーにあるかわからないため、各サーバーにすべてのデータの完全なキャッシュが必要でした。最終的になります。データの増加に伴い、この設定は将来不可能になることに気づきました。

カフカに入る

データを処理するためのより良い方法を探すために、データ処理パイプラインとしてKafkaを使用することにしました。データベース内のメトリックを集約する代わりに、Kafkaプロセッサでメトリックを集約するようになりました。 。私たちの目標は、集約されたデータをフラッシュする必要があるまで、Kafkaパイプラインがデータベースにクエリを実行しないことです。これにより、ペイロードあたりのクエリの量が、パイプラインの最後で最大10回の読み取りと書き込みから1回の書き込みに減少します。

各Kafkaメッセージにキーを指定すると、Kafkaは、同じサーバーによって消費される同じパーティションに同じキーが配置されることを保証します。アプリのIDをメッセージのキーとして使用します。つまり、サーバー上のすべての顧客のキャッシュを使用する代わりに、サーバーがKafkaから受信するアプリのデータのみをキャッシュする必要があります。すべてのアプリはキャッシュしません。

Kafkaは優れたシステムであり、過去2年間で移行しました。現在、ほとんどすべての処理はRustでKafkaを介して実行されますが、通知の送信やその他のデータベースを多用するタスクなど、Rubyでより簡単に実行できることがまだあります。これは、KafkaからRailsスタックにデータを取得するための何らかの方法が必要であることを意味しました。

KafkaとRuby/Railsの接続

この移行を開始したとき、Kafka Rubyの宝石がいくつかありましたが、Kafkaの最新(当時は0.10.x)のリリースでは機能せず、ほとんどが保守されていませんでした。

私たちは自分たちの宝石を書くことを検討しました(最終的にはそれを行いました)。これについては、別の記事で詳しく説明します。しかし、優れたドライバーを持つことは要件の一部にすぎません。また、データを消費してRubyでタスクを実行し、古いワーカーがクラッシュしたときに新しいワーカーを生成するシステムも必要でした。

最終的に、私たちは別の解決策を思いつきました。 KafkaスタックはRustに組み込まれており、sidekiq_outを消費する小さなバイナリを作成しました。 トピックを作成し、RedisでSidekiq互換のジョブを作成します。このようにして、このバイナリをワーカーマシンにデプロイし、Rails自体の場合と同じように新しいジョブをSidekiqにフィードできます。

バイナリには、しきい値がクリアされるまでKafkaトピックの消費を停止するために、Redisのデータ量を制限するなどのいくつかのオプションがあります。このように、バックログがある場合、KafkaからのすべてのデータがワーカーのRedisのメモリに保存されることはありません。

Rubyの観点からは、Railsで生成されたジョブとKafkaから生成されたジョブの間にまったく違いはありません。これにより、Kafkaからデータを取得してRailsで処理する新しいワーカーのプロトタイプを作成し、Kafkaについて何も知らなくても、通知を送信してデータベースを更新できます。

新しいRubyコードをデプロイしなくても、Kafkaに切り替えて元に戻すことができるため、Kafkaへの移行が容易になりました。また、Kafkaスタック全体をローカルにセットアップしなくても、Rubyが使用するジョブをテストスイートで簡単に生成できるため、テストが非常に簡単になりました。

Protobufを使用してすべての(内部)メッセージを定義します。これにより、テストに合格した場合、ワーカーがKafkaからのジョブを正しく処理することを確信できます。

最終的に、このソリューションは私たちに多くの時間とエネルギーを節約し、Rubyチームの生活を大幅に簡素化しました。

長所と短所

すべての場合と同様に、この設定にはいくつかの長所と短所があります:

長所:

  • Rubyでの変更は不要で、APIと互換性があります
  • 簡単に導入して元に戻すことができます
  • KafkaとRubyを簡単に切り替えることができます
  • リミッターを使用するときにRedisがメッセージで過負荷になることはなく、サーバーのメモリを節約し、代わりにメッセージをKafkaに保持します。
  • 水平方向のスケーリングでは、キー付きメッセージがあるため、各サーバーのキャッシュが小さくなります。

短所:

  • それでも、各Sidekiqスレッドは、サーバーが消費するパーティションからアプリのすべてのデータのキャッシュにアクセスする必要があるという問題があります。 (例:Memcache)。
  • サーバーで実行されている別のプロセス
  • メッセージがRedisにフラッシュされると、rustプロセッサはメッセージオフセットをコミットします。これは、メッセージがRedisにあることが保証されていることを意味しますが、メッセージがRubyによって処理される保証はありません。つまり、サーバーがクラッシュした場合、 Redisにあったが、処理されなかった一部のメッセージが処理されない可能性があります。

SidekiqとKafka

Sidekiqを使用することで、処理パイプラインをKafkaに移行する際に非常に役立ちました。現在、Sidekiqからほぼ完全に離れており、Kafkaドライバーを介してすべてを直接処理していますが、それは別の記事です。

このハッピーエンドはラブストーリーを締めくくります。パフォーマンスとスケーリングに関するこの視点と、AppSignalのスケーリングの経験を楽しんでいただけたでしょうか。私たちがスタックに関して下した決定についてのこの話が、あなたの助けになることを願っています。

ブログの残りの部分をチェックするか、Kafkaのセットアップに関する次のエピソードが公開されるときに注目してください。そして、本当に開発者が開発者のために作ったオールインワンAPMを探しているなら、私たちを見つけに来てください。


  1. RuboCopを使用したRubyコードのリンティングと自動フォーマット

    リンティングは、プログラムおよびスタイルのエラーについてソースコードを自動チェックすることです。このチェックは、リンターと呼ばれる静的コード分析ツールによって実行されます。ただし、コードフォーマッタは、事前に構成された一連のルールに厳密に準拠するようにソースコードをフォーマットするためのツールです。リンターは通常違反を報告しますが、問題を修正するのは通常プログラマー次第ですが、コードフォーマッターはそのルールをソースコードに直接適用する傾向があるため、フォーマットの間違いを自動的に修正します。 プロジェクトでより一貫性のあるコードスタイルを作成するタスクでは、通常、個別のリンティングツールと

  2. LoggerとLogrageを使用してRubyにログインする

    Rubyでのログの操作 ロギングは、アプリケーションが通常対処する主要なタスクの1つです。ログは、たとえば、必要なときに使用されます アプリ内で何が起こっているかを確認します それらを監視する、または 特定のデータの指標を収集します。 新しいプログラミング言語を学ぶとき、情報を記録するための最初の明白な選択は、ネイティブメカニズムです。通常、それは簡単で、文書化されており、コミュニティ全体に広く行き渡っています。 ログデータは、使用している会社、ビジネス、アプリケーションの種類によって大きく異なります。したがって、あなたとあなたのチームが選択したロギングソリューションがその全体的な使