Skip to content

Dùng lý thuyết củ hành để tìm hiểu gRPC

Nguyen Nguyen edited this page Apr 9, 2019 · 2 revisions

gRPC là một RPC platform được phát triển bởi Google nhằm tối ưu hoá và tăng tốc việc giao tiếp giữa các service với nhau trong kiến trúc microservice.

gRPC dùng Protocal Buffer giảm kích thước request và response data, RPC để đơn giản hoá trong việc tạo ra các giao tiếp giữa các service với nhau, HTTP/2 để tăng tốc gửi/nhận HTTP request.

Bóc tách từng thành phần trong gRPC ra để xem gRPC có những gì

RPC

RPC là từ viết tắc của Remote Procedure Call, nó được xây dựng với ý tưởng là đơn giản hoá việc giao tiếp giữa những service với nhau, thay vì những service giao tiếp với nhau theo kiểu RESTful API thì giờ đơn giản là gọi hàm như những object nói chuyện với nhau thôi, còn việc phân tán các service là chuyện của tương lai không dính liếu đến việc code.

Một ví dụ đơn giản, client greeter_client.py sẽ SayHello tới service GreeterStub như sau:

def run():
  channel = grpc.insecure_channel('localhost:50051')
  stub = helloworld_pb2_grpc.GreeterStub(channel)
  response = stub.SayHello(helloworld_pb2.HelloRequest(name='Johnny'))
  print("Greeter client received: " + response.message)

Protocal Buffer

Protocal Buffer là một ngôn ngữ trung lập để serializing structured data sử dụng cho việc giao tiếp giữa các service với nhau. Protocal Buffer được tạo ra với ý tưởng là làm nhỏ kích thước data truyền đi trong giao tiếp và chỉ cần định nghĩa một lần và sử dụng cho các service với các ngôn ngữ lập trình khác nhau.

Ví dụ đơn giản về cấu trúc file Protocal Buffer để mô tả Person object addressbook.proto

syntax = "proto2";

package tutorial;

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2;
  }

  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}

Sau đó dùng một thư viện có sẵn để generate ra các object tương ứng với ngôn ngữ mình cần như Python, Java, PHP, Go... sau khi generate ra file addressbook_pb2.py , ta có thể sử dụng như sau:

import addressbook_pb2

person = addressbook_pb2.Person()
person.id = 1234
person.name = "John Doe"
person.email = "jdoe@example.com"
phone = person.phones.add()
phone.number = "555-4321"
phone.type = addressbook_pb2.Person.HOME
print(person.SerializeToString())

Còn data sau khi Serialize To String sẽ rất nhỏ như thế này:

b'\n\x08John Doe\x10\xd2\t\x1a\x10jdoe@example.com"\x0c\n\x08555-4321\x10\x01'

HTTP/2

HTTP/2 là một phiên bản nâng cấp của HTTP/1.1, HTTP/2 sinh với với mục đích cải thiện tốc độ giao tiếp giữa client/server trên nền tảng Web.

Một vài điểm hay của HTTP/2:

  • Request multiplexing: HTTP/2 có thể gửi cùng lúc nhiều request đến 1 TCP connection và kết quả được trả về bất đồng bộ với nhau.

  • Header compression: như bạn biết mỗi request của HTTP sẽ mang rất nhiều data header đi và đến cho dù nó giống nhau từ request thứ 2 trở đi, HTTP/2 tối ưu chổ này tí xíu, HTTP/2 sẽ loại bỏ những data header dư thừa ở những lần request thứ 2 trở đi và nén chúng lại trước khi gửi đi.

  • Binary protocol: Browser sẽ convert text sang binary trước khi gửi qua đường network.

  • HTTP/2 Server Push: Thêm một cách để tối ưu tốc độ loading của website, thay vì phải có request từ client thì server mới trả resource về, HTTP/2 sẽ đẩy resource về cho client luôn mà không cần client gửi request.

gRPC

Vậy những ưu điểm rất lớn của RPC, Protocal Buffer, HTTP/2 sẽ gói trong gRPC, giờ là một ví dự đơn giản

  • Step 1: dùng cấu trúc của Protocal Buffer khai báo message và service.
syntax = "proto3";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}
  • Step 2: Install grpc library, grpc-tools và generate grpc code.
python -m pip install grpcio
python -m pip install grpcio-tools
$ python -m grpc_tools.protoc -I../../protos --python_out=. --grpc_python_out=. ../../protos/helloworld.proto
  • Step 3: Khởi tạo server connection và khai báo những public enpoints:
from concurrent import futures
import time
import logging

import grpc

import helloworld_pb2
import helloworld_pb2_grpc

_ONE_DAY_IN_SECONDS = 60 * 60 * 24


class Greeter(helloworld_pb2_grpc.GreeterServicer):

    def SayHello(self, request, context):
        return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)


def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    try:
        while True:
            time.sleep(_ONE_DAY_IN_SECONDS)
    except KeyboardInterrupt:
        server.stop(0)


if __name__ == '__main__':
    logging.basicConfig()
    serve()
  • Step 4: Tạo file greeter_client.py để thử kết nối
from __future__ import print_function
import logging

import grpc

import helloworld_pb2
import helloworld_pb2_grpc


def run():
    # NOTE(gRPC Python Team): .close() is possible on a channel and should be
    # used in circumstances in which the with statement does not fit the needs
    # of the code.
    with grpc.insecure_channel('localhost:50051') as channel:
        stub = helloworld_pb2_grpc.GreeterStub(channel)
        response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
    print("Greeter client received: " + response.message)


if __name__ == '__main__':
    logging.basicConfig()
    run()

References: