首页 > 其他分享 >go语言中protobuf以及grpc的使用

go语言中protobuf以及grpc的使用

时间:2023-06-03 09:15:04浏览次数:41  
标签:int32 protobuf err grpc calculator go message

首先定义数据结构,保存为.proto文件

syntax = "proto3";

package tutorial;

// The protocol compiler generates a class from the following .proto file with
// methods for writing and reading the message fields. The class is called
// "Person" and is in a package called "tutorial". You can specify a different
// class name using the java_outer_classname option.

message Person {
  string name = 1;
  int32 id = 2;  // Unique ID number for this person.
  string email = 3;

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

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

  repeated PhoneNumber phones = 4;
}

// Our address book file is just one of these.
message AddressBook {
  repeated Person people = 1;
}

然后,用protocol buffer编译器以生成go语言源代码。没想多遇见了好几次问题。当然,已经有现成且有效的解决方案了。

编译指令:

protoc -I=$SRC_DIR --go_out=$DST_DIR $SRC_DIR/addressbook.proto
首先报错的是,没有protoc-gen-go。于是百度,搜到了下面这篇博客。
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
没有程序,首先就要安装。使用上条指令安装后,需要手动复制protoc-gen-go.exe到proto的bin路径下。
随后,又执行上面的编译指令,就出现了下面的问题。
protoc-gen-go: unable to determine Go import path for "person.proto"

Please specify either:
        • a "go_package" option in the .proto source file, or
        • a "M" argument on the command line.
See https://protobuf.dev/reference/go/go-generated#package for more information.
解决方案是在proto文件中添加一行
option go_package="/main";也就是需要自己设定生成的pb.go的包名

解决方案,这篇博客非常详细,清楚,也可以直接查看。

main.go

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/protobuf/proto"
	"io/ioutil"
	"log"
	"net"
	"protoTest/calculator"
	"protoTest/person"
)

const fileName = "D:\\protoTest\\main\\addressbook.data"

func testProc() {

	in, err := ioutil.ReadFile(fileName)
	if err != nil {
		log.Fatalln("Error reading file:", err)
	}
	book := &person.AddressBook{}
	if err := proto.Unmarshal(in, book); err != nil {
		log.Fatalln("Failed to parse address book:", err)
	}

	// Add an address.
	book.People = append(book.People, &person.Person{
		Name:  "Alice",
		Id:    100,
		Email: "[email protected]",
	})

	// Write the new address book back to disk.
	out, err := proto.Marshal(book)
	if err != nil {
		log.Fatalln("Failed to encode address book:", err)
	}
	if err := ioutil.WriteFile(fileName, out, 0644); err != nil {
		log.Fatalln("Failed to write address book:", err)
	}
	fmt.Println("that is ok")
}

func main() {
	testProc()
}

通过简短的例子和bing的解释,大致了解了protobuf的优势。以二进制的方式压缩数据,在传输过程中就能节省资源,减轻网络负荷。

例子比较简易,然后就想试一下protobuf在grpc中的使用,就有了下面的例子。

calculator.proto

package calculator;

// The calculator service definition.
service Calculator {
  // Sends two integers and returns their sum
  rpc Add (AddRequest) returns (AddResponse) {}
  // Sends two integers and returns their difference
  rpc Subtract (SubtractRequest) returns (SubtractResponse) {}
  // Sends two integers and returns their product
  rpc Multiply (MultiplyRequest) returns (MultiplyResponse) {}
  // Sends two integers and returns their quotient
  rpc Divide (DivideRequest) returns (DivideResponse) {}
}

// The request message containing two integers
message AddRequest {
  int32 a = 1;
  int32 b = 2;
}

// The response message containing the sum
message AddResponse {
  int32 result = 1;
}

// The request message containing two integers
message SubtractRequest {
  int32 a = 1;
  int32 b = 2;
}

// The response message containing the difference
message SubtractResponse {
  int32 result = 1;
}

// The request message containing two integers
message MultiplyRequest {
  int32 a = 1;
  int32 b = 2;
}

