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

相关文章
|
6天前
|
存储 Go 索引
go语言使用for循环遍历
go语言使用for循环遍历
21 7
|
9天前
|
存储 Go
go语言 遍历映射(map)
go语言 遍历映射(map)
22 2
|
10天前
|
Go 调度 开发者
Go语言中的并发编程:深入理解goroutines和channels####
本文旨在探讨Go语言中并发编程的核心概念——goroutines和channels。通过分析它们的工作原理、使用场景以及最佳实践,帮助开发者更好地理解和运用这两种强大的工具来构建高效、可扩展的应用程序。文章还将涵盖一些常见的陷阱和解决方案,以确保在实际应用中能够避免潜在的问题。 ####
|
10天前
|
测试技术 Go 索引
go语言使用 range 关键字遍历
go语言使用 range 关键字遍历
16 3
|
10天前
|
测试技术 Go 索引
go语言通过 for 循环遍历
go语言通过 for 循环遍历
19 3
|
12天前
|
安全 Go 数据处理
Go语言中的并发编程:掌握goroutine和channel的艺术####
本文深入探讨了Go语言在并发编程领域的核心概念——goroutine与channel。不同于传统的单线程执行模式,Go通过轻量级的goroutine实现了高效的并发处理,而channel作为goroutines之间通信的桥梁,确保了数据传递的安全性与高效性。文章首先简述了goroutine的基本特性及其创建方法,随后详细解析了channel的类型、操作以及它们如何协同工作以构建健壮的并发应用。此外,还介绍了select语句在多路复用中的应用,以及如何利用WaitGroup等待一组goroutine完成。最后,通过一个实际案例展示了如何在Go中设计并实现一个简单的并发程序,旨在帮助读者理解并掌
|
11天前
|
Go 索引
go语言按字符(Rune)遍历
go语言按字符(Rune)遍历
23 3
|
7月前
|
设计模式 负载均衡 网络协议
【分布式技术专题】「分布式技术架构」实践见真知,手把手教你如何实现一个属于自己的RPC框架(架构技术引导篇)
【分布式技术专题】「分布式技术架构」实践见真知,手把手教你如何实现一个属于自己的RPC框架(架构技术引导篇)
285 0
|
1月前
|
自然语言处理 负载均衡 API
gRPC 一种现代、开源、高性能的远程过程调用 (RPC) 可以在任何地方运行的框架
gRPC 是一种现代开源高性能远程过程调用(RPC)框架,支持多种编程语言,可在任何环境中运行。它通过高效的连接方式,支持负载平衡、跟踪、健康检查和身份验证,适用于微服务架构、移动设备和浏览器客户端连接后端服务等场景。gRPC 使用 Protocol Buffers 作为接口定义语言,支持四种服务方法:一元 RPC、服务器流式处理、客户端流式处理和双向流式处理。
|
4月前
|
Dubbo 网络协议 Java
RPC框架:一文带你搞懂RPC
这篇文章全面介绍了RPC(远程过程调用)的概念、原理和应用场景,解释了RPC如何工作以及为什么在分布式系统中广泛使用,并探讨了几种常用的RPC框架如Thrift、gRPC、Dubbo和Spring Cloud,同时详细阐述了RPC调用流程和实现透明化远程服务调用的关键技术,包括动态代理和消息的编码解码过程。
RPC框架:一文带你搞懂RPC