• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

grpc学习笔记 | 青训营

武飞扬头像
慕有钱
帮助2

1.概述

单体架构存在的问题:

1、—旦某个服务宕机,会引起整个应用不可用,隔离性差

2、只能整体应用进行伸缩,浪费资源,可伸缩性差

3、代码耦合在一起,可维护性差

微服务架构:解决了单体架构的弊端但同时引入了新的问题

1、代码冗余

2、服务和服务之间存在调用关系

服务拆分后,服务和服务之间发生的是进程和进程之间的调用,服务器和服务器之间的调用。

那么就需要发起网络调用,网络调用我们能立马想起的就是http,但是在微服务架构中,http虽然便捷方便,但性能较低,这时候就需要引入RPC(远程过程调用),通过自定义协议发起TCP调用,来加快传输效率。

2.安装命令

go get 谷歌.golang.org/grpc

go install 谷歌.golang.org/protobuf/cmd/protoc-gen-go@latest

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

3.编写proto文件:

// 这里是说明使用的是proto3语法

syntax = "proto3";


// 说明最后生成的go文件是处在哪个目录哪个包中,. 代表当前目录生成,service代表了生成的go文件的包名是 service  (两个参数之间用 ; 隔开)
option go_package = ".;service";

//然后我们定义一个服务,在这个服务中需要一个方法,这个方法可以接受客户端的参数,再返回服务端的响应。
//其实很容易看出,我们定义了一个service,称为SayHello,这个服务中有一个 rpc 方法,名为SayHello
//这个方法会发送一个HelloRequest,然后返回一个HelloResponse
service SayHello{
  rpc SayHello(HelloRequest) returns (HelloResponse) {}
}

//message 关键字,其实你可以理解为Golang中的结构体,
// 这里比较特别的是变量后面的“赋值”,注意:这里不是赋值,而是在定义这个变量在这个message中的位置
message HelloRequest {
  string requestName = 1;
//  int64 age = 2;
}
message HelloResponse {
  string requestMsg = 1;
}

执行命令:

protoc --go_out=. hello.proto   // . 表示生成的文件在当前目录 , 第二个参数表示对哪个文件进行生成
protoc --go-grpc_out=. hello.proto

使用grpc的话,就是后续运用protoc --go-grpc_out=. hello.proto命令生成的文件,填充里面的具体业务逻辑内容即可。

proto文件:

message

message:protobuf 中定义一个消息类型是通过关键字message字段指定的,消息就是需要传输的数据格式定义

message 关键字类似 go 语言中的struct结构体。

在消息中承载的数据分别对应每一个字段,==其中每一个字段都有一个名字和一种类型==

一个proto 文件中可以定义多个消息类型。

字段规则

required: 消息体中必填字段,不设置会导致编码异常,在protobuf2中使用,protobuf3中删除

optional:消息体中**==可选字段==**,protobuf3中默认都是optional,没有其他说明关键字

repeate: 消息体中可重复字段,==重复的值的顺序会被保留,在go中重复的会被定义为切片。==

消息号

在消息体的定义中,==每个字段都必须有一个唯一的标识号==,标识号是 [1,2^29 -1] 范围内的一个**==整数==**。

嵌套消息

可以在其他消息类型中定义、使用消息类型、在下面的例子中,person 消息就定义在Personinfo 消息内如:

message PersonInfo{
  message Person{
    string name = 1;
    int32 age = 2;
    repeated int32 weight = 3;
  }
  repeated Person info = 1;
}

如果要在它的父消息类型的外部重用这个消息类型,需要PersonInfo.Person的形式使用它,如:

message PersonMessage{
  PersonInfo.Person info = 1;
}

服务定义

如果想要将消息类型用在gRPC系统中,可以在一个**==.proto文件中定义一个 RPC 服务接口==**,protocol buffer 编译器将会根据所选择的不同语言生成服务接口代码及存跟。

service SearchService{
	// rpc  服务函数名(参数) 返回 (返回参数)
  rpc Search(SearchRequest) returns (SearchResponse) {}
}

上述代表表示,定义一个RPC服务,该方法接受参数是:SearchRequest,返回参数是:SearchResponse


