小工具      在线工具  汉语词典  css  js  c++  java

gRPC教程与应用

微服务,java,网络,rpc 额外说明

收录于:152天前

gRPC 是 Google 的开源跨语言 RPC 框架,专为移动和 HTTP/2 设计。

环球中文网

在gRPC中,客户端应用程序可以像调用本地对象一样直接调用不同机器上的服务器应用程序的方法,从而更轻松地创建分布式应用程序和服务。

gRPC 还基于以下概念:定义一个服务并指定其可以远程调用的方法(包括参数和返回类型)。在服务器端实现此接口并运行 gRPC 服务器来处理客户端调用。

在这里插入图片描述
gRPC 默认使用 protocol buffers,这是 Google 开源的一套成熟的结构数据序列化机制。

在这里插入图片描述
protocol buffers能够生成RPC服务器,并使用proto文件来预先定义的消息格式。数据包是按照proto文件所定义的消息格式完成二进制码流的编码和解码。从而使基于RPC的服务器提供相关的接口,那么客户端通过服务端提供接口就可以访问服务器端的方法。

gRPC通过服务器-客户端的方式实现客户端直接访问服务器。并完成用户自定义报文格式字节的编解码。

gRPC官方文档中文版

gRPC官网

gRPC 使用协议缓冲区来定义接口并将方法或类注册到服务中。客户端也使用相同的接口。只要接口连接成功,就可以调用服务中注册的接口。

protocol buffers =是跨语言的,有自己的语法,通过自身语法定义接口定义语言来定义服务方法,并整合到其他语言,将程序的方法绑定protocol buffers的接口,实现跨语言访问。

协议缓冲区教程

安装协议编译器插件

只需按照以下步骤并按照教程进行操作即可

本项目的gitee地址

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

在这里插入图片描述

如下图所示,在本机的GOPATH目录下下载了插件:

在这里插入图片描述

这两个插件是protocol buffer文件使用的,用户将它们编译成相应语言的代码。这是编译到 go 中的代码。

了解rmi的人可以知道,rpc只能在java中使用。这是因为rmi使用java程序的接口,缺乏可扩展性,而protocol buffer使用自己的语法来定义接口,并将接口与其他语言的方法绑定。这样就实现了语言的可扩展性。

安装rpc服务器

go get google.golang.org/grpc

在这里插入图片描述

安装go语言rpc服务器

Go语言gRPC的安装及简单实践

rpc服务的实现可能有很多库,也有很多实现方法。例如rpc官方网站也提供了github库。如下,

在这里插入图片描述
这里使用google官方的库。

编写protobuf规则

//proto语法版本 
syntax = "proto3";

//编译为对应语言
//第一个参数是目录位置分号隔开,第二个参数是服务名称
//option java_package = "io.grpc.examples";
option go_package = "./;golang";

package helloworld;

// The greeter service definition.
//service定义接口(方法)
service Greeter {
    
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {
    }
}

// The request message containing the user's name.
//message定义结构体或者类
message HelloRequest {
    
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
    
  string message = 1;
}

在这里插入图片描述
在这里插入图片描述

切换到proto文件目录并生成代码

在这里插入图片描述
protocol buffer文件以proto为后缀,切换到对应目录,执行插件。

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./grpc-test/make.proto

协议命令分析

  1. –go_out=。 user.proto 说明:go_out指定生成go源码的位置。 =后面第一个参数是生成的目录地址,第二个参数是调用proto文件的地址。

  2. --go-grpc_out= . user.proto 描述:go-grpc_out 指定rpc服务源码位置,第二个参数指定使用proto文件路径

protoc 是插件命令,grpc-test/make.proto 是 proto 文件位置。

在这里插入图片描述
在这里插入图片描述
执行完成后生成了后缀带.pb的两个文件,如下:

在这里插入图片描述
该步骤对应官网的命令行步骤:

在这里插入图片描述

至此,go-rpc服务端代码已经自动生成(根据自定义规则proto文件生成)。下一步是启动 rpc 服务器实例并将方法注册到服务器实例。

在这里插入图片描述
生成服务器实例

注意生成的文件一个是grpc.pb,一个是.pb前者是接口,后者是rpc服务器。
在这里插入图片描述

