golang + dart grpc 學習總結

今天本來想用dart做一些native的小工具,但是準備開始寫的時候才發現dart的生態的確還欠缺很多,dart連獲取自身運行時內存信息的方法都沒有,別提執行shell或其他監控了。那麼,如果dart自身不行,能不能通過已有的工具組合起來爲dart提供服務呢?畢竟dart的 isolate 自動釋放內存和安全的內存隔離是一個亮點,而且還能通過主線程控制其他的isolate空間,等於一個 FPM,如果因爲生態而無法使用,就有點遺憾了。爲了能給dart增加能力,想到了官方提供的dart grpc包,就有了今天的 golang + dart grpc入門了。

我打算用 golang 創建 grpc server ,用 dart 作爲 client 進行通信,因爲golang的生態和各方面都比較優秀,只有在內存上,對堆內存的釋放和我的預期有差距。

PS: 假設我們在golang中需要利用不同的協程進行數據篩選和處理,然後通過chan彙總到main,那麼內存的消耗可能會超出我們的預期,這個時候,我們就需要像dart這樣,當isolate執行完沒有引用後,立即釋放其內存,節約寶貴的系統資源。當然,這並不是說golang不適用,golang在內存複用上,可以減少很多內存開闢和釋放的開銷,只是應用的場景不同而已。

很久沒有搞golang了,這裏記錄一下開發過程和參考資料。

Golang 端:

 

  1. 下載 protoc 編譯工具,https://github.com/google/protobuf/releases
  2. 開啓protobuf-golang 代碼生成器插件
 go get -u github.com/golang/protobuf/protoc-gen-go
  1. 配置 command PATH
export PATH=$PATH:$GOPATH/bin
  1. 創建 Golang 項目,這裏我直接用 Goland IDE 創建,創建在 GOPATH 之外,使用了 go mod 作爲單獨的包管理工具,並且創建 vendor 目錄。
mkdir grpc_server //創建項目目錄
cd grpc_server  
mkdir src //創建源碼目錄
cd src //進入源碼目錄
go mod init grpc_server 
ls -al .  //你會看到有一個Mod文件表示 mod 命令執行成功
touch main.go  //創建項目主執行文件
  1. 約定目錄結構
/ 
/proto/   //proto 定義文件目錄
/main.go  
  1. 編寫 protobuf 文件,定義 service 和 message. 語法參考:https://colobu.com/2015/01/07/Protobuf-language-guide/#%E5%8C%85%EF%BC%88Package%EF%BC%89\

文件:/proto/site_info.proto

syntax = "proto3";  //protobuf 語法版本,2 和 3的版本有差異,要注意和客戶端用一致的

package siteinfo;  //定義生成代碼的包名

//定義一個 grpc 服務,每個服務可以看成是一個API 微服務,微服務下有很多相應的接口
service SiteInfo { 
//定義RPC方法,此方法接收一個 SearchById 結構的消息,返回一個 User 結構的消息
    rpc GetUserInfoById(SearchById) returns  (User) {}
}

//定義消息結構細節
message SearchById{
    //定義消息體中的參數 userId 爲 int32 類型,在消息體中順序爲第一位
    int32 userId = 1;
}

message User{

    string id = 1;
    string name = 2;
    string phone = 3;
    string status = 4;

}
  1. 生成 golang protobuf 代碼
path: grpc_server/src/
//注意 --go_out 參數的 plugins=grpc:.  如不加這個也可以執行成功,
//但是生成的文件會缺少 grpc 方法,例如 定義的 GetUserInfoById 方法會缺失
protoc -I $PWD/ proto/site_info.proto --go_out=plugins=grpc:.
  1. 實現 protoc 工具生成代碼中的 SiteInfoServer 接口,也就是我們再 proto 文件中定義的服務和方法,覆蓋生成方法實現具體的業務邏輯。
path: main.go
type Server struct {}
func (s *Server) GetUserInfoById(ctx context.Context, in *pb.SearchById) (*pb.User, error){
   //Mysql 查詢
   return &pb.User{
      Id: "1",
      Name: "User",
      Phone: "1376776",
   },nil
}
  1. 創建 grpc 服務,綁定 tcp 端口監聽,將生成的 服務註冊到 grpc server中
path: main.go
func main(){

   listen, err := net.Listen("tcp",Port)
   if err != nil {
         panic(err)
   }

   s := grpc.NewServer()
   pb.RegisterSiteInfoServer(s,&Server{})
   s.Serve(listen)
}
  1. 這裏注意一下因爲用了 go mod ,還需要同步import包, 創建go項目獨立的 vendor 目錄
go mod vendor
  1. 啓動服務, server 端完成
import (
   "context"
   "net"
   grpc "google.golang.org/grpc"
   pb "grpc_server/proto"
)

func main(){

   listen, err := net.Listen("tcp",Port)
   if err != nil {
         panic(err)
   }

   s := grpc.NewServer()
   pb.RegisterSiteInfoServer(s,&Server{})
   s.Serve(listen)
}

const (
   Port = ":3389"
)

type Server struct {}

func (s *Server) GetUserInfoById(ctx context.Context, in *pb.SearchById) (*pb.User, error){

   //Mysql 查詢
   return &pb.User{
      Id: "1",
      Name: "User",
      Phone: "1376776",
   },nil
}

 

Dart 端:

 

  1. 開啓protobuf-dart 代碼生成器插件
pub global activate protoc_plugin
  1. 配置 PATH
export PATH=$PATH:$HOME/.pub-cache/bin
  1. 創建 Dart console command 項目,項目名稱: grpc_client ,這裏我用的是 idea ,直接選擇就會創建默認目錄結構
//約定proto 文件目錄在創建的項目根目錄下的 proto 目錄中
mkdir /grpc_client/proto/
  1. 將 golang 項目中的 site_info.proto 文件複製到 dart 項目的 proto目錄
  2. 生成dart 端 grpc 代碼
cd grpc_client

-I後緊接着是 proto 文件所在的目錄,路徑是相對當前執行命令目錄的,目錄後面有空格
--dart_out=grpc:[dir_path]  這裏的dir_path 也是相對當前目錄的路徑

protoc --dart_out=grpc:proto -Iproto/ site_info.proto 
  1. 註冊項目依賴包

/grpc_client/pubspec.yml:

name: grpc_client
description: A sample command-line application.
# version: 1.0.0
# homepage: https://www.example.com

environment:
  sdk: '>=2.7.0 <3.0.0'

dependencies:
  grpc: ^2.1.3
  protobuf: ^1.0.1
  protoc_plugin: 19.0.1

dev_dependencies:
  pedantic: ^1.8.0
  test: ^1.6.0

 

  1. 在 mian.dart 中調用RPC
import '../proto/site_info.pb.dart';
import '../proto/site_info.pbgrpc.dart';
import 'package:grpc/grpc.dart';

Future<Null> main(List<String> arguments) async{

  final channel = new ClientChannel('localhost',
  port:3389,
      options:  const ChannelOptions(credentials: ChannelCredentials.insecure())
  );

  final stub = SiteInfoClient(channel);

  final arg = 3;

  try{

    final res = await stub.getUserInfoById(SearchById()..userId = arg);
    print(res.toString());
    print(res.runtimeType);

  }catch(e){
    print(e);
  }
//  await channel.shutdown();
}

到這裏就ok了,dart 和 golang 的grpc 交互成功,後續可以根據每個語言的特點來進行服務劃分,通過grpc來跨語言調用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章