大佬教程收集整理的这篇文章主要介绍了gRPC入门—golang实现,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
RPC(Remote Procedure Call),即远程过程调用,过程就是方法,简单来说,它就是一种能够像调用本地方法一样调用远程计算机进程中的方法的技术,在这种调用中,我们不需要了解任何网络通信的细节(当然,就使用来说)
最终解决的问题:让分布式或者微服务系统中不同服务之间的调用像本地调用一样简单
调用远程服务,http 就可以完成的任务,为什么还需要 RPC 呢?需要注意,这两个并不是同一层次的概念,http 是一种传输协议,RPC 应该是比 http 更高层级的概念。完整的 RPC 实现包含有 传输协议 和 序列化协议,其中,传输协议既可以使用 http,也可以使用 TCP 等,不同的选择可以适应不同的场景
RPC 并不是一个崭新的概念,它实际上就是远程通信的一个更高层级的封装,不同传输协议和序列化协议的组合构成了不同的具体 RPC 实现,比如我们熟知的 RESTful,就是 http + JSON + 一些其他细节构成
早期有一些很流行的 RPC 实现,比如 CORBA(通用对象请求代理体系结构),Java RMI(远程方法调用),它们都用来构建和链接服务或应用程序,但是,大多数传统 RPC 实现极其复杂,因为它们构建在 TCP 之上,并且还有大量的规范限制
鉴于以上 RPC 实现的局限性,SOAP(简单对象访问协议)应运而生,SOAP 是 SOA(面向服务的架构)中的标准通信技术,能够基于任意底层通信协议进行通信,最常用的是 http,序列化协议使用的是 XML
REST(描述性状态转移)是 roa(面向资源的架构)的基础,在这种架构中,将应用程序建模为各种资源的集合,客户端可以变更这些资源的状态(增删改查)。REST 的通用实现是 http + JSON,通过 http 将应用程序建模为能够通过唯一标识符表示的资源集合,状态变更操作会采用 http 动作(GET,POST,PUT,deletE等)。实际上,REST 架构风格已经成为了各种服务间通信中非常流行的方法,但是,随着微服务大行其道以及网络交互的激增,REST 已经无法满足现代化的需求了,其主要原因是以下三个主要的局限性:
由于 REST 的局限性,出现了许多新兴的 RPC 技术,较为流行的有 gRPC、Thrift、GraphQL等
gRPC 是一个现代化的开源 RPC 框架,一开始由 google 开发,是一款语言中立、平台中立、的 RPC 系统,与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个 服务,指定能够被远程调用的 方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个gRPC 服务器来处理客户端调用,在客户端拥有一个 stub 连接服务端上的方法
gRPC 的优势是它被越来越多人采用的关键所在,主要有以下几个方面:
gRPC 也存在一定劣势,选择它用来构建应用程序时,需要注意以下三点:
go install google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
go mod init product-info
初始化模块。当然这里只是示例程序,实际场景中服务代码和客户端代码一般都不在同一个机器上,更不可能在同一个模块下了,最终目录结构如下:开发 gRPC 应用程序时,要首先定义服务接口,然后生成服务端骨架和客户端 stub,客户端通过调用其中定义的方法来访问远程服务器上的方法,服务定义都以 protocol buffers 的形式记录,也就是 gRPC 所使用的服务定义语言
// 版本
syntax = "proto3";
// proto文件所属包名
package proto;
// 声明生成的go文件所属的包,路径末尾为包名,相对路径是相对于编译生成目标代码时的工作路径
option go_package = "./proto";
// 包含两个远程方法的 rpc 服务,远程方法只能有一个参数和一个返回值
service ProducTinfo {
rpc addProduct(Product) returns (ProductID);
rpc getProduct(ProductID) returns (Product);
}
// 自定义消息类型,用这种方法传递多个参数,必须使用唯一数字标识每个字段
message Product {
String id = 1;
String name = 2;
String description = 3;
float price = 4;
}
message ProductID {
String value = 1;
}
# go_out 和 go-grpc-out 目录是相对于服务定义文件中 go_package 指定的目录
protoc proto/product-info.proto --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative
编译生成服务端骨架的时候,已经得到了建立 gRPC 连接、相关消息类型和接口的基础代码,接下来就是实现得到的接口,在 server 文件夹中新建服务端主程序 main.go:
package main
import (
"context"
"log"
"net"
pb "product-info/proto"
"github.com/gofrs/uuid"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
const (
port = ":50051"
)
// 对服务器的抽象,用来实现服务方法
type server struct {
pb.UnimplementedProducTinfoServer
}
// 存放商品,模拟业务逻辑
var productMap map[String]*pb.Product
// 实现 AddProduct 方法
func (s *server) AddProduct(ctx context.Context, in *pb.Product) (*pb.ProductID, error) {
out, err := uuid.NewV4()
if err != nil {
return nil, status.Errorf(codes.Internal, "Error while generaTing Product ID", err)
}
in.Id = out.String()
if productMap == nil {
productMap = make(map[String]*pb.Product)
}
productMap[in.Id] = in
log.Printf("Product %v : %v - Added.", in.Id, in.Name)
return &pb.ProductID{Value: in.ID}, nil
}
// 实现 GetProduct 方法
func (s *server) GetProduct(ctx context.Context, in *pb.ProductID) (*pb.Product, error) {
product, exists := productMap[in.Value]
if exists && product != nil {
log.Printf("Product %v : %v - Retrieved.", product.Id, product.Name)
return product, nil
}
return nil, status.Errorf(codes.NotFound, "Product does not exist.", in.@R_616_7548@
}
func main() {
// 创建一个 tcp 监听器
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
// 创建一个 gRPC 服务器实例
s := grpc.NewServer()
// 将服务注册到 gRPC 服务器上
pb.RegisterProducTinfoServer(s, &server{})
// 绑定 gRPC 服务器到指定 tcp
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
接下来创建客户端程序来与服务器对话,之前编译服务定义文件生成的目标源代码已经包含了访问细节的实现,我们只需要创建客户端实例就可以直接调用远程方法。在 client 文件夹中创建客户端主程序 main.go:
package main
import (
"context"
"log"
"time"
pb "product-info/proto"
"google.golang.org/grpc"
)
const (
// 服务端地址
address = "localhost:50051"
)
func main() {
// 创建 gRPC 连接
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
// 创建客户端 stub,利用它调用远程方法
c := pb.NewProducTinfoClient(conn)
name := "XiaoMi 11"
description := "XiaoMi 11 with MIUI 12.5"
price := float32(3999.00)
// 创建 Context 传递给远程调用,包含一些元数据,如终端用户标识、授权令牌以及请求截止时间等
ctx, cancel := context.WithTimeout(context.BACkground(), time.Second)
defer cancel()
// 调用远程方法
r, err := c.AddProduct(ctx, &pb.Product{name: name, Description: description, Price: pricE})
if err != nil {
log.Fatalf("Could not add product: %v", err)
}
log.Printf("Product ID: %s added successfully", r.@R_616_7548@
product, err := c.GetProduct(ctx, &pb.ProductID{Value: r.value})
if err != nil {
log.Fatalf("Could not get product: %v", err)
}
log.Printf("Product: %v", product.String())
}
最终工作空间如下:
@H_874_167@
分别构建运行服务端和客户端程序,go build 或者直接 go run
go run ./server/main.go
go run ./client/main.go
到这里就成功构建了一个简单的 gRPC 服务,并在客户端调用成功。当然这只是一个简单的入门程序,更多的细节还需要更加深入的学习,另外,gRPC 是支持多语言的,这里采用 golang 实现了服务端和客户端程序,其他的语言构建 gRPC 服务也都遵循类似的步骤,且客户端和服务端代码无关,也可用不同的语言实现,其他语言的用法可见 官方文档
以上是大佬教程为你收集整理的gRPC入门—golang实现全部内容,希望文章能够帮你解决gRPC入门—golang实现所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。