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来跨语言调用。

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页