一.Protobuf
1. Protobuf是什么:
实现数格式序列化的工具,序列化的目的之一是进行网络传输,在传输过程中数据流越小传输速度自然越快,可以整体提升系统性能。
2.文件基本使用编写:
syntax="proto3"://指定proto版本
package hello_grpc;//指定默认包名
option go_package = "/hello_grpc", //指定go中的生成目录
service 服务的名{rpc 方法}
请求消息
响应消息
3.proto文件编写
syntax = "proto3"; // 指定proto版本
package hello_grpc; // 指定默认包名
// 指定golang包名,代码生成到的包下
option go_package = "/hello_grpc";
//定义rpc服务
service HelloService {
// 定义函数
rpc SayHello (HelloRequest) returns (HelloResponse) {}
}
// HelloRequest 请求内容
message HelloRequest {
string name = 1;
string message = 2;
}
// HelloResponse 响应内容
message HelloResponse{
string name = 1;
string message = 2;
}
4.proto语法:proto中只定义接口方法
(1)service对应方法
(2)rpc对应结构体中的方法
(3)message对应的结构体
5.proto数据类型
(1)基本数据类型及其对应
.proto Type 解释 Go Type
double float64
float float32
int32 使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代 int32
uint32 使用变长编码 uint32
uint64 使用变长编码 uint64
sint32 使用变长编码,这些编码在负值时比int32高效的多 int32
sint64 使用变长编码,有符号的整型值。编码时比通常的int64高效 int64
fixed32 总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。 uint32
fixed64 总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。 uint64
sfixed32 总是4个字节 int32
sfixed64 总是8个字节 int64
bool bool
string 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本 string
bytes 可能包含任意顺序的字节数据 []byte
(2)数组类型:repeated关键字 类型 变量
message ArrayRequest {
repeated int64 a1 = 1;
repeated string a2 = 2;
repeated Request request_list = 3;
}
(3)键值对map类型:map<基本类型:类型>
message MapRequest {
map<int64, string> m_i_s = 1;
map<string, bool> m_i_b = 2;
map<string, ArrayRequest> m_i_arr = 3;
(4)嵌套类型
message Q1 {
mssage Q2{
string name2 = 2;
}
string name1 = 1;
Q2 q2 = 2;
}
二.grpc
1.gRPC作用:
微服务之间的通信,不同的服务之间会相互调用,分为客户端和服务端。
2.golang写RPC程序的3个基本条件
(1)结构体字段首字母要大写,可以别人调用
(2)函数名必须首字母大写
(3)函数第一参数是接收参数,第二个参数是返回给客户端的参数,必须是指针类型,还必须有一个返回值error
func (r *Rect) Perimeter(p Params, ret *int) error {
*ret = (p.Height + p.Width) * 2
return nil
}
3.grpc文件服务端和客户端
(1)服务端编写
1) 编写一个结构体,用于注册服务
2) 实现protobuf中的rpc定义的所有方法
3) 监听端口
4) 注册服务
(2)客户端编写
1) 建立连接
2) 初始化服务
3) 调用方法
//服务端
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"grpc/demo03/duo_proto"
"net"
)
// 编写一个结构体
type Vserver struct {
duo_proto.UnimplementedVideoServiceServer // 必须嵌入
}
// 实现proto中的方法
func (Vserver) Look(ctx context.Context, req *duo_proto.Request) (res *duo_proto.Response, err error) {
fmt.Println(req)//req接收客户端的请求参数
return &duo_proto.Response{
Name: "look", //res响应给客户端的消息
}, nil
}
// 定义一个结构体
type Oserver struct {
duo_proto.UnimplementedOrderServiceServer
}
func (Oserver) Buy(ctx context.Context, req *duo_proto.Request) (res *duo_proto.Response, err error) {
// 响应给客户端的消息
return &duo_proto.Response{
Name: "buy",
}, nil
}
func main() {
//监听端口
listen, err := net.Listen("tcp", ":8080")
if err != nil {
panic(err)
}
//注册服务
s := grpc.NewServer()
duo_proto.RegisterVideoServiceServer(s, &Vserver{})
duo_proto.RegisterOrderServiceServer(s, &Oserver{})
err = s.Serve(listen)
fmt.Println("程序运行在8080")
if err!=nil{
panic(err)
}
}
//客户端
package main
import (
"context"
"fmt"
"grpc/demo03/duo_proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
//建立连接
conn,err :=grpc.Dial(":8080",grpc.WithTransportCredentials(insecure.NewCredentials()))
if err!=nil{
panic(err)
}
defer conn.Close()
//调用方法
orderClient :=duo_proto.NewOrderServiceClient(conn)
res,err :=orderClient.Buy(context.Background(),&duo_proto.Request{
Name:"ff", //客户端请求的参数
})
fmt.Println(res,err)
//调用方法
videoClient :=duo_proto.NewVideoServiceClient(conn)
res,err =videoClient.Look(context.Background(),&duo_proto.Request{
Name:"ff",
})
fmt.Println(res,err)
}
4.原生rpc代码编写调用
(1)服务端
package main
import (
"log"
"net/http"
"net/rpc"
)
// 例题:golang实现RPC程序,实现求矩形面积和周长
type Params struct {
Width, Height int
}
type Rect struct{} //定义对象调用方法
// RPC服务端方法,求矩形面积
func (r *Rect) Area(p Params, ret *int) error {
*ret = p.Height * p.Width
return nil
}
// 周长
func (r *Rect) Perimeter(p Params, ret *int) error {
*ret = (p.Height + p.Width) * 2
return nil
}
// 主函数
func main() {
// 1.注册一个rect对象的服务
rpc.Register(new(Rect))
// 2.服务处理绑定到http协议上
rpc.HandleHTTP()
// 3.监听服务
err := http.ListenAndServe(":8000", nil)
}
(2)客户端:
package main
import (
"fmt"
"log"
"net/rpc"
)
// 传的参数
type Params struct {
Width, Height int
}
// 主函数
func main() {
// 1.连接远程rpc服务
conn, err := rpc.DialHTTP("tcp", ":8000")
// 2.调用方法
// 面积
ret := 0
err2 := conn.Call("Rect.Area", Params{50, 100}, &ret)
//第一个参数是类.方法名,第二个是传递的参数对象,第三个是指针
}
4.grpc的传输方式:
核心:客户端发送请求,接收响应,服务端接收请求,发送响应
(1)一问一答
1)Proto文件
syntax = "proto3";
option go_package = "/proto";
service Simple {
rpc Fun(Request)returns(Response){}
}
message Request {
string name = 1;
}
message Response {
string Text = 1;
}
2)服务端客户端同上
(2)服务端流式传输(流式响应)
使用场景:
1.客户端不知道什么时候结束
2.下载文件
Proto文件编写
//基础定义
syntax="proto3";
package proto;
option go_package="./proto";
//请求
message Request{
string Name =1;
}
//响应
message Response{
string Text =1;
}
//流式响应
service Streamres{
rpc StreamFunc(Request)returns(stream Response){}
}
Service编写
package main
import (
"grpc/demo4/stream_proto/proto"
"net"
"google.golang.org/grpc"
"fmt"
)
// 定义服务端结构体
type StreamresServer struct {
proto.UnimplementedStreamresServer
}
// 实现服务器端流式传输方法
func (s *StreamresServer) StreamFunc(req *proto.Request, stream proto.Streamres_StreamFuncServer) error {
//服务端发送数据
for i :=0;i<10;i++{
//发送res结构体
stream.Send(&proto.Response{
Text: fmt.Sprintf("第%d轮数据",i),
})
}
return nil
}
func main() {
//监听端口
listen,err :=net.Listen("tcp",":8080")
if err !=nil{
panic(err)
}
//注册service服务
s :=grpc.NewServer()
proto.RegisterStreamresServer(s,&StreamresServer{})
s.Serve(listen)
}
Client编写
package main
import (
"context"
"fmt"
"grpc/demo4/stream_proto/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
//建立连接
conn,err :=grpc.Dial(":8080", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err !=nil{
panic(err)
}
defer conn.Close()
//初始化客户端服务
client :=proto.NewStreamresClient(conn)
//调用方法,传输req结构体
stream,_ :=client.StreamFunc(context.Background(),&proto.Request{
Name:"张三",
})
//接收数据
for i :=0;i<10;i++{
res,err :=stream.Recv()
fmt.Println(res,err)
}
}
(3)客户端流式传输(原理和服务端流式相反)
使用案例:上传文件
service clientstrean{
rpc UploadFile(stream FileRequest)returns(Response){}
}
(4)双向流
使用案例:聊天
syntax = "proto3";
option go_package = "/proto";
message Request {
string name = 1;
}
message Response {
string Text = 1;
}
service BothStream{
rpc Chat(stream Request)returns(stream Response){}
}
//服务端
package main
import (
"fmt"
"grpc/double_stream/stream_proto/proto"
"log"
"net"
"google.golang.org/grpc"
)
type BothStream struct{
proto.UnsafeBothStreamServer
}
func (BothStream) Chat(stream proto.BothStream_ChatServer) error {
for i :=0;i<10;i++{
req,_ :=stream.Recv()
fmt.Println("接收到的请求:",req)
//发送响应
stream.Send(&proto.Response{
Text:"客户端返回的text字段",
})
}
return nil
}
func main() {
listen, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal(err)
}
server := grpc.NewServer()
proto.RegisterBothStreamServer(server, &BothStream{})
server.Serve(listen)
}
//客户端
package main
import (
"context"
"fmt"
"grpc/double_stream/stream_proto/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
//建立连接
conn, _ := grpc.Dial(":8080", grpc.WithTransportCredentials(insecure.NewCredentials()))
// 初始化客户端
client := proto.NewBothStreamClient(conn)
//调用方法,返回操作对象
stream,_:= client.Chat(context.Background())
for i := 0; i < 10; i++ {
stream.Send(&proto.Request{
Name: fmt.Sprintf("第%d次", i),
})
response, err := stream.Recv()
fmt.Println(response, err)
}
}
三.proto(定义方法等)+grpc(微服务注册方法)结合使用实现微服务调用
1.使用步骤,proto文件的多服务
(1)生成grpc的go文件:文件生成在proto文件定义的go包路径下
protoc --go_out=. --go-grpc_out=. path/to/video.proto(proto文件)
(2)编写服务端客户端文件
2.多proto文件生成与使用
(1)将方法和message进行拆分
(2)分别生成文件到同一个包下
(3)使用方式同上
protobuf和grpc使用
最新推荐文章于 2025-05-03 10:13:19 发布