IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> grpc使用及意义 -> 正文阅读

[网络协议]grpc使用及意义

远程过程调用 RPC

RPC(Remote Procedure Call)远程过程调用协议,一种通过网络从远程计算机上请求服务,而不需要了解底层网络技术的协议。RPC假定了某些协议的存在,例如TCP/UDP等,为通信程序之间携带信息数据。在OSI七层模型中,RPC跨越了传输层和应用层,RPC使得开发,包括网络分布式多程序在内的应用程序更加容易。

为何使用grpc

对比http版本的不同来做解释

protobuf的使用及规范

protobuf介绍

protobuf是谷歌开源的一种数据格式,适合高性能,对响应速度有要求的数据传输场景。protobuf是 二进制数据格式,需要编码和解码。 数据本身不具有可读性,因此只能反序列化之后才能得到真正可读的数据。

  • 优势
    • 序列化后体积相比json和xml很小,适合网络传输
    • 支持跨语言平台
    • 消息格式升级和兼容性还不错
    • 序列化和反序列化速度很快

字段规则

  • required:消息体中的必填字段,不设置会导致编解码异常
  • optional:消息题中可选字段
  • repeated:消息体中可重复字段,重复的值的顺序会被保留,在go中重复的会被定义为切片

示例 golang

//HelloReply协议内容
message HelloReply {
  string name = 1;
  string message = 2;
  optional string ip = 3;
  repeated string port = 4;
}
//HelloReply协议内容
type HelloReply struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Name    string   `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	Message string   `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
	Ip      *string  `protobuf:"bytes,3,opt,name=ip,proto3,oneof" json:"ip,omitempty"`
	Port    []string `protobuf:"bytes,4,rep,name=port,proto3" json:"port,omitempty"`
}

默认值

类型默认值
boolfalse
整型0
string空字符串 “”
枚举第一个枚举元素的值,因为protobuf3强制要求第一个枚举元素的值必须是0,所以枚举的默认值就是0
message不是null,而是DEFAULT_INSTANCE

标识号

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

示例

//HelloReply协议内容
message HelloReply {
  string name = 1; // 1即标识号
  string message = 2;
  optional string ip = 3;
  repeated string port = 4;
}

嵌套消息

message SubHello {
  string code = 1;
  string msg = 2;
}

//HelloReply协议内容
message HelloReply {
  string name = 1;
  string message = 2;
  optional string ip = 3;
  repeated string port = 4;
  repeated SubHello http = 5;
}
type HelloReply struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Name    string      `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	Message string      `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
	Ip      *string     `protobuf:"bytes,3,opt,name=ip,proto3,oneof" json:"ip,omitempty"`
	Port    []string    `protobuf:"bytes,4,rep,name=port,proto3" json:"port,omitempty"`
	Http    []*SubHello `protobuf:"bytes,5,rep,name=http,proto3" json:"http,omitempty"`
}

openssl生成签名证书

生成普通证书

  • 生成服务器私钥
 openssl genrsa -des3 -out server.key 4096
  • 生成证书请求文件csr(windows)或者pem(linux)
 openssl req -new -key server.key -out server.pem

Enter pass phrase for server.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) []:cn # 国家
State or Province Name (full name) []:beijing # 城市
Locality Name (eg, city) []:beijing # 城市
Organization Name (eg, company) []:huidev
Organizational Unit Name (eg, section) []:ihuidev
Common Name (eg, fully qualified host name) []:devhui.org # 服务器域名
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
  • 删除key的密码生成一个新的密钥
openssl rsa -in server.key -out server_no_password.key
  • 生成crt证书
openssl x509 -req -days 365 -in server.pem -signkey server.key -out server.crt

至此普通证书生成完成

生成CA签名证书

  • 生成ca私钥
 openssl genrsa -des3 -out ca.key 4096
  • 生成证书请求文件csr(windows)或者pem(linux)
 openssl req -new -x509 -days 3650 -key ca.key -out ca.pem

