2023-03-19 YAPC::Kyoto 2023 https://yapcjapan.org/2023kyoto/timetable.html#talk-117
ジョブキューシステムFireworqのアーキテクチャ設計と運用時のベストプラクティスINA Lintaroid:tarao @oarat2023-03-19 YAPC::Kyoto 2023
View Slide
自己紹介2id:tarao@oarat@taraoエンジニア (バックエンド)・エンジニアリングマネージャ● 2013~ はてなに新卒入社● 2015~ はてなブックマークのシステム刷新 (テックリード)● 2021~ エンジニアリングマネージャ今回に関連する話● 10年でどう変わった? はてなブックマークでのPerlの使い方YAPC::Nagoya::Tiny 2019, 名古屋市中村区,November 2019 (ゲストスピーカー)
3Fireworq is 何?
Fireworq is 何?● ジョブキュー (メッセージキュー)● Go製● ストレージはMySQL● at-least-once4
Fireworq is 何?● HTTPでジョブを投げる● 指定ワーカーにPOSTしてくれる● 投入元 = ワーカー ならつまり非同期処理5
Fireworq is 何?● HTTPでジョブを投げる● 指定ワーカーにPOSTしてくれる● 投入元 ≠ ワーカー ならつまりメッセージング6
7以前のジョブキューソリューション
TheSchwartz + WorkerManager困りどころ● Perlでしか使えない● 重いジョブによるリソース占有● ワーカー並列数を増やすと詰まる● ワーカーをメンテしづらい○ エントリーポイントが特殊○ 環境セットアップが特殊8
TheSchwartz + WorkerManager困りどころ● Perlでしか使えない● 重いジョブによるリソース占有● ワーカー並列数を増やすと詰まる● ワーカーをメンテしづらい○ エントリーポイントが特殊○ 環境セットアップが特殊9
TheSchwartz + WorkerManager● 複数のワーカーが一斉にジョブを掴む● ワーカーを増やしていくと詰まる10
11Fireworqの設計
設計思想12Portability 言語非依存 (インタフェースはHTTP)Reliability RDBMS (MySQL)で永続化Availability プライマリ/バックアップのノード構成Scalability 単一ディスパッチャ+多数のワーカーFlexibility 複数のキューを動的に設定可能APIや管理Webコンソールで操作可能
アーキテクチャ● 単一ディスパッチャでポーリング13
しくみ - MySQLエンジン● 1テーブル = 1キュー● INSERTとUPDATEを競合させない● TheSchwartzやMogileFSもだいたい同じ14id next_try status6 12:05 claimed5 11:05 claimed4 10:32 claimed3 10:27 claimed2 09:41 grabbed1 09:07 grabbed現在時刻新規ジョブINSERT位置次に掴むUPDATEclaimed↓grabbed
しくみ - ノード昇格● プライマリ○ GET_LOCK成功● バックアップ○ GET_LOCK待ち○ INSERTは可○ プライマリが落ちると直ちに昇格15
16TheSchwartzからの移行
元のワーカーの想定package My::Workeruse parent qw(TheSchwartz::Worker);sub work {my ($class, $job) = @_;...$job->completed;}17
TheSchwartz::Fireworqワーカーをweb app化: app.psgiに以下を追加enable ‘TheSchwartz::Fireworq’, path => ‘/work’;18
TheSchwartz::Fireworqジョブの投入側: クライアントが変わるだけmy $client = TheSchwartz::Fireworq->new(server => 'http://localhost:8080', # Fireworqworker => 'http://localhost:5000/work', # ワーカー);$client->insert('My::Worker', { @_ });19
20Fireworqの運用プラクティス
複数キューの使い分け● 時間のかかるジョブはキューを別にする● ジョブカテゴリは細かく分けておく○ カテゴリごとに配送先キューを設定するため21
スロットリング● 前提: ジョブは羃等にしておく● TheSchwartzではuniqkeyで可能だった○ 本当の意味のスロットリングではない● 自前でやる必要がある○ キーごとに最終実行予定時刻を記録すれば可能○ 投入ジョブの実行予定より後の予定があればスキップ○ なければrun_afterでインターバルを空けて投入22
失敗時の再送● 自動で再送される○ 指定したmax_retriesの回数以内の場合○ ジョブを掴んでいる途中でFireworqが落ちた場合● それでも失敗したもの(permanent failure)○ GET /queue//failedで一覧が取れる○ POST /job/で必要に応じて再投入する23
Mackerelで監視● mackerel-plugin-fireworq○ キューやノードの状態のメトリックを取る● mackerel-check-fireworq○ ジョブの失敗(permanent failure)をアラート24
25まとめ
まとめ● Fireworqはジョブキュー○ 言語非依存でスケールする○ ワーカーもweb appのためわかりやすい● TheSchwartzからの移行は簡単● 本番運用に必要なものは揃っている● コントリビュータ・メンテナ募集中26
27質問?
28FAQ
Q. HTTP以外はサポートしないの?A.● gRPCとか? HTTP/2?○ サポートしてもよいかも○ コントリビュータ歓迎● 必要なほどパフォーマンスがシビアな状況?○ 今のところ聞いたことがない○ 試してみてHTTPでは無理だったらおしえてください29
Q. MySQL以外のストレージではダメ?A.● 内部コード的には変えられる○ インタフェースさえ満たせばなんでもよい設計● 当初はRedisエンジンも実装予定だった○ 単にめんどうでやってないだけ○ コントリビュータ歓迎30
31おわり