Go 语言如何进行 RPC 调用

简介: 云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 今天我们来了解一下 Go 语言是如何进行远程方法调用的,远程方法调用是服务间进行通信的基础方式之一,是 Go 语言实现微服务架构必须掌握的开发知识和原理。
云栖号资讯:【 点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!

今天我们来了解一下 Go 语言是如何进行远程方法调用的,远程方法调用是服务间进行通信的基础方式之一,是 Go 语言实现微服务架构必须掌握的开发知识和原理。

gRPC

gRPC 是一个高性能、开源、通用的 RPC 框架,由 Google 推出,基于HTTP/2 协议标准设计开发,默认采用 Protocol Buffers 数据序列化协议,支持多种开发语言。gRPC 提供了一种简单的方法来精确的定义服务,并且为客户端和服务端自动生成可靠代码的功能库。

我们来详细了解一下 gRPC 的众多特性:

  • gRPC 使用 ProtoBuf 来定义服务、接口和数据类型,ProtoBuf 是由 Google 开发的一种数据序列化协议(类似于XML、JSON和hessian)。ProtoBuf 能够将数据进行序列化,并广泛应用在数据存储和通信协议等方面。
  • gRPC 支持多种语言,并能够基于语言自动生成客户端和服务端代码。gRPC支持 C、C++、Node.js、Python、Ruby、Objective-C、PHP和C# 等语言,目前已提供了 C 语言版本的 gRPC、Java 语言版本的 grpc-java 和 Go 语言版本的 grpc-go,其他语言的版本正在积极开发中,其中,grpc-java 已经支持 Android 开发。

1

如上图所示为 gRPC 的调用示意图,我们可以看到,一个 C++ 语言的服务器可以通过 gRPC 分别与 Ruby 语言开发的桌面客户端和 Java 语言开发的 Android 客户端进行交互。

gRPC基于 HTTP/2 标准设计,所以相对于其他 RPC 框架,gRPC 拥有更多强大功能,如双向流、头部压缩、多复用请求等。这些功能给移动设备带来重大益处,如节省带宽、降低 TCP 连接次数、提高 CPU 利用率和延长电池寿命等。同时,gRPC 还提高了云端服务和 Web 应用的性能。gRPC 既能够在客户端应用,也能够在服务器端应用,从而以透明的方式实现客户端和服务器端的通信和简化通信系统的构建。

gRPC 的安装

首先使用 go get 命令安装 grpc-go。

go get -u google.golang.org/grpc

接着要安装插件,先使用 which protoc 命令检查是否安装了protoc;如果没有,则使用 go install 命令安装 proto 和 protoc-gen-go 两个库,最后可以使用 protoc 方法判断是否成功安装了。

----- 查看 protoc 是否安装,确保是3.0版本
$ which protoc
$ protoc --version
----- 安装插件
$ go install github.com/golang/protobuf/proto
$ go install github.com/golang/protobuf/protoc-gen-go
----- 测试是否安装成功
$ protoc -I pb/string.proto--go_out=plugins=grpc:.pb/string.proto

gRPC 过程调用实践

gRPC 过程调用时,服务端和客户端需要依赖共同的 proto 文件。proto 文件可以定义远程调用的接口、方法名、参数和返回值等。通过 proto 文件可以自动生成客户端和客户端的相应 RPC 代码。借助这些代码,客户端可以十分方便地发送 RPC 请求,并且服务端也可以很简单地建立 RPC 服务器,处理 RPC 请求并且将返回值作为响应发送给客户端。

定义和编译 proto 文件

首先,我们要定义一个 proto 文件,其具体语法请查看 Protobuf3 语言指南。在该文件中,我们定义了两个参数结果,分别是 StringRequest 和 StringResponse,同时还有一个服务结构 StringService,代码如下:

syntax = "proto3";
package pb;
service StringService{
rpc Concat(StringRequest) returns (StringResponse) {}
rpc Diff(StringRequest) returns (StringResponse) {}
}

message StringRequest {
string A = 1;
string B = 2;
}

message StringResponse {
string Ret = 1;
string err = 2;
} 

StrtingService 有两个方法,分别为 Concat 和 Diff,每个方法都有对应的输入参数和返回值,这些值也都定义在 proto 文件中。

gRPC 可以定义 4 种类型的服务接口,分别是一元 RPC、服务器流 RPC、客户端流式 RPC 和双向流 RPC。

