Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up
for free
iosdc_2018.pdf
Kyohei Ito
August 31, 2018
2
1.7k
iosdc_2018.pdf
Kyohei Ito
August 31, 2018
Tweet
Share
More Decks by Kyohei Ito
See All by Kyohei Ito
kyoheig3
0
520
kyoheig3
0
430
kyoheig3
4
1.1k
kyoheig3
4
620
kyoheig3
9
920
kyoheig3
1
1.7k
kyoheig3
6
5.1k
kyoheig3
8
6.1k
Featured
See All Featured
bryan
99
11k
vanstee
118
4.9k
andyhume
64
3.8k
jensimmons
208
10k
phodgson
88
4k
brettharned
93
3.1k
brianwarren
82
4.8k
jnunemaker
PRO
40
4.7k
tammielis
237
23k
keavy
107
14k
lauravandoore
440
28k
akmur
252
19k
Transcript
grpc-swiftΛͬͯ iOSΞϓϦͰշదͳ gRPC௨৴Λߦ͏ɹɹ 2018/08/31 iOSDC
About Me ҏ౻ɹګฏ Github : KyoheiG3 Twitter : @KyoheiG3
About Me ΩϡϨʔγϣϯ Ameba OWND AbemaTV SUPERCHOICE ࠓ·ͨ৽ن
gPRC
gRPCͬͯԿʁ • Google͕࡞ͬͨRPCΛ࣮ݱ͢ΔͨΊͷϑϨʔϜϫʔΫ • ͚ࣾͷStubbyΛɺ2015ʹΦʔϓϯԽͨ͠ͷ • HTTP2
RPCͬͯԿʁ • ϦϞʔτϓϩγʔδϟίʔϧͷུ • ϓϩάϥϜ͔ΒผͷΞυϨεۭؒʹ͋Δαϒϧʔνϯखଓ ͖Λ࣮ߦ͢Δ͜ͱΛՄೳʹ͢Δٕज़
HTTP2 • SPDYΛج൫ʹ࡞Γग़͞Εͨ • SPDYGoogle
Մೳͳ௨৴ํࣜ
Unaryʢ୯ൃͷ௨৴ʣ • 1ͭͷϦΫΤετʹର͠ɺϨεϙϯεΛ1ͭฦ͢ • HTTP1ܥͱಉ͡Α͏ͳεςʔτϨεͳ௨৴ํࣜ
Server StreamingʢαʔόετϦʔϛϯάʣ • 1ͭͷϦΫΤετʹର͠ɺϨεϙϯεΛෳฦ͢ • αʔόpush ϙʔϦϯάʹஔ͖ΘΔ
Client StreamingʢΫϥΠΞϯτετϦʔϛϯάʣ • ෳͷϦΫΤετʹର͠ɺϨεϙϯεΛ1ͭฦ͢ • ϑΝΠϧΞοϓϩʔυͳͲ
Bi-Directional StreamingʢํετϦʔϛϯάʣ • ෳͷϦΫΤετʹର͠ɺϨεϙϯεΛෳฦ͢ • νϟοτͳͲ
Αࣖ͘ʹ͢ΔProtocol Buffersͱͷҧ͍ͬͯʁ • Protocol Buffers௨৴͢Δσʔλͷܗࣜʢjson, xml...ʣ • gRPC௨৴ͷखஈ
͢͜͠Protocol Buffersͷ͓͞Β͍
protobuf • protobufͱུ͞ΕΔ͜ͱ͕ଟ͍ • όΠφϦϕʔε • .protoϑΝΠϧͰσʔλܗࣜΛఆٛ
echo.proto syntax = "proto3"; package echo; message EchoRequest { string
text = 1; } message EchoResponse { string text = 1; }
protobuf • .protoϑΝΠϧ͔ΒɺswiftgoϑΝΠϧΛ࡞Մೳ
protobuf $ protoc echo.proto --plugin=./protoc-gen-swift --swift_out=.
echo.pb.swift struct Echo_EchoRequest { var text: String = String() var
unknownFields = SwiftProtobuf.UnknownStorage() init() {} } struct Echo_EchoResponse { var text: String = String() var unknownFields = SwiftProtobuf.UnknownStorage() init() {} }
protobuf • swiftͰApple͕ެࣜʹαϙʔτ1 • όΠφϦͷγϦΞϥΠζɾσγϦΞϥΠζͷϥΠϒϥ Ϧʹґଘ • ܰͯ͘ܕ҆શ 1 https://github.com/apple/swift-protobuf
protobuf σϝϦοτ • ௨৴σʔλͷՄಡੑ͕͗͢Δʢͱ͍͏͔ಡΊͳ͍ʣ • ಋೖʹҰखؒඞཁ
protobuf ϝϦοτ ׂѪ͠·͢ɻ ϝϦοτͨ͘͞Μ͋ΔͷͰɺσʔλܗࣜͷબࢶͷҰͭʹೖ ΕΔͷ͋Γͩͱࢥ͍·͢ɻ
gRPC × protobuf • gRPC͕ར༻͢ΔσϑΥϧτͷσʔλܗ͕ࣜprotobuf • gRPC͋͘·Ͱ௨৴ͷखஈͳͷͰɺσʔλܗࣜҰԠjsonͱ ͔Մೳ • ͚ͲɺprotobufͱgRPCζϒζϒͳͷͰҰॹʹ͏લఏ
SwiftGRPC
SwiftGRPC • objective-cͰc++Ͱ࣮͞ΕͨgrpcΛར༻2 • swiftͰར༻͢Δʹগʑ໘ 2 https://github.com/grpc/grpc
SwiftGRPC • grpcͷΠϯλʔϑΣʔεΛswiftͰϥοϓ
Officially Supported Platforms3 C/C++, C#, Dart *, Go, Java, Node.js,
PHP *, Python, Ruby * ·ͩbeta SwiftΞϯΦϑΟγϟϧͰ͔͢?! 3 https://grpc.io/about/#osp
ಋೖ
ಋೖ • ͱʹ͔͘protobufલఏ • SwiftGRPCͷprotocϓϥάΠϯΛ࡞ • .protoϑΝΠϧ͔Β.swiftϑΝΠϧΛ࡞
protobufΛΠϯετʔϧ $ brew install protobuf
SwiftGRPCͷprotocϓϥάΠϯΛ࡞ • grpc-swiftΛcloneͯ͠build $ git clone https://github.com/grpc/grpc-swift.git $ cd grpc-swift
$ make all > .protoc-gen-swift .protoc-gen-swiftgrpc
ඞཁʹԠͯ͡pathΛ௨͢ • ࣮ߦ࣌ʹϓϥάΠϯͷpathΛࢦఆͰ͖ΔͷͰඞਢͰͳ͍ $ mkdir ~/.protoc $ cp ./protoc-gen-swift ./protoc-gen-swiftgrpc
~/.protoc $ echo 'export PATH=$PATH:$HOME/.protoc' >> ~/.bash_profile $ source ~/.bash_profile
protoc-gen-swiftgrpc • SwiftGRPCͷprotocϓϥάΠϯ • protobufͱಉ༷ʹ.proto͔Β.swiftΛ࡞
echo.protoʢbeforeʣ syntax = "proto3"; package echo; message EchoRequest { string
text = 1; } message EchoResponse { string text = 1; }
echo.protoʢserviceʣ • serviceͷதʹɺrpcϑΟʔϧυΛఆٛ͢Δ service Echo { rpc }
echo.protoʢserviceʣ • rpcϑΟʔϧυʹҙͷϝιουΛఆٛ͢Δ service Echo { rpc Get }
echo.protoʢserviceʣ • ϦΫΤετͷΦϒδΣΫτΛఆٛ͢Δ service Echo { rpc Get(EchoRequest) }
echo.protoʢserviceʣ • ϨεϙϯεͷΦϒδΣΫτΛఆٛ͢Δ service Echo { rpc Get(EchoRequest) returns(EchoResponse) {}
}
echo.protoʢserviceʣ • streamෳճͷॲཧΛՄೳʹ͢Δఆٛ service Echo { rpc Get(EchoRequest) returns (EchoResponse)
{} rpc Expand(EchoRequest) returns (stream EchoResponse) {} rpc Collect(stream EchoRequest) returns (EchoResponse) {} rpc Update(stream EchoRequest) returns (stream EchoResponse) {} }
echo.protoʢafterʣ syntax = "proto3"; package echo; message EchoRequest { string
text = 1; } message EchoResponse { string text = 1; } service Echo { rpc Get(EchoRequest) returns (EchoResponse) {} rpc Expand(EchoRequest) returns (stream EchoResponse) {} rpc Collect(stream EchoRequest) returns (EchoResponse) {} rpc Update(stream EchoRequest) returns (stream EchoResponse) {} }
protoc-gen-swiftgrpc $ protoc echo.proto --plugin=./protoc-gen-swiftgrpc --swiftgrpc_out=.
echo.grpc.swift protocol Echo_EchoProvider: ServiceProvider { func get(request: Echo_EchoRequest, session: Echo_EchoGetSession)
throws -> Echo_EchoResponse func expand(request: Echo_EchoRequest, session: Echo_EchoExpandSession) throws -> ServerStatus? func collect(session: Echo_EchoCollectSession) throws -> Echo_EchoResponse? func update(session: Echo_EchoUpdateSession) throws -> ServerStatus? } protocol Echo_EchoService: ServiceClient { func get(_ request: Echo_EchoRequest) throws -> Echo_EchoResponse func get(_ request: Echo_EchoRequest, completion: @escaping (Echo_EchoResponse?, CallResult) -> Void) throws -> Echo_EchoGetCall func expand(_ request: Echo_EchoRequest, completion: ((CallResult) -> Void)?) throws -> Echo_EchoExpandCall func collect(completion: ((CallResult) -> Void)?) throws -> Echo_EchoCollectCall func update(completion: ((CallResult) -> Void)?) throws -> Echo_EchoUpdateCall }
EchoʢαϯϓϧʣͰίʔυղઆ
Echoͷಈ͔͠ํ • SwiftGRPC.xcodeprojΛ࡞͢Δ $ make project
Echoͷಈ͔͠ํ • ./third_party/swift-protobuf͕ඞཁ • ࠷ۙऔಘεΫϦϓτ͕আ͞Εͨ4 $ mkdir third_party $ cd
third_party $ git clone https://github.com/apple/swift-protobuf.git 4 https://github.com/grpc/grpc-swift/pull/296
Echoͷಈ͔͠ํ • ./Examples/EchoXcode/Echo.xcodeprojΛ։͘
Echoͷಈ͔͠ํ • ./SwiftGRPC.xcodeprojΛϓϩδΣΫτʹՃ͢Δ
Echoͷಈ͔͠ํ • Target DependenciesʹSwiftGRPC.frameworkΛՃ͢Δ
Echo for Client
Echo_EchoServiceClient • ΫϥΠΞϯτ͔ΒαʔόͷଓΛཧ • ෦ͰChannelΦϒδΣΫτΛอ࣋ • Echo_EchoServiceʹ४ڌ • get, expand,
collect, updateϝιουΛ࣮ߦͯ͠ɺετ ϦʔϛϯάΛߦ͏ͨΊͷCallΦϒδΣΫτΛ࡞
Echo_EchoServiceClient let service = Echo_EchoServiceClient(address: "YOUR_ADDRESS", secure: false)
Echo_EchoExpandCallʢServerStreamingʣ • expand()ϝιουͰऔಘ • αʔόετϦʔϛϯά͕Մೳ • receive()ϝιουΛ࣮ߦՄೳ
Echo_EchoExpandCallʢServerStreamingʣ var requestMessage = Echo_EchoRequest() requestMessage.text = "message" let expandCall
= try service.expand(requestMessage) { _ in } try expandCall.receive { response in // ϨεϙϯεΛड͚औΔ }
Echo_EchoCollectCallʢClientStreamingʣ • collect()ϝιουͰऔಘ • ΫϥΠΞϯτετϦʔϛϯά͕Մೳ • send(), closeAndReceive()ϝιουΛ࣮ߦՄೳ
Echo_EchoCollectCallʢClientStreamingʣ let collectCall = try service.collect { _ in }
var requestMessage = Echo_EchoRequest() requestMessage.text = "message" try collectCall.send(requestMessage) { error in // ΤϥʔΛड͚औΔ } try collectCall.closeAndReceive { response in // close࣌ʹ1͚ͩσʔλΛड͚औΕΔ }
Echo_EchoUpdateCallʢBidirectionalStreamingʣ • update()ϝιουͰऔಘ • ํετϦʔϛϯά͕Մೳ • send(), receive(), closeSend()ϝιουΛ࣮ߦՄೳ
Echo_EchoUpdateCallʢBidirectionalStreamingʣ let updateCall = try service.update { _ in }
try updateCall.receive { response in // ϨεϙϯεΛड͚औΔ } var requestMessage = Echo_EchoRequest() requestMessage.text = "message" try updateCall.send(requestMessage) { error in // ΤϥʔΛड͚औΔ } try updateCall.closeSend {}
ΫϥΠΞϯτ࣮ͷجຊతͳྲྀΕ • Service͔ΒCallΦϒδΣΫτΛ࡞͢Δ • ࡞ͨ͠CallΦϒδΣΫτʹ༻ҙ͞Ε͍ͯΔstreamingͷϝ ιουΛݺͼग़͢ • CallΦϒδΣΫτstreaming͕ऴྃ͢Δ·Ͱอ͓࣋ͯ͘͠ ※ Unaryྫ֎
Echo_EchoGetCallʢUnaryʣ • get()ϝιουͰऔಘ • streaming༻ͷϝιου༻ҙ͞Ε͍ͯͳ͍
Echo_EchoGetCallʢUnaryʣ var requestMessage = Echo_EchoRequest() requestMessage.text = "message" let getCall
= try service.get(requestMessage) { response, callResult in }
ΫϥΠΞϯτ࣮ͷಛ • CallΦϒδΣΫτΛ࡞͔ͬͨ࣌ΒtimeoutΧϯτμϯ • σϑΥϧτ600ඵͰɺϦΫΤετݸผʹઃఆෆՄ • ClientStreamingͷsend()ʹҾͰͤΔtimeoutɺಉظ ௨৴͢Δࡍͷsemaphoreͷػ࣌ؒ
ΫϥΠΞϯτ࣮ͷಛ • receive()ɺ1ͰσʔλΛड͚औΔͱͦΕҎ࣮߱ߦ͞ Εͳ͍ͷͰɺ࠶receive()ΛݺͿඞཁ͕͋Δ • ͳʹ͔͠ΒͷΤϥʔ͕ग़ͨΒͦΕҎ߱ૹड৴Ͱ͖ͳ͍ͷͰɺ CallΦϒδΣΫτ͔Β࠶࡞͢Δඞཁ͕͋Δ • throws͕ͨΒଟ͍
ΫϥΠΞϯτͷstubʹ͍ͭͯ • ServerΛϩʔΧϧʹ࣮ͯ͠stubԽ
Echo for Server
ServiceServer • αʔόͷΫϥΠΞϯτ͔ΒͷଓΛཧ • ෦ͰServerΦϒδΣΫτΛอ࣋ • Echo_EchoProviderʹ४ڌͨ͠ΦϒδΣΫτΛड͚औΔ • startϝιουͰɺଓͷड৴Λ։࢝
ServiceServer class EchoProvider: Echo_EchoProvider { func get() {} func expand()
{} func collect() {} func update() {} } let provider = EchoProvider() let server = ServiceServer(address: "YOUR_ADDRESS", serviceProviders: [provider]) server.start()
Echo_EchoProvider • get, expand, collect, updateϝιουͰɺΫϥΠΞϯ τͷϨεϙϯεΛߦ͏ • ҾͰ͞ΕΔSessionΦϒδΣΫτͰৼΔ͍Λ࣮
Echo_EchoProviderʢget == Unaryʣ • Echo_EchoRequestΦϒδΣΫτΛऔಘ • Echo_EchoResponseΛฦͨ͠Βऴྃ
Echo_EchoProviderʢget == Unaryʣ func get(request: Echo_EchoRequest, session _: Echo_EchoGetSession) throws
-> Echo_EchoResponse { var response = Echo_EchoResponse() response.text = request.text + " response" return response }
Echo_EchoProviderʢexpand == ServerStreamingʣ • Echo_EchoRequestΦϒδΣΫτΛऔಘ • ඞཁʹԠͯ͡sessionͷsend()Λ࣮ߦ • ServerStatusΛฦ͠ɺ͔ͭsend()͕ྃͨ͠Βऴྃ
Echo_EchoProviderʢexpand == ServerStreamingʣ func expand(request: Echo_EchoRequest, session: Echo_EchoExpandSession) throws ->
ServerStatus? { var response = Echo_EchoResponse() response.text = request.text + " response" try! session.send(response) { // ΤϥʔΛड͚औΔ } return .ok }
Echo_EchoProviderʢcollect == ClientStreamingʣ • ϦΫΤετsessionͷreceive()Ͱऔಘ • Echo_EchoResponseΛฦͨ͠Βऴྃ
Echo_EchoProviderʢcollect == ClientStreamingʣ func collect(session: Echo_EchoCollectSession) throws -> Echo_EchoResponse? {
var response = Echo_EchoResponse() let request = try! session.receive() response.text = request.text + " response" return response }
Echo_EchoProviderʢupdate == BidiStreamingʣ • ϦΫΤετsessionͷreceive()Ͱऔಘ • ඞཁʹԠͯ͡sessionͷsend()Λ࣮ߦ • ServerStatusΛฦ͠ɺ͔ͭsend()͕ྃͨ͠Βऴྃ
Echo_EchoProviderʢupdate == BidiStreamingʣ func update(session: Echo_EchoUpdateSession) throws -> ServerStatus? {
let request = try! session.receive() var response = Echo_EchoResponse() response.text = request.text + " response" try! session.send(response) { // ΤϥʔΛड͚औΔ } return .ok }
αʔό࣮ͷجຊతͳྲྀΕ • Echo_EchoProviderʹ४ڌͨ͠ΦϒδΣΫτΛ࡞͢Δ • ProviderͷϝιουͰಉظతʹॲཧ͠ɺΛฦͯ͠ऴྃ
SwiftGRPCClient
None
SwiftGRPCClient > https://github.com/cats-oss/grpc-swift-client • ΫϥΠΞϯτઐ༻ͷgRPCϥΠϒϥϦ • SwiftGRPCʹґଘ • timeoutΛϦΫΤετ୯ҐͰܾΊͨΓ •
ϦΫΤετͷ్தͰॲཧΛΠϯλʔηϓτͨ͠Γ • receive()ΛࣗಈͰϦτϥΠͯ͘͠ΕͨΓ
protoc-gen-swiftgrpc-client $ protoc echo.proto --plugin=./protoc-gen-swiftgrpc-client --swiftgrpc-client_out=.
protoc-gen-swiftgrpc-client enum Echo_EchoMethod: String, CallMethod { case get = "Get"
static let service = "echo.Echo" } protocol _Echo_EchoGetRequest { typealias InputType = Echo_EchoRequest typealias OutputType = Echo_EchoResponse } protocol Echo_EchoGetRequest: _Echo_EchoGetRequest, UnaryStreamingRequest {} extension Echo_EchoGetRequest { var method: CallMethod { return Echo_EchoMethod.get } }
SwiftGRPCʢClientStreamingʣ let collectCall = try service.collect { _ in }
var requestMessage = Echo_EchoRequest() requestMessage.text = "message" try collectCall.send(requestMessage) { error in // ΤϥʔΛड͚औΔ } try collectCall.closeAndReceive { response in // close࣌ʹ1͚ͩσʔλΛड͚औΕΔ }
SwiftGRPCClientʢClientStreamingʣ let stream = Session.shared.stream(with: EchoClientRequest()) stream .send("message") { response
in // ૹ৴݁ՌΛड͚औΔ } stream .closeAndReceive { response in // close࣌ʹ1͚ͩσʔλΛड͚औΕΔ }
SwiftGRPCʢServerStreamingʣ var requestMessage = Echo_EchoRequest() requestMessage.text = "message" let expandCall
= try service.expand(requestMessage) { _ in } try expandCall.receive { response in // ϨεϙϯεΛड͚औΔ }
SwiftGRPCClientʢServerStreamingʣ let stream = Session.shared.stream(with: EchoServerRequest(text: "message")) .receive { response
in // ϨεϙϯεΛड͚औΔ }
SwiftGPRCClientʢRxʣ extension Reactive where Base: Streaming, Base.Request: UnaryRequest { func
data() -> Observable<Base.Request.OutputType> { return .create { observer in self.base.data { result in switch result { case .success(let data): observer.onNext(data) observer.onCompleted() case .failure(let error): observer.onError(error) } } return Disposables.create { self.base.cancel() } } } }
SwiftGPRCClientʢRxʣ Session.shared.stream(with: EchoUnaryRequest()).rx.data() .subscribe(onNext: { response in // ϨεϙϯεΛड͚औΔ },
onError: { error in // ΤϥʔΛड͚औΔ }) .disposed(by: disposeBag)
SwiftGPRCClient • ΫϥΠΞϯτʹSwiftGPRCClientΛར༻ͯ͠ɺstubͱͯ͠ SwiftGRPCͷαʔό࣮Λར༻͢Δ
ॴײͱࠓޙ
ॴײ • ࠷ॳcallͷcancel͢Βແ͔ͬͨ • ಋೖ͕ਏ͔ͬͨ • Τϥʔ࣌ͷࡉ͔͍ڍಈ͕௫ΈͮΒ͍
Swift Summit
ࠓޙ • ࠷ۙυΠπਓͷΤϯδχΞ͕ฃಆͯ͠ϝϯςφϯε • Network.frameworkͷಋೖͷݕ౼ͳͲ͕͞Ε͍ͯΔ • ͱΓ͋͑ͣਖ਼ࣜʹgrpcҰʹೖͬͯཉ͍͠
grpc-swiftΛͬͯiOSΞϓϦͰշదͳgRPC௨৴Λߦ͏ɹɹ https://github.com/cats-oss/grpc-swift-client Github : KyoheiG3 Twitter : @KyoheiG3
Thanks!