// The response message containing the product
message MultiplyResponse {
  int32 result = 1;
}

// The request message containing two integers
message DivideRequest {
  int32 a = 1;
  int32 b = 2;
}

// The response message containing the quotient
message DivideResponse {
  int32 result = 1;
}

protoc -I=D:\Git\protoTest\protobuf --go_out=D:\Git\protoTest calculator.proto

没注意,以为都是用protoc-gen-go.exe就好了呢。直接编译,还是生成了一堆文件,没有报错。
但是,发觉和官方给的例子差异很大。比如,官方的helloworld例子,我直接看的在线代码,里面proto生成的pb.go包含客户端和服务器的部分,但是我的pb.go文件就没有。我还觉得很奇怪呢,但还是有样学样的,添加一些代码,终于改好了client。于是开始改server端,但是太麻烦了,越改越觉得离谱。于是重新看了下bing给我的编译指令,和我这个可差太远了。才意识到问题所在。
protoc calculator.proto --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative

使用这个指令编译,遇见的问题,就是类似于上面的,没有protoc-gen-go-grpc。

于是有样学样,安装就好了。

go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

同理,安装后需要复制protoc-gen-go-grpc.exe到protoc的bin路径下。

然后就可以正常编译了。

protoc calculator.proto --go_out=..  --go-grpc_out=..	//修改了生成文件保存的路径。

server.go

package main

import (
	"context"
	"grpcTest/calculator"
)

type Server struct {
	calculator.UnimplementedCalculatorServer
}

// Add implements the Add rpc method
func (s *Server) Add(ctx context.Context, in *calculator.AddRequest) (*calculator.AddResponse, error) {
	// Get the two integers from the request
	a := in.GetA()
	b := in.GetB()
	// Compute the sum
	result := a + b
	// Return the response with the sum
	return &calculator.AddResponse{Result: result}, nil
}

func (s *Server) Subtract(ctx context.Context, in *calculator.SubtractRequest) (*calculator.SubtractResponse, error) {
	// Get the two integers from the request
	a := in.GetA()
	b := in.GetB()
	// Compute the sum
	result := a - b
	// Return the response with the sum
	return &calculator.SubtractResponse{Result: result}, nil
}
func (s *Server) Multiply(ctx context.Context, in *calculator.MultiplyRequest) (*calculator.MultiplyResponse, error) {
	// Get the two integers from the request
	a := in.GetA()
	b := in.GetB()
	// Compute the sum
	result := a * b
	// Return the response with the sum
	return &calculator.MultiplyResponse{Result: result}, nil
}
func (s *Server) Divide(ctx context.Context, in *calculator.DivideRequest) (*calculator.DivideResponse, error) {
	// Get the two integers from the request
	a := in.GetA()
	b := in.GetB()
	// Compute the sum
	result := a / b
	// Return the response with the sum
	return &calculator.DivideResponse{Result: result}, nil
}

client.go

package main

import (
	"context"
	"google.golang.org/grpc"
	"grpcTest/calculator"
)

type Client struct {
	conn *grpc.ClientConn
}

// NewClient creates a new Client with the given connection
func NewClient(conn *grpc.ClientConn) *Client {
	return &Client{conn: conn}
}

// Add calls the Add rpc method with two integers and returns their sum
func (c *Client) Add(a, b int32) (int32, error) {
	// Create a new CalculatorClient with the connection
	//client := NewCalculatorClient(c.conn)
	client := calculator.NewCalculatorClient(c.conn)
	// Create a new AddRequest with the two integers
	req := &calculator.AddRequest{A: a, B: b}
	// Call the Add method with the request and get the response
	res, err := client.Add(context.Background(), req)
	if err != nil {
		return 0, err
	}
	// Return the result from the response
	return res.GetResult(), nil
}

