【go语言】grpc 快速入门

news/2025/2/3 6:25:46 标签: golang, 开发语言, 后端

一、什么是 grpc 和 protobuf

1.1 grpc

       gRPC 是由 Google 开发的一个高效、开源的远程过程调用(RPC)框架,用于在分布式系统中进行通信。它是基于 HTTP/2 协议,支持多种语言,能够让不同的系统或应用程序(即使使用不同的编程语言)进行高效的通信。

1.1.1 主要特点

  • 高性能:gRPC 基于 HTTP/2 协议,支持流式传输、请求多路复用、头部压缩等特性,这些使得 gRPC 比传统的 HTTP/1.x 更加高效。
  • 跨语言支持:gRPC 支持多种编程语言(如 C++, Java, Go, Python, Ruby, Node.js 等),使得不同语言开发的系统能够无缝通信。
  • 支持同步和异步调用:gRPC 提供了同步和异步的调用方式,可以根据需求选择使用。
  • 内置支持流式传输:gRPC 支持单向流和双向流,可以非常方便地实现实时通信。
  • 自动生成代码:通过定义服务接口,gRPC 自动生成客户端和服务端的代码,减少手动编写的工作量。

1.1.2 典型应用场景

  • 微服务架构中的服务之间的高效通信。
  • 跨平台、跨语言的分布式系统通信。
  • 实时数据传输或流处理。

1.2 protobuf

       Protocol Buffers(简称 Protobuf)是 Google 开发的一种高效的序列化结构数据的机制,类似于 XML 或 JSON,但比它们更紧凑、更高效。Protobuf 用于数据的定义、传输和持久化,通常与 gRPC 一起使用。

1.2.1 主要特点

  • 紧凑高效:Protobuf 使用二进制格式来存储数据,相比于 JSON 或 XML,它占用更少的空间,传输更快,解析也更高效。
  • 平台和语言无关:Protobuf 支持多种语言(如 C++, Java, Python, Go 等)并能在不同平台间进行无缝传输。
  • 结构化数据:数据的定义通过 .proto 文件进行,可以清晰地指定数据的类型、字段以及数据结构。
  • 向后兼容与向前兼容:Protobuf 支持字段的增删和修改时,不影响已经存在的消息结构。新版本和旧版本可以兼容地进行通信。

1.2.2 工作原理

  1. 定义数据结构:你需要创建一个 .proto 文件来定义数据结构和服务接口。例如:
    syntax = "proto3";
    
    message Person {
      string name = 1;
      int32 id = 2;
      string email = 3;
    }
    
    service Greeter {
      rpc SayHello (Person) returns (Person);
    }
    
  2. 编译 .proto 文件:使用 protoc 编译器,将 .proto 文件转化为目标语言的代码。

  3. 生成代码:Protobuf 会根据 .proto 文件生成语言特定的类和方法,可以通过这些类和方法来序列化、反序列化数据。

  4. 使用 gRPC:如果你定义了服务接口,gRPC 会根据 .proto 文件生成客户端和服务器端代码,帮助你构建高效的通信。

1.2.3 为什么结合使用 gRPC 和 Protobuf?

gRPC 通常与 Protobuf 一起使用,因为它们可以提供以下优势:

  • 性能:Protobuf 的二进制格式非常高效,适合需要高性能的场景,尤其是微服务通信时。
  • 简洁性:gRPC 和 Protobuf 的结合可以让你轻松地实现分布式服务,而无需担心数据格式的兼容性、传输效率等问题。
  • 自动化:通过 Protobuf 定义的数据结构和 gRPC 服务接口,所有的代码生成和数据序列化/反序列化都自动化处理,极大地减少了开发工作量。
  • gRPC 是一个高效的远程过程调用框架,用于不同系统之间的通信,支持多种语言,并且支持高效的流式传输。
  • Protobuf 是一种高效的序列化数据格式,通常用于 gRPC 中,能够减少数据的存储空间,提高传输效率。

这两者结合使用,可以构建出高效、可靠且跨语言的分布式系统。

二、RPC 有四种需求

2.1 简单模式

       这种模式最为传统,即客户端发起一次请求,服务端响应一个数据,这个大家平时熟悉的RPC没有大的区别,所以不再详细介绍。

 2.1.1 proto 文件

syntax = "proto3";

package helloworld;

// 指定 Go 包的路径
option go_package = ".;proto";

// 这是一个rpc服务
service Hello {
    rpc Hello(HelloRequest) returns (HelloResponse);
}

message HelloRequest {
    string name = 1; // 1 是编号
}

message HelloResponse {
    string reply = 1;
}

       我们在编译 proto 文件的时候,我们需要先将安装了最新版本的 protoc-gen-goprotoc-gen-go-grpc 插件。如果你还没有安装或需要更新它们,可以使用以下命令:

# 安装最新的 protoc-gen-go 插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

