golang grpc配置使用实战教程

news2024/9/25 11:17:43

什么是PRC&GRPC

RPC是远程过程调用(Remote Procedure Call)的缩写形式, RPC 的主要功能目标是让构建分布式计算(应用)更容易,在提供强大的远程调用能力时不损失本地调用的语义简洁性。通俗地讲,使用RPC进行通信,调用远程函数就像调用本地函数一样,RPC底层会做好数据的序列化与传输。 下图是dubbo rpc实现的图解,以便于大家理解RPC:
在这里插入图片描述
GRPC是rpc框架的实现,是一个基于Protobuf序列化协议开发的高性能,开源和通用的RPC框架,且支持众多开发语言。

在这里插入图片描述
从图中还可以看出,proto文件的编译支持多种语言(Go、Java、Python等),可以轻松实现跨语言调用。

RPC调用之前需要进行IDL文件定义编写和对应语言调用模板方法生成(protoc自动生成)

RPC调用大致步骤:

  1. 客户端建立连接(gRPC Stub)并调用A方法,发起RPC调用
  2. gRPC框架对请求信息使用Protobuf进行对象序列化压缩(IDL)
  3. 服务端(gPRC Server)接收到请求后,解码反序列化,进行业务逻辑处理并返回。
  4. 对响应结果使用Protobuf进行对象序列化压缩(IDL)
  5. 客户端接受到服务端响应,解码发序列化。回调被调用的A方法,唤醒正在等待响应(阻塞)的客户端调用并返回响应结果。

Go gRPC 环境准备

本人是在WSL环境(window linux 子系统)进行的,window 和 mac 可以自行尝试,原理和步骤都一样。

  1. Go 语言环境安装,下载对应的安装包,配置GOPATH、GOROOT、GOPROXY,以及GO111MODULE 设置为on,具体安装和配置细节可参考官网和其他教程,这里列出自己的go env信息:
# GO111MODULE on模式
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/lizheng/.cache/go-build"
GOENV="/home/lizheng/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/lizheng/gopath/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
# GOPATH 配置
GOPATH="/home/lizheng/gopath"
GOPRIVATE=""
# GOPROXY 配置
GOPROXY="https://goproxy.cn"
# GOROOT 配置
GOROOT="/home/lizheng/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/lizheng/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.17.7"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build1495300227=/tmp/go-build -gno-record-gcc-switches"
  1. Protocol buffer 编译器配置
    这里的Protocol buffer编译器用来编译 .proto RPC协议定义文件,自动生成对应语言的目标代码,减少开发量。安装步骤参考:protoc 安装文档
lizheng@lz-x:~$  apt install -y protobuf-compiler
lizheng@lz-x:~$ protoc --version
libprotoc 3.6.1
  1. Protocol buffer Go语言编译插件配置安装
    因为我们使用的是go语言实现grpc,所以 protoc 命令在执行编译的时候,会调用go语言插件,来生成golang代码。
lizheng@lz-x:~$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
lizheng@lz-x:~$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2

注意:这里安装完成之后,会将对应命令安装到gopath配置目录下的bin文件夹下如下图
在这里插入图片描述
我们需要确保bin下文件命令可以被全局访问到,配置到PATH中即可,如果是window需要配置到环境变量path中
在这里插入图片描述

实战编写和调用

经过上述步骤环境已经完成配置,我们开始一个helloword的程序开发,包括服务端和客户端两部分。

  1. 新建一个文件夹 my-grpc,使用 go mod init example.com/ggrpc初始化项目
  2. 建立子文件夹:client、server、proto,分别存储客户端、服务端、grpc存根文件。
  3. 进入proto,新建一个 helloworld.proto 文件,编写一下内容:
// 使用的语法协议版本 proto3
syntax = "proto3";
package proto;
// 定义生成go文件的package位置和名称
option go_package = "./;proto";

// 定义Greeter服务
service Greeter {
    // 定义SayHello方法,接受HelloRequest消息, 并返回HelloReply消息
    rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// 定义请求对象
message HelloRequest {
    string name = 1;
}

// 定义返回对象
message HelloReply {
    string message = 1;
}

上面只是一个简单的定义,细节proto语法可自行官网学习

  1. 执行protoc命令,生成目标语言文件
# \ 为命令换行但不执行,可以写一行那就不需要 \ 了
protoc --go_out=. --go_opt=paths=source_relative \
    --go-grpc_out=. --go-grpc_opt=paths=source_relative \
    helloworld.proto

执行成功会生成:helloworld.pb.go 和 helloworld_grpc.pb.go 两个文件
在这里插入图片描述
5. 执行 go mod tidy 下载依赖包,主要是grpc相关的包
6. 编写服务端和客户端代码

  • 服务端
package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net"
	"time"

	h "example.com/ggrpc/handler"
	pb "example.com/ggrpc/proto"
	"google.golang.org/grpc"
)

var (
	port = flag.Int("port", 8000, "The server port")
)
// 定义一个server实现UnimplementedGreeterServer
// UnimplementedGreeterServer 是第四步自动生成的,可以打开对应文件查看
type server struct {
	pb.UnimplementedGreeterServer
}

