微服务通讯:gRPC入门教程

41 篇文章 7 订阅

微服务通讯:个、RPC入门教程

gRPC是一个RPC框架,用于服务器之间服务的相互通讯,常见微服务项目开发中。市面上的RPC有很多,例如:dubbo、SpringCloud底层封装的等

1 概念

1.1 gRPC

  • gRPC是一个高性能、开源的通用RPC(远程过程调用remote procedure call)框架。
  • gRPC与语言无关,你可以用C++作为服务器,使用Go、Java等作为客户端
    在这里插入图片描述
  • gPRC会屏蔽底层的细节,client只需要直接定义好方法,就能拿到预期的返回结果,对于Server端来说,还需要实现我们定义的方法。【类比于Java中的Feign,但是feign不是RPC,feign是基于HTTP的,有7层】
  • 官网地址:https://grpc.io/docs/what-is-grpc/introduction/
  • 中文教程地址:http://doc.oschina.net/grpc
  • gRPC使用了Protocol Buffss(简称protobuf),这是谷歌开源的一套成熟的数据结构序列化机制

①服务端编写过程

  • 创建gRPC Server对象,你可以理解为它是Serverdaunt抽象的对象
  • 将server(其包含被调用的服务端接口)注册到gRPC Server的内部注册中心

    这样可以在接收到请求时,通过内部的服务发现,发现该服务端接口并转接进行逻辑处理

  • 创建Listen,监听TCP端口
  • gRPC Server开始Listen、Accept,直到Stop

②客户端编写

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

1.2 protobuf

①介绍
  1. 谷歌开源的一种数据格式,适合高性能
  2. 二进制数据格式,需要编码和解码,数据本身不具有可读性,只能反序列化后才能得到真正可读的数据
  3. 序列化后体积比json和XML更小,适合网络传输
  4. 支持跨平台多语言
  5. 序列化、反序列化很快
  6. 消息格式升级和兼容性很不错
②安装protobuf
  1. 进入对应的GitHub
  • 根据自己操作系统下载对应版本:
    在这里插入图片描述
  1. 下载成功后解压并配置环境变量

配置为系统环境变量,因为配置为用户环境变量Goland可能不识别

# 打开cmd执行以下命令查看是否配置成功
protoc

在这里插入图片描述

2 gRPC安装及使用

2.1 安装

  1. 安装gRPC核心库
// 下载grpc
go get google.golang.org/grpc

//安装对应工具
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

2.2 使用

①proto文件编写【定义接口】
  1. 编写proto文件

hello.proto:

//使用proto3的语法
syntax = "proto3";

//指明最后生成的go文件夹最后放在哪个目录的哪个包中
// . 表示当前目录; service表示包名为service
option go_package = ".;service";

//1. 定义一个服务【SayHello】,服务中可以有对应的方法【SayHello】
service SayHello {
  rpc SayHello(HelloRequest) returns (HelloResponse) {}
}

//关键字:message,可以理解为go中的结构体
message HelloRequest{
  string requestName = 1; //1表示消息顺序
//  int64 age = 2;
}

message HelloResponse {
  string responseMsg = 1;
}

  1. 执行以下命令,生成对应文件
 protoc --go_out=. .\hello.proto
 protoc --go-grpc_out=. hello.proto

在这里插入图片描述
3. 将server/proto文件下的hello.pb.go和hello_grpc.pb.go同时复制一份放在client/ptoro下

最终结构:

在这里插入图片描述

注意:

如果发现Goland识别不了protoc命令,

  • 报如下错误:protoc‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件
  • 解决办法:将protoc配置为系统环境变量,然后重启Goland即可
②服务端编写
  • 编写服务端的启动代码
    main.go:
package main

import (
	"context"
	"fmt"
	"go_code/demo01/grpc/client/proto"
	"google.golang.org/grpc"
	"net"
)

// hello Server
type server struct {
	proto.UnimplementedSayHelloServer
}

func (s *server) SayHello(ctx context.Context, req *proto.HelloRequest) (*proto.HelloResponse, error) {
	//服务端实现接口【具体处理逻辑】
	fmt.Printf("client端远程调用成功..., 传入参数为=%v", req.GetRequestName())
	return &proto.HelloResponse{ResponseMsg: "hello," + req.RequestName}, nil
}

