当前位置: 首页 > news >正文

Go网络编程-RPC程序设计

gRPC 通信

RPC 介绍

RPC, Remote Procedure Call,远程过程调用。与 HTTP 一致,也是应用层协议。该协议的目标是实现:调用远程过程(方法、函数)就如调用本地方法一致。

如图所示:

说明:

  • ServiceA 需要调用 ServiceB 的 FuncOnB 函数,对于 ServiceA 来说 FuncOnB 就是远程过程

  • RPC 的目的是让 ServiceA 可以像调用 ServiceA 本地的函数一样调用远程函数 FuncOnB,也就是 ServieA 上代码层面使用:serviceB.FuncOnB() 即可完成调用

  • RPC 是 C/S 模式,调用方为 Client,远程方为 Server

  • RPC 把整体的调用过程,数据打包、网络请求等,封装完毕,在 C、S 两端的 Stub 中。Stub(代码存根)

  • 调用流程如下

    1. ServiceA 将调回需求告知 Client Sub

    2. Client Sub 将调用目标(Call ID)、参数数据(params)等调用信息进行打包(序列化),并将打包好的调用信息通过网络传输给 Server Sub

    3. Server Sub 将根据调用信息,调用相应过程。期间涉及到数据的拆包(反序列化)等操作。

    4. 远程过程 FuncOnB 运行,并得到结果,将结果告知 Server Sub

    5. Server Sub 将结果打包,并传输回给 Client Sub

    6. Client Sub 将结果拆包,把最终函数调用的结果告知 ServiceA

以上就是典型 RPC 的流程。

RPC 协议没有对网络层做规范,那也就意味着具体的 RPC 实现可以基于 TCP,也可以基于其他协议,例如 HTTP,UDP 等。RPC 也没有对数据传输格式做规范,也就是逻辑层面,传输 JSON、Text、protobuf 都可以。这些都要看具体的 RPC 产品的实现。广泛使用的 RPC 产品有 gRPC,Thrift 等。

gRPC 介绍