// server 重写SayHello方法,做业务处理
func (s *server) SayHello(c context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
	log.Printf("接收到客户端的消息: %v", req.GetName())
	time.Sleep(time.Second)
	ms := fmt.Sprintf("好的收到,%s %s", req.GetName(), time.Now())
	log.Printf("回复客户端的消息: %s", ms)
	return &pb.HelloReply{Message: ms}, nil
}

func main() {
	// 解析命令行参数
	flag.Parse()
	// 监听本地tcp端口
	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	// 创建一个grpc Server服务对象,Handler非必传
	// s := grpc.NewServer() // 可以直接创建对象
	s := grpc.NewServer(grpc.StatsHandler(&h.MyHandler{}))
	// 注册服务
	pb.RegisterGreeterServer(s, &server{})
	// 启动RPC并监听
	log.Printf("server listening at %v", lis.Addr())
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

  • 客户端
package main

import (
	"context"
	"flag"
	"log"
	"time"

	pb "example.com/ggrpc/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

var serAddr = flag.String("addr", "localhost:8000", "the address to connect to")

func main() {
	// 解析命令行参数
	flag.Parse()

	// 连接服务端
	conn, err := grpc.Dial(*serAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatalf("连接服务器失败: %v", err)
	}
	log.Printf("建立连接成功: %s", *serAddr)
	// 执行完方法自动关闭资源
	defer conn.Close()

	// 创建客户端
	c := pb.NewGreeterClient(conn)

	log.Println("5秒中之后调用SayHello方法")
	time.Sleep(time.Second * 5)
	// 创建2秒超时ctx
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
	defer cancel()
	// 发起RPC请求
	log.Println("开始调用SayHello方法")
	res, err := c.SayHello(ctx, &pb.HelloRequest{Name: "一号"})
	if err != nil {
		log.Fatalf("请求失败: %v", err)
	}
	log.Printf("请求结果: %s", res.GetMessage())

	// 睡眠一会再结束
	log.Println("3秒后结束,客户端自动断开连接")
	time.Sleep(time.Second * 3)

}

因为本人测试加了一个server端的handler,给出handle的代码,可以忽略

package handler

import (
	"context"
	"log"

	"google.golang.org/grpc/stats"
)

// 自定义handler实现stats.Handler打印一些信息
type MyHandler struct {
}

func (h *MyHandler) TagRPC(c context.Context, tag *stats.RPCTagInfo) context.Context {
	log.Printf("TagRPC: %v", tag)
	return c
}

func (h *MyHandler) HandleRPC(c context.Context, s stats.RPCStats) {
	log.Printf("HandleRPC: %v", s)
}

func (h *MyHandler) TagConn(c context.Context, tag *stats.ConnTagInfo) context.Context {
	log.Printf("TagConn: %v", tag)
	return c
}
func (h *MyHandler) HandleConn(c context.Context, s stats.ConnStats) {
	log.Printf("HandleConn: %v", s)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/498010.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软考 软件设计师 数据结构

大O表示法 常数阶,他的次数不会随着n的变大而变长 抓大头 取次方最大的 时间复杂度 没有循环 没有递归没有跟n相关的东西,那么他的复杂度就是o(1) 为什么ii*2那里会加1阿? 因为需要加一次才能跳出循环1 2 4 8 中间加…

有趣的回文检测

英文中有很多的回文词,回文词的拼法十分有趣,无论是从前往后拼读,还是从后往前拼读,他们的拼法和词义都不变。例如:dad(爸爸),mum(妈妈),noon&…

flac格式怎么转换mp3格式?

flac格式怎么转换mp3格式?什么是flac格式呢?通常来说,flac是一种无损音频压缩编码。flac格式主要特点就是无损压缩。对于flac格式而言,与其他有损压缩编码不同,比如与aac、mp3等相较而言,flac对原有的音频信…

PySide6/PyQT多线程之 生命周期:从创建到销毁的完整解析

前言 在PySide6/PyQT 中使用多线程时,多线程生命周期是一个重要的概念。如果不能正确地管理多线程生命周期,可能会导致程序崩溃、内存泄漏等问题。 在前面的文章中有介绍到 PySide6/PyQT 可以实现多线程的多个类, 有 QObject、QThread、QRun…

【C++刷题笔记】继承和多态常见面试题汇总

对C继承和多态方面的部分面试题进行了汇总 一、概念考察 1. 下面哪种面向对象的方法可以让你变得富有( ) A: 继承 B: 封装 C: 多态 D: 抽象 2. ( )是面向对象程序设计语言中的一种机制。这种机制实现了方法的定义与具体的对象无关,而对方法的调用则可以关联于具体…

双目测距--4 双目立体匹配 获取深度图

在这之前需要已经完成双目标定,这里是利用双目标定结果利用SGBM算法获取深度图,以及转伪彩图。 目录 StereoSGBM用到的参数: 一、 预处理参数 二 、代价参数 三 、动态规划参数 四、后处理参数 reprojectImageTo3D函数 获取真实距离 …

ChatGPT - 快速生成 流程图

文章目录 Prompt输出Copy 到 drawio Prompt 我想做一个研发标准化的流程,但是我是一个小白,不懂研发管理的流 程,我希望你作为一个经验丰富的技术管理人员,请帮我梳理一个完整流程,包括需求分析、概要设计,代码走查等等,输出的节点不少于18个,包含逻辑判断的分支,要通循实事求…

【SpringCloud微服务实践】服务注册与发现

注册与发现 在之前的示例中,采取的是硬编码的方式,需要调用的微服务的地址是被我们写死在文件或代码中的。在传统应用程序中,一般都是这么做的,然而这种方式存在不少缺陷: 静态配置:因为是写死的网络地址…

力扣sql中等篇练习(十五)

力扣sql中等篇练习(十五) 1 页面推荐 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 a 示例输入 b 示例输出 1.2 示例sql语句 # ①找到1所对应的朋友 ②找到其朋友喜欢的页面 ③删选掉自己喜欢的页面 # 可能朋友中存在喜欢同样的界面 SELECT distinct page_id reco…

Java每日一练(20230507) 组合总和、缺失的正数、单词搜索II

目录 1. 组合总和 🌟🌟 2. 缺失的第一个正数 🌟🌟🌟 3. 单词搜索 II 🌟🌟🌟 🌟 每日一练刷题专栏 🌟 Golang每日一练 专栏 Python每日一练 专栏 C/…

数字化转型导师坚鹏:企业干部如何进行数字化转型

企业干部如何进行数字化转型 ——数字化转型背景下重塑企业干部核心竞争力 授课背景: 很多银行存在以下问题: 企业干部不知道如何进行数字化转型? 企业干部不清楚银行数字化能力模型的内涵? 企业干部不知道如何通过数字化…

总结843

学习目标: 5月(张宇强化18讲,背诵25篇短文,熟词僻义300词基础词) 每日必复习(5分钟) 做记录本上3道题 学习内容: 暴力英语:回环诵读,继续背一篇阅读理解&…

前端实战项目:网易云静态页面——轮播图

前言 目前这个前端项目持续更新中~ 网易云静态页面——导航栏 Flex布局 文章目录 前言实现目标静态实现页面大致样子添加模糊的背景图添加图片下载客户端部分轮播图小圆点第一个小圆点变成红色以及当鼠标放上其他任一小圆点也变成红色左右按钮总代码 动态实现 实现目标 建立相…

Shift_RAM ip核的使用——ROM调用mif文件、传至Shift_RAM输出

Altera_Shift Register(RAM-based)ip核 前言1.创建mif文件,通过ROM ip核调用该mif文件1.1创建mif文件1.2顺序填充mif文件1.3创建ROM ip核调用mif文件 2.计数器读取mif文件中的数据2.1写一个0-15的循环计数器2.2实例化ROM ip核、调用计数器模块…

【C++学习】类和对象--多态

多态的基本语法 多态是C面向对象三大特性之一 静态多态: 函数重载和运算符重载属于静态多态,复用函数名(函数地址早绑定,编译阶段确定函数地址) 动态多态: 派生类和虚函数实现运行时多态(函数地…

Python |浅谈爬虫的由来

本文概要 本篇文章主要介绍Python爬虫的由来以及过程,适合刚入门爬虫的同学,文中描述和代码示例很详细,干货满满,感兴趣的小伙伴快来一起学习吧! 🌟🌟🌟个人简介🌟&…

快速搭建一个spring入门案例及整合日志

目录 环境要求 构建模块 程序开发 引入依赖 创建java类 创建配置文件 创建测试类测试 运行测试程序 程序分析 spring中配置启用Log4j2日志框架 Log4j2日志概述 引入Log4j2依赖 加入日志配置文件 测试 使用日志 环境要求 JDK:Java17(Spring6要…

Linux高性能服务器编程|阅读笔记:第6章 - 高级I/O函数

目录 简介6.1 pipe函数6.2 dup函数和dup2函数6.3 readv函数和writev函数6.4 sendfile函数6.5 mmap函数和munmap函数6.6 splice函数6.7 tee函数6.8 fcntl函数结语 简介 Hello! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出&…

10-Vue技术栈之脚手架配置代理(解决跨域问题)+ GitHub用户搜索案例

目录 1、基本使用1.1 方法一1.2 方法二 2、GitHub用户搜索案例 1、基本使用 1.1 方法一 ​ 在vue.config.js中添加如下配置: devServer:{proxy:"http://localhost:5000" }说明: 优点:配置简单,请求资源时直接发给前…

用三角函数解决几何问题

如图,在 △ A B C \triangle ABC △ABC 中, A C > 5 , A B > A C AC>5,AB>AC AC>5,AB>AC,点 E E E 是 A B AB AB 上一点,链接 C E CE CE,将 △ B C E \triangle BCE △BCE 沿 C E CE CE 折叠&…