根据grpc.pc提供的类实现服务器实例:

在这里插入图片描述
只需要实现该结构体即可,实现其SayHello方法。自定义Server实现方法。

在这里插入图片描述

实现后请注意,前面有继承关系的ICON才算成功。

注册到服务器的方法

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"grpc-test/pb"
	"net"
)

// 生成服务器实例(自定义server实现生成的server)
type Server struct {
    
	pb.UnimplementedGreeterServer
}

// SayHello 将grpc接口绑定程序方法(借助接口实现,接口必须是grpc接口中注册了的)
// 方法在proto文件中定义了
func (s *Server) SayHello(context.Context, *pb.HelloRequest) (*pb.HelloReply, error) {
    
	return &pb.HelloReply{
    Message: "conect server successful"}, nil
}

// 运行服务
func main() {
    
	//基于tcp实现
	listen, err := net.Listen("tcp", ":1099")
	if err != nil {
    
		fmt.Println("failed to listen", err)
	}
	//创建grpc服务
	server := grpc.NewServer()
	
	//*************** 关键 **********
	//注册自定义方法 
	pb.RegisterGreeterServer(server, &Server{
    })
	// *********************************
	
	//启动服务
	err = server.Serve(listen)
	if err != nil {
    
		fmt.Println("failed to serve", err)
		return
	}
}

客户端连接到服务器

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"grpc-test/pb"
)

func main() {
    
	//配置连连接参数(无加密)
	dial, _ := grpc.Dial("localhost:1099", grpc.WithTransportCredentials(insecure.NewCredentials()))
	defer dial.Close()
	//创建客户端连接
	client := pb.NewGreeterClient(dial)
	//通过客户端调用方法
	res, _ := client.SayHello(context.Background(), &pb.HelloRequest{
    Name: "xiaoxu"})
	fmt.Println(res.GetMessage())

}

客户端连接后直接通关接口调用方法,服务器端也需要存在如下的两个文件。
在这里插入图片描述
客户端调用的是如下文件提供的接口,而改接口被rpc服务器封装,并在服务器端与服务器端方法绑定,grpc封装了传输的细节,因此使用是来就想调用本地方法一样方便。

运行结果如下,
在这里插入图片描述
返回了服务端的信息,

在这里插入图片描述

案例gitee地址

原理分析

grpc是rpc的具体实现方法。它采用服务器与客户端之间的通信方式。与HTTP的不同之处在于它避免了许多不相关的部分。比如HTTP的B/C架构需要借助浏览器,所以在远程调用时,在程序中必须先模拟浏览器访问,然后再解析数据。 grpc直接实现了一个新的服务器,并允许用户自定义接口,将这些接口暴露在服务器中,将grpc服务器集成到程序中,然后将接口绑定到程序方法上。客户端连接rpc服务器时调用该接口。它会直接指向服务器程序的方法,因此可以在本地调用。

在这里插入图片描述
由protobuf生成的接口,程序中实现该接口。

Protocol buffer配置文件定义了接口规则,用于生成自定义接口:

在这里插入图片描述
.proto文件定义生成规则,在.pb文件生成定制化接口。

gprc的跨语言特性

grpc 使用术语本身的语法来定义接口。上面的例子中,使用go语言将接口绑定到go语言的接口上。如果grpc的接口绑定了其他语言,也可以使用。

由于Go语言是基于源码的,所以在其他语言中需要下载protobuf工具包来帮助将proto接口转换成对应语言的接口。

转换其他语言的接口主要区别在于命令:

option java_package = "com.zy.tutor";  // 生成的java文件的包名 
option java_outer_classname = "xunjianProtos"; // 生成的java文件的类名

//插件生成命令
protoc --java_out . plateDetection.proto
<dependency>
	<groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>3.19.1</version>
</dependency>

grpc支持多种语言,就不一一赘述了。

普托数据类型

每种语言都有自己独特的数据类型,例如 Java 类和 Go 结构。显然protobuf无法满足所有语言的数据结构,所以需要对一些特殊的数据结构进行转换。 proto的直接数据结构如下:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

参考自:proto文件详解感谢作者@Sacred

