Golang GRPC学习记录

1、概述

1.1 什么是GRPC

RPC的全称是Remote Procedure Call,远程过程调用。RPC是一种协议,它实际是提供了一套机制,使得应用程序之间可以进行通信,而且也遵从server/client模型。使用的时候客户端调用server端提供的接口就像是调用本地的函数一样。
而gRPC又是什么呢?用官方的话来说:

A high-performance, open-source universal RPC framework

gRPC是一个高性能的、开源的通用的RPC框架。

在gRPC中,我们称调用方为client,被调用方为server。 跟其他的RPC框架一样,gRPC也是基于”服务定义“的思想。简单的来讲,就是我们通过某种方式来描述一个服务,这种描述方式是语言无关的。在这个”服务定义“的过程中,我们描述了我们提供的服务的服务名是什么,有哪些方法可以被调用,这些方法有什么样的入参,有什么样的回参。
也就是说,在定义好了这些服务、这些方法之后,gRPC会屏蔽底层的细节,client只需要直接调用定义好的方法,就能拿到预期的返回结果。对于server端来说,还需要实现我们定义的方法。同样的,gRPC也会帮我们屏蔽底层的细节,我们只需要实现所定义的方法的具体逻辑即可。
你可以发现,在上面的描述过程中,所谓的”服务定义“,就跟定义接口的语义是很接近的。我更愿意理解为这是一种”约定“,双方约定好接口,然后server实现这个接口,client调用这个接口的代理对象。至于其他的细节,交给gRPC。
此外,gRPC还是语言无关的。你可以用C++作为服务端,使用Golang、Java等作为客户端。为了实现这一点,我们在”定义服务“和在编码和解码的过程中,应该是做到语言无关的。

如下图所示就是一个典型的RPC结构图。

在这里插入图片描述
通过上图可以看到gRPC使用了Protocol Buffers。本文不会展开来讲Protocol Buffers(详细proto语法参见:Golang使用Protobuf),你可以把他当成一个代码生成工具以及序列化工具。这个工具可以把我们定义的方法,转换成特定语言的代码。比如你定义了一种类型的参数,他会帮你转换成Golang中的struct结构体,你定义的方法,他会帮你转换成func函数。此外,在发送请求和接受响应的时候,这个工具还会完成对应的编码和解码工作,将你即将发送的数据编码成gRPC能够传输的形式,又或者将即将接收到的数据解码为编程语言能够理解的数据格式。

1.2 使用场景

  1. 低延时、高可用的分布式系统;
  2. 移动端与云服务端的通讯;
  3. 使用protobuf,独立于语言的协议,支持多语言之间的通讯;
  4. 可以分层扩展,如:身份验证,负载均衡,日志记录,监控等;

1.3 gRPC 与 RESTful API比较

特性gRPCRESTful API
规范必须.proto可选 OpenAPI
协议HTTP/2 任意版本的HTTP 协议
有效载荷Protobuf(小、二进制)JSON(大、易读)
浏览器支持否(需要 grpc-web)
流传输客户端、服务端、双向客户端、服务端
代码生成OpenAPI + 第三方工具

2、 环境配置

2.1 安装Protocol Buffers

安装用于生成gRPC服务代码的协议编译器

下载地址:https://github.com/google/protobuf/releases
在这里插入图片描述
在这里插入图片描述

  • bin 目录下的 protoc 是可执行文件。
  • include 目录下的是 google 定义的.proto文件,我们import "google/protobuf/timestamp.proto"就是从此处导入。
    我们需要将下载得到的可执行文件protoc所在的 bin 目录加到我们电脑的环境变量中。
    或者直接将解压后得到的protoc.exe二进制文件移动到$GOPATH/bin里。

2.2 安装Protoc Plugin

编译器插件

我们是使用Go语言做开发,接下来执行下面的命令安装protoc的Go插件:

go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28

protoc-gen-go-grpc是Google协议缓冲区编译器生成Go代码的插件。

该插件会生成一个后缀为_grpc.pb.go的文件,其中包含:

一种接口类型(或存根) ,供客户端调用的服务方法。
服务器要实现的接口类型。
上述命令会默认将插件安装到 G O P A T H / b i n ,为了 p r o t o c 编译器能找到这些插件,请确保你的 GOPATH/bin,为了protoc编译器能找到这些插件,请确保你的 GOPATH/bin,为了protoc编译器能找到这些插件,请确保你的GOPATH/bin在环境变量中。

两次安装后在GOPATH/bin目录下生成exe,如下图:
在这里插入图片描述
安装检查
在这里插入图片描述

2.2 获取gRPC

go get google.golang.org/grpc

这一步安装的是gRPC的核心库。

2.3 获取protoc-gen-go-grpc

go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

安装protoc-gen-go-grpc用于.proto–>***_grpc.pb.go。

3、 gRPC入门示例

代码编写:
在这里插入图片描述

  1. 首先要编写message.proto文件
    rgpc/pkg/proto/message.proto
syntax = "proto3";
// 这部分的内容是关于最后生成的go文件是处在哪个目录哪个包中,../pb代表在当前目录的上一级pb目录中生成,message代表了生成的go文件的包名是message。
option go_package = "../pb;pb";

message MessageResponse {
  string responseSomething = 1;
}

message MessageRequest {
  string saySomething = 1;
}

service MessageSender {
  rpc Send(MessageRequest) returns (MessageResponse) {}
}
  1. 执行命令生成代码,代码将生成再pb包下
protoc --go_out=. message.proto
protoc --go-grpc_out=. message.proto
  1. Server端代码
    rgpc/pkg/service/main.proto
package main

import (
	"google.golang.org/grpc"
	"log"
	"net"
	"rgpc-demo/pkg/pb"
	"rgpc-demo/pkg/serviceImpl"
)

func main() {
	srv := grpc.NewServer()
	pb.RegisterMessageSenderServer(srv, serviceImpl.MessageSenderServerImpl{})
	listener, err := net.Listen("tcp", ":8002")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	err = srv.Serve(listener)
	if err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

rgpc/pkg/serviceImpl/MessageSenderServerImpl.go

package serviceImpl

import (
	"context"
	"log"
	"rgpc-demo/pkg/pb"
)

type MessageSenderServerImpl struct {
	*pb.UnimplementedMessageSenderServer
}

func (MessageSenderServerImpl) Send(context context.Context, request *pb.MessageRequest) (*pb.MessageResponse, error) {
	log.Println("receive message:", request.GetSaySomething())
	resp := &pb.MessageResponse{}
	resp.ResponseSomething = "roger that!哈哈哈"
	return resp, nil
}

  1. Client端
    rgpc/pkg/client/main.go
package main

import (
	"context"
	"google.golang.org/grpc"
	"log"
	"rgpc-demo/pkg/pb"
)

func main() {
	conn, err := grpc.Dial("127.0.0.1:8002", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()

	client := pb.NewMessageSenderClient(conn)
	resp, err := client.Send(context.Background(), &pb.MessageRequest{SaySomething: "hello world!第二次"})
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}
	log.Println("receive message:", resp.GetResponseSomething())
}

  1. 结果
    在这里插入图片描述
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值