Sử dụng đồng thời Restful API và gRPC trong grpc service golang

Tổng quan về Restful API và gRPC Restful API: Khi sử dụng các API REST, phản hồi từ dữ liệu back-end được chuyển đến các máy khách (client) thông qua định dạng nhắn tin JSON hoặc XML . Công nghệ này sử dụng giao thức HTTP gRPC: gRPC(Google Remote Process Call) client cần server thực

Tổng quan về Restful API và gRPC

Restful API: Khi sử dụng các API REST, phản hồi từ dữ liệu back-end được chuyển đến các máy khách (client) thông qua định dạng nhắn tin JSON hoặc XML . Công nghệ này sử dụng giao thức HTTP

gRPC: gRPC(Google Remote Process Call) client cần server thực hiện tính toán hoặc trả về một thông tin cụ thể nào đó. Bản chất giống y như ta đang gọi hàm, chỉ là hàm đó ở máy chủ khác hoặc service khác. Công nghệ này sử dụng giao thức HTTP 2.

Vấn đề

Một hạn chế với gRPC là không phải nền tảng nào cũng có thể sử dụng nó. Các trình duyệt không hỗ trợ đầy đủ HTTP 2, làm cho REST và JSON trở thành cách chính để tải dữ liệu vào các ứng dụng trình duyệt.

Ngay cả với những lợi ích mà gRPC mang lại, REST và JSON vẫn có một vị trí quan trọng trong các ứng dụng hiện nay. Vì vậy việc xây dựng 1 service cung cấp cả giao thức grpc HTTP 2 và Restful API HTTP là điều hoàn toàn có thể xảy ra.

Ở đây mình có tham khảo một repo GIT có hơn 11k start. Theo mình hiểu thì khi gRPC cung cấp giao thức HTTP 2 ra thì chúng ta cần thêm 1 gRPC-Gateway để convert cái giao thức HTTP 2 đấy về HTTP rồi cung cấp các URL như làm Restful API thôi. Các contributor đã đưa cách làm này chạy mượt mà từ năm 2018. Nên chắc không cần lo lắng về tính đúng đắn của nó đâu!

  • Mô hình phát triển
Triển khai
  • Tạo go mudule
    go mod init github.com/your-git/grpc-gateway-demo
  • Copy thư mục google từ về folder dự án của bạn
  • Tạo thư mục proto và thêm file service.proto chứa những định nghĩa request, response và các service bạn muốn implement.
syntax = "proto3";

package grpc_gateway_demo;

option go_package = "/.;proto";

import "google/api/annotations.proto";

message StringMessage {
    string value = 1;
}

message SumRequest {
    int32 num1 = 1;
    int32 num2 = 2;
}

message SumResponse {
    int32 result = 1;
}

service MyService {
    rpc Echo(StringMessage) returns (StringMessage) {
        option (google.api.http) = {
            post: "/v1/echo"
            body: "*"
        };
    };
    rpc Sum(SumRequest) returns (SumResponse) {
        option (google.api.http) = {
            get: "/v1/sum"
        };
    }
}
  • Tiếp theo chúng ta cần chạy protoc để gen gRPC stubs và reverse-proxy
  • Trước tiên tạo file tools.go
//go:build tools
// +build tools

package tools

import (
	_ "github.com/golang/protobuf/protoc-gen-go"
	_ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"
)
  • Run terminal go mod tidy
  • Install tools
$ go install github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
    go install github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
    go install github.com/golang/protobuf/protoc-gen-go
  • Run protoc để gen

service.pb.go

protoc --go_out=. --go_opt plugins=grpc --go_opt paths=source_relative .protoservice.proto

service.pb.gw.go

protoc --grpc-gateway_out=. --grpc-gateway_opt logtostderr=true --grpc-gateway_opt paths=source_relative .protoservice.proto
  • Tạo file main.go để implement các service trong file proto vừa tạo và chạy server
package main

import (
	"context"
	"fmt"
	"log"
	"net"

	"github.com/dainh-2247/grpc-gateway-demo/proto"
	"google.golang.org/grpc"
)

type server struct{}

// Echo
func (*server) Echo(ctx context.Context, req *proto.StringMessage) (*proto.StringMessage, error) {
	log.Printf("receive message %sn", req.GetValue())
	return req, nil
}

// Sum
func (*server) Sum(ctx context.Context, req *proto.SumRequest) (*proto.SumResponse, error) {
	log.Printf("Sum is called...")

	resp := &proto.SumResponse{
		Result: req.GetNum1() + req.GetNum2(),
	}

	return resp, nil
}

func main() {
	lis, err := net.Listen("tcp", "0.0.0.0:3000")
	if err != nil {
		log.Fatalf("err while create listen  %v", err)
	}

	s := grpc.NewServer()
	proto.RegisterMyServiceServer(s, &server{})

	fmt.Printf("server is running...")

	err = s.Serve(lis)
	if err != nil {
		log.Fatalf("err while serve %v", err)
	}
}
  • 2 func Echo và Sum là mk implement từ file proto

  • Func Echo “kiểu” POST thì mk sẽ lấy value từ request body rồi trả về đúng cái value đấy 😃

  • Func Sum “kiểu” GET thì mk sẽ tính tổng của 2 số từ param

  • Server chạy trên localhost:3000

  • Tạo thư mục proxy chưa file proxy.go

package main

import (
	"context"
	"flag"
	"log"
	"net/http"

	"github.com/golang/glog"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"google.golang.org/grpc"

	gw "github.com/dainh-2247/grpc-gateway-demo/proto" // Update
)

var (
	// command-line options:
	// gRPC server endpoint
	grpcServerEndpoint = flag.String("grpc-server-endpoint", "localhost:3000", "gRPC server endpoint")
)

func run() error {
	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	// Register gRPC server endpoint
	// Note: Make sure the gRPC server is running properly and accessible
	mux := runtime.NewServeMux()
	opts := []grpc.DialOption{grpc.WithInsecure()}
	err := gw.RegisterMyServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
	if err != nil {
		return err
	}

	// Start HTTP server (and proxy calls to gRPC server endpoint)
	return http.ListenAndServe(":8081", mux)
}

func main() {
	flag.Parse()
	defer glog.Flush()
	log.Printf("proxy is running...")
	if err := run(); err != nil {
		glog.Fatal(err)
	}
}
  • Lưu ý ở khai báo grpcServerEndpoint = flag.String("grpc-server-endpoint", "localhost:3000", "gRPC server endpoint") parameter thứ 2 là url mà server cung cấp. Ở đây là localhost:3000
  • Port để chạy gateway là 8081.

Lúc này thì thư mục sẽ như thế này

  • Run server và proxy
go run .main.go
go run .proxyproxy.go
  • Dùng postman để test

  • POST

  • GET

  • Chạy khá là ok đấy nhỉ :v
Kết Luận
  • Thực ra cách cung cấp cả Restful API HTTP và gRPC HTTP 2 trên GIT các contributor đang triển khai 1 version mới. Version mà mình triển khai là v1 version cũ.
  • Nhưng mình nghĩ hiểu từ cái căn bản nhất rồi tới những phần nâng cao hơn, mới hơn cũng tốt phải không 😃
  • Ở lần sau mk sẽ triển khai theo version mới để phục vụ bạn đọc.
  • Link source code của mk Cảm ơn các bạn đã theo dõi.

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