Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Pawoo(Mastodon)が動く仕組み
Search
abcang
September 26, 2017
Technology
0
1.7k
Pawoo(Mastodon)が動く仕組み
pixiv SUMMER BOOT CAMP 2017 講義資料
abcang
September 26, 2017
Tweet
Share
More Decks by abcang
See All by abcang
カスタマイズしたMastodonを本家に追従するときのコツ
abcang
0
1.4k
Other Decks in Technology
See All in Technology
dbt開発 with Claude Codeのためのガードレール設計
10xinc
2
1.3k
20250913_JAWS_sysad_kobe
takuyay0ne
2
240
新アイテムをどう使っていくか?みんなであーだこーだ言ってみよう / 20250911-rpi-jam-tokyo
akkiesoft
0
310
AWSで始める実践Dagster入門
kitagawaz
1
680
企業の生成AIガバナンスにおけるエージェントとセキュリティ
lycorptech_jp
PRO
2
190
なぜスクラムはこうなったのか?歴史が教えてくれたこと/Shall we explore the roots of Scrum
sanogemaru
5
1.6k
はじめてのOSS開発からみえたGo言語の強み
shibukazu
3
890
Automating Web Accessibility Testing with AI Agents
maminami373
0
1.3k
AI時代を生き抜くエンジニアキャリアの築き方 (AI-Native 時代、エンジニアという道は 「最大の挑戦の場」となる) / Building an Engineering Career to Thrive in the Age of AI (In the AI-Native Era, the Path of Engineering Becomes the Ultimate Arena of Challenge)
jeongjaesoon
0
210
開発者を支える Internal Developer Portal のイマとコレカラ / To-day and To-morrow of Internal Developer Portals: Supporting Developers
aoto
PRO
1
470
実践!カスタムインストラクション&スラッシュコマンド
puku0x
0
480
ブロックテーマ時代における、テーマの CSS について考える Toro_Unit / 2025.09.13 @ Shinshu WordPress Meetup
torounit
0
130
Featured
See All Featured
RailsConf 2023
tenderlove
30
1.2k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
1.6k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
33
2.4k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
8
920
Documentation Writing (for coders)
carmenintech
74
5k
Become a Pro
speakerdeck
PRO
29
5.5k
Bootstrapping a Software Product
garrettdimon
PRO
307
110k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
23
1.4k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
131
19k
Writing Fast Ruby
sferik
628
62k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
Thoughts on Productivity
jonyablonski
70
4.8k
Transcript
Pawoo(Mastodon)が 動く仕組み @abcang
自己紹介 • abcang(@abcang1015) ◦ 「あべちゃん」と呼ばれている • 17年度新卒 • Pawooの開発に携わってる •
Mastodonのコントリビュートもしてる
今日話すこと • MastodonとPawoo • Masotodonの構成 ◦ ディレクトリ構成や役割 ◦ Mastodonで使われているプロトコル •
実際にどうやってインスタンス間で通信しているか ◦ フォローしたときの流れ ◦ トゥートしたときの流れ
MastodonとPawoo • Mastodon ◦ 自由でオープンソースなソーシャルネットワーク ▪ Twitterと同じマイクロブログ ◦ 誰でもMastodonインスタンスを立てることができる •
Pawoo ◦ Mastodonインスタンスの一つ ◦ Mastodonをベースにして拡張している ▪ pixiv連携や自動消滅トゥート、メディアタイムラインなどなど
Mastodonの構成 nginx Rails Node.js Sidekiq Mastodonの本体部分 (APIの提供など) トゥートや通知の ストリーミング配信 トゥートの配信処理
などの非同期処理 PostgreSQL Redis リバースプロキシ
サーバサイド(Ruby on Rails) • webpackerを導入している ◦ ReactなどのJavaScriptのパッケージも使用している • 非同期処理が非常に多い ◦
インスタンス内の処理 ▪ トゥートをTLに反映、通知の送信、メールの送信、ストリーミング配信 ◦ インスタンス間の処理 ▪ トゥートの送受信、フォロー、通知の送信
ディレクトリ構造 通常のJavaScriptファイルapp/assets/javascriptsに配置するが、 webpackerでバンドルするファイルは app/javascriptに配置する ワーカークラス(約50ファイル) Sidekiqを使用した非同期処理を記述 サービスクラス(約50ファイル) 複数のモデルの操作や外部との通信などの複雑な処理を記述
サーバサイド(Node.js) • トゥートや通知のストリーミングAPIを提供 ◦ /api/v1/streaming/ • 500行ぐらいの1つのファイルで構成されている ◦ streaming/index.js •
やっていること ◦ リクエスト時に付与されたtokenを使って認証処理 ◦ redisのチャンネルを購読して、受け取ったデータを返す
Mastodonのフロントエンド • タイムラインのページはSPA ◦ ReactとReduxが使われている • webpackerを使ってファイルをバンドル ◦ JavaScriptだけでなくscssや画像もバンドル ◦
mastodon: JavaScriptのモジュール ▪ ReactやReduxでよくある構成になってる ◦ packs: エントリーポイント ▪ ビルドの起点となるファイルがある ▪ mastodonディレクトリのファイルをimport
app/javascript/mastodon コンポーネントを機能単位でまとめている。 機能ごとのディレクトリがあり、 それぞれの中にcomponentやcontainersがある。 翻訳ファイル ReactやReduxを使用したときの 一般的なディレクトリ構成
Mastodonで使われているプロトコル • OStatus ◦ 分散SNSを実現するためのプロトコル ▪ インスタンス間のフォローや投稿の配信の方法を決めている ◦ PubSubHubbub、Activity Streams、Webfinger、Salmonなどの
プロトコルを組み合わせて実現している • ActivityPub ◦ 分散SNSを実現するためのプロトコル ◦ データの形式にActivity Streamsを採用している ◦ Mastodon v1.6.0から対応 ◦ (今回は話しません)
OStatusで使用されているプロトコル • PubSubHubbub ◦ リアルタイムに更新通知を配信するためのプロトコル • Activity Streams ◦ フォローや投稿を表現できるようにAtomを拡張したフォーマット
• Webfinger ◦ アカウントの情報を検出するプロトコル • Salmon ◦ 発信元のサーバーに返信を反映させるためのプロトコル
実際にどうやって インスタンス間で通信しているか
フォローしたときの流れ 1. /api/v1/accounts/:id/follow にリクエストを送る 2. Api::V1::FollowsController#createが実行 ◦ FollowService.new.call(current_user.account, target_uri) 3.
FollowServiceでフォロー処理を行う
FollowService 1. アカウントの情報を取得 ◦ ResolveRemoteAccountService ◦ リモートのユーザーの場合はWebFingerを使って取得 2. フォロー情報を作成 ◦
account.follow!(target_account) 3. リモートユーザーかつ購読していない場合は購読を開始 ◦ Pubsubhubbub::SubscribeWorkerを実行 4. フォローを通知(NotifyService または NotificationWorker) 5. フォローした人のトゥートをホームTLに反映 ◦ MergeWorkerを実行
PubSubHubbubによる購読 • Pubsubhubbub::SubscribeWorker ◦ SubscribeServiceを実行 ◦ 自分のインスタンスの情報を相手のインスタンスに伝える インスタンスA インスタンスB インスタンスBの
Xさんを購読したい インスタンスAにインスタンスBのXさんのアカウントを作成しました トゥートしたら/api/subscriptions/1に送ってください POST /api/push
トゥートしたときの流れ nginx Rails Node.js Sidekiq /api/v1/statuses { in_reply_to_id: null, //
リプライ先のid media_ids: [], // 添付するメディアのid sensitive: false, // NSFWの設定 spoiler_text: "", // Content Warningの設定 status: "ぱうー", // トゥート内容 visibility: "public", // 公開範囲 } PostgreSQL Redis トゥート! Api::V1::StatusesController#create が呼び出される PostStatusServiceを呼び出して トゥートの送信処理を行う
PostStatusServiceの処理 nginx Rails Node.js Sidekiq PostgreSQL Redis 1. トゥートの作成 2.
メディアの紐付け 3. ハッシュタグの紐付け(ProcessHashtagsService) 4. メンションの生成(ProcessMentionsService) 5. PreviewCardの作成(LinkCrawlWorker) 6. トゥートの配信(DistributionWorker) 7. トゥートを他のインスタンスに配信 (Pubsubhubbub::DistributionWorker) 1,2,3,4でDBに書き込み 5,6,7でSidekiqのジョブを Redisに積む
PreviewCardの作成(LinkCrawlWorker) • PreviewCard ◦ OGPやOEmbedを展開する機能 • LinkCrawlWorker ◦ FetchLinkCardServiceを実行 •
FetchLinkCardService ◦ トゥート内に含まれるURLにアクセスして情報を取得 ◦ 非同期で実行される
トゥートの配信(DistributionWorker) • DistributionWorker ◦ FanOutOnWriteServiceを実行するだけ • FanOutOnWriteService ◦ フォローワーの数だけFeedInsertWorkerを実行 ▪
トゥートをフォローワーのホームTLに挿入 ▪ トゥートをフォローワーにストリーミング配信する ▪ (Pawooでは何件かまとめて処理するように改良している) ◦ ローカルTL、連合TL、ハッシュタグTLにストリーミング配信する
• Redisのpub/subを利用 ◦ Sidekiqからストリーミングに流すデータをpublish ◦ Node.js側でsubscribeして、受け取ったデータをWebSocketで流す ストリーミング配信 nginx Rails Node.js
Sidekiq PostgreSQL Redis Redis.current.publish('timeline:public', @payload) redisClient.on('message', (channel, message) => { log.silly(`New message on channel ${channel}`); });
トゥートを他のインスタンスに配信 • Pubsubhubbub::DistributionWorker ◦ トゥートをActivityStreamsの形式に変換 ◦ 購読しているアカウントに配信 ▪ 実際の送信処理はPubsubhubbub::DeliveryWorkerで行う •
Pubsubhubbub::DeliveryWorker ◦ 実際にAtomを送信する ▪ /api/subscriptions/:id に送る ◦ 配信に失敗すると3回までリトライする ▪ インスタンスが落ちていた間のトゥートは、インスタンスが復活したあとに 再送信されない
トゥートの受信 • Api::SubscriptionsController#update ◦ トゥート(Atom)が送られてくると呼ばれる ◦ 署名を確認して問題なければProcessingWorkerを実行 • ProcessingWorker ◦
Atomを読み込んで、自身のインスタンスにトゥートを作成する ◦ 実際はProcessFeedServiceが処理する
ProcessFeedService • Atomを読んで、ActivityStreamsの種類を確認する ◦ 種類に応じてトゥートの作成や削除を行う • トゥートの作成はPostStatusServiceとほぼ同じ流れ 1. トゥートの作成 2.
メディアの紐付け 3. ハッシュタグの紐付け(ProcessHashtagsService) 4. メンションの生成 5. PreviewCardの作成(LinkCrawlWorker) 6. トゥートの配信(DistributionWorker)
まとめ • Mastodonの構成 ◦ RailsやNode.jsを使用している ◦ フロントはReactやReduxを使用している • トゥートしたときの流れ ◦
トゥートの作成は同期実行、トゥートの配信は非同期実行 ◦ Worker内でさらにWorkerを実行するため非同期処理が非常に多い
おまけ • Mastodonにコントリビュートするには ◦ トゥートの配信の仕組みをよく知らなくても大丈夫 ◦ 実際に自分で使っていると挙動がおかしいことに気づくことがある ▪ 特にJavaScript周りは正しく動かなくなっていることが多い •
質問があればどうぞ!