Slide 1

Slide 1 text

Flutter gRPC Flutter 박제창 Flutter Seoul & GDG Golang Korea 1

Slide 2

Slide 2 text

gPRC 기술을 사용해서 피트니스 센터 관제 시스템을 만들어봅시다. + Flutter 목표 Overview 2 Intermediate / Advanced

Slide 3

Slide 3 text

33 Overview

Slide 4

Slide 4 text

Communication 4

Slide 5

Slide 5 text

만약 미국에서 거주하는 사용자 A의 앱에서 한국에 거주하는 사용자 B의 앱으로 데이터를 보내려면 어떻게 처리가 되어야 할까요? 5

Slide 6

Slide 6 text

Transmission Media https://en.wikipedia.org/wiki/Transatlantic_telegraph_cable Contemporary map of the 1858 transatlantic cable route 6

Slide 7

Slide 7 text

Transmission Media Submarine communications cable The first submarine communications cables were laid beginning in the 1850s and carried telegraphy traffic, establishing the first instant telecommunications links between continents, such as the first transatlantic telegraph cable which became operational on 16 August 1858. 7

Slide 8

Slide 8 text

Transmission Media Submarine communications cable 8

Slide 9

Slide 9 text

Transmission Media Submarine communications cable New Cross Pacific (NCP) Cable System Owners China Mobile, China Telecom, China Unicom, Chunghwa Telecom, KT, Microsoft, Softbank Corp 9

Slide 10

Slide 10 text

https://en.wikipedia.org/wiki/OSI_model OSI model ? 10

Slide 11

Slide 11 text

https://en.wikipedia.org/wiki/OSI_model OSI model 11

Slide 12

Slide 12 text

“A communication protocol is a system of rules that allows two or more entities of a communications system to transmit information via any variation of a physical quantity.” https://en.wikipedia.org/wiki/Communication_protocol 12

Slide 13

Slide 13 text

https://en.wikipedia.org/wiki/Transmission_Control_Protocol TCP: Transmission Control Protocol IP Header TCP Header TCP Data(Payload) 13

Slide 14

Slide 14 text

https://www.reddit.com/r/ProgrammerHumor/comments/9gcwgw/tcp_vs_udp/ 14

Slide 15

Slide 15 text

HyperText Transfer Protocol HTTP/HTTP2 Flutter 15

Slide 16

Slide 16 text

https://datatracker.ietf.org/doc/html/rfc1945 HTTP (HyperText Transfer Protocol) ● 웹 기반의 요청-응답 방식으로 데이터를 전송하기 위해 설계된 애플리케이션 계층 프로토콜 ● OSI 모델의 최상위 계층인 애플리케이션 계층에서 작동 ● 클라이언트(예: 웹 브라우저)가 서버에 요청을 보내고, 서버가 응답을 반환하는 요청/응답 모델을 사용 ● 데이터는 일반적으로 텍스트 기반(예: HTML, JSON, XML) 형식으로 전송 16

Slide 17

Slide 17 text

HTTP (HyperText Transfer Protocol) 1.0 ~ 1.1 connectionless means that for every new request, we need to create a new connection from the client to the server. Head-of-line blocking (HOL blocking) in computer networking is a performance-limiting phenomenon that occurs when a queue of packets is held up by the first packet in the queue. Connectionless Head Of Line Blocking(HOLB) 17

Slide 18

Slide 18 text

HTTP (HyperText Transfer Protocol) 1.0 ~ 1.1 Head Of Line Blocking(HOLB) ● One form of HOL blocking in HTTP/1.1 is when the number of allowed parallel requests in the browser is used up, and subsequent requests need to wait for the former ones to complete. ● HTTP/2 addresses this issue through request multiplexing, which eliminates HOL blocking at the application layer, but HOL still exists at the transport (TCP) layer 18

Slide 19

Slide 19 text

HTTP (HyperText Transfer Protocol) 2 19 https://datatracker.ietf.org/doc/html/rfc9113

Slide 20

Slide 20 text