func (c *Client) Subtract(a, b int32) (int32, error) {
	// Create a new CalculatorClient with the connection
	//client := NewCalculatorClient(c.conn)
	client := calculator.NewCalculatorClient(c.conn)
	// Create a new AddRequest with the two integers
	req := &calculator.SubtractRequest{A: a, B: b}
	// Call the Add method with the request and get the response
	res, err := client.Subtract(context.Background(), req)
	if err != nil {
		return 0, err
	}
	// Return the result from the response
	return res.GetResult(), nil
}

func (c *Client) Multiply(a, b int32) (int32, error) {
	// Create a new CalculatorClient with the connection
	//client := NewCalculatorClient(c.conn)
	client := calculator.NewCalculatorClient(c.conn)
	// Create a new AddRequest with the two integers
	req := &calculator.MultiplyRequest{A: a, B: b}
	// Call the Add method with the request and get the response
	res, err := client.Multiply(context.Background(), req)
	if err != nil {
		return 0, err
	}
	// Return the result from the response
	return res.GetResult(), nil
}

func (c *Client) Divide(a, b int32) (int32, error) {
	// Create a new CalculatorClient with the connection
	//client := NewCalculatorClient(c.conn)
	client := calculator.NewCalculatorClient(c.conn)
	// Create a new AddRequest with the two integers
	req := &calculator.DivideRequest{A: a, B: b}
	// Call the Add method with the request and get the response
	res, err := client.Divide(context.Background(), req)
	if err != nil {
		return 0, err
	}
	// Return the result from the response
	return res.GetResult(), nil
}

// Close closes the connection
func (c *Client) Close() error {
	return c.conn.Close()
}

main.go

package main

import (
	"context"
	"google.golang.org/grpc"
	"grpcTest/calculator"
	"log"
	"net"
)

const address = "localhost:50051"
const (
	// Port on which the server listens.
	port = ":50051"
)

