Share
Embed
Transcript
44 slides
-
この講義では • ⽬的: Web API を俯瞰してこの講義以降の講義,調べ物をラクにする • そのために: • ⽤語の確認
• 知識の概要 • Web API をいくつかピックアップ -
この講義で取り扱うもの • ⽬次 • API • Web API • REST
• GraphQL • gRPC • コードを少し触ります -
API の例 • システム 対 アプリケーション画像 • libdrm : 描画
• DRM(カーネル)を動かすための API • glibc : システムコール • 直接起動するのではなくラッパー関数を呼び出す • ブラウザ 対 Web アプリケーション • document.getElementById : 要素取得 • ブラウザなど 対 Web サーバー : Web API • REST • curl -s https:!"random.dog/woof.json 画像 This image is licensed under the Creative Commons Attribution- Share Alike ;.= International license. Attribution: Shmuel Csaba Otto Traian. source https://commons.wikimedia.org/wiki/ File:Linux_API.svg -
Web API の設計指針 • LSUDs (Large Set of Unknown Developers)
• 最⼤公約数的な API を提供する • SSKDs (Small Set of Known Developers) • クライアントに最適化した API を提供する -
REST • Representational State Transfer • HTTP の仕組みをうまく使う • ⼀意な
URI を持つ • https:!"api.github.com/repos/hatena/example/ issues/1 • 提唱者である Roy T. Fielding ⽒の 2000 年頃の論⽂ -
CRUD HTTP メソッド Create POST Read GET Update PUT /
PATCH Delete DELETE -
GitHub のイシューコメント(再掲) • GET /repos/:owner/:repo/issues/ comments/:comment_id • POST /repos/:owner/:repo/issues/:issue_number/ comments
• PATCH /repos/:owner/:repo/issues/ comments/:comment_id • DELETE /repos/:owner/:repo/issues/ comments/:comment_id -
OpenAPI • Open API Spesification の略称 • Swagger • 元々
Swagger フレームワークの⼀部だったという歴史がある • REST API を記述するための仕様 • YAML や JSON で API の仕様を記述できる • ツールを使ってそのままドキュメントやコード⽣成をすることも できる -
SDL (Schema Definition Language) interface Actor { login: String! }
type Issue { author: Actor body: String! title: String! } type Repository { issue(number: Int!): Issue } type Query { repository(name: String!, owner: String!): Repository } -
データの取得例 query GetFirstIssue { repository(name: "Hatena-Intern-2023", owner: "hatena") { issue(number:
1) { author { login } body title } } } -
gRPC • RPC • Remote Procedure Call の略 • gRPC
• Google が開発したプロトコル • IDL をもとにしてサーバーおよびク ライアントの雛形を作成するのが特 徴 • g の意味はバージョンごとに違う -
gRPC over HTTP/- • HTTP/& 上に構築されたもの • HEADERSフレームやDATAフレームを使って通信する • 1
つの TCP コネクション内に複数のストリームを持つことが できる -
gRPC Web • Web ブラウザ向けに HTTP/1 と切り離したもの • サーバー側にもプロキシが必要 •
Envoy などがよく使われる -
Protocol Buffers • 構造化されたデータをシリアライズする仕組み • バイナリフォーマット • IDL (Interface Description
Language) • .protoファイル • gRPC のデフォルトの IDL • RPC をサポートしている -
proto ファイルの例 syntax = "proto3"; package account; service Account {
rpc Signup(SignupRequest) returns (SignupReply); } message SignupRequest { string name = 1; string password = 2; } message SignupReply { string token = 1; } -
メッセージ型 message SignupRequest { string name = 1; string password
= 2; } ϑΟʔϧυܕ ϑΟʔϧυ໊ = ϑΟʔϧυ൪߸; • フィールド番号が重要 • シリアライズ時にはこのフィールド番号が使われる • 1-15 までは 1byte(16 〜 2047 は 2byte) • 19000-19999 は内部実装で予約済み -
スカラー型 double float int32 int64 uint32 uint64 sint32 sint64 fixed32
fixed64 sfixed32 sfixed64 bool string bytes -
その他の proto の⽂法 (1) repeated で配列を表現することができる message Result { repeated
int32 score = 1; } map で辞書構造を表現できる map<string, Project> projects = 3; map は厳密には型ではくシンタックスシュガーで下記の構⽂と糖化です message MapField { key_type key = 1; value_type value = 2; } repeated MapField map_field = 1; -
その他の proto の⽂法 (2) oneof は排他的に現れるフィールドを表現できる oneof test_oneof { string
name = 4; SubMessage sub_message = 9; } -
その他の proto の⽂法 (3) enum も定義できる message SearchRequest { string
query = 1; int32 page_number = 2; int32 result_per_page = 3; enum Corpus { UNIVERSAL = 0; WEB = 1; IMAGES = 2; LOCAL = 3; NEWS = 4; PRODUCTS = 5; VIDEO = 6; } Corpus corpus = 4; } -
外部ファイルの import import "google/protobuf/any.proto"; message Message { google.protobuf.Any field =
1; } • google.protobuf.Empty • google.protobuf.Duration • google.protobuf.Timestamp -
スキーマ変更時の互換性 • ⼀度割り当てたフィールド番号は原則変更できない • 過去のバージョンの.protoファイルを読み込まれる恐れがあるため • フィールド番号などを使わない仕組みがいくつか⽤意されている deprecated int32 old_field
= 6 [deprecated = true]; reserved reserved 2, 15, 9 to 11; reserved "foo", "bar"; -
オプション option go_package = "github.com/hatena/example/pb"; enum EnumAllowingAlias { option allow_alias
= true; UNKNOWN = 0; STARTED = 1; RUNNING = 1; } message Example { int32 old_field = 6 [deprecated = true]; } -
サービスとストリーム service RouteGuide { rpc GetFeature(Point) returns (Feature) {} rpc
ListFeatures(Rectangle) returns (stream Feature) {} rpc RecordRoute(stream Point) returns (RouteSummary) {} rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} } -
RPC の種類 (1) • Unary RPCs (SimpleRPC) • single request
single response • シンプルな通信 • Server streaming RPC • クライアント側はすべてのメッセージを受信すると処理を完 了する -
RPC の種類 (2) • Client streaming RPC • クライアント側は複数のメッセージを送り単⼀のメッセージを受 信する
• Bidirectional streaming RPC • 双⽅向ストリーミングのこと • クライアント側とサーバー側のストリームは独⽴している • そのためそれぞれ任意の順序でメッセージの読み書きができる -
gRPC における API 設計 CRUD + List • CreateEntity •
GetEntity • UpdateEntity • DeleteEntity • ListEntities -
Cloud Functions の例 • CallFunction • CreateFunction • GetFunction •
UpdateFunction • DeleteFunction • ListFunctions -
proto ファイル まずは proto ファイルを作成して定義をしていきます syntax = "proto3"; option go_package
= "./pb"; package welcome; service Welcome { rpc Greet(GreetRequest) returns (GreetReply); } message GreetRequest { string name = 1; } message GreetReply { string message = 1; } -
Protocol Compiler # protoc ͷηοτΞοϓ $ sudo apt update $
sudo apt install protobuf-compiler # macͷ߹ $ brew install protobuf # goͷϓϥάΠϯΛΠϯετʔϧ $ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest -
Protocol Compiler で Go の定義を書き出す $ mkdir pb $ protoc
!"go_out ./pb \ !"go-grpc_out ./pb \ !"go_opt=paths=source_relative \ !"go-grpc_opt paths=source_relative welcome.proto -
Protocol Compiler で Go の実装 (1) package main import (
"context" "fmt" "grpc_adventure/pb" !" module໊/σΟϨΫτϦ໊ "log" "net" "os" "os/signal" "google.golang.org/grpc" "google.golang.org/grpc/reflection" ) -
Protocol Compiler で Go の実装 (2) type welcomeServer struct {
pb.UnimplementedWelcomeServer } func (s *welcomeServer) Greet( ctx context.Context, req *pb.GreetRequest, ) (*pb.GreetReply, error) { return &pb.GreetReply{ Message: fmt.Sprintf("Welcome %s", req.Name), }, nil } func newServer() *welcomeServer { return &welcomeServer{} } -
Protocol Compiler で Go の実装 (3) func main() { port
!" 10000 lis, err !" net.Listen("tcp", fmt.Sprintf(":%d", port)) if err !# nil { log.Fatalf("failed to listen: %v", err) } grpcServer !" grpc.NewServer() pb.RegisterWelcomeServer(grpcServer, newServer()) reflection.Register(grpcServer) go func() { log.Printf("start gRPC server port: %v", port) grpcServer.Serve(lis) }() quit !" make(chan os.Signal, 1) signal.Notify(quit, os.Interrupt) !$quit log.Printf("stopping gRPC server!!%") grpcServer.GracefulStop() } -
grpcurl # grpcurl ͷ install go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest # αʔϏεͷҰཡ
grpcurl -plaintext localhost:10000 list # ϝιουܕͷৄࡉ grpcurl -plaintext localhost:10000 describe welcome.Welcome.Greet grpcurl -plaintext localhost:10000 describe welcome.GreetRequest # αʔϏεͷݺͼग़͠ grpcurl -plaintext -d '{"name": "John Appleseed"}' localhost:10000 welcome.Welcome/Greet