Enter pass phrase for server.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) []:cn # 国家
State or Province Name (full name) []:beijing # 城市
Locality Name (eg, city) []:beijing # 城市
Organization Name (eg, company) []:huidev
Organizational Unit Name (eg, section) []:ihuidev
Common Name (eg, fully qualified host name) []:devhui.org # 服务器域名
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
  • 生成server密钥
openssl genrsa -des3 -out server.key 4096
  • 生成证书请求文件csr(windows)或者pem(linux)
 openssl req -new -x509 -days 3650 -key server.key -out server.pem

Enter pass phrase for server.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) []:cn # 国家
State or Province Name (full name) []:beijing # 城市
Locality Name (eg, city) []:beijing # 城市
Organization Name (eg, company) []:huidev
Organizational Unit Name (eg, section) []:ihuidev
Common Name (eg, fully qualified host name) []:devhui.org # 服务器域名
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:

注意:两次的域名一定要写成一致的

  • 生成crt证书
openssl x509 -req -sha256 -CA ca.pem -CAkey ca.key -CAcreateserial -days 3650 -in server.pem -out server.crt

至此普通证书生成完成

grpc的四大模式

gRPC中文文档
gRPC官方文档

生成proto的go文件命令

protoc --go_out=plugins=grpc,paths=source_relative:. authentication.proto

这个命令只会生成xxx.pb.go的go文件

官方为我们提供的是

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative authentication.proto

会生成grpc的框架的go文件 如:xxx_grpc.pb.go

问题:

新版本的protoc-gen-go不支持grpc服务的生成,需要借助protoc-gen-go-grpc生成grpc服务接口,但会出现一个问题
missing xxxpb.mustEmbedUnimplementedxxxServer method

解决方法

1、使用命令行的时候protoc --go-grpc_out=require_unimplemented_servers=false
2、或者server结构体中匿名嵌入xxxpb.UnimplementedxxxServer

说明:第一种方案不推荐,官方的解释是为了保持前后兼容
具体可看官方文档
protoc-gen-go-grpc官方readme

普通rpc

一个简单的RPC,客户端使用存根发送请求到服务器并等待响应返回,就像平常的函数调用一样

示例:

server.go

package main

import (
	"context"
	"fmt"
	authenticationpb "gitee.com/wlzhazhahui/gin-blog/blog_grpc/grpc_proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/reflection"
	"net"
)

type UserAuth struct {
	authenticationpb.UnimplementedBlogServiceAuthServer
}

func (user *UserAuth) UserLogin(context.Context, *authenticationpb.LoginRequest) (*authenticationpb.LoginResponse, error) {
	return &authenticationpb.LoginResponse{Success: "ok"}, nil
}

func main() {
	//协议类型以及ip,port
	listen, err := net.Listen("tcp", ":8002")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("正在运行grpc服务...")
	//定义一个rpc的server
	server := grpc.NewServer()
	//注册服务,相当与注册SayHello接口
	authenticationpb.RegisterBlogServiceAuthServer(server, &UserAuth{})
	//进行映射绑定
	reflection.Register(server)

	//启动服务
	err = server.Serve(listen)
	if err != nil {
		fmt.Println(err)
	}
}

authentication.proto

syntax = "proto3";

package authentication;
option go_package = ".;authenticationpb";  // authenticationpb代表包名。强烈建议包名的写法是name+pb的形式

service BlogServiceAuth{
  rpc UserLogin (LoginRequest) returns(LoginResponse) {}
}


message LoginRequest{
  string UserUid = 1;
  string UserNickName = 2;
}

message LoginResponse{
  string Success = 1;
}

client.go

package main

import (
	"context"
	"fmt"
	authenticationpb "gitee.com/wlzhazhahui/gin-blog/blog_grpc/grpc_proto"
	"google.golang.org/grpc"
	"time"
)