# 安装最新的 protoc-gen-go-grpc 插件
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

       我们需要生成普通Go代码的时候,使用 protoc 命令时,命令格式如下,我们会根据 helloworld.proto 文件生成普通的 Go 代码文件。

protoc -I . helloworld.proto --go_out=.

       我们需要生成gRPC代码的时候,使用 protoc 命令时,命令格式如下,我们会使用 --go-grpc_out 来生成 gRPC 相关的代码(包括客户端和服务端的 gRPC 代码):

protoc -I . helloworld.proto --go-grpc_out=.

       我们可以使用完整的命令行,来一次性生成所有的代码(普通的 Go 代码和 gRPC 相关的代码),你可以将两个命令合并成一个:

protoc -I . helloworld.proto --go_out=. --go-grpc_out=.

2.1.2 server端

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"log"
	"net"
	pb "xuego/protobuf/helloworld/proto"
)

type Server struct {
	pb.UnimplementedHelloServer // 继承自动生成的 Unimplemented 方法,可以确保将来不破坏接口兼容性
}

// SayHello 实现了 Greeter 服务的 SayHello 方法
func (s *Server) Hello(ctx context.Context, request *pb.HelloRequest) (*pb.HelloResponse, error) {
	// 构造并返回响应
	return &pb.HelloResponse{
		Reply: "Hello " + request.Name,
	}, nil
}

func main() {
	// 创建一个新的 gRPC 服务器
	g := grpc.NewServer()

	// 注册 Greeter 服务到 gRPC 服务器
	pb.RegisterHelloServer(g, &Server{})

	// 定义服务器监听地址和端口
	listen, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	// 输出日志,表明服务器开始监听
	fmt.Println("Server is listening on port :50051")

	// 启动 gRPC 服务器
	if err := g.Serve(listen); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

2.1.3 client端

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"xuego/protobuf/helloworld/proto"
)

func main() {
	conn, err := grpc.Dial("localhost:8080", grpc.WithInsecure())
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	c := proto.NewHelloClient(conn)
	r, err := c.Hello(context.Background(), &proto.HelloRequest{Name: "xuego"})
	if err != nil {
		panic(err)
	}
	fmt.Errorf(r.Reply)
}

2.2 服务端数据流模式

       这种模式是客户端发起一次请求,服务端返回一段连续的数据流。典型的例子是客户端向服务端发送一个股票代码,服务端就把该股票的实时数据源源不断的返回给客户端。

2.3 客户端数据流模式

       与服务端数据流模式相反,这次是客户端源源不断的向服务端发送数据流,而在发送结束后,由服务端返回一个响应。典型的例子就是物联网终端向服务器报发数据。

2.4 双向数据流模式

       顾名思义,这是客户端和服务端都可以向对方发送数据流,这个时候双方的数据可以同时互相发送,也就是可以实现实时交互。典型的例子就是聊天机器人。

2.5 服务端数据流模式

&& 客户端数据流模式

&& 双向数据流模式

2.5.1 proto 文件

syntax = "proto3";

option go_package = ".;proto";

service Greeter {
    rpc GetStream(StreamReqData) returns (stream StreamResData); // 服务端流模式
    rpc PutStream(stream StreamReqData) returns (StreamResData); // 客户端流模式
    rpc AllStream(stream StreamReqData) returns (stream StreamResData); // 双向流模式
}

message StreamReqData {
    string data = 1;
}

message StreamResData {
    string data = 1;
}

在这些模式下,他们在服务端中写的函数的参数与简单模式的不同,需要注意一下!!!!

2.5.2 server端

package main

import (
	"fmt"
	"google.golang.org/grpc"
	"net"
	"sync"
	"time"
	pb "xuego/stream_grpc_test/proto"
)

const PORT = ":50052"

type Server struct {
	pb.UnimplementedGreeterServer
}

func (s *Server) GetStream(req *pb.StreamReqData, res pb.Greeter_GetStreamServer) error {
	i := 0
	for {
		i++
		_ = res.Send(&pb.StreamResData{
			Data: fmt.Sprintf("%v", time.Now().Unix()),
		})
		time.Sleep(time.Second)
		if i > 10 {
			break
		}
	}
	return nil
}

func (s *Server) PutStream(cliStr pb.Greeter_PutStreamServer) error {
	for {
		if a, err := cliStr.Recv(); err != nil {
			fmt.Println(err)
			break
		} else {
			fmt.Println(a.Data)
		}
	}
	return nil
}

func (s *Server) AllStream(allStr pb.Greeter_AllStreamServer) error {
	wg := sync.WaitGroup{}
	wg.Add(2)
	go func() {
		defer wg.Done()
		for {
			data, _ := allStr.Recv()
			fmt.Println("收到客户端下消息:" + data.Data)
		}
	}()
	go func() {
		defer wg.Done()
		for {
			_ = allStr.Send(&pb.StreamResData{Data: "我是服务器"})
			time.Sleep(time.Second)
		}
	}()
	wg.Wait()
	return nil
}