func main() {
	//1. 开启端口
	listen, _ := net.Listen("tcp", ":9090")
	//2. 创建grpc服务
	grpcServer := grpc.NewServer()
	//3. 将我们自己编写好的服务注册到grpc
	proto.RegisterSayHelloServer(grpcServer, &server{})

	//4. 启动服务
	err := grpcServer.Serve(listen)
	if err != nil {
		fmt.Printf("failed to server: %v", err)
		return
	}
}

整体结构:
在这里插入图片描述

③客户端编写

main.go:

package main

import (
	"context"
	"fmt"
	pb "go_code/demo01/grpc/server/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"log"
)

func main() {
	//1. 与Server建立连接[案例中:此处禁用安全传输,我们这里没有使用加密验证]
	conn, err := grpc.Dial("127.0.0.1:9090", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close() //延时关闭连接

	//2. 与对应服务建立连接
	client := pb.NewSayHelloClient(conn)
	//3. 执行grpc调用[对应方法已经在对应的Server端实现了]
	resp, _ := client.SayHello(context.Background(), &pb.HelloRequest{RequestName: "curry"})
	fmt.Println(resp.GetResponseMsg())

}
④测试结果
  • 配置goland同时启动多个实例
    在这里插入图片描述
  • 启动server和client,查看结果
    在这里插入图片描述

3 https及实现grpc加密通信

3.1 加密相关知识

3.2 实现grpc加密通信【基于TLS】

1. 安装openssl并配置环境变量

在github上找到对应版本并安装:https://github.com/openssl/openssl/releases

# 在cmd窗口输入以下命令,查看是否配置成功
openssl

在这里插入图片描述

2. 生成对应key和证书

在上面入门的基础上新建文件夹key,通过cmd来到key目录下,执行下面命令

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

# 2. 生成证书,全部回车即可,可以不填[就是一些基本信息:国家、地区什么的]
openssl req -new -x509 -key server.key -out server.crt -days 36500

# 3. 生成csr[同样可以全部回车,不填写任何信息]
openssl req -new -key server.key -out server.csr

最终效果:
在这里插入图片描述

3. 配置openssl.cnf

更改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 = *.ziyi.com

在这里插入图片描述

# 1. 生成证书私钥test.key
openssl genpkey -algorithm RSA -out test.key

# 2. 通过私钥test.key生成证书请求文件test.csr(注意cfg和cnf,linux上是cfg)
openssl req -new -nodes -key test.key -out test.csr -days 3650 -subj "/C=cn/OU=myorg/O=mycomp/CN=myname" -config ./openssl.cnf -extensions v3_req
#test.csr是上面生成的证书请求文件。ca.crt/server.key是CA证书文件和key,用来对test.csr进行签名认证,这两个文件在第2大部分已经生成

# 3. 生成SAN证书 pem
openssl x509 -req -days 365 -in test.csr -out test.pem -CA server.crt -CAkey server.key -CAcreateserial -extfile ./openssl.cnf -extensions v3_req
4. 编写main.go代码(客户端、服务端)
  • 服务端
package main

import (
	"context"
	"fmt"
	"go_code/demo01/grpc/client/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"net"
)

// hello Server
type server struct {
	proto.UnimplementedSayHelloServer
}

func (s *server) SayHello(ctx context.Context, req *proto.HelloRequest) (*proto.HelloResponse, error) {
	//服务端实现接口【具体处理逻辑】
	fmt.Printf("client端远程调用成功..., 传入参数为=%v", req.GetRequestName())
	return &proto.HelloResponse{ResponseMsg: "hello," + req.RequestName}, nil
}

func main() {
	//TSL认证
	//两个参数分别是crtFile、keyFile
	//自带签名证书文件和私钥文件
	//0. 配置证书和私钥文件
	creds, _ := credentials.NewServerTLSFromFile("E:\\Go\\GoPro\\src\\go_code\\demo01\\grpc\\key\\test.pem",
		"E:\\Go\\GoPro\\src\\go_code\\demo01\\grpc\\key\\test.key")
	//1. 开启端口
	listen, _ := net.Listen("tcp", ":9090")
	2. 创建grpc服务
	//grpcServer := grpc.NewServer()
	
	//2. 创建带证书的服务
	grpcServer := grpc.NewServer(grpc.Creds(creds))
	//3. 将我们自己编写好的服务注册到grpc
	proto.RegisterSayHelloServer(grpcServer, &server{})

	//4. 启动服务
	err := grpcServer.Serve(listen)
	if err != nil {
		fmt.Printf("failed to server: %v", err)
		return
	}
}

结果:
在这里插入图片描述

3.3 实现grpc加密通信【基于Token】

1. 认识对应接口

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

type PerRPCCredentials interface {
	/*
	获取元数据信息,也就是客户端提供了k-v对,
	context用于控制超时和取消,uri是请求入口处的uri
	*/
	GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)

	/*
	用于设置是否需要TLS认证进行安全传入,如果返回true则必须加上TLS验证
	*/
	RequireTransportSecurity() bool
}

