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
新卒エンジニアが0からNon-BlockingなgPRCサーバーを作った話
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
株式会社出前館
June 17, 2024
Technology
1k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
新卒エンジニアが0からNon-BlockingなgPRCサーバーを作った話
JJUGにて登壇した内容です
株式会社出前館
June 17, 2024
More Decks by 株式会社出前館
See All by 株式会社出前館
中期計画、2回作ってみた ~業務委託と正社員、両方の視点から~
demaecan
1
1.1k
複雑にからみあう複数のシステムを要する出前館QAの実情、展望
demaecan
0
200
QA業務を変える(!?)AIを併用した不具合分析の実践
demaecan
0
210
出前館アプリの品質を支えるリリーストレインとその実践
demaecan
0
260
出前館アプリ進化論 アーキテクチャと組織のリアルな変⾰の舞台裏
demaecan
0
790
Flutterにしてよかった?出前館アプリを2年運用して気づいたことを全部話します
demaecan
1
1.2k
Boxを“使われる場”にする統制と自動化の仕組み
demaecan
1
480
生成AI導入における「短期ROIを超えた」共存戦略
demaecan
0
170
Okta Identity Governanceで実現する最小権限の原則
demaecan
1
490
Other Decks in Technology
See All in Technology
SoccerMaster: A Vision Foundation Model for Soccer Understanding
kzykmyzw
0
170
伝票作成AIエージェントを支える、LLMOpsとインフラの選択肢 / AICon2026_takeda
rakus_dev
0
260
AIエージェントがあれば技術書なんてすぐ書けるでしょ→無理でした
watany
2
260
大 AI 時代におけるC# の事情 ~ぶっちゃけトークを交えながら~
nenonaninu
0
110
プロダクト開発組織の現在地(Ver.2026/07) / product-organization
kaonavi
0
400
仕様駆動開発、導入半年。「本当に速くなってるの?」にデータで答える / AICon2026_hirakawa
rakus_dev
0
320
AIが当たり前の組織で エンジニアはどう育つか
nishihira
1
970
Webアプリ認証の全体像 / The Big Picture of Web App Authentication
kitano_yuichi
1
420
そのドキュメント、自動化しませんか?
yuksew
1
410
Multicaで30個のミニプロジェクトをAIエージェント運用して見えてきたこと
eiei114
1
630
kaonavi Tech Night#1
kaonavi
0
160
[Droidcon Orlando '26] The Android Lens: Applying Mobile Forensics to AI Performance
amanda_hinchman
1
110
Featured
See All Featured
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
550
The Illustrated Children's Guide to Kubernetes
chrisshort
51
53k
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
Building Flexible Design Systems
yeseniaperezcruz
330
40k
The Art of Programming - Codeland 2020
erikaheidi
57
14k
Visualization
eitanlees
152
17k
Google's AI Overviews - The New Search
badams
0
1.1k
Building AI with AI
inesmontani
PRO
1
1.1k
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
62
45k
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
1
3.7k
Paper Plane
katiecoart
PRO
2
52k
Building the Perfect Custom Keyboard
takai
2
820
Transcript
新卒エンジニアが 0 から Non-Blocking な gRPC サーバーを作った話 株式会社出前館 佐藤 幸樹
⾃⼰紹介 2023年4⽉ LINE株式会社 (現LINEヤフー株式会社) 新卒⼊社 2023年7⽉ 株式会社出前館へ出向 商品管理・決済周りのバックエンド開発 学⽣の頃はRailsなどで開発していたが、LINEに⼊ってJVM系の⾔語で 開発するようになりました。
2
主な対象 • REST API は書いたことがあるが gRPC でサービスを作ったことがない⽅ • Non-Blockingな処理をするサーバーを作ったことがない⽅ •
(Spring Bootでアプリケーションを作ったことがある⽅) gRPC や Non-Blocking に興味を持ってもらい、今後開発する際の技術選定の 参考にしてもらいたい。 3 https://github.com/sato9818/jjug-sample
出前館について システム刷新 gRPC と Non-Blocking 技術選定 苦労した点 01 02 03
04 05 4 ⽬次
出前館について システム刷新 gRPC と Non-Blocking 技術選定 苦労した点 01 02 03
04 05 5 ⽬次
出前館 6 Web サイトやアプリから料理、⾷品、⽇⽤品を注⽂できる 国内最⼤級のデリバリーサービス 2000年 10⽉ 「出前館」サービス開始 2023年 •
アクティブユーザー数約650万⼈ • 年間7730万件注⽂
出前館 7 バックエンドは • Spring Boot • Java • Kotlin
をメインに構築されています 決済サービスとカートサービスを担当
出前館について システム刷新 gRPC と Non-Blocking 技術選定 苦労した点 01 02 03
04 05 8 ⽬次
システム刷新 問題 • システムのレガシー化 • オンプレ環境を管理する⼯数がかかる • フレームワークや⾔語が古く、サポートが終了している • 古い技術に対応できる⼈が少ない
Þ2020年にマイクロサービス化を進めていく過程で、ある程度解消 ✅ オンプレからAWSに移⾏ ✅ コードのリライト 9
システム刷新 しかしまだ問題が… • 技術的負債 • 1つの Oracle DB のテーブルに複数のサーバーが参照している(密結合) •
仕様的負債 • 加盟店(レストラン等)によって少しずつ違う仕様があることで、開発コスト増 10
システム刷新 再度システム刷新が必要︕ • 仕様のシンプル化: 可能な限りお店独⾃仕様の撤廃 • DB の分離: マイクロサービスごとにDBを持つ (Oracle
→ MySQL、MongoDB) • コスト削減: AWS → プライベートクラウド • etc… 11
After Before 通信⽅式 処理⽅式 REST API Blocking gRPC Non-Blocking 12
システム刷新 DB Oracle MySQL
出前館について システム刷新 gRPC と Non-Blocking 技術選定 苦労した点 01 02 03
04 05 13 ⽬次
gRPC Google によって開発された、クライアントとサーバ間の通信⼿段の1つ PRC: Remote Procedure Call (遠隔⼿続き呼び出し) `g`については諸説あり (Google?
General?) 特徴 • HTTP/2を使った⾼速な通信が可能 • ローカルにある関数を呼び出すかのように、サーバーにリクエストできる • さまざまな⾔語に対応している (Java, Kotlin, Go...) 14
gRPC 4つの通信⽅式 • Unary RPC: 1リクエスト 1レスポンス • 例: ユーザー情報の取得
• Server streaming RPC: 1リクエスト nレスポンス • 例: ビデオストリーミング、株価のリアルタイム更新 • Client streaming RPC: nリクエスト 1レスポンス • 例: ファイルアップロード、センサーデータの収集 • Bidirectional streaming RPC: nリクエスト nレスポンス • 例: リアルタイムチャットアプリケーション 15
Protocol Buffers (protobuf) Protocol Buffers という IDL (Interface Definition Language*)
を⽤いて、 APIのインターフェースを定義する ソースコードを⽣成するには…? • Protobuf のコンパイラである protoc を使う 厳密にはソースコードを⽣成する際には protoc プラグインが必要 (Javaコードを⽣成したい場合は、それ⽤のプラグインが必要) 16 *Interface Definition Language: プログラミング⾔語とは異なり、インターフェースを記述することに重きを置いた⾔語
Protocol Buffers (protobuf) 17 syntax = “proto3”; package sample; //
ユーザーの情報を取得 service UserService { // IDに基づいてユーザーの情報を取得 rpc GetUser(GetUserRequest) returns (GetUserResponse); } // ユーザー情報を取得するリクエスト message GetUserRequest { int32 id = 1; // 取得したいユーザーのID } // 取得したユーザーの情報 message GetUserResponse { int32 id = 1; // ユーザーのID string name = 2; // ユーザーの名前 string email = 3; // ユーザーのメールアドレス }
Protocol Buffers (protobuf) 18 syntax = “proto3”; package sample; //
ユーザーの情報を取得 service UserService { // IDに基づいてユーザーの情報を取得 rpc GetUser(GetUserRequest) returns (GetUserResponse); } // ユーザー情報を取得するリクエスト message GetUserRequest { int32 id = 1; // 取得したいユーザーのID } // 取得したユーザーの情報 message GetUserResponse { int32 id = 1; // ユーザーのID string name = 2; // ユーザーの名前 string email = 3; // ユーザーのメールアドレス } package sample; public final class UserServiceOuterClass { private UserServiceOuterClass() {} public static void registerAllExtensions( com.google.protobuf.ExtensionRegistryLite registry) { } public static void registerAllExtensions( com.google.protobuf.ExtensionRegistry registry) { registerAllExtensions( (com.google.protobuf.ExtensionRegistryLite) registry); } public interface GetUserRequestOrBuilder extends // @@protoc_insertion_point(interface_extends:sample.GetUserReq uest) com.google.protobuf.MessageOrBuilder { … $ protoc --java_out=. example.proto protobuf Java クラス
REST API 共通 gRPC プロトコル HTTP/1.1が⼀般的 HTTP/2 データフォーマット JSONやXMLなどテキスト ベース
Protocol Buffers 開発の容易さ シンプルで直感的 Protocol Buffersを 覚えなきゃいけない分⼤変 Client Sever間の規約 ゆるい 厳しい 互換性 異なる⾔語間で通信可能 19 gRPC vs REST API
gRPC 個⼈的に便利だと思った点 • Protocol Buffers で型が定義できるので、リクエストする際に型を間違えて エラーにならない • クライアントとサーバーで同じスキーマを共有するので、タイポしてエラーにならない •
Streaming など拡張性が⾼い 20
Non-Blocking 処理 スレッドや関数を実⾏した際に、その実⾏したスレッドをブロックしないで結果を得る処理 óBlocking 処理 スレッドや関数を実⾏した際に、その実⾏したスレッドをブロックして結果を得る処理 21
⽐較 22 Client Server1 Server2 リクエスト1 リクエスト2 レスポンス1 レスポンス2 待ち
待ち Blocking Client Server1 Server2 リクエスト1 リクエスト2 レスポンス1 レスポンス2 待ちがない Non-Blocking スレッドをブロッキングしている スレッドをブロッキングしない ※リクエスト1とリクエスト2には依存関係がない
Blocking 23 public void getUserAndItem(String userId, String itemId) { final
User user = getUser(userId); final Item item = getItem(itemId); System.out.println("user: " + user.toString() + ", item: " + item.toString()); } public User getUser(String id) { // サーバーAにUser情報を取りに⾏く。 } public Item getItem(String id) { // サーバーBにItem情報を取りに⾏く } 1秒 2秒 1秒
Non-Blocking 24 public void getUserAndItem(String userId, String itemId) { CompletableFuture<User>
userFuture = getUser(userId); CompletableFuture<Item> itemFuture = getItem(itemId); userFuture.thenCombine(itemFuture, (user, item) -> { System.out.println("user: " + user.toString() + ", item: " + item.toString()); return null; }).join(); } public CompletableFuture<User> getUser(String id) { // サーバーAにUser情報を取りに⾏く。 } public CompletableFuture<Item> getItem(String id) { // サーバーBにItem情報を取りに⾏く } 1秒 1秒 1秒ちょい
Non-Blocking ⼿法 Javaの場合 • CompletableFuture • Reative Streams Kotlinの場合 •
Kotlin Coroutine 25
Non-Blocking IO アプリケーションの内部の処理の改善だけでもでも処理は効率化する さらなる効率化をするには・・・ アプリケーションがリクエストを受けて、レスポンスを返すまで全て Non-Blocking にする ネットワーク越しでデータのやり取りをするようなライブラリを全て Non-Blocking (Non-Blocking
IO) に 対応させる必要がある 例えば・・・ • DBからのデータの取得 • HTTP Clientを使⽤した他サーバーからのデータの取得 • アプリケーションサーバー 26
1つのリクエストにつき、1つのスレッドを割り当てる。 課題 もし⼤量のリクエストが来たら︖ → ⽣成できるスレッドの数は限られている。 → スレッドを増やしすぎるとコンテキストスイッチのコストも上がる。 Blocking IO 27
Client Blockingサーバー Client Client Thread Thread Thread Multi-Thread Blocking IO
Blocking IO 28 Client Blockingサーバー Client Client Thread Thread Thread
Multi-Thread Blocking IO 課題 別サーバーからのレスポンスが遅延したら︖ → そのスレッドはブロックされて 他のリクエストを処理するのに使えなくなる。 → 内部の処理をNon-blockingにしても スレッド⾃体は占有されてしまう。 別サーバー 遅延 Client New! 占有
Non-Blocking IO 29 Non-Blocking IO イベントループという仕組みを使うことでスレッドを ブロックしなくなり、少数のスレッドで複数のリクエストを 処理できるようになる。 暇なスレッドがリクエストを処理に⾏けるイメージ。 Client
Non-Blockingサーバー Client Client イベントループ
Non-Blocking IO 30 Multi-Thread Blocking IO 別サーバーなどからのレスポンスが 遅延したとしてもその待ち時間にスレッドは 他のリクエストの処理をすることができる。 Client
Non-Blockingサーバー Client Client イベントループ 別サーバー 遅延 Client New!
Non-Blocking IO • Javaではイベントループを実装しているNettyというネットワークライブラリが 使われることが多い。 参考*: 「JJUG CCC 2018 Spring
- I-7 (俺が)はじめての Netty」 • Non-Blocking IO ではアプリケーションの内部でスレッドをブロックしてしまうと、 致命的にパフォーマンスが落ちる。 • 少数のスレッドだけで、スレッドがブロッキングされないこと前提で動いているので、処理 できるスレッドが減ってしまう。 31 * https://www.slideshare.net/slideshow/jjug-ccc-2018-spring-i7-netty/99033170
Non-Blocking IO 32 Multi-Thread Blocking IO 別サーバーへのリクエストはNon-Blocking にする必要がある。 Non-Blockingに対応しているライブラリを使おう。 Client
Non-Blockingサーバー Client Client イベントループ 別サーバー 遅延 Client New! ブロックしちゃダメ!!
Non-Blocking IO対応ライブラリ 33 Blocking IO Non-Blocking IO DB JDBC R2DBC
Redis Jedis Lettuce Web framework Spring MVC Spring WebFlux Armeria HTTP Client Spring RestClient Spring Webclient
Non-Blocking IO WEBフレームワーク • Spring WebFlux • Armeria • Vert.x
• Quarkus • Micronaut • Helidon 34
Blocking Non-Blocking 多くのリクエストを捌くのに 必要なリソース 多くのリソース 少数のリソース スケーラビリティ 低い ⾼い 実装の複雑性
(コードの追いやすさ) シンプル 複雑 学習コスト 低い ⾼い 35 Non-Blocking vs Blocking
出前館について システム刷新 gRPC と Non-Blocking 技術選定 苦労した点 01 02 03
04 05 36 ⽬次 https://github.com/sato9818/jjug-sample
システム概要 37
システム概要 38
Buf Protobuf のコンパイラ。Protoc のプラグインを使える。 特徴 • Protoc の2倍の速さのコンパイル • Protobuf
の Linter を備えている • Protobuf が破壊的変更をしたかどうか検知できる • gRPC の Request の validation をできる • Buf Schema Registry という Protobuf を⼀元管理してくれるプラットフォームを 提供している 39
Buf Gradleプラグインも提供されている。 (https://github.com/bufbuild/buf-gradle-plugin) Gralde タスクを通して • ソースコードの⽣成 • Lint •
破壊的変更の検知 などができる。 40
Buf 実際にgradleで⽣成する⽅法をみてみましょう。 ブランチ: 1-buf-build 41
技術選定 42
gRPC-java Java で gRPC サーバー、クライアントを作るライブラリ群。 • サーバー側は Netty でできているのでこのライブラリでサーバーを作成すると Non-Blocking
なサーバーになる。 • しかし、アプリケーションが実⾏されるスレッドはブロッキングされても良いような 実装になっているのでブロッキングする実装でもOK。 • ⼀般的に java で gRPC サーバーやクライアントを作りたい場合はこのライブラリ を使うことになる。 参考: https://github.com/grpc/grpc-java 43
gRPC-java ⼀旦 gRPC-java だけを使ってサーバーとクライアントがどのように動くかみてみましょう。 ブランチ: 2-grpc-java 44
技術選定 45
Spring Boot ⾔わずと知れた Java の Web フレームワーク 特徴 • Dependency
Injection • Transaction Manager • Data Binding 選定理由 出前館のマイクロサービスのほとんどは Spring Boot を使っているので、 共通フレームワークの1つとして認識されている。 46
Spring Boot with gRPC gRPC-java と Spring Boot は親和性がない。 Spring
Boot の機能を使って gRPC を実装したい︕ • LogNet/grpc-spring-boot-starter: 2023年9⽉に開発が⽌まってる。 • grpc-ecosystem/grpc-spring • line/armeria 47
Spring Boot with gRPC grpc-ecosystem/grpc-spring • Spring Security も対応している •
アノテーションベースで Spring Like な開発ができる line/armeria • LINEヤフー社で開発しているので、サポートしてもらいやすい • LINEヤフー社内事例が多く存在し、困った時に社内のコードを検索すると 参考になるコードがたくさんある • 便利機能がたくさんある 参考: https://github.com/grpc/grpc-java 48
Armeria Netty の創始者である Trustin が作った Non-Blocking な RPC フレームワーク 特徴
• 旧LINEで開発が始まったOSS • Netty をベースとした Non-Blocking HTTP/2 サーバー • gRPC-java を内包しているので、Non-Blocking な gRPC サーバーを作成できる 49
Armeria いいところ • LINEヤフー社内事例が多く存在し、困った時に社内のコードを検索すると 参考になるコードがたくさんある。 • LINEヤフー社内 slack に help
チャンネルがあり、困った時にすぐに聞ける環境 が存在する。(外部向けにも discord server があり、そこでも質問できる) • 便利機能が多くある。 50
Armeria 便利機能その1 • DocService: SwaggerのようなgRPC対応のドキュメンテーションサービス 51
Armeria 便利機能その2 • Decorator Decorator機能を使うことで、リクエストやレスポンスの前後に処理を挟むことができる 例 • Logging: リクエスト、レスポンスのログをいい感じに出してくれる。 •
Retry: クライアントのリクエストでタイムアウトなどが発⽣した際にリトライをする機能。 • CircuitBreaker: リクエスト先のサーバーで何か異常があった時に、リクエストを遮断する機能。 52
Armeria ⼀旦 Spring Boot は置いといて、Armeria単体でどのようにサーバー実装 をするのかみてみましょう。 ブランチ: 3-armeria 53
Armeria with Spring Boot Armeriaは Spring Boot との統合も提供している。 何がいいか サーバーはArmeriaのものを使って
• Dependency Injection • Transaction Manager • Data Binding といった 機能は Spring Boot のものを使うということができる。 54
Armeria with Spring Boot どのように Armeria と Spring Boot を統合させるかみてみましょう。
ブランチ: 4-armeria-spring 55
技術選定 56
HTTP Client 決済をするには外部決済代⾏会社にリクエストをする必要がある Armeria が HTTP Client を提供している。 • Retrofit
• Spring WebClient 57
HTTP Client Retrofit • Square社が開発している Java や Android 向けの Http
Client。 Non-Blocking に対応している。 • インターフェイスを定義する形で API リクエストを定義できる。 • Mock ライブラリなど、周辺ライブラリも充実している。 WebClient • Spring Framework の1つで Non-Blocking に対応した Http Client。 • プログラム的に API リクエストを設定する。 • Reactive Streamsに対応している。 58
Armeria with Retrofit Armeriaと統合することで何がいいか • Decoratorが使える • Retry機能 • Circuit
breaker機能 59 ArmeriaRetrofit armeriaRetrofit = ArmeriaRetrofit.builder() .baseUrl("http://example.com") .decorator(LoggingClient.newDecorator()) .decorator(CircuitBreakerClient.newDecorator(circuitBreaker)) .decorator(RetryingClient.newDecorator(retryConfig)) .build();
技術選定 60
R2DBC (Reactive Relational Database Connectivity) Reactive Streams をベースにアクセスを Non-Blocking にするDBドライバ。
JDBCのようなドライバーでDBに Blocking アクセスをすると、DBアクセスの間 スレッドがブロックされて、パフォーマンス悪化の原因になる。 • r2dbc-mysql: MySQL ⽤のドライバー • r2dbc-pool: コネクションプーリングライブラリ • Spring-boot-starter-data-r2dbc: Spring Boot と R2DBC の統合 61
技術選定 62
出前館について システム刷新 gRPC と Non-Blocking 技術選定 苦労した点 01 02 03
04 05 63 ⽬次
苦労した点 • モダンな技術を使っているので、資料がたくさんあるわけではなく、理解するために 実際にコード読んだりするのが⼤変だった。 • 欲しい機能がない場合は実際 PR 作ってコントリビュートしたりした。 • https://github.com/line/armeria/pull/5555
• https://github.com/line/armeria/pull/5613 • https://github.com/line/armeria/pull/5617 • https://github.com/bufbuild/buf-gradle-plugin/pull/170 • でも総じてモダンな技術に触れられて楽しい︕ 64
宣伝 こんなモダンな技術を使える出前館に⼊社してみませんか︕︖ • 新卒採⽤ • エンジニア • 中途採⽤ • サーバーサイドエンジニア
• シニアサーバーサイドエンジニア • フロントエンドエンジニア • インフラエンジニア • データベースエンジニア • SRE • QAエンジニア https://recruit.demae-can.co.jp/ 65
66