【微服务】RPC、gRPC 和 C/S 架构的基本实现(Golang)

一、RPC

1.1 what & why need?

一言以蔽之,RPC 是分布式系统的基石

RPC(Remote Procedure Call),中文名为远程过程调用。它最初由 Xerox 公司提出并对其定义为:
“RPC 是一种语言级别的通讯协议,它允许运行于一台计算机上的程序以某种管道作为通讯媒介,去调用另外一个地址空间”。

  1. 类型上说,RPC 是一种通讯协议;
  2. 功能上说,RPC 实现的功能是在一台机器上调用另一台机器的地址空间,该地址空间可能对应函数、变量等;
  3. 实现手段上说,RPC 需要借助计算机网络中的传输层来实现管道通讯。传输层的管道通信可以理解为通过 IP 地址和端口号来确定通信管道的两端。

随着互联网的发展,“客户端—服务器—数据库” 的单体架构(Monolithic Architecture)已无法满足日益复杂的业务逻辑和日渐增多的业务访问,因此需要转向微服务架构(Microservices Architecture)。以下是 Google Cloud Platform 中对微服务的定义:

微服务架构(通常简称为微服务)是指开发应用所用的一种架构形式。通过微服务,可将大型应用分解成多个独立的组件,其中每个组件都有各自的责任领域。在处理一个用户请求时,基于微服务的应用可能会调用许多内部微服务来共同生成其响应。

根据上述定义可知,对于微服务架构中的一个请求,各个服务间需要相互调用才能最终生成对应的响应,因此服务间的调用问题就是成为一个关键问题。而 RPC 正好能解决该问题,所以会有人说 “要想搞懂微服务,先搞定RPC”。在微服务架构中加入合适的 RPC 框架,不仅能降低微服务架构的开发成本,也能提高服务间的调用效率。

二、gRPC

2.1 一个通用开源框架

gRPC 官方文档中文版:开源中国。似乎很久没有更新了,最新的英文文档请参考 docs

gRPC 最开始由 Google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多路复用请求等特性。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。根据 grpc.io 的数据,目前该框架已支持多种语言,包括 Java、C#、Go、C++、Dart、Python、Kotlin、PHP、Ruby、Objective-C 和 Node。

2.2 配置 gRPC 环境

  • 在 go.mod 中配置 gRPC 核心库(会放在 $GOPATH/pkg 中)
$ go get google.golang.org/grpc
  • 下载对应语言的代码生成插件(会放在 $GOPATH/bin 中)
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

三、Golang 实现简单的 RPC 服务

3.1 编写 proto 文件并生成对应的 Go 代码

proto 文件的部分语法

  1. service 服务名 { rpc rpc方法名(请求消息) returns(响应消息) { } }
  2. message 消息名 { 变量类型 变量名 = 在消息中的位次; }

编写 server.proto 文件,声明语法为 proto3service 类似于 Go 中的 funcmessage 类似于 Go 中的 structrpc 表示 ShakeHands 是 RPC 方法,接受请求 ShakeReq,返回结果 ShakeRes

syntax = "proto3";

option go_package = ".;service"; // 生成的.go文件在哪个目录的哪个包下(用;隔开 目录;包名)

service ShakeHands {
    
    
    rpc ShakeHands(ShakeReq) returns (ShakeRes) {
    
    }
}

message ShakeReq {
    
    
    string requestName = 1; // 这是在定义变量在 message 中的位置
}
message ShakeRes {
    
    
    string responseMsg = 1;
}

然后进入 proto 文件所在目录,执行 protoc 命令。

$ cd path/to/proto/file
$ protoc --go_out=. server.proto
$ protoc --go-grpc_out=. server.proto

生成的文件如下,–go_out 表示 .pb.go 文件输出的目录位置,–go-grpc_out 表示 _grpc.pb.go 文件输出的目录位置。

3.2 实现服务端代码

主要流程如下:

  1. 实现 _grpc.pb.go 文件中定义的服务(或叫 RPC 方法)。
  2. 开启 TCP 端口。
  3. 创建 gRPC 服务并在 gRPC 服务中注册实现了的服务。
  4. 启动 gRPC 服务。
package main

import (
	"context"
	"fmt"
	"net"

	"google.golang.org/grpc"

	pb "gRPC/server/proto"
)

type server struct {
    
    
	pb.UnimplementedShakeHandsServer
}

func (s *server) ShakeHands(ctx context.Context, req *pb.ShakeReq) (res *pb.ShakeRes, err error) {
    
    
	return &pb.ShakeRes{
    
    ResponseMsg: "res: hello!" + req.RequestName}, nil
}

func main() {
    
    
	listen, _ := net.Listen("tcp", "127.0.0.1:9999")             // 开启tcp端口
	grpcServer := grpc.NewServer()                     // 创建grpc服务
	pb.RegisterShakeHandsServer(grpcServer, &server{
    
    }) // 在grpc服务中注册我们的服务
	err := grpcServer.Serve(listen)                    // 启动服务
	if err != nil {
    
    
		fmt.Println("server error!")
		return
	}
}

3.3 不带安全认证的客户端

客户端的逻辑很简单,就是建立与服务端的连接:

  1. 拨号,发起连接。grpc.Dial() 服务端的 IP 地址和端口;
  2. 通过 _grpc.pb.go 文件提供的方法创建客户端实例;
  3. 调用 RPC 方法发起请求。
package main

import (
	"context"
	"fmt"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"

	pb "gRPC/server/proto"
)

func main() {
    
    
	conn, err := grpc.Dial("127.0.0.1:9999", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
    
    
		fmt.Println("connection error!")
	}
	defer conn.Close()

	client := pb.NewShakeHandsClient(conn) // 建立连接
	resp, err := client.ShakeHands(context.Background(), &pb.ShakeReq{
    
    RequestName: "test"})
	if err != nil {
    
    
		fmt.Println(err.Error())
	}
	fmt.Println(resp.GetResponseMsg())
}

3.4 进行 SSL/TLS 安全认证

猜你喜欢

转载自blog.csdn.net/weixin_45651194/article/details/129930250