HTTP (HyperText Transfer Protocol) 2 20 https://httpwg.org/specs/rfc7540.html#FrameHeader

Slide 21

Slide 21 text

Remote Procedure Call RPC / gRPC Flutter 21

Slide 22

Slide 22 text

RPC(Remote procedure call) 22 ● RPC는 원격 서버에 있는 프로시저(함수나 메서드)를 호출하는 방식으로, 클라이언트가 서버의 함수를 로컬에서 호출하는 것처럼 사용 가능 ● 네트워크를 통해 다른 컴퓨터에 있는 프로그램과 상호 작용할 수 있게 해주는 방법 ● 다양한 프로토콜과 데이터 형식(JSON, XML 등)을 사용할 수 있음. 그러나 텍스트 기반으로 크기가 상대적으로 큼 ● 기본적으로 HTTP, TCP 등 다양한 전송 프로토콜을 사용할 수 있음 ● 다양한 언어 및 플랫폼 언어 지원 ● Interface Description Language

Slide 23

Slide 23 text

RPC(Remote procedure call) 23

Slide 24

Slide 24 text

gRPC 24 ● A high performance, open source universal RPC framework. ● gRPC was initially created by Google, which used a single general-purpose RPC infrastructure called Stubby to connect the large number of microservices running within and across its data centers from about 2001. ● Core features that make it awesome ○ Idiomatic client libraries in 11 languages ○ Highly efficient on wire and with a simple service definition framework ○ Bi-directional streaming with http/2 based transport ○ Pluggable auth, tracing, load balancing and health checking ● Initial release: August 2016 (7 years ago)

Slide 25

Slide 25 text

gRPC 25 ● gRPC는 RPC 개념을 현대화하고, 다양한 언어를 지원하며, 성능과 효율성을 개선한 형태 ● HTTP/2를 기반으로 하며 프로토콜 버퍼(Protocol Buffer)를 데이터 직렬화 형식으로 사용 ○ HTTP/2를 사용하여 멀티플렉싱, 서버 푸시, 흐름 제어 등을 지원 ○ Protocol Buffers를 사용하여 데이터를 이진 형식으로 직렬화 ● 마이크로서비스 아키텍처, 클라우드 네이티브 애플리케이션, 고성능 시스템 통신 등에 널리 사용

Slide 26

Slide 26 text

a language-neutral, platform-neutral extensible mechanism for serializing structured data Protocol Buffer Flutter 26

Slide 27

Slide 27 text

JSON 27 { "id": 1, "name": "Dreamwalker", "username": "Dream", "email": "[email protected]", "address": { "street": "Seoul", "suite": "", "city": "Seoul", "zipcode": "123-4567", "geo": { "lat": "33", "lng": "124" } }, "phone": "123-4567", "website": "", "company": { "name": "", "catchPhrase": "", "bs": "" } },

Slide 28

Slide 28 text

Dart/Flutter JSON 28 { "id": 1, "name": "Dreamwalker", "username": "Dream", "email": "[email protected]", "address": { "street": "Seoul", "suite": "", "city": "Seoul", "zipcode": "123-4567", "geo": { "lat": "33", "lng": "124" } }, "phone": "123-4567", "website": "", "company": { "name": "", "catchPhrase": "", "bs": "" } }, Go/Rust Client Server Marshalling Unmarshalling Dart/Flutter Go/Rust

Slide 29

Slide 29 text

Protocol Buffer 29 ● Protocol Buffers are a language-neutral, platform-neutral extensible mechanism for serializing structured data. ● Protocol buffers are a combination of the definition language (created in .proto files), the code that the proto compiler generates to interface with data,

Slide 30

Slide 30 text

proto3 proto2 Version of Protocol Buffers Flutter 30

Slide 31

Slide 31 text

