gRPC框架

1、gRPC 与 Protobuf 介绍

  • 微服务架构中,由于每个服务对应的代码库是独立运行的,无法直接调用,彼此间
    的通信就是个大问题
  • gRPC 可以实现微服务, 将大的项目拆分为多个小且独立的业务模块, 也就是服务,
    各服务间使用高效的protobuf 协议进行RPC 调用, gRPC 默认使用protocol buffers,
    这是 google 开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格
    式如JSON)
  • 可以用 proto files 创建 gRPC 服务,用 message 类型来定义方法参数和返回类型

参考文章:gRPC教程

2、Mac下安装Protobuf和gRPC

参考文章:https://cloud.tencent.com/developer/article/2163004

brew命令安装

1. 安装的是 gRPC 的核心库
brew install grpc

2. 安装的是protocol编译器
brew install protobuf

3. 各个语言的代码生成工具,对于 Golang 来说,称为 protoc-gen-go
brew install protoc-gen-go
brew install protoc-gen-go-grpc

查看安装是否成功

apple@appledeMacBook-Pro ~ % protoc --version
libprotoc 25.1
apple@appledeMacBook-Pro ~ % protoc-gen-go --version
protoc-gen-go v1.31.0
apple@appledeMacBook-Pro ~ % protoc-gen-go-grpc --version
protoc-gen-go-grpc 1.3.0

如果查不到指令,检查一下环境变量

export GOPATH="/Users/apple/go"
export PATH=$PATH:$GOPATH/bin

3、Demo1

3.1 目录结构

实现服务端和客户端的数据传输

├── client
│   └── client.go
├── go.mod
├── go.sum
├── proto
│   └──  user.proto
└── server
    └── server.go

3.2 user.proto编写

// 编译指令:protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative proto/user.proto

// 版本号
syntax = "proto3";

// 生成文件所在的目录
option go_package="/proto";

// 制定生成 user.pb.go 的包名
package proto;

// 定义message服务端响应的数据格式,相当于结构体,
message UserInfoResponse {
    // 定义字段,相当于结构体的属性
    int32 id = 1;
    string name = 2;
    int32 age = 3;
    // 字段修饰符,repeated表示可以重复出现,就是可变数组,类似于切片类型
    repeated string hobbies = 4;
}

// 定义message客户端请求的数据格式,相当于结构体,
message UserInfoRequest {
    // 定义字段,相当于结构体的属性
    string name = 1;
}

// 定义一个service服务,相当于接口
service UserInfoService {
    // 定义一个rpc方法,相当于接口方法
    // 定义请求参数为UserInfoRequest,返回值为UserInfoResponse
    rpc GetUserInfo(UserInfoRequest) returns (UserInfoResponse);
}

执行编译指令

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

这是一个使用 Protocol Buffers(protobuf)和 Go gRPC 插件生成代码的示例命令。该命令根据 proto/user.proto 文件生成对应的 Go 代码。

这个命令的参数含义如下:

--go_out=.:指定生成的 Go 代码输出目录为当前目录。
--go_opt=paths=source_relative:设置生成的 Go 代码中的导入路径为相对于源文件的相对路径。
--go-grpc_out=.:指定生成的 Go gRPC 代码输出目录为当前目录。
--go-grpc_opt=paths=source_relative:设置生成的 Go gRPC 代码中的导入路径为相对于源文件的相对路径。
proto/user.proto:指定要生成代码的 protobuf 文件路径。

最后会在proto目录下生成user.pb.go和user_grpc.pb.go

  • user.pb.go:这个文件包含了用户自定义的消息类型的定义,它描述了在通信过程中需要传输的数据结构,比如用户信息、请求参数等。
  • user_grpc.pb.go:这个文件包含了用户自定义的服务接口的定义,它描述了可以远程调用的方法和参数,以及返回值等。

3.3 server服务端

package main

import (
	"context"
	"fmt"
	pb "main/proto"
	"net"

	"google.golang.org/grpc"
)

// 定义服务端实现约定的接口
type UserInfoService struct {
	pb.UnimplementedUserInfoServiceServer
}

