こんにちは!LayerXエンジニアの高際 @shun_tak です!
この記事では、LayerX インボイスの請求書AI-OCRを支える非同期処理の仕組みについて解説したいと思います。
いきなりサマリーですが、今回お伝えしたいのは以下の2点です。
- 請求書は突然大量にアップロードされるので(大歓迎です!)、Amazon SQSとGoの machinery を活用して非同期処理しているよ!
- AI-OCRの処理は重たいけど、AWS Lambdaを活用してシステム全体の負荷を分散し、スケーラビリティと可用性を確保し、コストも抑えることができたよ!
では早速ですが、前回のブログ LayerX インボイスにおける請求書AI-OCRの概要 の復習です。LayerX インボイスの請求書AI-OCRは、以下の図のように複数の処理によって構成されています。
図にするとあっさりしてますが、前処理も後処理も複数の処理で構成され、その中にはOCRの処理だけでなく、重たい画像処理等も含まれています。そのため、もちろん速度改善に努力してはいるものの、全部の処理が終了するまで請求書1件あたり数秒かかります。
このような重たい処理をアップロード処理と同期的におこなってしまうと様々な弊害が現れるため、処理の一部をメッセージキューとジョブワーカーを活用した非同期処理に分離しています。
ちなみにLayerX インボイス関連サービスにおいては、メッセージキューとしてAmazon SQSを、メッセージの送受信やジョブワーカーの管理にはGoの machinery というライブラリを利用しています。このあたりはまた別の記事で解説したいと思います。
アーキテクチャ概要
以下の図は、AI-OCRにまつわる処理を管理する様子を簡単に示したものです。
この図には、APIサーバーとジョブワーカーの2種類のサーバーが存在します。これら2つのサーバーはともにSQSにメッセージを送信します。
一方で、メッセージを受信して処理するのはジョブワーカーだけです。APIサーバーはユーザーアクション起因で動きますが、ジョブワーカーはイベント駆動で動きます。
APIサーバーはストレージへの請求書アップロードが成功すると、メッセージをキューに送信し、一旦クライアントに成功レスポンスを返します。ジョブワーカーは受信できるメッセージがないかキューを監視しており、メッセージを受信すると該当のタスクを実行します。
最初のタスクには処理の最後に新たなメッセージをキューに送信するような実装をしておくことで、AI-OCRを構成する処理が次々と動くようになっています。最後のタスクはキューにメッセージを送信しないため、そこで一連の処理も終了します。
より詳しいAI-OCRの処理の様子を次節で紹介します。
AI-OCRの処理を支える具体的な構成
以下の図はAI-OCRの処理をさらに具体的に図示したものです。アイコンがたくさんあって複雑に見えるかもしれませんが、やってることは先程の概要図で示したものの繰り返しです。
1クライアントから大量の請求書が同時にアップロードされることは日常茶飯事で、ジョブワーカーは大量のタスクを管理しなければなりません。そのタスクの管理だけでも相当負荷が上がります。
一方で、請求書のアップロードタイミングやそのボリュームは分単位では予測不可能なため、ジョブワーカーに重たい処理を担わせるとオートスケーリングでも負荷分散が間に合わなくなります。
そのため、汎用OCRの実行やデータベースへの結果の保存など、ジョブワーカーでタスクが完結することもありますが、前処理、ラベル検出、一部の後処理はAWS Lambdaにオフロードしています。
データベースアクセスなしで動く重たい機能(特に画像処理)は、並列起動が可能で可用性が高く使った分だけ課金されるAWS Lambdaを利用することで、システム全体の負荷削減、スケーラビリティ向上、可用性の確保、コストの最適化を実現することができます。一石四鳥!
エンジニア募集中!
AI-OCRはうまく運用できてるように見えるかもしれないですが、まだまだ改善したいことがたくさんあります。
Go machinery じゃなくてAWS Step Functions使えばいいじゃんって思ったそこのあなた!ぜひ下記のリンクからご応募ください!面接やカジュアル面談にてディスカッションさせてください!
最後までお読みいただきありがとうございました!