Scalar Value Types 31 .proto Type C++ Type Java/Kotlin Type[1] Python Type[3] Go Type Ruby Type Dart Type Rust Type double double double float float64 Float double f64 float float float float float32 Float double f32 int32 int32 int int int32 Fixnum or Bignum (as required) int i32 int64 int64 long int/long[4] int64 Bignum Int64 i64 uint32 uint32 int[2] int/long[4] uint32 Fixnum or Bignum (as required) int u32 uint64 uint64 long[2] int/long[4] uint64 Bignum Int64 u64 sint32 int32 int int int32 Fixnum or Bignum (as required) int i32 sint64 int64 long int/long[4] int64 Bignum Int64 i64 fixed32 uint32 int[2] int/long[4] uint32 Fixnum or Bignum (as required) int u32 fixed64 uint64 long[2] int/long[4] uint64 Bignum Int64 u64 sfixed32 int32 int int int32 Fixnum or Bignum (as required) int i32 sfixed64 int64 long int/long[4] int64 Bignum Int64 i64 bool bool boolean bool bool TrueClass/FalseClass bool bool string string String str/unicode[5] string String (UTF-8) String ProtoString bytes string ByteString str (Python 2) bytes (Python 3) []byte String (ASCII-8BIT) List ProtoBytes

Slide 32

Slide 32 text

Default Value 32 ● 숫자 타입 (int32, int64, uint32, uint64, sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, float, double) ⇒ 0 ● Bool ⇒ false ● String ⇒ 빈 문자열 ("") ● Bytes ⇒ 빈 바이트 배열 ● Enum ⇒ 열거형의 첫 번째 값

Slide 33

Slide 33 text

syntax = "proto3"; message SearchRequest { string query = 1; int32 page_number = 2; int32 results_per_page = 3; } 33

Slide 34

Slide 34 text

syntax = "proto3"; message SearchRequest { int32 results_per_page = 3; int32 page_number = 2; string query = 1; } 34

Slide 35

Slide 35 text

● 프로토콜 버퍼 메시지의 필드는 필드 번호(tag)로 식별. ● 순서가 아닌 번호를 통해 각 필드를 구분 → 필드의 순서는 바꿔도 되지만 필드 번호는 유지해야 호환이 가능 ● 필드 번호가 유지되는 한 순서 변경은 프로토콜 버퍼 메시지의 직렬화/역직렬화 과정에 영향을 미치지 않음 Order 35

Slide 36

Slide 36 text

enum Corpus { CORPUS_UNSPECIFIED = 0; CORPUS_UNIVERSAL = 1; CORPUS_WEB = 2; CORPUS_IMAGES = 3; CORPUS_LOCAL = 4; CORPUS_NEWS = 5; CORPUS_PRODUCTS = 6; CORPUS_VIDEO = 7; } message SearchRequest { string query = 1; int32 page_number = 2; int32 results_per_page = 3; Corpus corpus = 4; } 36

Slide 37

Slide 37 text

● optional: An optional field is in one of two possible states: ○ the field is set, and contains a value that was explicitly set or parsed from the wire. It will be serialized to the wire. ○ the field is unset, and will return the default value. It will not be serialized to the wire. ○ You can check to see if the value was explicitly set. ● repeated: this field type can be repeated zero or more times in a well-formed message. The order of the repeated values will be preserved. ● map: this is a paired key/value field type. Specifying Field Labels 37

Slide 38

Slide 38 text

syntax = "proto3"; message SearchRequest { optional int32 results_per_page = 3; optional int32 page_number = 2; optional string query = 1; } 38

Slide 39

Slide 39 text

message Result { string url = 1; string title = 2; repeated string snippets = 3; } message SearchResponse { repeated Result results = 1; } 39

Slide 40

Slide 40 text