func main() {
	//创建一个grpc的连接
	grpcConn, err := grpc.Dial("127.0.0.1"+":8002", grpc.WithInsecure())
	if err != nil {
		fmt.Println(err)
		return
	}

	//创建grpc的client
	client := authenticationpb.NewBlogServiceAuthClient(grpcConn)
	//设置超时时间
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	//调用rpc方法,获得流接口
	res, err := client.UserLogin(ctx, &authenticationpb.LoginRequest{UserUid: "23eu89eqfuiqe", UserNickName: "高姿清高"})
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("接受到的值是 %v", res)

}

服务端流式rpc

一个服务端流式RPC,客户端放请求到服务器,拿到一个流去读取返回的消息序列。客户端读取返回的流,直到里面没有任何消息。

客户端流式rpc

客户端流式RPC,客户端卸乳一个消息序列并将其发送到服务器,同样也是使用流,一旦客户端完成写入消息,他等待服务器完成读取返回他的响应。

双向流式rpc

双向流式RPC是双方使用读写流去发送一个消息序列。两个流独立操作,因此客户端和服务器可以以任意喜欢的顺序读写:比如,服务器可以在写入响应前等待接受所有的客户端消息,或者可以交替的读取和写入消息,或者其他读写的组合。

使用GRPC-GATEWAY代理REST API请求

  • server.go
package main

import (
	"context"
	"fmt"
	authenticationpb "gitee.com/wlzhazhahui/gin-blog/blog_grpc/grpc_proto"
	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"google.golang.org/grpc"
	"google.golang.org/grpc/reflection"
	"log"
	"net"
	"net/http"
)

type UserAuth struct {
	authenticationpb.UnimplementedBlogServiceAuthServer
}

func (user *UserAuth) UserLogin(c context.Context, auth *authenticationpb.LoginRequest) (*authenticationpb.LoginResponse, error) {
	return &authenticationpb.LoginResponse{Success: auth.UserNickName}, nil
}

func main() {
	//协议类型以及ip,port
	go startGRPCGateway()
	listen, err := net.Listen("tcp", ":8002")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("正在运行grpc服务...")
	//定义一个rpc的server
	server := grpc.NewServer()
	//注册服务,相当与注册SayHello接口
	authenticationpb.RegisterBlogServiceAuthServer(server, &UserAuth{})
	//进行映射绑定
	reflection.Register(server)

	//启动服务
	err = server.Serve(listen)
	if err != nil {
		fmt.Println(err)
	}
}

func startGRPCGateway() {
	c := context.Background()
	c, cancle := context.WithCancel(c)
	defer cancle()
	Mux := runtime.NewServeMux()
	err := authenticationpb.RegisterBlogServiceAuthHandlerFromEndpoint(c, Mux, ":8002", []grpc.DialOption{grpc.WithInsecure()})
	if err != nil {
		log.Fatalf("cannot start grpc gateway: %v", err)
	}
	err = http.ListenAndServe(":8080", Mux)
	if err != nil {
		log.Fatalf("cannot start grpc gateway: %v", err)
	}

}

  • authentication.proto
syntax = "proto3";

package authentication;
option go_package = ".;authenticationpb"; // authenticationpb代表包名。强烈建议包名的写法是name+pb的形式

service BlogServiceAuth{
  rpc UserLogin (LoginRequest) returns(LoginResponse) {}
}


message LoginRequest{
  string UserUid = 1;
  string UserNickName = 2;
}

message LoginResponse{
  string Success = 1;
}

  • 生成xx.gw.go文件

gen.sh

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative authentication.proto
protoc --grpc-gateway_out=paths=source_relative,grpc_api_configuration=authentication.yaml:. authentication.proto
  • 编写grpc-gateway的yaml文件 authentication.yaml

注意:

yaml文件的格式要求很严格

比如:缩进要求

** 运行上面脚本生成gw.go文件时,可能会出 mapping values are not allowed in this context,这个问题是因为我的selector这行和post没有对齐导致**

type: google.api.Service
config_version: 3

http:
    rules:
    - selector: authentication.BlogServiceAuth.UserLogin 
      post: /auth //缩进和上行保持一致
      body: "*"

这个服务启动,我们就可以通过RESTAPI访问RPC

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-05-24 18:32:39  更:2022-05-24 18:33:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 1:55:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码