// 实现服务端需要实现的接口
func (s *UserInfoService) GetUserInfo(ctx context.Context, req *pb.UserInfoRequest) (resp *pb.UserInfoResponse, err error) {

	// 服务端接收参数name,再进行业务操作
	name := req.Name

	if name == "zs" {
		resp = &pb.UserInfoResponse{
			Id:      1,
			Name:    name,
			Age:     18,
			Hobbies: []string{"swimming", "running"},
		}
	}

	err = nil
	return
}

func main() {
	// 1. 监听
	addr := "127.0.0.1:8080"
	lis, err := net.Listen("tcp", addr)
	if err != nil {
		fmt.Println("监听异常, err = ", err)
		return
	}

	fmt.Println("开始监听:", addr)

	// 2. 实例化gRPC
	s := grpc.NewServer()

	// 3. 在gRPC上注册微服务,第二个参数要接口类型的变量
	pb.RegisterUserInfoServiceServer(s, &UserInfoService{})

	// 4. 启动gRPC服务端
	s.Serve(lis)
}

3.4 client客户端

package main

import (
	"context"
	"fmt"
	pb "main/proto"

	"google.golang.org/grpc"
	//"google.golang.org/grpc/credentials/insecure"
)

func main() {
	// 1. 创建与gRPC服务器的连接
	addr := "127.0.0.1:8080"
	conn, err := grpc.Dial(addr, grpc.WithInsecure())
	if err != nil {
		fmt.Println("连接异常, err = ", err)
		return
	}
	defer conn.Close()

	// 2. 实例化gRPC客户端
	client := pb.NewUserInfoServiceClient(conn)

	// 3. 组装参数
	req := new(pb.UserInfoRequest)
	req.Name = "zs"

	// 4. 调用接口
	resp, err := client.GetUserInfo(context.Background(), req)
	if err != nil {
		fmt.Println("响应异常, err = ", err)
		return
	}
	fmt.Println("响应结果, resp = ", resp)
}

3.5 编译

先执行服务端,再执行客户端
在这里插入图片描述

4、Demo2

4.1 目录结构

├── client
│   └── client.go
├── go.mod
├── go.sum
├── proto
│   └── hello.proto
└── server
    └── server.go

4.2 hello.proto

// 编译指令:protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative proto/hello.proto

syntax = "proto3";

option go_package="/proto";

package Business;

service Hello {
  rpc Say (SayRequest) returns (SayResponse);
}

message SayResponse {
  string Message = 1;
}

message SayRequest {
  string Name = 1;
}

4.3 server服务端

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"main/proto"
	"net"
)

type server struct {
	proto.UnimplementedHelloServer
}

func (s *server) Say(ctx context.Context, req *proto.SayRequest) (*proto.SayResponse, error) {
	fmt.Println("request:", req.Name)
	return &proto.SayResponse{Message: "Hello " + req.Name}, nil
}

func main() {
	listen, err := net.Listen("tcp", ":8001")
	if err != nil {
		fmt.Printf("failed to listen: %v", err)
		return
	}
	s := grpc.NewServer()
	proto.RegisterHelloServer(s, &server{})
	//reflection.Register(s)

	defer func() {
		s.Stop()
		listen.Close()
	}()

	fmt.Println("Serving 8001...")
	err = s.Serve(listen)
	if err != nil {
		fmt.Printf("failed to serve: %v", err)
		return
	}
}

4.4 client客户端

package main

import (
	"bufio"
	"context"
	"fmt"
	"main/proto"
	"os"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func main() {

	var serviceHost = "127.0.0.1:8001"

	conn, err := grpc.Dial(serviceHost, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		fmt.Println(err)
	}
	defer conn.Close()

	client := proto.NewHelloClient(conn)
	rsp, err := client.Say(context.TODO(), &proto.SayRequest{
		Name: "BOSIMA",
	})

	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(rsp)

	fmt.Println("按回车键退出程序...")
	in := bufio.NewReader(os.Stdin)
	_, _, _ = in.ReadLine()
}

4.5 编译

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闲谈社

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值