message SearchResponse { // level 0 message Result { // level 1 string url = 1; string title = 2; repeated string snippets = 3; } repeated Result results = 1; } 40

Slide 41

Slide 41 text

message SearchResponse { message Result { string url = 1; string title = 2; repeated string snippets = 3; } repeated Result results = 1; } 41

Slide 42

Slide 42 text

message TestMap { map g_map = 7; } 42

Slide 43

Slide 43 text

43 message TestMap { message g_Entry { optional string key = 1; optional int32 value = 2; } repeated g_Entry g_map = 7; }

Slide 44

Slide 44 text

message TestMap { map g = 7; } 44 message TestMap { message g_Entry { optional string key = 1; optional int32 value = 2; } repeated g_Entry g = 7; }

Slide 45

Slide 45 text

{ "id": 1, "name": "Dreamwalker", "username": "Dream", "email": "[email protected]", "address": { "street": "Seoul", "suite": "", "city": "Seoul", "zipcode": "123-4567", "geo": { "lat": "33", "lng": "124" } }, "phone": "123-4567", "website": "", "company": { "name": "", "catchPhrase": "", "bs": "" } }, 45 syntax = "proto3"; message UserMessage { message Geo { string lat = 1; string lng = 2; } message Address { string street = 1; string suite = 2; string city = 3; string zipcode = 4; Geo geo = 5; } message Company { string name = 1; string catch_phrase = 2; string bs = 3; } message Nested { uint32 id = 1; string name = 2; string username = 3; string email = 4; Address address = 5; string phone = 6; string website = 7; Company company = 8; } repeated Nested items = 1; }

Slide 46

Slide 46 text

Encoding 좀 더 깊게 들어가보기 46 message Example { int32 id = 1; } 0x08 0x01 id의 값이 1인 경우 인코딩 0x08 field_tag=(field_number<<3) ∣ wire_type Field Tag Value Encode Field Tag ID Name Used For 0 VARINT int32, int64, uint32, uint64, sint32, sint64, bool, enum 1 I64 fixed64, sfixed64, double 2 LEN string, bytes, embedded messages, packed repeated fields 3 SGROUP group start (deprecated) 4 EGROUP group end (deprecated) 5 I32 fixed32, sfixed32, float wire_type field_tag Value

Slide 47

Slide 47 text

Encoding 좀 더 깊게 들어가보기 47 message Example { int32 id = 1; } 0x08 0x01 id의 값이 1인 경우 인코딩 0x08 field_tag=(field_number<<3) ∣ wire_type field_tag=(1 << 3) ∣ 0 = 8 1. 0000 0001 (field_number) 2. 0000 1000 (field_number<<3) 3. 0000 1000 | 0 (wire_type) ⇒ 0x08 Field Tag Value Encode Field Tag field_tag Value field_tag

Slide 48

Slide 48 text

Encoding 좀 더 깊게 들어가보기 48 message Example { int32 id = 1; } 0x08 0x D7 05 id의 값이 727인 경우 인코딩 ● 727 = 10 1101 0111 (2) ● 첫 7비트: 101 0111 (87) ● 나머지: 000 0101 (5) ● 첫 번째 바이트: 1101 0111 (0xD7) (다음 바이트가 있으므로 MSB가 1) ● 두 번째 바이트: 0000 0101 (0x05) (마지막 바이트이므로 MSB가 0) ● 0x08 D7 05 Field Tag Varint Value Encode 0xD7 0x05 Varint Value Encode 101 101 0111 101 101 0111 101 0111 101 1101 0111 0000 0101 D7 05

Slide 49

Slide 49 text

Encoding 좀 더 깊게 들어가보기 49 message Example { string name = 1; } name의 값이 io extended(문자)의 경우 인코딩을 어떻게 처리하는가? field_tag Length Value

Slide 50

Slide 50 text

Encoding 좀 더 깊게 들어가보기 50 message Example { string name = 1; } field_tag 계산 방법 field_tag=(field_number<<3) ∣ wire_type field_tag=(1 << 3) ∣ 2 = 10 0x0A field_tag 2 LEN string, bytes, embedded messages, packed repeated fields

Slide 51

Slide 51 text

Encoding 좀 더 깊게 들어가보기 51 message Example { string name = 1; } 문자 길이 계산 방법 문자열 "io extended"의 길이는 12 (0x0C) Length

Slide 52

Slide 52 text

Encoding 좀 더 깊게 들어가보기 52 message Example { string name = 1; } 문자열 "io extended"를 UTF-8로 인코딩 방법 i = 0x69 o = 0x6F (공백) = 0x20 e = 0x65 x = 0x78 t = 0x74 e = 0x65 n = 0x6E d = 0x64 e = 0x65 d = 0x64 Value

Slide 53

Slide 53 text

Encoding 좀 더 깊게 들어가보기 53 message Example { string name = 1; } Encoding 결과 name의 값이 “io extended” (문자)인 경우 인코딩 field_tag Length Value 0x0A 0C 69 6F 20 65 78 74 65 6E 64 65 64

Slide 54

Slide 54 text

Encoding 좀 더 깊게 들어가보기 54 message Example { string name = 1; int32 id = 2; } 그럼 연속된 메세지는? Io extended 727 field_tag Length Value 0x0A 0C 69 6F 20 65 78 74 65 6E 64 65 64 0x08 D7 05 field_tag Value

Slide 55

Slide 55 text

Protocol Buffer 55 https://pub.dev/packages/protobuf

Slide 56

Slide 56 text

Protocol Buffer 56 https://github.com/google/protobuf.dart

Slide 57

Slide 57 text

> dart pub global activate protoc_plugin 57

Slide 58

Slide 58 text

58 Use Generation .proto 생성

Slide 59

Slide 59 text

59 Use Generation .proto 생성

Slide 60

Slide 60 text

syntax = "proto3"; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; } 60

