Upgrade to Pro — share decks privately, control downloads, hide ads and more …

新卒エンジニアが0からNon-BlockingなgPRCサーバーを作った話

 新卒エンジニアが0からNon-BlockingなgPRCサーバーを作った話

JJUGにて登壇した内容です

株式会社出前館

June 17, 2024
Tweet

More Decks by 株式会社出前館

Other Decks in Technology

Transcript

  1. 主な対象 • REST API は書いたことがあるが gRPC でサービスを作ったことがない⽅ • Non-Blockingな処理をするサーバーを作ったことがない⽅ •

    (Spring Bootでアプリケーションを作ったことがある⽅) gRPC や Non-Blocking に興味を持ってもらい、今後開発する際の技術選定の 参考にしてもらいたい。 3 https://github.com/sato9818/jjug-sample
  2. 出前館 7 バックエンドは • Spring Boot • Java • Kotlin

    をメインに構築されています 決済サービスとカートサービスを担当
  3. システム刷新 しかしまだ問題が… • 技術的負債 • 1つの Oracle DB のテーブルに複数のサーバーが参照している(密結合) •

    仕様的負債 • 加盟店(レストラン等)によって少しずつ違う仕様があることで、開発コスト増 10
  4. gRPC Google によって開発された、クライアントとサーバ間の通信⼿段の1つ PRC: Remote Procedure Call (遠隔⼿続き呼び出し) `g`については諸説あり (Google?

    General?) 特徴 • HTTP/2を使った⾼速な通信が可能 • ローカルにある関数を呼び出すかのように、サーバーにリクエストできる • さまざまな⾔語に対応している (Java, Kotlin, Go...) 14
  5. gRPC 4つの通信⽅式 • Unary RPC: 1リクエスト 1レスポンス • 例: ユーザー情報の取得

    • Server streaming RPC: 1リクエスト nレスポンス • 例: ビデオストリーミング、株価のリアルタイム更新 • Client streaming RPC: nリクエスト 1レスポンス • 例: ファイルアップロード、センサーデータの収集 • Bidirectional streaming RPC: nリクエスト nレスポンス • 例: リアルタイムチャットアプリケーション 15
  6. Protocol Buffers (protobuf) Protocol Buffers という IDL (Interface Definition Language*)

    を⽤いて、 APIのインターフェースを定義する ソースコードを⽣成するには…? • Protobuf のコンパイラである protoc を使う 厳密にはソースコードを⽣成する際には protoc プラグインが必要 (Javaコードを⽣成したい場合は、それ⽤のプラグインが必要) 16 *Interface Definition Language: プログラミング⾔語とは異なり、インターフェースを記述することに重きを置いた⾔語
  7. 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; // ユーザーのメールアドレス }
  8. 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 クラス
  9. REST API 共通 gRPC プロトコル HTTP/1.1が⼀般的 HTTP/2 データフォーマット JSONやXMLなどテキスト ベース

    Protocol Buffers 開発の容易さ シンプルで直感的 Protocol Buffersを 覚えなきゃいけない分⼤変 Client Sever間の規約 ゆるい 厳しい 互換性 異なる⾔語間で通信可能 19 gRPC vs REST API
  10. ⽐較 22 Client Server1 Server2 リクエスト1 リクエスト2 レスポンス1 レスポンス2 待ち

    待ち Blocking Client Server1 Server2 リクエスト1 リクエスト2 レスポンス1 レスポンス2 待ちがない Non-Blocking スレッドをブロッキングしている スレッドをブロッキングしない ※リクエスト1とリクエスト2には依存関係がない
  11. 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秒
  12. 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秒ちょい
  13. Blocking IO 28 Client Blockingサーバー Client Client Thread Thread Thread

    Multi-Thread Blocking IO 課題 別サーバーからのレスポンスが遅延したら︖ → そのスレッドはブロックされて 他のリクエストを処理するのに使えなくなる。 → 内部の処理をNon-blockingにしても スレッド⾃体は占有されてしまう。 別サーバー 遅延 Client New! 占有
  14. 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
  15. Non-Blocking IO 32 Multi-Thread Blocking IO 別サーバーへのリクエストはNon-Blocking にする必要がある。 Non-Blockingに対応しているライブラリを使おう。 Client

    Non-Blockingサーバー Client Client イベントループ 別サーバー 遅延 Client New! ブロックしちゃダメ!!
  16. 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
  17. Buf Protobuf のコンパイラ。Protoc のプラグインを使える。 特徴 • Protoc の2倍の速さのコンパイル • Protobuf

    の Linter を備えている • Protobuf が破壊的変更をしたかどうか検知できる • gRPC の Request の validation をできる • Buf Schema Registry という Protobuf を⼀元管理してくれるプラットフォームを 提供している 39
  18. gRPC-java Java で gRPC サーバー、クライアントを作るライブラリ群。 • サーバー側は Netty でできているのでこのライブラリでサーバーを作成すると Non-Blocking

    なサーバーになる。 • しかし、アプリケーションが実⾏されるスレッドはブロッキングされても良いような 実装になっているのでブロッキングする実装でもOK。 • ⼀般的に java で gRPC サーバーやクライアントを作りたい場合はこのライブラリ を使うことになる。 参考: https://github.com/grpc/grpc-java 43
  19. Spring Boot ⾔わずと知れた Java の Web フレームワーク 特徴 • Dependency

    Injection • Transaction Manager • Data Binding 選定理由 出前館のマイクロサービスのほとんどは Spring Boot を使っているので、 共通フレームワークの1つとして認識されている。 46
  20. 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
  21. Spring Boot with gRPC grpc-ecosystem/grpc-spring • Spring Security も対応している •

    アノテーションベースで Spring Like な開発ができる line/armeria • LINEヤフー社で開発しているので、サポートしてもらいやすい • LINEヤフー社内事例が多く存在し、困った時に社内のコードを検索すると 参考になるコードがたくさんある • 便利機能がたくさんある 参考: https://github.com/grpc/grpc-java 48
  22. Armeria Netty の創始者である Trustin が作った Non-Blocking な RPC フレームワーク 特徴

    • 旧LINEで開発が始まったOSS • Netty をベースとした Non-Blocking HTTP/2 サーバー • gRPC-java を内包しているので、Non-Blocking な gRPC サーバーを作成できる 49
  23. Armeria いいところ • LINEヤフー社内事例が多く存在し、困った時に社内のコードを検索すると 参考になるコードがたくさんある。 • LINEヤフー社内 slack に help

    チャンネルがあり、困った時にすぐに聞ける環境 が存在する。(外部向けにも discord server があり、そこでも質問できる) • 便利機能が多くある。 50
  24. Armeria 便利機能その2 • Decorator Decorator機能を使うことで、リクエストやレスポンスの前後に処理を挟むことができる 例 • Logging: リクエスト、レスポンスのログをいい感じに出してくれる。 •

    Retry: クライアントのリクエストでタイムアウトなどが発⽣した際にリトライをする機能。 • CircuitBreaker: リクエスト先のサーバーで何か異常があった時に、リクエストを遮断する機能。 52
  25. Armeria with Spring Boot Armeriaは Spring Boot との統合も提供している。 何がいいか サーバーはArmeriaのものを使って

    • Dependency Injection • Transaction Manager • Data Binding といった 機能は Spring Boot のものを使うということができる。 54
  26. HTTP Client Retrofit • Square社が開発している Java や Android 向けの Http

    Client。 Non-Blocking に対応している。 • インターフェイスを定義する形で API リクエストを定義できる。 • Mock ライブラリなど、周辺ライブラリも充実している。 WebClient • Spring Framework の1つで Non-Blocking に対応した Http Client。 • プログラム的に API リクエストを設定する。 • Reactive Streamsに対応している。 58
  27. 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();
  28. 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
  29. 宣伝 こんなモダンな技術を使える出前館に⼊社してみませんか︕︖ • 新卒採⽤ • エンジニア • 中途採⽤ • サーバーサイドエンジニア

    • シニアサーバーサイドエンジニア • フロントエンドエンジニア • インフラエンジニア • データベースエンジニア • SRE • QAエンジニア https://recruit.demae-can.co.jp/ 65
  30. 66