Share
Embed
Transcript
70 slides
-
• LINE • (Maeda
Kentaro) • Twitter: kencharos JJUG CCC 2019 Spring #ccc_a1 2 -
Agenda • gRPC • Protocol Buffers • gRPC
Java • gRPC JJUG CCC 2019 Spring #ccc_a1 3 -
&" l !% gRPC '
#($ ) 4 JJUG CCC 2019 Spring #ccc_a1 -
Agenda • gRPC • Protocol Buffres • gRPC
Java • gRPC JJUG CCC 2019 Spring #ccc_a1 5 -
0 / I ) / / ) / / F
P l oil l epC ) rc l 2 / R P N F nm l ( F 1 0 / C l 0 /t u g bj da F 6 0 ) CG https://grpc.io/ JJUG CCC 2019 Spring #ccc_a1 -
l e > p • - I >c A B
T • > tPT P c DH o B >gp I • D >c DL I BD C B C l u • + ( 2 s • L / )r l f l > RAL g I DH T 7 4/ ) JJUG CCC 2019 Spring #ccc_a1 -
l ) 1 / 2 • ( m UT RU
C C l S t C • a • UTe a • L P l n H i • s C L P c P C i 8 JJUG CCC 2019 Spring #ccc_a1 -
) PR o i hli fi l ) I R
nH SR l o rLR+( e g e l +( H tuwJ a yG ld i • ,1B1# ) # # # D# # # D # 0C ### l ptuwJ i i fi • s c b LR v 9 JJUG CCC 2019 Spring #ccc_a1 -
l I L P C D l I R B
10 proto file message ( ) service (PRC ) protoc Protocol Buffers gRPC plugin Message Service Protocol Buffers gRPC JJUG CCC 2019 Spring #ccc_a1 -
l e / UWT N l . gab UWT p
tP • P ( . / P l / A H NPL T A J l L . / P . UWT w E wEL . / . r LC • A . R O SI • . . / )/ + L o T 11 . / ( / P n JJUG CCC 2019 Spring #ccc_a1 -
Agenda • gRPC • Protocol Buffers • gRPC
Java • gRPC JJUG CCC 2019 Spring #ccc_a1 12 -
l proto"&A5!<;3CLI & • https://github.com/protocolbuffers/protobuf l #%'8F$,B • gRPCOpen API12/B?+
E l gRPC Java :4protoc gradle/maven 0D .=@ 6 )7 9 • (*:4>=6 • gRPC-web, gRPC-gateway, doc-gen Java(-#%' 6:4 • Go, C++ C12!<; :4 13 protoc JJUG CCC 2019 Spring #ccc_a1 -
l gRPC version 3 l
message, RPC service ! • packageOptionimport " ! 14 proto syntax = "proto3"; package my.rpc; option java_package = "my.rpc"; message SomeMessage{///} service SomeService{///} JJUG CCC 2019 Spring #ccc_a1 -
l Java 1!' message 0 8.5%* l
Java1! 3249* 15 message message Person { int32 id = 1; string name = 2; Gender gender = 3; enum Gender { man = 0; woman = 1; } } -/ int32, int64, uint32, uint64,double, , bool 6(: string : bytes :"$ enum #7 repeated $. )+ map<key$, value$> message 1!&message JJUG CCC 2019 Spring #ccc_a1 -
l Protocol Buffers ('.I7+/&H8?K = L9 • +/&,#*0 .-!/$ .-!
36 • +/&H8:NM l EA Person id150DG , 08 96 01 (%P 16 0000 0100 212)#%6@O5@K <+/&H8 = 1 1001 0110 0000 0001 ;C1)#%$"QF> 21(%J 4B 000 0001 ++ 001 0110 -> 10010110 -> 128 + 16 + 4 + 2 -> 150 JJUG CCC 2019 Spring #ccc_a1 -
l proto #%C?,#%!A2 D8 • " $&' proto
<; !:3 0G • message /=B.#%!A2>)14F E1 • #%!A26E-79 reserved 17 //version1 message Something { int32 id = 1; string name = 2; } //version2 *@+ message Something { int32 id = 1; string name = 2[deprecated=true]; string fullName = 3; } //version3 (15 message Something { int32 id = 1; reserved 2; string fullName = 3; } JJUG CCC 2019 Spring #ccc_a1 -
l Java service & RPC ' / ,$"message-%message
( l *"' / stream " -% ). RPC 40 +0 l !# 18 service service GreetingService { rpc Hello(Person) returns (Greeting) rpc HelloStream(stream Person) returns (stream Greeting) } JJUG CCC 2019 Spring #ccc_a1 -
Agenda • gRPC • Protocol Buffers • gRPC
Java • gRPC JJUG CCC 2019 Spring #ccc_a1 19 -
20 proto JJUG CCC 2019 Spring #ccc_a1 syntax
= "proto3"; // The fizbuz service definition. service FizBuzService { rpc FizBuzOne (InputNumber) returns (FizBuzAnswer) {} rpc FizBuzRange (FromTo) returns (stream FizBuzAnswer) {} rpc FizBuzBatch (stream InputNumber) returns (FizBuzList) {} rpc FizBuzMany (stream InputNumber) returns (stream FizBuzAnswer) {} } message InputNumber { int32 num = 1; } message FizBuzAnswer { string answer = 1; } // -
21 Protocol Buffers JJUG CCC 2019 Spring #ccc_a1 l maven/gradle
Protocol Buffers gRPC plugin $ • src/main/proto proto " apply plugin: 'com.google.protobuf' apply plugin: "idea" protobuf { protoc { artifact = "com.google.protobuf:protoc: 3.6.1 " } plugins { grpc { // grpc plugin # artifact = "io.grpc:protoc-gen-grpc-java:1.18.1" } } generateProtoTasks { //plugin " all()*.plugins { grpc {} // grpc plugin !$ } } ProtocolBuffrers gRPC plugin# gRPC plugin!$ -
22 JJUG CCC 2019 Spring #ccc_a1 l build/generated
l gradle • https://github.com/google/pr otobuf-gradle-plugin -
23 gRPC Java JJUG CCC 2019 Spring #ccc_a1
Stub RPCproto'5 .- gRPC Server Stub /12 gRPC Sevice gRPC Server & RPCProto '5.-%!3 Server 4: Channel gRPC Server /1 7,RPC$) #8 message PRC*96 Stub-gRPC Service " 0(+ Protocol Buferes message -
24 gRPC Java JJUG CCC 2019 Spring #ccc_a1
Stub Stub gRPC Server gRPC Service gRPC Service Channel RPC RPC message message l Proto -
25 :gRPC Server JJUG CCC 2019 Spring #ccc_a1 l ServerBuilder
addService GrpcService&+ ' • !$ Netty TLS #% NettryServiceBuilder () • RPC"* Netty NIO ! public static void main(String[] args)throws Exception{ var builder = (NettyServerBuilder)ServerBuilder .forPort(6565) .addService(new FizBuzBasicGrpcService()); // builder.sslContext(...); var server = builder.build(); server.start(); server.awaitTermination(); } -
26 :Channel JJUG CCC 2019 Spring #ccc_a1 l ManagedChannel
* 9 l - 5)8< RPC#3 .' 2! 1 * ; l gRPC TLS 4%, TLS +/ & usePlaintext :"0 $76 .( ManagedChannel createChannel() { return ManagedChannelBuilder.forAddress("localhost", 6565) .usePlaintext() .build(); } -
27 :Stub JJUG CCC 2019 Spring #ccc_a1 l 1;65 ServiceGrpc
(33C $65' !" Channel : 65 l 0B-=RPC%*(&?@, • 942/+ BlockingStub FutureStub0 var blockStub = FizBuzServiceGrpc.newBlockingStub(channel); var futureStub = FizBuzServiceGrpc.newFutureStub(channel); var stub = FizBuzServiceGrpc.newStub(channel); Stub RPC BlockingStub unary, server side stream <.('!A8) FutureStub unary ><. (Future<T> A8) Stub 7 StreamObsever)#! -
28 RPC JJUG CCC 2019 Spring #ccc_a1 l RPC6# .;3
stream RPC(= $ l !RPC4:.87" 0), • 5+ Unary& 1 • 87"*<9- '/:% RPC RPC Unary RPC rpc Hello(In) returns (Out) 121 Server Streaming RPC rpc Hello(In) returns (stream Out) 121 Client Streaming RPC rpc Hello(stream In) returns (Out) 121 Bidirectional Streaming RPC rpc Hello(stream In) returns (stream Out) 121 -
29 RPC JJUG CCC 2019 Spring #ccc_a1 l
%)-,/!1TRPCZI6(&NCH : • (&NCH(&NHD (=RPC Da)L[?RP @KY^ • 4TWJ RH>`3 XU8E_ ".*' Y^ l gRPC Java StreamObserver +$ )] F9@KA; • Stub, #*b[RPCQ5 SG • RPC%&+Bc gRPC E_2d AM7O • <= A;V\0 -
30 StreamObserver JJUG CCC 2019 Spring #ccc_a1 l
2StreamObserver Stub gRPC Service RPC (Unary ) Stream Observer Stream Observer message message onNext onCompleted onNext onCompleted -
31 StreamObserver JJUG CCC 2019 Spring #ccc_a1 l onNext
: .+0/(+!/ • $2 *# -& .+ • -& *# $2 (+ • )3 1,%" l onCompleted : .+ 4 0/!/ • %" RPC '5 l onError : !/ -
32 Unary RPC JJUG CCC 2019 Spring #ccc_a1 l &*"!
+ ; # ! + : • 84'")%* /0 1 .2 • &*"7 StreamObserver gRPC!$("36 9- 36,8 Stub gRPC Service RPC5< Stream Observer Stream Observer message message onNext onCompleted onNext onCompleted -
33 Unary RPC JJUG CCC 2019 Spring #ccc_a1 //
Client InputNumber input = InputNumber.newBuilder().setNum(1).build(); FizBuzAnswer a1 = blockStub.fizBuzOne(input); System.out.println(a1.getAnswer()); // Server Unary @Override public void fizBuzOne(InputNumber request, StreamObserver<FizBuzAnswer> responseObserver) { String answer = calc.calc(request.getNum()); // send response to client responseObserver.onNext(FizBuzAnswer.newBuilder().setAnswer(answer).build()); // send RPC finish to client responseObserver.onCompleted(); } -
34 ()message POJO JJUG CCC 2019 Spring #ccc_a1 l
Protocol Buffers message 67(immutable) getter setter , l message (4 builder +9 l message AbstractMessage %&-"! /"!%/8*. , var num = InputNumber.newBuilder() // builder )1 .setNum(10) // #25 .build(); // message (4 var num2 = InputNumber.newBuilder(num) // $3 builder )1 .setNum(100) // '0 .build(); // (4 -
35 Server Streaming RPC JJUG CCC 2019 Spring #ccc_a1 l
* (& ) • Unary onNext(&!# • BlockingStub Iterator " $' Stub gRPC Service RPC%+ Stream Observer Stream Observer message message onNext onCompleted onNext onCompleted message onNext -
Iterator<FizBuzAnswer> a2 = blockStub.fizBuzRange(FromTo.newBuilder().setFrom(2).setTo(20).build()); while (a2.hasNext()) { System.out.println(a2.next().getAnswer()); } //
server side stream @Override public void fizBuzRange(FromTo request, StreamObserver<FizBuzAnswer> observer) { var anserList = IntStream.range(request.getFrom(), request.getTo()) .boxed().map(i -> calc.calc(i)).collect(Collectors.toList()); // send stream data for (String answer : anserList) { observer.onNext(FizBuzAnswer.newBuilder().setAnswer(answer).build()); } observer.onCompleted(); } 36 Server Streaming RPC JJUG CCC 2019 Spring #ccc_a1 -
37 Client Streaming RPC JJUG CCC 2019 Spring #ccc_a1 l
($&&" ) • !%' Stub gRPC Service RPC#* Stream Observer Stream Observer message onNext onCompleted onCompleted message onNext message onNext -
38 Client Streaming RPC(Stub) JJUG CCC 2019 Spring #ccc_a1 //
32"(5 #- Latch CountDownLatch waitFinish = new CountDownLatch(1); // RPC %& 1 StreamObserver +/ var requestSender = stub.fizBuzBatch(new StreamObserver<FizBuzList>() { @Override public void onNext(FizBuzList value) { // 1Client Streaming 0 System.out.println(“received answers” + value); } @Override public void onCompleted() { waitFinish.countDown(); // !6 '6 } @Override public void onError(Throwable t) {waitFinish.countDown();} }); // ,)4*$ requestSender.onNext(InputNumber.newBuilder().setNum(11).build()); requestSender.onNext(InputNumber.newBuilder().setNum(10).build()); requestSender.onNext(InputNumber.newBuilder().setNum(27).build()); // ,)'6. requestSender.onCompleted(); // RPC '6 waitFinish.await(); ,) 1 -
39 Client Streaming RPC(Server) JJUG CCC 2019 Spring #ccc_a1 @Override
public StreamObserver<InputNumber> fizBuzBatch(StreamObserver<FizBuzList> responseObserver) { // ! $ var inputNumbers = new ArrayList<InputNumber>(); // " return new StreamObserver<InputNumber>() { @Override public void onNext(InputNumber value) { // $ inputNumbers.add(value); } @Override public void onCompleted() { // StubonComplete# var answers = inputNumbers.stream().map(i -> i.getNum()) .map(i -> calc.calc(i)).collect(toList()); responseObserver.onNext(FizBuzList.newBuilder().addAllAnswer(answers).build()); responseObserver.onCompleted(); } @Override public void onError(Throwable t) {t.printStackTrace();} }; } % ! -
40 Bidirectional Streaming RPC JJUG CCC 2019 Spring #ccc_a1 l
"# !:5604 • Ping-Pong $8$%9)*2/ 6+ • onCompleted ( RPC'7 3.& -,; Stub gRPC Service RPC1< Stream Observer Stream Observer message onNext onCompleted onCompleted message onNext message onNext message onNext -
var bdRequestSender = stub.fizBuzMany(new StreamObserver<FizBuzAnswer>() { @Override public void onNext(FizBuzAnswer
value) { System.out.println("receive " + value.getAnswer()); } @Override public void onCompleted() { waitFinish.countDown(); } @Override public void onError(Throwable t) { waitFinish.countDown(); } }); // receive server data each onNext. bdRequestSender.onNext(InputNumber.newBuilder().setNum(3).build()); bdRequestSender.onNext(InputNumber.newBuilder().setNum(1).build()); bdRequestSender.onNext(InputNumber.newBuilder().setNum(80).build()); // send finish to server. bdRequestSender.onCompleted(); waitFinish.countDown(); 41 Bidirectional Streaming RPC(Stub) JJUG CCC 2019 Spring #ccc_a1 -
@Override public StreamObserver<InputNumber> fizBuzMany(StreamObserver<FizBuzAnswer> responseObserver) { return new StreamObserver<InputNumber>() {
@Override public void onNext(InputNumber value) { // one by one data send. var answer = calc.calc(value.getNum()); responseObserver.onNext( FizBuzAnswer.newBuilder().setAnswer(answer).build()); } @Override public void onCompleted() { responseObserver.onCompleted(); } @Override public void onError(Throwable t) { t.printStackTrace(); } }; } 42 Bidirectional Streaming RPC(Server) JJUG CCC 2019 Spring #ccc_a1 -
43 gRPC JJUG CCC 2019 Spring #ccc_a1
l API ,<9 Request-Response Unary ,< • 04 *7 l Streaming API=- .: • #6? ;)13 • %'!6? ;)53? • #2> /+ 2> $"&'? l Streaming 2> deadline 8( -
44 deadline JJUG CCC 2019 Spring #ccc_a1 l l
Stub - +1deadline 6 2* • RPC'5,. " 0&! '5$(% 0 +1 l Server - deadline )#/ // deadline 100 4 +1 var blockStub = FizBuzServiceGrpc.newBlockingStub(channel) .withDeadlineAfter(100, TimeUnit.MICROSECONDS); // Server - deadline 31 if(Context.current().getDeadline().isExpired()) { responseObserver.onError(new RuntimeException()); } -
45 ( )Stream JJUG CCC 2019 Spring #ccc_a1 l
Reactive-gRPC(https://github.com/salesforce/reactive-grpc) • Salesforce gRPC4HRxJava/Reactor ; Stub, Service $)BA • StreamObserver 2O LF6@ $ )BA '%( D ." ' + -!+-.78 • 1K."' +N3 gRPC :>N2O J9M #%*!#% l gRPC-kotlin (https://github.com/rouzwawi/grpc-kotlin) • $/&0;M $)BA • Stream >NE=/,G5<CI $)? -
Agenda • gRPC • Protocol Buffers • gRPC
Java • gRPC JJUG CCC 2019 Spring #ccc_a1 49 -
l API Gateway API #! l ! REST API
Gateway REST-gRPC " l ! gRPC l proto %$ 50 JJUG CCC 2019 Spring #ccc_a1 Service Service Service API Gateway Browser REST gRPC proto proto proto -
l gRPC &* "$ • %+ (, !)
• • Protocol Bufferes POJO gRPC # ' 51 JJUG CCC 2019 Spring #ccc_a1 Domain Logic gRPC Service Web Controller service message queue gRPC Stub JDBC Other service Database Kafka REST Other service Web Client External WebAPI -
2 A . I . I u b l p
r t l 2 s . I dl A i go s • 1 1 • 2 AP em 52 . I JJUG CCC 2019 Spring #ccc_a1 -
l - S CmsC PR B R C h gn
S P CmsC • B iptb cotBr t P l . :: - S R D • : : :: / • : 2: / :: / • S : 2 P G vI L l S R • - . @ . @ N . / P B B td cot • . :: Nu - aC C y • - . @ t Cel CR 53 . :: - JJUG CCC 2019 Spring #ccc_a1 -
54 JJUG CCC 2019 Spring #ccc_a1 // GrpcService Spring
Bean @GRpcService(interceptors = ValidationInterceptor.class) public class FizBuzReactorGrpcService extends ReactorFizBuzServiceGrpc.FizBuzServiceImplBase { // Spring Bean @Autowired private FizBuzCalc cal; @Override public Mono<FizBuzAnswer> fizBuzOne(Mono<InputNumber> request) { return request.map(InputNumber::getNum) .map(cal::calc) .map(a -> FizBuzAnswer.newBuilder().setAnswer(a).build()); } -
l E / G C 55 JJUG CCC 2019 Spring
#ccc_a1 syntax = "proto3"; import "google/api/annotations.proto"; // The fizbuz service definition. service FizBuzService { // calculate Fizbuz Answer rpc FizBuzOne (InputNumber) returns (FizBuzAnswer) { option (google.api.http) = { get: "/fizbuz/{num}" }; } rpc FizBuzRange (FromTo) returns (stream FizBuzAnswer) {} } http Google api proto RPC http REST -
l - 2 B https://github.com/grpc-ecosystem/grpc- gateway S 1 2 Rc
ePurR w H fgR a tsPJ 2 R ). - h a • ( j N R aSiT RPC ySp • ) R aSp v Slo • . R a tsPJ HnGO 56 - 2 B JJUG CCC 2019 Spring #ccc_a1 -
l CI2 GO t Ta tS • R RG t
S l Gg tS u 2 aGO t • Ch R 2 PlL C l a t CI2 a n L • a C t PlL 2 S Ae lL C 59 t JJUG CCC 2019 Spring #ccc_a1 -
l VP K • R l • R TP l
V u C- a g VP K - l V l e H 60 V JJUG CCC 2019 Spring #ccc_a1 -
l B B o B • Pe a BPe v
• np g l S P e i • S P e C u • C r • ,, C ce i l Bv t B b B R b 61 Pe a JJUG CCC 2019 Spring #ccc_a1 -
l 62 JJUG CCC
2019 Spring #ccc_a1 var stub = FizBuzServiceGrpc.newBlockingStub(ch).withInterceptors( new ClientInterceptor() { public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall( MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) { var call = next.newCall(method, callOptions); return new ForwardingClientCall.SimpleForwardingClientCall<>(call){ @Override public void start(Listener<RespT> responseListener, Metadata headers) { // headers.put(Metadata.Key.of("cred", Metadata.ASCII_STRING_MARSHALLER), "password" ); super.start(responseListener, headers); } }; }}); -
l 63
JJUG CCC 2019 Spring #ccc_a1 public class AuthenticateInterceptor implements ServerInterceptor { @Override public <ReqT, RespT> Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) { var cred = headers.keys().stream().filter(k -> k.equals(“cred”)).findFirst().get(); // Metadata & boolean ok = cred.equals(“password”); // '(, return new ForwardingServerCallListener .SimpleForwardingServerCallListener<ReqT>(next.startCall(call, headers)) { @Override // %$ , ) public void onMessage(ReqT message) { if (ok) { // '(OK ,# super.onMessage(message); } else { // '(NG "+/ Status status = Status.UNAUTHENTICATED; // '(NG var metadata = new Metadata(); metadata.put( // 0. - Metadata.Key.of(“error_detail”, Metadata.ASCII_STRING_MARSHALLER), “cred invalid”); call.close(status, metadata); // close RPC+/*! Status, Metadata " } } -
l R • P R • C 64 JJUG CCC
2019 Spring #ccc_a1 public <ReqT, RespT> Listener<ReqT> interceptCall( ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) { var cred = headers.keys().stream().filter(k -> k.equals("cred")).findFirst().get(); // var username = getUsrename(cred); // # Context username % ! var newContext = Context.current().withValue(Context.key("username"), username); // Context " $ RPC Context.get("username').get() return Contexts.interceptCall(newContext, call, headers, next); } -
l ) 1 . e PD )( ( J .
1 u J l O d P e )( ( lD . 1 t • 2 ai BPD p ) 1 . o D rD d ai P • BP Vfe DG m s P • n ) https://github.com/envoyproxy/protoc-gen-validate/ c i JP D J 65 ai JJUG CCC 2019 Spring #ccc_a1 -
l R l l 66 JJUG CCC 2019 Spring #ccc_a1
// validator.proto syntax = “proto3”; package validator; import “google/protobuf/descriptor.proto”; // range extend google.protobuf.FieldOptions { Range range = 50000; } // Range message Range { int32 min = 1; int32 max = 2; } package my.rpc; // proto import import "validator/validator.proto"; message InputNumber { // Range int32 num = 1 [(validator.range) = {min:0, max:100}]; } -
l C 67 JJUG CCC 2019 Spring #ccc_a1 // AbstractMessage
% public void checkSample(AbstractMessage message) { // "$ ! Map<FieldDescriptor, Object> fields = message.getAllFields(); fields.forEach((desc, val) -> { // Range #' ! var rangeOpt = desc.getOptions().getAllFields().values().stream() .filter(v -> v instanceof Range).map(r -> (Range)r).findFirst(); // Range & rangeOpt.ifPresent(range -> { if (range.getMin() <= (Integer)val && (Integer)val <= range.getMax()) { System.out.println("OK"); } }); }); } -
l e CH rH p vL H I x 69
p v JJUG CCC 2019 Spring #ccc_a1 i 4 nH C vo U v i /2 n C - L / G v o X TR T p v X TR C t sNP /2 7 A H G T k y o -
l R TH G a SP u l I H
G I • e 4 / 2P n G 2P C R G C G • l HhyL 2P w l I L G AC • 4 7 L U S b t nG 70 T a S JJUG CCC 2019 Spring #ccc_a1 gRPC Server A gRPC Server B -
l NR s n Ev g u y D l
R s , , h Ek E l llo Ee r SR n i c CD PD 71 SR S a JJUG CCC 2019 Spring #ccc_a1 gRPC Server A gRPC Server B DNS Consul Eureka,, Channel -
l ) l v ) • v ) no) (
• e e 72 JJUG CCC 2019 Spring #ccc_a1 gRPC Server A gRPC Server B envoy envoy envoy xDS -
l !! JJUG
CCC 2019 Spring #ccc_a1 API Gateway Browser REST gRPC gRPC gateway proto proto proto Spring boot gRPC envoy envoy Spring boot gRPC envoy Spring boot gRPC envoy Envoy consul prometheus zipkin elasticsearch 73 73 -
l Google7< .=-4 gPRC >;6 8*9,0 l proto %(
API % #+:&) $ %!# l "' 2/?5 31 74 JJUG CCC 2019 Spring #ccc_a1 -
$/17. 2(*' • https://grpc.io/ • https://github.com/grpc-ecosystem • https://github.com/grpc/grpc-web •
https://developers.google.com/protocol-buffers/ • https://github.com/pseudomuto/protoc-gen-doc • https://github.com/salesforce/reactive-grpc • https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md • https://github.com/envoyproxy/protoc-gen-validate/ 0#49-) 3! • https://github.com/kencharos/grpc-sample • gRPC%8gRPC-gateway, gRPC-Web, L4L7!5"6+ • https://github.com/kencharos/envoy-service-discovery-spring • envoy6+ !kotlinconsul;& 75 *',: JJUG CCC 2019 Spring #ccc_a1