Slide 61

Slide 61 text

syntax = "proto3"; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; } 61

Slide 62

Slide 62 text

1. > protoc --dart_out=grpc:lib/src/generated --proto_path protos protos/helloworld.proto 2. > protoc --dart_out=grpc:lib/src/generated -I protos protos/helloworld.proto 62

Slide 63

Slide 63 text

gRPC 63 https://pub.dev/packages/grpc

Slide 64

Slide 64 text

class GreeterService extends GreeterServiceBase { @override Future sayHello(ServiceCall call, HelloRequest request) async { return HelloReply()..message = 'Hello, ${request.name}!'; } } 64 Server Side

Slide 65

Slide 65 text

class GreeterService extends GreeterServiceBase { @override Future sayHello(ServiceCall call, HelloRequest request) async { return HelloReply()..message = 'Hello, ${request.name}!'; } } 65 Server Side

Slide 66

Slide 66 text

class GreeterService extends GreeterServiceBase { @override Future sayHello(ServiceCall call, HelloRequest request) async { return HelloReply()..message = 'Hello, ${request.name}!'; } } 66 Server Side

Slide 67

Slide 67 text

final server = Server.create( services: [ GreeterService(), ], codecRegistry: CodecRegistry( codecs: const [ GzipCodec(), IdentityCodec(), ], ), ); await server.serve(port: 50051); print('Server listening on port ${server.port}...'); 67 Server Side

Slide 68

Slide 68 text

final server = Server.create( services: [ GreeterService(), ], codecRegistry: CodecRegistry( codecs: const [ GzipCodec(), IdentityCodec(), ], ), ); await server.serve(port: 50051); print('Server listening on port ${server.port}...'); 68 Server Side

Slide 69

Slide 69 text

channel = ClientChannel( 'localhost', port: 50051, options: ChannelOptions( credentials: const ChannelCredentials.insecure(), codecRegistry: CodecRegistry( codecs: const [ GzipCodec(), IdentityCodec(), ], ), ), ); 69 Client Side

Slide 70

Slide 70 text

final stub = GreeterClient(channel); 70 Client Side

Slide 71

Slide 71 text

try { final response = await stub.sayHello( HelloRequest()..name = name, options: CallOptions( compression: const GzipCodec(), ), ); print('Greeter client received: ${response.message}'); } catch (e) { print('Caught error: $e'); } 71 Client Side

Slide 72

Slide 72 text

Fitness Monitoring (Flutter + gRPC) 관제 시스템 설계 Fluter 72

Slide 73

Slide 73 text

구성 73 Machine B Fitness Center 1 Machine A Machine C Machine B Fitness Center 2 Machine A Machine C Fitness Center 1 Clients Server gRPC gRPC

Slide 74

Slide 74 text