func testServer(c chan bool) {
	// Listen on a port
	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	// Create a new gRPC server
	s := grpc.NewServer()
	// Register a new Calculator service with the server
	calculator.RegisterCalculatorServer(s, &Server{})
	c <- true
	log.Println("server listening at :50051")
	// Serve gRPC requests on the port
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

func test() {
	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	// Create a new gRPC server
	s := grpc.NewServer()
	// Register a new Calculator service with the server
	calculator.RegisterCalculatorServer(s, &Server{})
	log.Println("server listening at :50051")
	// Serve gRPC requests on the port
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}
func main() {
	test()
	return
	//testProc()
	ch := make(chan bool)
	go testServer(ch)

	conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()

	// Create a calculator client
	c := calculator.NewCalculatorClient(conn)

	// Call the Add method with two integers
	r, err := c.Add(context.Background(), &calculator.AddRequest{A: 3, B: 5})
	if err != nil {
		log.Fatalf("could not add: %v", err)
	}
	// Print the result
	log.Printf("The sum is: %d", r.GetResult())

	r1, err := c.Subtract(context.Background(), &calculator.SubtractRequest{A: 3, B: 5})
	if err != nil {
		log.Fatalf("could not add: %v", err)
	}
	// Print the result
	log.Printf("The sum is: %d", r1.GetResult())

	r2, err := c.Multiply(context.Background(), &calculator.MultiplyRequest{A: 3, B: 5})
	if err != nil {
		log.Fatalf("could not add: %v", err)
	}
	// Print the result
	log.Printf("The sum is: %d", r2.GetResult())

	r3, err := c.Divide(context.Background(), &calculator.DivideRequest{A: 13, B: 5})
	if err != nil {
		log.Fatalf("could not add: %v", err)
	}
	// Print the result
	log.Printf("The sum is: %d", r3.GetResult())
}

过程中缺什么包啥的,直接go get就好了。一执行,还是没有任何问题的。

这是目录结构:

image-20230602215300462

于是又想着,protobuf是可以支持不同平台的。所以还想弄个python请求go的例子

没想到处处碰壁,python/Java/C++/node。都很困难。暂时就先不弄了。

标签:int32,protobuf,err,grpc,calculator,go,message
From: https://www.cnblogs.com/dayq/p/17453267.html

相关文章

  • picgo配置以及上传失败解决问题的若干办法
    本文方法仅针对个人其它自测GitHub图床设置GitHub获取tokenpicgo设置上传失败未开启时间戳重命名功能(该错误特点:第一次上传成功,第二次上传同样的照片失败)开启时间戳重命名功能......
  • CF321E - Ciel and Gondolas
    考虑\(dp_{i,j}\)表示用\(i\)条船载走前\(j\)个人的最小贡献,\(w_{i,j}\)表示区间\([i,j]\)里的人同乘一条船的代价。则\(dp_{i,j}=\min_{1\lek\ltj}(dp_{i-1,k}+w_{k+1,j})\)。我们发现,\(w_{i,j}\)可以通过\(w_{i,j-1}+s_{j,j}-s_{j,i-1}\)递推计算。其中\(s_{i,......
  • Gorm使用的一些经验--如何彻底删除一条数据
    中文文档:https://gorm.io/zh_CN/我们知道,在使用gorm的时候,如果我们使用了gorm内置的model,会存在一个delete_at字段,当我们删除一条数据,这条数据并不会在数据库中被彻底删除举个例子:数据库中的数据如下: 现在通过实现的接口,去删除id=402的数据,在这里因为我设计的接口原......
  • [原]Android上GTalk以及Push机制的XMPP数据选择使用protobuf格式而非XML格式
    [url]http://code.google.com/p/protobuf/[/url][b]先介绍下什么是protobuf以及有什么好处.[/b][i]ProtocolBuffersareawayofencodingstructureddatainanefficientyetextensibleformat.GoogleusesProtocolBuffersforalmostallofit......
  • [原创]通过代码及流程图说明Google在Android上的Push机制的实现
    [color=red][b]声明:如果您要转载,请事先征得本人的同意后方可并且请您附上原文链接.本人保留一切权利.多谢![/b][/color]Google从FroYo版本后引入了C2DM(CloudtoDeviceMessaging)框架:[url]http://code.google.com/android/c2dm/index.html[/url......
  • Linux 主机名被修改成bogon问题的几种解决办法
        当Linux主机名由@myhostname变成了@bogon了之后,访问网络就会出现问题,重启后也没有解决。网上搜索N久之后,发现了如下几种解决方式,特此记录一下。 1、在linux下添加一个127.0.0.2名叫bogon的主机此方法使用后,bogon主机名得以解析,使用的主机名仍为bogon,但进入linux已不......
  • Google Java编程风格指南
    作者:Hawstein目录前言源文件基础源文件结构格式命名约定编程实践Javadoc后记前言这份文档是GoogleJava编程风格规范的完整定义。当且仅当一个Java源文件符合此文档中的规则,我们才认为它符合Google的Java编程风格。与其它的编程风格指南一样,这里所讨论的不仅仅是编码格......
  • 为什么说 Go 语言字符串是不可变的?
    原文链接:为什么说Go语言字符串是不可变的?最近有读者留言说,平时在写代码的过程中,是会对字符串进行修改的,但网上都说Go语言字符串是不可变的,这是为什么呢?这个问题本身并不困难,但对于新手来说确实容易产生困惑,今天就来回答一下。首先来看看它的底层结构:typestringStructst......
  • goto语句
    跳转语句C语言的跳转语句主要包括continue,break,retuen,还有就是goto啦goto语句goto语句是在所有跳转语句中最自由的一种,但在大型工程和多人协作工程中并不推荐,原因就在于它太过于自由,会导致代码的可读性变得较差但这也无法撼动goto语句的地位合理的使用goto会大大简......
  • Angular Google Charts教程_编程入门自学教程_菜鸟教程-免费教程分享
    教程简介GoogleCharts是一个纯粹的基于JavaScript的图表库,旨在通过添加交互式图表功能来增强Web应用程序.它支持各种图表.在Chrome,Firefox,Safari,InternetExplorer(IE)等标准浏览器中使用SVG绘制图表.在传统的IE6中,VML用于绘制图形.AngularGoogleCharts是一个基于开源角度......