那么最复杂的数据类型只能序列化成STRING然后再反序列化。

案件

  • 下载protobuf插件和grpc工具包

轻微地

  • 定义proto文件约定接口
syntax = "proto3";

//编译为对应语言
//option java_package = "io.grpc.examples";
option go_package = "./;protoInterface";

package protoInterface;

// 定义接口
service Interface {
    
  // 方法1
  rpc GetProduct (Request) returns (Response) {
    }
  // 方法2
  rpc GetOrder (Request) returns (Response) {
    }

}

// 定义数据类型
message Request {
    
  string paramString = 1;
}

//
message Response {
    
  string messageString = 1;
}
  • 插件命令将proto文件转换为接口和服务
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./build.proto

插件命令如果是windows在bat文件下,如果是linux在sh文件下。

  • 服务层逻辑
package service

import "encoding/json"

type Product struct {
    
	Id              int64  `json:"id" xorm:"not null pk autoincr comment('主键 id') BIGINT"`
	CustomerId      int64  `json:"customer_id" xorm:"comment('客户 id') BIGINT(20)"`
	ProductName     string `json:"product_name" xorm:"comment('产品名称') VARCHAR(20)"`
	AvailableAreaId int    `json:"available_area_id"` // 可用区id
	ResourcePoolId  int    `json:"resource_pool_id"`  //资源池id
	ProductId       int64  `json:"product_id"`        //产品id
	CardType        string `json:"card_type"`         //显卡型号
	CardNum         int    `json:"card_num"`          //显卡数量
	ContainerName   string `json:"container_name"`    // 容器名称
	ContainerId     string `json:"container_id"`      // 容器id
	CreatedAt       string `json:"created_at" xorm:"comment('创建时间') DATETIME created"`
	UpdatedAt       string `json:"updated_at" xorm:"comment('修改时间') DATETIME updated"`
}

func (Product) DefaultProduct() Product {
    
	return Product{
    
		Id:              1,
		CustomerId:      1,
		ProductName:     "xiaoxu",
		AvailableAreaId: 1,
		ResourcePoolId:  1,
		ProductId:       1,
		CardType:        "bike",
		CardNum:         1,
		ContainerName:   "xiaoxu",
		ContainerId:     "1",
		CreatedAt:       "2023",
		UpdatedAt:       "2023",
	}
}

func (Product) ToJSON(product Product) string {
    
	marshal, err := json.Marshal(product)
	if err != nil {
    
		panic(err)
	}
	return string(marshal)
}

func (Product) ToSTRUCT(product string) Product {
    
	var pro Product
	err := json.Unmarshal([]byte(product), &pro)
	if err != nil {
    
		panic(err)
	}
	return pro
}

  • grpc服务注册服务层逻辑
package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"grpc-demo/protobuf"
	"grpc-demo/service"
	"net"
)

// Server 实现服务类
type Server struct {
    
	protoInterface.UnimplementedInterfaceServer
}

// GetProduct
func (Server) GetProduct(context.Context, *protoInterface.Request) (*protoInterface.Response, error) {
    
	//获取程序中的返回值
	param := service.Product{
    }
	product := param.DefaultProduct()
	json := param.ToJSON(product)
	return &protoInterface.Response{
    
		MessageString: json,
	}, nil

}

// GetOrder
func (Server) Util(context.Context, *protoInterface.Request) (*protoInterface.Response, error) {
    
	return nil, nil
}

// 运行服务
func main() {
    
	//基于tcp实现
	listen, err := net.Listen("tcp", ":1099")
	if err != nil {
    
		fmt.Println("failed to listen", err)
	}
	//创建grpc服务
	server := grpc.NewServer()
	//注册自定义方法
	protoInterface.RegisterInterfaceServer(server, &Server{
    })
	//启动服务
	err = server.Serve(listen)
	if err != nil {
    
		fmt.Println("failed to serve", err)
		return
	}
}

  • 客户来电
package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"grpc-demo/protobuf"
	"grpc-demo/service"
)