token是可以和TLS结合的

2. 编写服务端代码
package main

import (
	"context"
	"errors"
	"fmt"
	"go_code/demo01/grpc/client/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/metadata"
	"net"
)

// hello Server
type server struct {
	proto.UnimplementedSayHelloServer
}

// 业务
func (s *server) SayHello(ctx context.Context, req *proto.HelloRequest) (*proto.HelloResponse, error) {
	fmt.Printf("---------------")
	//【校验token】
	//获取客户端传入的元数据信息
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return nil, errors.New("未传输token")
	}
	var appId string
	var appKey string
	if v, ok := md["appId"]; ok {
		appId = v[0]
	}
	if v, ok := md["appKey"]; ok {
		appKey = v[0]
	}

	if appId != "ziyi" || appKey != "123456" {
		return nil, errors.New("token 不正确")
	}

	//服务端实现接口【具体处理逻辑】
	fmt.Printf("client端远程调用成功..., 传入参数为=%v", req.GetRequestName())
	return &proto.HelloResponse{ResponseMsg: "hello," + req.RequestName}, nil
}

func main() {
	TSL认证
	两个参数分别是crtFile、keyFile
	自带签名证书文件和私钥文件
	0. 配置证书和私钥文件
	//creds, _ := credentials.NewServerTLSFromFile("E:\\Go\\GoPro\\src\\go_code\\demo01\\grpc\\key\\test.pem",
	//	"E:\\Go\\GoPro\\src\\go_code\\demo01\\grpc\\key\\test.key")

	//1. 开启端口
	listen, _ := net.Listen("tcp", ":9090")
	2. 创建grpc服务
	//grpcServer := grpc.NewServer()

	//2. 创建带证书的服务
	grpcServer := grpc.NewServer(grpc.Creds(insecure.NewCredentials()))
	//3. 将我们自己编写好的服务注册到grpc
	proto.RegisterSayHelloServer(grpcServer, &server{})

	//4. 启动服务
	err := grpcServer.Serve(listen)
	if err != nil {
		fmt.Printf("failed to server: %v", err)
		return
	}
}

3. 客户端代码
package main

import (
	"context"
	"fmt"
	pb "go_code/demo01/grpc/server/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"log"
)

type ClientTokenAuth struct {

}

func (c ClientTokenAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error){
	return map[string]string{
		"appId": "ziyi",
		"appKey": "123456",
	}, nil
}

func (c ClientTokenAuth) RequireTransportSecurity() bool {
	return false
}

func main() {
	0. 设置证书文件test.pem 【因为是测试,故:域名此处写死:*.ziyi.com】,线上应该通过浏览器去获取
	//creds, _ := credentials.NewClientTLSFromFile("E:\\Go\\GoPro\\src\\go_code\\demo01\\grpc\\key\\test.pem",
	//	"*.ziyi2.com")

	//1. 与Server建立连接[案例中:此处禁用安全传输,我们这里没有使用加密验证]
	var opts []grpc.DialOption
	opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) //这里我们不使用TLS,因此这里传入空
	opts = append(opts, grpc.WithPerRPCCredentials(new(ClientTokenAuth))) //传入我们自定义的验证方式【Token】
	conn, err := grpc.Dial("127.0.0.1:9090", opts...)

	1. 与Server建立带加密的连接
	//conn, err := grpc.Dial("127.0.0.1", grpc.WithTransportCredentials(creds))
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close() //延时关闭连接

	//2. 与对应服务建立连接
	client := pb.NewSayHelloClient(conn)
	//3. 执行grpc调用[对应方法已经在对应的Server端实现了]
	resp, _ := client.SayHello(context.Background(), &pb.HelloRequest{RequestName: "curry"})
	fmt.Println(resp.GetResponseMsg())

}

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值