大地老师Golang入门实战系列教程地址:https://www.itying.com/category-90-b0.html
五. GRPC框架
gRPC是一个高性能、开源和通用的 RPC 框架,面向移动端和 HTTP/2 设计。目前提供 C、Java 和 Go
语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby,
Objective-C, PHP 和 C# 支持。
:::info
**GRPC 特点: **
1、提供几乎所有主流语言的实现,打破语言隔阂
2、基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特
3、默认使用Protocol Buffers序列化,性能相较于RESTful Json好很多
4、工具链成熟,代码生成便捷,开箱即用
5、支持双向流式的请求和响应,对批量处理、低延时场景友好
这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
有了GRPC, 我们可以一次性的在一个 .proto 文件中定义服务,并使用任何支持它的语言去实现客户端 和服务端。GRPC默认使用protocol buffers,它是google开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如JSON),可以用proto files创建gRPC服务,用protocol buffers消息类型来定义方法参数和返回类型。
:::
在GRPC客户端可以直接调用不同服务器上的远程程序,就像调用本地程序一样,很容易构建分布式应
用和服务。和很多RPC系统一样,服务负责实现定义好的接口并处理客户端请求,客户端根据接口描述
直接调用需要的服务。客户端和服务器可以分别使用gRPC支持的不同语言实现。
:::info
**参考资料 **
gRPC 官方文档中文版:http://doc.oschina.net/grpc?t=60133
gRPC官网:https://grpc.io
:::
5.2 GRPC使用
:::info
GRPC使用的包是 google.golang.org/grpc ,我们可以在项目中使用 go get -u -v
google.golang.org/grpc 来下载包,也可以使用go mod tidy下载包。
如果从Protobuf的角度看,GRPC只不过是一个针对service接口生成代码的生成器。接着我们来学习
一下GRPC的用法。这里我们创建一个简单的proto文件,定义一个HelloService接口:
:::
新建pb/greeter.proto
syntax = "proto3";
option go_package = "./service"; //表示pb目录下面生成greeter.pb.go
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
对proto文件进行编译:
$ protoc --go_out=plugins=grpc:. *.proto
GRPC插件会为服务端和客户端生成不同的接口:
//客户端接口
type GreeterClient interface {
SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption)
(*HelloReply, error)
}
//服务器接口
type GreeterServer interface {
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}
我们接着可以基于他们给的服务端接口重新实现GreeterServer服务:
type Server struct{}
func (s *Server) SayHello(ctx context.Context, in *service.HelloRequest)
(*service.HelloReply, error) {
return &service.HelloReply{Message: "hello " + in.Name}, nil
}
GRPC的启动流程和RPC的启动流程类似,服务端代码如下:
func main() {
//1. 初始一个 grpc 对象
grpcServer := grpc.NewServer()
//2. 注册服务
proto.RegisterGreeterServer(grpcServer, new(Server))
//3. 设置监听, 指定 IP、port
listener, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil {
fmt.Println("Listen err:", err)
return
}
defer listener.Close()
//4. 启动服务。---- serve()
grpcServer.Serve(listener)
}
然后我们就可以通过客户端来连接GRPC服务了:
func main() {
// 连接服务器
conn, err := grpc.Dial(":8080", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
fmt.Printf("连接服务端失败: %s", err)
return
}
defer conn.Close()
// 新建一个客户端
c := service.NewGreeterClient(conn)
// 调用服务端函数
r, err := c.SayHello(context.Background(), &service.HelloRequest {
Name: "itying"
})
if err != nil {
fmt.Printf("调用服务端代码失败: %s", err)
return
}
fmt.Printf("调用成功: %s", r.Message)
}
:::info
credentials.NewClientTLSFromFile :从输入的证书文件中为客户端构造TLS凭证。
grpc.WithTransportCredentials :配置连接级别的安全凭证(例如,TLS/SSL),返回一个
DialOption,用于连接服务器。
:::
**5.3 GRPC实现一个获取导航的微服务 **
:::info
要求通过GetOneNav获取一个导航,通过GetNavList获取导航列表
新建proto/nav.proto
:::
syntax = "proto3";
option go_package = "./nav;nav";
service Nav {
rpc GetOneNav(OneNavRequest) returns (OneNavResponse) {}
rpc GetNavList(NavListRequest) returns (NavListResponse) {}
}
//定义NavModel 字段和数据库表统一
message NavModel {
int32 id = 1;
string title = 2;
string url = 3;
int32 position = 4;
int32 status = 5;
}
//获取一条数据传入参数
message OneNavRequest {
int32 id = 1;
}
//获取一条数据返回参数
message OneNavResponse {
NavModel oneNav=1;
}
//定义获取数据传入的参数
message NavListRequest {
}
//定义获取数据返回的参数
message NavListResponse {
repeated NavModel navList = 1;
}
编译proto 生成go文件
protoc --go_out=plugins=grpc:. *.proto
server端
package main
import (
"context"
"fmt"
proto "nav/proto/nav"
"net"
"google.golang.org/grpc"
)
type Nav struct {
}
func (n *Nav) GetOneNav(ctx context.Context, req *proto.OneNavRequest)
(*proto.OneNavResponse, error) {
fmt.Println(req.Id)
oneNav := &proto.NavModel{
Id: 1,
Title: "导航1",
Url: "www.baidu.com",
Status: 1,
}
return &proto.OneNavResponse{
OneNav: oneNav,
}, nil
}
func (n *Nav) GetNavList(ctx context.Context, req *proto.NavListRequest)
(*proto.NavListResponse, error) {
var NavList []*proto.NavModel
NavList = append(NavList, &proto.NavModel{
Id: 1,
Title: "导航1",
Url: "www.baidu.com",
Status: 1,
})
return &proto.NavListResponse{
NavList: NavList,
}, nil
}
func main() {
//1. 初始一个 grpc 对象
grpcServer := grpc.NewServer()
//2. 注册服务
proto.RegisterNavServer(grpcServer, new(Nav))
//3. 设置监听, 指定 IP、port
listener, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil {
fmt.Println("Listen err:", err)
return
}
defer listener.Close()
//4. 启动服务。---- serve()
grpcServer.Serve(listener)
}
client端
package main
import (
"context"
"fmt"
navProto "client/proto/nav"
"google.golang.org/grpc"
)
func main() {
// 连接服务器
conn, err := grpc.Dial(":8080", grpc.WithInsecure())
if err != nil {
fmt.Printf("连接服务端失败: %s", err)
return
}
defer conn.Close()
// 新建一个客户端
c := navProto.NewNavClient(conn)
// 调用服务端函数
r, err := c.GetOneNav(context.Background(), &navProto.OneNavRequest{Id: 1})
if err != nil {
fmt.Printf("调用服务端代码失败: %s", err)
return
}
fmt.Printf("调用成功: %s", r)
}