func main() {
    
	//配置连连接参数(无加密)
	dial, err := grpc.Dial("localhost:1099", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
    
		println("failed grpc connect", err)
	}
	defer dial.Close()
	//创建客户端连接
	client := protoInterface.NewInterfaceClient(dial)
	//通过客户端调用方法
	res, err := client.GetProduct(context.Background(), &protoInterface.Request{
    
		ParamString: "hello",
	})
	if err != nil {
    
		println("failed grpc recive err", err)
	}
	//打印接受的字符
	fmt.Printf("%v", res)
	/* //获取带product结构体在反序列化 param := service.Product{} product := param.ToSTRUCT(res.MessageString) fmt.Println(product) */

}

启动grpc服务后,启动客户端请求,如下返回的字符流。
在这里插入图片描述
反序列化后得到结构体

//获取带product结构体在反序列化
param := service.Product{
    }
product := param.ToSTRUCT(res.MessageString)
fmt.Println(product)

在这里插入图片描述

因此,只要在客户端封装grpc客户端,就可以使用客户端来调用方法,就像先调用本地方法一样。

go-grpc

. . .

相关推荐

额外说明

web.xml配置选项,个人理解总结

这段时间写SSM(Spring+Shiro+Mybatis)代码,感觉web.xml的配置很多很杂,但又很重要,结合自己以前的代码配置和别人项目的代码配置项,查阅资料做了自己的知识点补充,现在就分享下。 <?xml version="1.0" encod

额外说明

Vue3视频播放器组件Vue3-video-play入门教程

Vue3-video-play适用于 Vue3 的 hls.js 播放器组件 | 并且支持MP4/WebM/Ogg格式。 1、支持快捷键操作 2、支持倍速播放设置 3、支持镜像画面设置 4、支持关灯模式设置 5、支持画中画模式播放 6、支持全屏/网页全屏

额外说明

MFC弹出确认和取消对话框

UINT flag; flag = MessageBox(TEXT("确认要退出程序吗?"), TEXT("温馨提示"), MB_YESNO | MB_ICONQUESTION); if (flag == IDNO) { return; } ![在这里

额外说明

java 选择排序

public class SelectionSort { public static void main(String[] args) { int[] array = { 64, 25, 12, 22, 11 };

额外说明

SpringSecurity - WebFlux环境下动态角色权限

一、SpringSecurity - WebFlux 在上篇文章中我们讲解了SpringSecurity 在WebFlux环境下的用户动态授权,本篇文章继续上篇文章讲解 WebFlux环境下动态角色权限。 上篇文章地址:https://blog.csdn

额外说明

AI实战营第二期——第一次作业:基于RTMPose的耳朵穴位关键点检测

文章目录 题目:基于RTMPose的耳朵穴位关键点检测 背景 任务 数据集 训练目标检测器 错误:NameError: name 'unicode' is not defined 评估目标检测器 训练关键点检测器 评估关键点检测器 模型轻量化转换 目标检

额外说明

hibernate和jdbc的渊源

这是我在思否上面的一个博客,关于spring和jdbc的渊源,感兴趣的可以去看一下,hibernate和jdbc的渊源

额外说明

老徐和阿珍的故事:Runnable和Callable有什么不同?

人物背景: 老徐,男,本名徐福贵,从事Java相关研发工作多年,职场老油条,摸鱼小能手,虽然岁数不大但长的比较着急,人称老徐。据说之前炒某币败光了所有家产,甚至现在还有欠债。 阿珍,女,本名陈家珍,刚刚入职不久的实习生,虽然是职场菜鸟但聪明好学。据说是学

额外说明

Mac OX 配置JDK环境变量

一直都是用Windows来开发,现在Mac开发安装JDK配置环境有点麻烦。查了很多文章,找到了一个简单的记录。 1.打开Mac自带的终端; 2、进入当前用户主目录,cd ~; (默认用户目录不需要) 3、临时授权,sudo su; 4、输入系统密码(被锁

额外说明

weex环境搭建---rax

官方解释:威克斯是一种用途网络用于开发高性能本机应用程序的框架。 威克斯结构解耦,渲染引擎和语法层分离,不依赖任何特定的前端框架。目前主要支持 Vue.js 和 拉克斯 这两个前端框架。 0.页面预览 执行 npm run start 预览。 终端中会显

ads via 小工具