※この文を削除し、「一覧ページでの見出し用の文章」として最大300字程度で自己紹介+概要の記入をお願いします。文内にリンクは置けますが、画像・数式・埋め込みなどのテキスト以外の要素を貼ることはできません。
開催から時間が経ってしまいましたが、Go Conference 2023 が6/2に開催されていました。弊社では、Platinum “Go”ld Sponsor として関わらせていただきました。4/1付けで、社名を株式会社GOへと変更した後であり、Go Conference に支援させていただけたのは、感慨深いものがあります。
この時には、スポンサーセッションとして、『タクシーアプリ『GO』高速マッチングシステムで実践したGoチューニングテクニック』というタイトルでお話しさせていただきました。
ここでは、『GO』サービスの根幹を担うマッチングアルゴリズムを担う Go で実装したシステムを刷新し、その時に行われた数々の Go のチューニングについて述べ、複数のマイクロサービスと連携する時のチューニングと、数秒単位で複数のプロセスが連携して動作する時にどのようなことを考えて実装したのかをお話ししました。
本記事では、Goのチューニングテクニックを、テックブログでもダイジェストで掲載します。
なお、本記事ではタクシーアプリのサービスを”『GO』”、プログラミング言語を Go と表記します。
タクシーアプリ『GO』では「タクシーを呼ぶ」ボタンを押すと、近隣のタクシー車両を手配してユーザの元へ向かってもらいます。このどのユーザに、どの車両を割り当てるのか決めるのがマッチングシステムの役割です。
最も近いタクシーを順番に割り当てていくだけでは、マッチングが遅れてしまうユーザが発生したり、一部遠い車両への割り当てが行われたりしてしまいます。あるユーザの到着時間は少し伸びてしまうこともありますが、より多くのマッチングが成立させるように、これを全体最適を目指してマッチングさせようとしています。
従来のマッチングを担うシステムは、ユーザの配車依頼をごとにサーバで処理し、一部DB経由で情報を連携することで、マッチングの効率化を目指していましたが、個別最適になりやすい状態になっていました。この状態を打破すべく、マッチングに関わるアーキテクチャーを刷新するプロジェクトが立ち上がりました。
新システムでは以下のようなアーキテクチャーに変更しました。
配車依頼をため込んでバッチ駆動してマッチングを実行し、タクシーを手配する
複数のマイクロサービスと連携する
これにより、より全体最適を実現する高速マッチングシステムを構築することができました。
このアーキテクチャーですが「ため込んで、複数の配車依頼をバッチ処理する」仕組みになった都合、多くの課題が見えてきました。
以前のアーキテクチャーでは1つの配車依頼について処理すれば良かったものから、複数の配車依頼を同時に処理すると、そのままでは扱うデータが増えた分、データの取得や処理に時間がかかるようになってしまいます。特に、多くの他のマイクロサービスからマッチングに必要なデータを集めいてくる必要があるため、その処理が
方針としては以下のようにしました。
並列化の実装は、Goルーチンをリクエストの数だけ作成し、セマフォを使って最大並列数を制御するようにしました。Goルーチンは1ms以上の処理であれば、リクエスト毎に使い捨てても支障ないと、経験上考えています。また、分散させた各Goルーチンで個別に発生するエラーは、golang.org/x/sync/errgroup というパッケージを使って収集する様にしました。
また、組み合わせを決めた後に、実際にタクシー上のアプリにリクエストを飛ばすなどの配車依頼ごと処理は全て新しいGoルーチンを起動して、非同期化させるようにしました。
非同期化後のGoルーチンでも、エラーは集めて管理しますが、先ほど説明したerrgroupパッケージでは、複数のGoルーチンでエラーが発生しても、1つのエラーしか収集できないため、複数のエラーに対応した errgroup を自作しました。
この2つのチューニングを、処理時間が長いタスクから適用して、サービス品質を落とさないレベルまで達成しました。
興味のある方は 採用ページ も見ていただけると嬉しいです。
Twitter @goinc_techtalk のフォローもよろしくお願いします!