syntax = "proto3"; service Treadmill{ rpc SendData(Data)returns (Reply) {} } enum MachineType { TREADMILL = 0; } message Data { string center_id = 1; string machine_id = 2; MachineType machine_type = 3; string distance = 4; string incline = 5; string speed = 6; string heartRate = 7; } message Reply { string message = 1; } 74 Proto Buf

Slide 75

Slide 75 text

syntax = "proto3"; service Treadmill{ rpc SendData(Data)returns (Reply) {} } enum MachineType { TREADMILL = 0; } message Data { string center_id = 1; string machine_id = 2; MachineType machine_type = 3; string distance = 4; string incline = 5; string speed = 6; string heartRate = 7; } message Reply { string message = 1; } 75 Proto Buf

Slide 76

Slide 76 text

class TreadmillService extends TreadmillServiceBase { Ref? ref; TreadmillService({this.ref}); @override Future sendData(ServiceCall call, Data request) async { ref?.read(treadmillNotifierProvider.notifier).update(request); return Reply(message: request.toString()); } } 76 Server Side

Slide 77

Slide 77 text

class TreadmillService extends TreadmillServiceBase { Ref? ref; TreadmillService({this.ref}); @override Future sendData(ServiceCall call, Data request) async { ref?.read(treadmillNotifierProvider.notifier).update(request); return Reply(message: request.toString()); } } 77 Server Side

Slide 78

Slide 78 text

@Riverpod(keepAlive: true) TreadmillService treadmillService(ref) { return TreadmillService(ref: ref); } 78 Server Side

Slide 79

Slide 79 text

@riverpod class TreadmillNotifier extends _$TreadmillNotifier { @override List build() { return []; } 79 Server Side

Slide 80

Slide 80 text

@JsonCodable() class TreadmillData { String? id; String? speed; String? centerId; String? distance; } 80 Server Side

Slide 81

Slide 81 text

final server = Server.create( services: [ ref.read(treadmillServiceProvider), ], codecRegistry: CodecRegistry( codecs: const [ GzipCodec(), IdentityCodec(), ], ), ); await server.serve(port: 50051); 81 Server Side

Slide 82

Slide 82 text

@JsonCodable() // Macros annotation. class TreadmillBase { final String? id; double distance; double incline; double speed; double heartRate; TreadmillBase({ this.id, required this.distance, required this.incline, required this.speed, required this.heartRate, }); } 82 Client Side

Slide 83

Slide 83 text

channel = ClientChannel( 'localhost', port: 50051, options: ChannelOptions( credentials: const ChannelCredentials.insecure(), codecRegistry: CodecRegistry( codecs: const [ GzipCodec(), IdentityCodec(), ], ), ), ); 83 Client Side

Slide 84

Slide 84 text

final stub = TreadmillClient(channel); 84 Client Side

Slide 85

Slide 85 text

try { final response = await stub.sendData( Data( centerId: treadmills[index].centerId, machineId: treadmills[index].treadmillBase.id, machineType: MachineType.TREADMILL, distance: treadmills[index].treadmillBase?.distance?.toStringAsFixed(3), heartRate: treadmills[index].treadmillBase?.heartRate.toString(), incline: treadmills[index].treadmillBase?.incline?.toStringAsFixed(1), speed: treadmills[index].treadmillBase?.speed?.toStringAsFixed(1), ), options: CallOptions( compression: const GzipCodec(), ), ); print('TreadmillClient client received: ${response.message}'); } catch (e) { print('Caught error: $e'); } 85 Client Side

Slide 86

Slide 86 text

Demo Flutter 86

Slide 87

Slide 87 text

Summary 87 ● gRPC는 HTTP2, 프로토콜버퍼가 적용된 Remote Procedure Call (RPC) framework ● gRPC는 Dart 언어에서도 사용할 수 있음. 서버는 꼭 Dart 언어가 아니여도 괜찮음. 11가지 이상의 프로그래밍 언어를 지원 ● Proto 작성 → 코드 생성 → 서비스, 채널 정의로 간단하게 사용가능

Slide 88

Slide 88 text

Thank You 박제창 @jaichangpark Flutter Seoul & GDG Golang Korea 88