4.服务端编写

  • 创建 gRPC Server 对象,你可以理解为它是 Server 端的抽象对象
  • 将 Server (其包含需要被调用的服务端接口)注册到 gRPC Server 的内部注册中心;这样可以在接受到请求时,通过内部的服务发现,发现该服务端接口并转接进行逻辑处理;
  • 创建 Listen , 监听 TCP 端口
  • gRPC Server 开始 lis.Accept ,直到 Stop

5.客户端编写

  • 创建与给定目标(服务端)的连接交互
  • 创建 server 的客户端对象
  • 发送 RPC 请求,等待同步响应,得到回调后返回响应结果
  • 输出响应结果

6.认证 - 安全传输

gRPC 是一个典型的 C/S 模型,需要开发客客户端 和 服务端,客户端 和 服务端需要达成协议,使用某一个确认的传输协议来传输数据,gPRC 通常默认使用protobuf 来作为传输协议,当然也可以使用其他自定义的。

学新通

那么,客户端 与 服务端进行通信之前,客户端如何知道自己的数据发送给哪一个明确的服务端呢?反过来,服务端是不是也需要有一种方式来弄清楚自己的数据发送给谁呢?

那么就不得不提到 gRPC 的认证:(此处的认证,不是用户的认证,而是指多个 server 和多个 client 之间,如何识别对方是谁,并且可以安全的进行数据传输

  • SSL/TLS认证方式(采用HTTP2协议)
  • 基于Tocken的认证方式(基于安全连接)
  • 不采用任何措施的连接,这不是安全的连接(默认采用HTTP1)
  • 自定义的身份认证

客户端 和 服务端之间调用,我们可以通过加入证书的方式,实现调用的安全性。

TLS(Transport Layer Security,安全传输层),TLS是建立在传输层 TCP 协议 上的协议,服务于应用层,它的前身是 SSL (Secure Socket Layer ,安全套接字层),它实现了将**==应用层的报文==**进行加密后再交由 TCP 进行传输的功能。

TLS 协议主要解决以下三个网络安全问题:

  1. 保密:保密通过加密 encryption 实现,所有信息都加密传输,第三方无法嗅探
  2. 完整性:通过 MAC 校验机制,一旦被篡改,通信双方立刻发现
  3. 认证:双方认证,双方都可以配备证书,防止身份泄漏

生产环境可以购买证书或者使用一些平台发放的免费证书

  • key:服务器上的私钥文件,用于发送给客户端数据的加密,以及对从客户端接收到数据的解密
  • csr:证书签名请求文件,用于提交给证书颁发机构(CA)对证书签名。
  • crt:由证书颁发机构(CA) 签名后的证书,或者是开发者自签名的证书,包含证书持有人的信息,持有人的公钥,以及签署者的签名等信息
  • pem:是基于Base64 编码的证书格式,扩展名包括 PEM、CRT和CER

一些名词解释:聊聊 HTTPS、和SSL/TLS 协议 -- 狂神


SSL/TLS 认证方式

首先通过 openssl 生成证书和私钥

  1. 官网下载:www.openssl.org/source/
    1. 其他人做的便捷版安装包:slproweb.com/products/Wi…
  2. 我们使用 便捷版安装包,一直下一步即可
  3. 配置环境变量
  4. 命令行测试 openssl

生成证书

# 1. 生成私钥
openssl genrsa -out server.key 2048

# 2. 生成证书 全部回车即可、可以不填
openssl req -new -x509 -key server.key -out server.crt -days 36500

# 国家代码,2位,中国CN
Country Name (2 letter code) [AU]:CN
# 机构所在省份
State or Province Name (full name) [Some-State]:ShangHai
# 机构所在城市
Locality Name (eg, city) [Default City]:ShangHai
# 机构名称
Organization Name (eg, company) [Default Company Ltd]:TEST COMPANY
# 机构部门
Organizational Unit Name (eg, section) []:R&D center
# 机构域名 或 服务域名
Common Name (eg, your name or your server's hostname) []:www.qiyu319.com 
# 邮箱
Email Address []:123467890@123.com

# 3. 生成csr
openssl req -new -key server.key -out server.csr
#更改openssl.cnf (Linux 是 openssl.cfg)**

1. 复制一份你安装的 openssl 的 bin 目录里面的 openssl.cnf 文件到你项目所在的目录
2. 找到`[ CA_default ]`,打开 copy_extensions = copy(就是把前面的#去掉)
3. 找到`[ req ]`,打开 req_extensions = v3_req # The extensions to add to a certificate request
4. 找到`[ v3_req ]`,添加subjectAltName = @alt_names
5. 添加新的标签[ alt_names ],和标签字段

DNS.1 = *.kuangstudy.com   # 通过指定域名访问网站
# 生成证书私钥 test.key
openssl genpkey -algorithm RSA -out test.key

# 通过私钥test.key 生成证书请求文件test.csr(注意cfg和cnf)

openssl req -new -nodes -key test.key -out test.csr -days 3650 -subj "/C=cn/OU=myorg/O=mycomp/CN=myname" -config ./openssl.cfg -extensions v3_req


#test.csr 是上面生成的证书请求文件,ca.crt/server.key 是CA 证书文件和key,用来对test.csr 进行签名认证,这两个文件在第一部分生成

#生成 SAN 证书 pem
openssl x509 -req -days 365 -in test.csr -out test.pem -CA server.crt -CAkey server.key -CAcreateserial -extfile ./openssl.cfg -extensions v3_req

Tocken认证

我们先看一个gRPC提供我们的一个接口,这个接口中有两个方法,接口位于credentials 包下,这个接口需要客户端来实现

type PerRPCCredentials interface {
	GetRequestMetadata(ctx context.Context,uri ...string) (map[string]string,error)
    RequireTransportSecurity() bool
}

第一个方法作用是:获取元数据信息,也就是客户端提供的key,value对,context用于控制超时和取消,uri是请求入口处的uri

第二个方法的作用是:是否需要基于TLS认证进行安全传输,如果返回值是true,则必须加上TLS验证,返回值是false则不用。

自定义Tocken认证的代码

//客户端代码
type ClientTokenAuth struct{}

// GetRequestMetadata 客户端发送请求时,所携带的元信息
func (c ClientTokenAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
	return map[string]string{
		"appid":  "kuangshen",
		"appkey": "123123",
	}, nil

}

// RequireTransportSecurity 如果使用安全认证,就返回True,否则返回false
func (c ClientTokenAuth) RequireTransportSecurity() bool {
	return true
}

在main函数中,配置客户端请求参数

	//配置请求参数
	var opts []grpc.DialOption
	//添加安全认证 (如果不需要安全连接的话,就添加grpc.WithTransportCredentials(insecure.NewCredentials()))
	opts = append(opts, grpc.WithTransportCredentials(creds))
	//添加自己的Tocken认证,传入自己的一个认证的对象
	opts = append(opts, grpc.WithPerRPCCredentials(new(ClientTokenAuth)))

	//1.连接到server 端,将上面配置的opts参数传入
	conn, err := grpc.Dial("127.0.0.1:9090", opts...)

==客户端配置完成==

==服务端==对客户端传来的元数据信息进行校验,进而完成具体的业务逻辑的实现

// 服务端代码
// SayHello 具体的业务处理代码
func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {
	//获取客户端传来的元数据信息
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return nil, errors.New("未传输tocken")
	}
	//传输成功,获取传过来的数据
	var appID, appkey string
	if v, ok := md["appid"]; ok {
		appID = v[0]
	}
	if v, ok := md["appkey"]; ok {
		appkey = v[0]
	}
	if appID != "kuangshen" || appkey != "123123" {
		return nil, errors.New("请求的元数据错误,tocken 不正确")
	}
	fmt.Println("正在调用服务端的SayHello方法")
	return &pb.HelloResponse{RequestMsg: "hello"   req.RequestName}, nil
}

==总结==

gRPC将各种认证方式浓缩统一到一个凭证(credentials)上,可以单独使用一种凭证,比如只使用TLS凭证或者只使用自定义凭证,也可以多种凭证组合,gRPC提供统一的API验证机制,使研发人员使用方便,这也是gRPC设计的巧妙之处

参考资料

protocol buffers使用指南 - 李文周

gRPC教程 - 李文周

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbbkeg
系列文章
更多 icon
同类精品
更多 icon
继续加载