Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Flutter gRPC - 박제창 @I/O Extended 2024 인천/송도

Flutter gRPC - 박제창 @I/O Extended 2024 인천/송도

2024 I/O Extended 인천/송도 Flutter gRPC - 박제창

JaiChangPark

July 27, 2024
Tweet

More Decks by JaiChangPark

Other Decks in Programming

Transcript

  1. 만약 미국에서 거주하는 사용자 A의 앱에서 한국에 거주하는 사용자 B의

    앱으로 데이터를 보내려면 어떻게 처리가 되어야 할까요? 5
  2. 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
  3. 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
  4. “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
  5. https://datatracker.ietf.org/doc/html/rfc1945 HTTP (HyperText Transfer Protocol) • 웹 기반의 요청-응답 방식으로

    데이터를 전송하기 위해 설계된 애플리케이션 계층 프로토콜 • OSI 모델의 최상위 계층인 애플리케이션 계층에서 작동 • 클라이언트(예: 웹 브라우저)가 서버에 요청을 보내고, 서버가 응답을 반환하는 요청/응답 모델을 사용 • 데이터는 일반적으로 텍스트 기반(예: HTML, JSON, XML) 형식으로 전송 16
  6. 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
  7. 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
  8. RPC(Remote procedure call) 22 • RPC는 원격 서버에 있는 프로시저(함수나

    메서드)를 호출하는 방식으로, 클라이언트가 서버의 함수를 로컬에서 호출하는 것처럼 사용 가능 • 네트워크를 통해 다른 컴퓨터에 있는 프로그램과 상호 작용할 수 있게 해주는 방법 • 다양한 프로토콜과 데이터 형식(JSON, XML 등)을 사용할 수 있음. 그러나 텍스트 기반으로 크기가 상대적으로 큼 • 기본적으로 HTTP, TCP 등 다양한 전송 프로토콜을 사용할 수 있음 • 다양한 언어 및 플랫폼 언어 지원 • Interface Description Language
  9. 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)
  10. gRPC 25 • gRPC는 RPC 개념을 현대화하고, 다양한 언어를 지원하며,

    성능과 효율성을 개선한 형태 • HTTP/2를 기반으로 하며 프로토콜 버퍼(Protocol Buffer)를 데이터 직렬화 형식으로 사용 ◦ HTTP/2를 사용하여 멀티플렉싱, 서버 푸시, 흐름 제어 등을 지원 ◦ Protocol Buffers를 사용하여 데이터를 이진 형식으로 직렬화 • 마이크로서비스 아키텍처, 클라우드 네이티브 애플리케이션, 고성능 시스템 통신 등에 널리 사용
  11. 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": "" } },
  12. 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
  13. 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,
  14. 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
  15. Default Value 32 • 숫자 타입 (int32, int64, uint32, uint64,

    sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, float, double) ⇒ 0 • Bool ⇒ false • String ⇒ 빈 문자열 ("") • Bytes ⇒ 빈 바이트 배열 • Enum ⇒ 열거형의 첫 번째 값
  16. syntax = "proto3"; message SearchRequest { string query = 1;

    int32 page_number = 2; int32 results_per_page = 3; } 33
  17. syntax = "proto3"; message SearchRequest { int32 results_per_page = 3;

    int32 page_number = 2; string query = 1; } 34
  18. • 프로토콜 버퍼 메시지의 필드는 필드 번호(tag)로 식별. • 순서가

    아닌 번호를 통해 각 필드를 구분 → 필드의 순서는 바꿔도 되지만 필드 번호는 유지해야 호환이 가능 • 필드 번호가 유지되는 한 순서 변경은 프로토콜 버퍼 메시지의 직렬화/역직렬화 과정에 영향을 미치지 않음 Order 35
  19. 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
  20. • 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
  21. syntax = "proto3"; message SearchRequest { optional int32 results_per_page =

    3; optional int32 page_number = 2; optional string query = 1; } 38
  22. message Result { string url = 1; string title =

    2; repeated string snippets = 3; } message SearchResponse { repeated Result results = 1; } 39
  23. message SearchResponse { // level 0 message Result { //

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

    string title = 2; repeated string snippets = 3; } repeated Result results = 1; } 41
  25. 43 message TestMap { message g_Entry { optional string key

    = 1; optional int32 value = 2; } repeated g_Entry g_map = 7; }
  26. message TestMap { map<string, int32> g = 7; } 44

    message TestMap { message g_Entry { optional string key = 1; optional int32 value = 2; } repeated g_Entry g = 7; }
  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": "" } }, 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; }
  28. 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
  29. 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
  30. 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
  31. Encoding 좀 더 깊게 들어가보기 49 message Example { string

    name = 1; } name의 값이 io extended(문자)의 경우 인코딩을 어떻게 처리하는가? field_tag Length Value
  32. 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
  33. Encoding 좀 더 깊게 들어가보기 51 message Example { string

    name = 1; } 문자 길이 계산 방법 문자열 "io extended"의 길이는 12 (0x0C) Length
  34. 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
  35. 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
  36. 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
  37. syntax = "proto3"; service Greeter { rpc SayHello (HelloRequest) returns

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

    (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; } 61
  39. 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
  40. class GreeterService extends GreeterServiceBase { @override Future<HelloReply> sayHello(ServiceCall call, HelloRequest

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

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

    request) async { return HelloReply()..message = 'Hello, ${request.name}!'; } } 66 Server Side
  43. 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
  44. 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
  45. channel = ClientChannel( 'localhost', port: 50051, options: ChannelOptions( credentials: const

    ChannelCredentials.insecure(), codecRegistry: CodecRegistry( codecs: const [ GzipCodec(), IdentityCodec(), ], ), ), ); 69 Client Side
  46. 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
  47. 구성 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
  48. 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
  49. 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
  50. class TreadmillService extends TreadmillServiceBase { Ref? ref; TreadmillService({this.ref}); @override Future<Reply>

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

    sendData(ServiceCall call, Data request) async { ref?.read(treadmillNotifierProvider.notifier).update(request); return Reply(message: request.toString()); } } 77 Server Side
  52. final server = Server.create( services: [ ref.read(treadmillServiceProvider), ], codecRegistry: CodecRegistry(

    codecs: const [ GzipCodec(), IdentityCodec(), ], ), ); await server.serve(port: 50051); 81 Server Side
  53. @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
  54. channel = ClientChannel( 'localhost', port: 50051, options: ChannelOptions( credentials: const

    ChannelCredentials.insecure(), codecRegistry: CodecRegistry( codecs: const [ GzipCodec(), IdentityCodec(), ], ), ), ); 83 Client Side
  55. 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
  56. Summary 87 • gRPC는 HTTP2, 프로토콜버퍼가 적용된 Remote Procedure Call

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