(1)一元 RPC 是指客户端向服务器发送请求并获得响应,就像正常的函数调用一样。

rpc Concat(StringRequest) returns (StringResponse) {} 

(2)服务器流 RPC 是指客户端发送一个对象,服务器端返回一个Stream(流式消息)。

rpc LotsOfServerStream(StringRequest) returns (stream StringResponse) {} 

(3)客户端流式 RPC,客户端发送一个 Stream(流式消息)服务端返回一个对象。

rpc LotsOfClientStream(stream StringRequest) returns (StringResponse) {} 

(4)双向流 RPC,两个流独立运行,客户端和服务器可以按照它们喜欢的顺序进行读取和写入;例如,服务器可以在写入响应之前等待接收所有客户端消息,也可以交替地进行消息的读取和写入,或读取和写入的其他组合。每个流中消息的顺序被保留。类似于 WebSocket(长连接),客户端可以向服务端请求消息,服务器端也可以向客户端请求消息。

rpc LotsOfServerAndClientStream(stream StringRequest) returns (stream StringResponse) {} 

接下来我们使用 protoc 编译工具编译这个protoc文件,生成服务端和客户端的代码,如下:

protoc --go_out=plugins=grpc:. pb/string.proto

从 proto 文件中的服务定义开始,gRPC 提供了生成客户机和服务器端代码的 protocol buffer 编译器插件。gRPC 用户通常在客户端调用这些 API,并在服务器端实现相应的 API。

在服务器端,服务器实现服务声明的方法,并运行 gRPC 服务器来处理客户端调用。gRPC 框架会接受网络传入请求,解析请求数据,执行相应服务方法和将方法结果编码成响应通过网络传递给客户端。客户端的本地定义方法,其方法名、参数和返回值与服务端定义的方法相同。客户端可以直接在本地对象上调用这些方法,将调用的参数包含在对应的 protocol buffer 消息类型中,gRPC再将请求发送到服务端,服务端解析请求。

客户端发送 RPC 请求

我们先来看客户端代码,首先调用 grpc.Dial 建立网络连接,然后使用 protoc 编译生成的 pb.NewStringServiceClient 函数创建 gRPC 客户端,然后调用客户端的 Concat 函数,进行RPC调用,代码如下所示:

package grpc
import (
"context"
"fmt"
"github.com/keets2012/Micro-Go-Pracrise/ch9-rpc/pb"
"google.golang.org/grpc"
)

func main() {
serviceAddress := "127.0.0.1:1234"
conn, err := grpc.Dial(serviceAddress, grpc.WithInsecure())
if err != nil {
    panic("connect error")
}

defer conn.Close()
stringClient := pb.NewStringServiceClient(conn)
stringReq := &pb.StringRequest{A: "A", B: "B"}
reply, _ := stringClient.Concat(context.Background(), stringReq)
fmt.Printf("StringService Concat : %s concat %s = %s", 
stringReq.A, stringReq.B, reply.Ret)
} 

服务端建立 RPC 服务

再来看看服务器端的代码,它首先需要调用 grpc.NewServer() 来建立RPC的服务端,然后将 StringService 注册到RPC服务端上,其具有的两个函数分别处理 Concat 和 Diff 请求,代码如下:

func main() {
flag.Parse()
lis, err := net.Listen("tcp","127.0.0.1:1234")
if err != nil {
    log.Fatalf("failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
stringService := new(string_service.StringService)
pb.RegisterStringServiceServer(grpcServer, stringService)
grpcServer.Serve(lis)
} 

最后我们来看 StringService 的具体代码实现,它首先定义了StringService 结构体,然后实现了它的 Concat 方法和 Diff 方法。

type StringService struct{}

func (s * StringService) Concat(ctx context.Context, req *pb.StringRequest) (*pb.StringResponse, error) {
if len(req.A)+len(req.B) > StrMaxSize {
    response := pb.StringResponse{Ret: ""}
    return &response, nil
}
response := pb.StringResponse{Ret: req.A + req.B}
return &response, nil
}

func (s * StringService) Diff(ctx context.Context, req *pb.StringRequest) (*pb.StringResponse, error) {
if len(req.A) < 1 || len(req.B) < 1 {
    response := pb.StringResponse{Ret: ""}
    return &response, nil
}
res := ""
if len(req.A) >= len(req.B) {
    for _, char := range req.B {
        if strings.Contains(req.A, string(char)) {
            res = res + string(char)
        }
    }
} else {
    for _, char := range req.A {
        if strings.Contains(req.B, string(char)) {
            res = res + string(char)
        }
    }
}
response := pb.StringResponse{Ret: res}
return &response, nil
} 

如上代码所示,StringService 的 Concat 方法和 Diff 方法实现起来都很简单,Concat 方法就是将 StringRequest 中的 A 和 B 字符拼接在一起;而 Diff 方法则是通过循环遍历,将 A 和 B 字符的差异部分计算出来。

从上面的讲述可以看出,客户端发送一个请求后,必须等待服务器发回响应才能继续发送下一个请求,这种交互模式具有一定局限性,它无法更好地利用网络带宽,传递更多的请求或响应。而 gRPC 支持流式的请求响应模式来优化解决这一问题。

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址: https://yqh.aliyun.com/live

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

相关文章
|
2天前
|
JavaScript Java Go
探索Go语言在微服务架构中的优势
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出。本文将深入探讨Go语言在构建微服务时的性能优势,包括其在内存管理、网络编程、并发模型以及工具链支持方面的特点。通过对比其他流行语言,我们将揭示Go语言如何成为微服务架构中的一股清流。
|
1天前
|
Ubuntu 编译器 Linux
go语言中SQLite3驱动安装
【11月更文挑战第2天】
16 7
|
1天前
|
关系型数据库 Go 网络安全
go语言中PostgreSQL驱动安装
【11月更文挑战第2天】
15 5
|
1天前
|
SQL 关系型数据库 MySQL
go语言数据库中mysql驱动安装
【11月更文挑战第2天】
11 4
|
1天前
|
存储 设计模式 安全
Go语言中的并发编程:从入门到精通###
本文深入探讨了Go语言中并发编程的核心概念与实践技巧,旨在帮助读者从理论到实战全面掌握Go的并发机制。不同于传统的技术文章摘要,本部分将通过一系列生动的案例和代码示例,直观展示Go语言如何优雅地处理并发任务,提升程序性能与响应速度。无论你是Go语言初学者还是有一定经验的开发者,都能在本文中找到实用的知识与灵感。 ###
|
1天前
|
安全 Go
用 Zap 轻松搞定 Go 语言中的结构化日志
在现代应用程序开发中,日志记录至关重要。Go 语言中有许多日志库,而 Zap 因其高性能和灵活性脱颖而出。本文详细介绍如何在 Go 项目中使用 Zap 进行结构化日志记录,并展示如何定制日志输出,满足生产环境需求。通过基础示例、SugaredLogger 的便捷使用以及自定义日志配置,帮助你在实际开发中高效管理日志。
8 1
|
2天前
|
关系型数据库 MySQL 数据库连接
go语言中打开数据库连接
【11月更文挑战第1天】
13 2
|
6月前
|
设计模式 负载均衡 网络协议
【分布式技术专题】「分布式技术架构」实践见真知,手把手教你如何实现一个属于自己的RPC框架(架构技术引导篇)
【分布式技术专题】「分布式技术架构」实践见真知,手把手教你如何实现一个属于自己的RPC框架(架构技术引导篇)
253 0
|
9天前
|
自然语言处理 负载均衡 API
gRPC 一种现代、开源、高性能的远程过程调用 (RPC) 可以在任何地方运行的框架
gRPC 是一种现代开源高性能远程过程调用(RPC)框架,支持多种编程语言,可在任何环境中运行。它通过高效的连接方式,支持负载平衡、跟踪、健康检查和身份验证,适用于微服务架构、移动设备和浏览器客户端连接后端服务等场景。gRPC 使用 Protocol Buffers 作为接口定义语言,支持四种服务方法:一元 RPC、服务器流式处理、客户端流式处理和双向流式处理。
|
3月前
|
Dubbo 网络协议 Java
RPC框架:一文带你搞懂RPC
这篇文章全面介绍了RPC(远程过程调用)的概念、原理和应用场景,解释了RPC如何工作以及为什么在分布式系统中广泛使用,并探讨了几种常用的RPC框架如Thrift、gRPC、Dubbo和Spring Cloud,同时详细阐述了RPC调用流程和实现透明化远程服务调用的关键技术,包括动态代理和消息的编码解码过程。
RPC框架:一文带你搞懂RPC