func main() {
	lis, err := net.Listen("tcp", PORT)
	if err != nil {
		panic(err)
	}
	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, &Server{})
	err = s.Serve(lis)
	if err != nil {
		panic(err)
	}
}

2.5.3 client端

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"sync"
	"time"
	pb "xuego/stream_grpc_test/proto"
)

func main() {
	conn, err := grpc.Dial("localhost:50052", grpc.WithInsecure())
	if err != nil {
		panic(err)
	}
	defer conn.Close()
	// 服务端流模式
	c := pb.NewGreeterClient(conn)
	res, _ := c.GetStream(context.Background(), &pb.StreamReqData{Data: "陌客网"})
	for {
		a, err := res.Recv()
		if err != nil {
			fmt.Println(err)
			break
		}
		fmt.Println(a.Data)
	}

	// 客户端流模式
	putS, _ := c.PutStream(context.Background())
	i := 0
	for {
		i++
		putS.Send(&pb.StreamReqData{
			Data: fmt.Sprintf("慕课网%d", i),
		})
		time.Sleep(time.Second)
		if i > 10 {
			break
		}
	}

	// 双向流模式
	allStr, _ := c.AllStream(context.Background())
	wg := sync.WaitGroup{}
	wg.Add(2)
	go func() {
		defer wg.Done()
		for {
			data, _ := allStr.Recv()
			fmt.Println("收到客户端下消息:" + data.Data)
		}
	}()
	go func() {
		defer wg.Done()
		for {
			_ = allStr.Send(&pb.StreamReqData{Data: "我是客户段"})
			time.Sleep(time.Second)
		}
	}()
	wg.Wait()
}


http://www.niftyadmin.cn/n/5840547.html

相关文章

【大数据技术】案例01:词频统计样例(hadoop+mapreduce+yarn)

词频统计(hadoop+mapreduce+yarn) 搭建完全分布式高可用大数据集群(VMware+CentOS+FinalShell) 搭建完全分布式高可用大数据集群(Hadoop+MapReduce+Yarn) 在阅读本文前,请确保已经阅读过以上两篇文章,成功搭建了Hadoop+MapReduce+Yarn的大数据集群环境。 写在前面 Wo…

【贪心算法篇】:“贪心”之旅--算法练习题中的智慧与策略(二)

✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨ ✨ 个人主页:余辉zmh–CSDN博客 ✨ 文章所属专栏:贪心算法篇–CSDN博客 文章目录 前言例题1.买卖股票的最佳时机2.买卖股票的最佳时机23.k次取…

git安装flutter

首先设置 Flutter 的镜像环境变量(在 PowerShell 中运行): # 设置 Flutter 镜像 $env:PUB_HOSTED_URL"https://pub.flutter-io.cn" $env:FLUTTER_STORAGE_BASE_URL"https://storage.flutter-io.cn"# 将这些环境变量永久…

vscode+vue3+高得地图开发过过程中本地视频及地图json文件的发布问题

很久没发blog了,最近vscodevue3高得地图开发中,因为有开发的视频教程,还有地图的边界的.json文件,这些静态文件发布时,如果处理不当,build命令会将这些静态文件进行打包。打包后文件名变化了,这…

97,【5】buuctf web [极客大挑战 2020]Greatphp

进入靶场 审代码 <?php // 关闭所有 PHP 错误报告&#xff0c;防止错误信息泄露可能的安全隐患 error_reporting(0);// 定义一个名为 SYCLOVER 的类 class SYCLOVER {// 定义类的公共属性 $sycpublic $syc;// 定义类的公共属性 $loverpublic $lover;// 定义魔术方法 __wa…

Maven全解析:从基础到精通的实战指南

概念&#xff1a; Maven 是跨平台的项目管理工具。主要服务基于 Java 平台的构建&#xff0c;依赖管理和项目信息管理项目构建&#xff1a;高度自动化&#xff0c;跨平台&#xff0c;可重用的组件&#xff0c;标准化的流程 依赖管理&#xff1a; 对第三方依赖包的管理&#xf…

Windows11 不依赖docker搭建 deepseek-R1 1.5B版本(附 Open WebUi搭建方式)

零、前言 过年这几天发现 DeepSeek 非常火&#xff0c;试用了一下发现确实不错。与豆包、kimi、perplexity 这些相比完全不是一个次元的存在&#xff0c;特别是用ta写文章的时候体验非常好。所以试着自己搭一个环境。 一、安装 Ollama和DeepSeek-R1 我的安装方式很简单&#xf…

AWS EMR上的Spark日志实时搜索关键指标网页呈现的设计和实现

为了在AWS EMR上实现基于Spark的大数据日志处理系统&#xff0c;并通过Kafka、ElasticSearch和Python Flask构建实时搜索与可视化平台&#xff0c;以下是详细的设计与实现方案&#xff1a; 一、架构设计 #mermaid-svg-yDbFJA2AyZXqUInz {font-family:"trebuchet ms",…