gPRC 官网(https://grpc.io/)上的 Slogan 是:A high performance, open source universal RPC framework。就是:一个高性能、开源的通用 RPC 框架。

支持多数主流语言:C#、C++、Dart、Go、Java、Kotlin、Node、Objective-C、PHP、Python、Ruby。其中 Go 支持 Windows, Linux, Mac 上的 Go 1.13+ 版本。

gRPC 是一个 Google 开源的高性能远程过程调用 (RPC) 框架,可以在任何环境中运行。它可以通过对负载平衡、跟踪、健康检查和身份验证的可插拔支持有效地连接数据中心内和跨数据中心的服务。它也适用于分布式计算的最后一步,将设备、移动应用程序和浏览器与后端服务接。

在 gRPC 中,客户端应用程序可以直接调用不同机器上的服务器应用程序的方法,就像它是本地对象一样,使您更容易创建分布式应用程序和服务。与许多 RPC 系统一样,gRPC 基于定义服务的思想,指定可以远程调用的方法及其参数和返回类型。在服务端,服务端实现这个接口并运行一个 gRPC 服务器来处理客户端调用。在客户端,客户端有一个存根(在某些语言中仅称为客户端),它提供与服务器相同的方法。

技术上,gRPC 基于 HTTP/2 通信,采用 Protocol Buffers 作数据序列化。

准备 gRPC 环境

使用 gRPC 需要:

  • Go

  • Protocol Buffer 编译器,protoc,推荐版本3

  • Go Plugin,用于 Protocol Buffer 编译器

安装 protoc:

可以使用 yum 或 apt 包管理器安装,但通常版本会比较滞后。因此更建议使用预编译的二进制安装。

下载地址:

 https://github.com/protocolbuffers/protobuf/releases

基于系统和版本找到合适的二进制下载并安装。

CentOS 演示:

 # 下载特定版本,当前(2022年08月)最新 21.4$ curl -LO https://github.com/protocolbuffers/protobuf/releases/download/v21.4/protoc-21.4-linux-x86_64.zip# 解压到特定目录$ sudo unzip protoc-21.4-linux-x86_64.zip -d /usr/local# 如果特定目录中的bin不在环境变量 path 中,手动加入 path​# 测试安装结果,注意版本应该是 3.x$ protoc --versionlibprotoc 3.21.4

Win 演示,下载,解压到指定目录,在 CMD 中运行:

 # 解压到指定目录即可,要保证 protoc/bin 位于环境变量 path 中,可以随处调用> protoc.exe --versionlibprotoc 3.21.4​

安装 Go Plugin:

 # 下载特定版本,当前(2022年08月)最新 v1.28.1> go install google.golang.org/protobuf/cmd/protoc-gen-go@latest# 下载特定版本,当前(2022年08月)最新 v1.2.0> go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest# 安装完毕后,要保证 $GOPATH/bin 位于环境变量 path 中​# 测试安装结果> protoc-gen-go --versionprotoc-gen-go.exe v1.28.1> protoc-gen-go-grpc --versionprotoc-gen-go-grpc 1.2.0
Protocol Buffer 的基础使用

默认情况下,gRPC 使用 Protocol Buffers,这是 Google 用于序列化结构化数据的成熟开源机制(尽管它可以与 JSON 等其他数据格式一起使用)。

Protocol Buffers 的文档:https://developers.google.com/protocol-buffers/docs/overview

使用 Protocol Buffers 的基本步骤是:

  1. 使用 protocol buffers 语法定义消息,消息是用于传递的数据

  2. 使用 protocol buffers 语法定义服务,服务是 RPC 方法的集合,来使用消息

  3. 使用 Protocol Buffer编 译工具 protoc 来编译,生成对应语言的代码,例如 Go 的代码

使用 Protocol Buffers 的第一步是在 .proto 文件中定义序列化的数据的结构,.proto 文件是普通的文本文件。Protocol Buffers 数据被结构化为消息,其中每条消息都是一个小的信息逻辑记录,包含一系列称为字段的 name-value 对。

除了核心内容外,.proto 文件还需要指定语法版本,目前主流的也是最新的 proto3 版本。在 .proto 文件的开头指定。

一个简单的产品信息示例:

product.proto

 syntax = "proto3";​// 定义 Product 消息message Product {string name = 1;int64 id = 2;bool is_sale = 3;}

第二步是在 .proto 文件中定义 gRPC 服务,将 RPC 方法参数和返回类型指定为 Protocol Buffers 消息,继续编辑 product.proto :

 syntax = "proto3";​// 为了生成 go 代码,需要增加 go_package 属性,表示代码所在的包。protoc 会基于包构建目录option go_package = "./proto-codes";​// 定义 ProductInfo 消息message ProductInfoResponse {string name = 1;int64 int64 = 2;bool is_sale = 3;}​// rpc 方法 ProductInfo 需要的参数消息message ProductInfoRequest {int64 int64 = 1;}​// 定义 Product 服务service Product {// 获取产品信息rpc ProductInfo (ProductInfoRequest) returns (ProductInfoResponse) {}}

第三步是使用 protoc 工具将 .proto 定义的消息和包含 rpc 方法的服务编译为目标语言的代码,我们选择 Go 代码。

 $ protoc --go_out=. --go-grpc_out=. product.proto# --go_out *.pb.go 目录# --go-grpc_out *_grpc.pb.go 目录

其中:

  • *.pb.go 包含消息类型的定义和操作的相关代码

  • *_grpc.pb.go 包含客户端和服务端的相关代码

生成的代码主要是结构上的封装,在继续使用时,还需要继续充实业务逻辑。

基于 gRPC 的服务间通信示例

示例说明,存在两个服务,订单服务和产品服务。其中:

  • 订单服务提供 HTTP 接口,用于完成订单查询。订单中包含产品信息,要利用 grpc 从产品服务获取产品信息

  • 产品服务提供 grpc 接口,用于响应微服务内部产品信息查询

本例中,对于 grpc 来说,产品服务为服务端、订单服务为客户端。

同时不考虑其他业务逻辑,例如产品服务也需要对外提供 http 接口等,仅在乎 grpc 的通信示例。同时不考虑服务发现和网关等。

image.png

编码实现:

一:基于之前定义的 .proto 文件生成 pb.go 文件

注意,客户端和服务端,都需要使用生成的 pb.go 文件

二:实现订单服务

orderService/httpService.go

 package main​import ("context""encoding/json""flag""fmt""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure""log""net/http""orderService/protos/codes""time")​var (// 目标 grpc 服务器地址gRPCAddr = flag.String("grpc", "localhost:50051", "the address to connect to")// http 命令行参数addr = flag.String("addr", "127.0.0.1", "The Address for listen. Default is 127.0.0.1")port = flag.Int("port", 8080, "The Port for listen. Default is 8080."))​func main() {flag.Parse()// 连接 grpc 服务器conn, err := grpc.Dial(*gRPCAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {log.Fatalf("did not connect: %v", err)}defer conn.Close()// 实例化 grpc 客户端c := codes.NewProductClient(conn)​// 定义业务逻辑服务,假设为产品服务service := http.NewServeMux()service.HandleFunc("/orders", func(writer http.ResponseWriter, request *http.Request) {// 调用 grpc 方法,完成对服务器资源请求ctx, cancel := context.WithTimeout(context.Background(), time.Second)defer cancel()r, err := c.ProductInfo(ctx, &codes.ProductInfoRequest{Int64: 42,})if err != nil {log.Fatalln(err)}​resp := struct {ID       int                          `json:"id"`Quantity int                          `json:"quantity"`Products []*codes.ProductInfoResponse `json:"products"`}{9527, 1,[]*codes.ProductInfoResponse{r,},}respJson, err := json.Marshal(resp)if err != nil {log.Fatalln(err)}writer.Header().Set("Content-Type", "application/json")_, err = fmt.Fprintf(writer, "%s", string(respJson))if err != nil {log.Fatalln(err)}})​// 启动监听address := fmt.Sprintf("%s:%d", *addr, *port)fmt.Printf("Order service is listening on %s.\n", address)log.Fatalln(http.ListenAndServe(address, service))}

三,实现产品服务

productService/grpcService.go

package mainimport ("context""flag""fmt""google.golang.org/grpc""log""net""productService/protos/compiles"
)//grpc 监听端口
var port = flag.Int("port", 50051, "The server port")// ProductServer 实现 UnimplementedProductServer
type ProductServer struct {compiles.UnimplementedProductServer
}func (ProductServer) ProductInfo(ctx context.Context, pr *compiles.ProductInfoRequest) (*compiles.ProductInfoResponse, error) {return &compiles.ProductInfoResponse{Name:   "马士兵 Go 云原生",Int64:  42,IsSale: true,}, 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 Servers := grpc.NewServer()// 将 ProductServer 注册到 grpc Server 中compiles.RegisterProductServer(s, ProductServer{})log.Printf("server listening at %v", lis.Addr())// 启动监听if err := s.Serve(lis); err != nil {log.Fatalf("failed to serve: %v", err)}
}

测试,访问 order 的 http 接口。获取订单信息中,包含产品信息。

gRPC 核心概念

相关文章:

Go网络编程-RPC程序设计

gRPC 通信 RPC 介绍 RPC, Remote Procedure Call,远程过程调用。与 HTTP 一致,也是应用层协议。该协议的目标是实现:调用远程过程(方法、函数)就如调用本地方法一致。 如图所示: 说明: Servi…...

Linux 性能优化:轻松入门

文章目录 前言一、磁盘性能优化1、 磁盘 RAID 模式选择2、文件系统优化 二、优化 CPU1、性能监控 :2、进程优先级调整 :3、进程与 CPU 绑定 : 三、优化内存四、网络性能优化1、调整 TCP 缓冲区大小2、修改系统级别的文件描述符的数量3、调整 …...

C++相关概念和易错语法(22)(final、纯虚函数、继承多态难点)

1.final final在继承和多态中都可以使用,在继承中是指不想将自己被继承,在多态中是指不想该函数被重写,比较简单,下面是一些使用例子。 2.纯虚函数 当我们需要抽象一个类的时候,我们就需要用到纯虚函数。所谓抽象的类…...

状态管理的艺术:探索Flutter的Provider库

状态管理的艺术:探索Flutter的Provider库 前言 上一篇文章中,我们详细介绍了 Flutter 应用中的状态管理,以及 StatefulWidget 和 setState 的使用。 本篇我们继续介绍另一个实现状态管理的方式:Provider。 Provider优缺点 基…...

玩转HarmonyOS NEXT之IM应用首页布局

本文从目前流行的垂类市场中,选择即时通讯应用作为典型案例详细介绍HarmonyOS NEXT的各类布局在实际开发中的综合应用。即时通讯应用的核心功能为用户交互,主要包含对话聊天、通讯录,社交圈等交互功能。 应用首页 创建一个包含一列的栅格布…...

GPT-4o大语言模型优化、本地私有化部署、从0-1搭建、智能体构建

原文链接:GPT-4o大语言模型优化、本地私有化部署、从0-1搭建、智能体构建https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247608565&idx3&snd4e9d447efd82e8dd8192f7573886dab&chksmfa826912cdf5e00414e01626b52bab83a96199a6bf69cbbef7f7fe…...

记录些MySQL题集(4)

1、数据库的三范式是什么? 第一范式:列不可再分 第二范式:在第一范式的基础上,要求数据库表中的所有非主键列完全依赖于主键,而不是仅依赖于主键的一部分 第三范式:满足第二范式的基础上,所有…...

pdf提取其中一页怎么操作?提取PDF其中一页的方法

pdf提取其中一页怎么操作?需要从一个PDF文件中提取特定页码的操作通常是在处理文档时常见的需求。这种操作允许用户选择性地获取所需的信息,而不必操作整个文档。通过选择性提取页面,你可以更高效地管理和利用PDF文件的内容,无论是…...

godot使用ws

go服务端 package mainimport ("encoding/json""fmt""github.com/gorilla/websocket""net/http" )var upgrader websocket.Upgrader{ReadBufferSize: 1024,WriteBufferSize: 1024, }// 处理函数 func handleWebSocket(w http.Respo…...

Windows FFmpeg 开发环境搭建

FFmpeg 开发环境搭建 FFmpeg命令行环境搭建使用FFmpeg官方编译的库Windows编译FFmpeg1. 下载[msys2](https://www.msys2.org/#installation)2. 安装完成之后,将安装⽬录下的msys2_shell.cmd中注释掉的 rem set3. 修改pacman 镜像源并安装依赖4. 下载并编译源码 FFmpeg命令行环境…...

链路聚合概述

目录 技术背景: 基本概念: 链路聚合的工作模式: 简介: 1)Manual Load-balance(手动负载分担) 简介: 配置实施: 2)LACP(链路聚合控制协议&#xff…...

【链表】算法题(二) ----- 力扣/牛客

一、链表的回文结构 思路: 找到链表的中间节点,然后逆置链表的后半部分,再一一遍历链表的前半部分和后半部分,判断是是否为回文结构。 快慢指针找到链表的中间节点 slow指针指向的就是中间节点 逆置链表后半部分 逆置链表后半部分…...

合成复用原则

合成复用原则 (Composite Reuse Principle, CRP) 合成复用原则(Composite Reuse Principle, CRP),也被称为组合/聚合复用原则,是面向对象设计中的一条重要原则。它的核心思想是:优先使用对象组合/聚合,而不…...

安卓自带camera hal3 实例README.md翻译

最近,遇到一个这样的问题,临时了解下这个驱动实现架构和特点,翻译如下 V4L2相机HALv3 camera.v4l2库使用视频Linux 2(V4L2)接口实现了camera HAL v3。这使得它在理论上可以与各种设备配合使用,尽管V4L2的…...

ActiViz实战:ActiViz中的自己实现鼠标双击事件

文章目录 1、添加鼠标事件2、网上实现双击事件的方式3、增加双击的时间限制4、补充说明1、添加鼠标事件 已知在C#中观察者/命令模式会报错,正常添加鼠标事件如下: private void VtkInteractorStyleTest() {vtkInteractorStyle style = vtkInteractorStyle.New();style.LeftB…...

Linux-交换空间(Swap)管理

引入概念 在计算机中,硬盘的容量一般比内存大,内存(4GB 8GB 16GB 32GB 64GB…),硬盘(512GB 1T 2T…)。 冯诺依曼的现代计算机结构体系里面的存储器就是内存 内存是一种易失性存储器&#xff0c…...

扫描某个网段下存活的IP:fping

前言: 之前用arp统计过某网段下的ip,但是有可能统计不全。网络管理平台又不允许登录。想要知道当前的ip占用情况,可以使用fping fping命令类似于ping,但比ping更强大。与ping需要等待某一主机连接超时或发回反馈信息不同&#x…...

【深度学习】PyTorch框架(3):优化与初始化

1.引言 在本文中,我们将探讨神经网络的优化与初始化技术。随着神经网络深度的增加,我们会遇到多种挑战。最关键的是确保网络中梯度流动的稳定性,否则可能会遭遇梯度消失或梯度爆炸的问题。因此,我们将深入探讨以下两个核心概念&a…...

Go-知识测试-子测试

Go-知识测试-子测试 1. 介绍2. 例子3. 子测试命名规则4. 选择性执行5. 子测试并发6. testing.T.Run7. testing.T.Parallel8. 子测试适用于单元测试9. 子测试适用于性能测试10. 总结10.1 启动子测试 Run10.2 启动并发测试 Parallel 建议先看:https://blog.csdn.net/a…...

.net core IConfiguration 读 appsettings.json 数据,举例

在.NET Core中,IConfiguration 接口是用来读取配置数据的,包括从 appsettings.json 文件中读取。下面是一个如何在使用.NET Core时通过 IConfiguration 读取 appsettings.json 数据的示例。 首先,假设你的 appsettings.json 文件内容如下&am…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...

Java 加密常用的各种算法及其选择

在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

站群服务器的应用场景都有哪些?

站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...