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.8k
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
最強のAIエージェントを諦めたら品質が上がった話 / how quality improved after giving up on the strongest AI agent
kt2mikan
0
190
Go標準パッケージのI/O処理をながめる
matumoto
0
220
JAWSDAYS2026 [C02] 楽しく学ぼう!AWSとは?AWSの歴史 入門
hiragahh
0
170
社内レビューは機能しているのか
matsuba
0
140
形式手法特論:SMT ソルバで解く認可ポリシの静的解析 #kernelvm / Kernel VM Study Tsukuba No3
ytaka23
1
270
決済サービスを支えるElastic Cloud - Elastic Cloudの導入と推進、決済サービスのObservability
suzukij
2
660
生成AI活用でQAエンジニアにどのような仕事が生まれるか/Support Required of QA Engineers for Generative AI
goyoki
1
240
実践 Datadog MCP Server
nulabinc
PRO
2
240
楽しく学ぼう!ネットワーク入門
shotashiratori
4
3.4k
visionOS 開発向けの MCP / Skills をつくり続けることで XR の探究と学習を最大化
karad
1
500
Cortex Code CLI と一緒に進めるAgentic Data Engineering
__allllllllez__
0
410
マルチアカウント環境でSecurity Hubの運用!導入の苦労とポイント / JAWS DAYS 2026
genda
0
840
Featured
See All Featured
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.4k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
320
Test your architecture with Archunit
thirion
1
2.2k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
2.5k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
1.9k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
1.9k
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
1
1.3k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
Evolving SEO for Evolving Search Engines
ryanjones
0
160
VelocityConf: Rendering Performance Case Studies
addyosmani
333
24k
Deep Space Network (abreviated)
tonyrice
0
92
Making the Leap to Tech Lead
cromwellryan
135
9.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周りは正しく動かなくなっていることが多い •
質問があればどうぞ!