go-gRPC 初体验

旧梦发癫
微服务想必大家都不陌生了。刚接触到golang,那么在golang中怎么使用微服务呢。这里使用gRRC框架写了一个简单的例子。
环境要求
示例代码 iris-grpc-example

项目结构

iris-grpc-example
│  .gitignore
│  go.mod
│  go.sum
│  README.md
│
├─proto
│      README.md
│      servers.pb.go
│      servers.proto
│
└─servers
        main.go
        services.go

通过目录可以看到这里使用了go mod,当前golang版本 1.13

proto
使用Protobuf定义了通信的IDL,可以理解为rpc中接口定义。
  • protocol buf

可扩展的序列化数据结构,在通信协议中使用的比较广泛。比json更快,更小。比xml更简洁。

servers.proto
syntax = "proto3";
package proto;
message Id {
    int32 id=1;
}
message Name {
    string name=1;
}
message Age {
    int32 age=1;
}
// 用户变量
message User {
    int32 id=1;
    string name=2;
    int32 age=3;
}
// 用户参数
message UserParams{
    Name name=1;
    Age age=2;
}
// 声明那些方法可以使用rpc
service ServiceSearch{
    rpc SaveUser(UserParams) returns (Id){}
    rpc UserInfo(Id) returns (User){}
}
简单说明

syntax = "proto3";声明了proto语法版本。

package声明包名

message Id {
    int32 id=1;
}

接口中使用的变量声明 变量名称:Id,类型:int32,等号后面表示字段编号为 1

service ServiceSearch{
    rpc SaveUser(UserParams) returns (Id){}
    rpc UserInfo(Id) returns (User){}
}

声明了两个函数 SaveUser(),UserInfo()是使用RPC协议,接收的参数与返回参数分别为什么。

servers.go

该文件是使用servers.proto编译生成的
。在完成servers.proto之后 在proto目录下执行

protoc --go_out=plugins=grpc:. *.proto

  • --go_out 指定生成go文件,这里会使用到Protoc plugin-go
  • plugins 参数告诉生成RPC代码,并指定了框架为grpc
编译命令执行完之后,就会生成servers.go。而我们在go的模块中实际使用的代码也就是这个文件。

如果有兴趣的同学可以看看里面的代码,主要就是一些参数定义【我们在proto中所定义的】,还有一个接口的声明。

......
// ServiceSearchServer is the server API for ServiceSearch service.
type ServiceSearchServer interface {
    SaveUser(context.Context, *UserParams) (*Id, error)
    UserInfo(context.Context, *Id) (*User, error)
}

// UnimplementedServiceSearchServer can be embedded to have forward compatible implementations.
type UnimplementedServiceSearchServer struct {
}

func (*UnimplementedServiceSearchServer) SaveUser(ctx context.Context, req *UserParams) (*Id, error) {
    return nil, status.Errorf(codes.Unimplemented, "method SaveUser not implemented")
}
func (*UnimplementedServiceSearchServer) UserInfo(ctx context.Context, req *Id) (*User, error) {
    return nil, status.Errorf(codes.Unimplemented, "method UserInfo not implemented")
}

func RegisterServiceSearchServer(s *grpc.Server, srv ServiceSearchServer) {
    s.RegisterService(&_ServiceSearch_serviceDesc, srv)
}
......

可以看到最后有一个 RegisterServiceSearchServer注册服务的方法,接受一个grpc.Server与一个ServiceSearchServer的接口。而我们在servers/services.go中,主要就是实现ServiceSearchServer这个接口,并通过RegisterServiceSearchServer将接口实现的函数注册rpc服务中。

servers
rpc 接口的实现与调用
services.go 接口的实现
package main

import (
    "context"
    "fmt"
    "google.golang.org/grpc"
    "iris-grpc-example/proto"
    "log"
    "math/rand"
    "net"
)

type ServiceSearch struct{}

func main() {
    listen, err := net.Listen("tcp", "127.0.0.1:9527")
    if err != nil {
        log.Fatalf("tcp listen failed:%v", err)
    }
    server := grpc.NewServer()
    fmt.Println("services start success")
    proto.RegisterServiceSearchServer(server, &ServiceSearch{})
    server.Serve(listen)

}

//保存用户
func (Service *ServiceSearch) SaveUser(ctx context.Context, params *proto.UserParams) (*proto.Id, error) {
    id := rand.Int31n(10) //随机生成id 模式保存成功
    res := &proto.Id{Id: id}
    fmt.Printf("username:%s,age:%d\r\n", params.Name, params.Age)
    return res, nil
}

func (Service *ServiceSearch) UserInfo(ctx context.Context, id *proto.Id) (*proto.User, error) {
    res := &proto.User{Id:id.GetId(),Name:"test",Age:31}
    return res, nil
}
  • 实现ServiceSearchServer接口,在代码中声明了一个ServiceSearch来实现了 ServiceSearchServer接口。
  • SaveUser,实现了proto中定义的SaveUser方法,需要注意的是这里需要返回两个参,数第一个是我们预先定好的参数,第二个为定义的错误信息。
  • main 函数声明当前服务的ip以及端口,并创建了一个grpc server然后通过proto.RegisterServiceSearchServer(server, &ServiceSearch{})ServiceSearch注册到grpc
main.go 接口的调用
package main

import (
    "context"
    "github.com/kataras/iris/v12"
    "google.golang.org/grpc"
    "iris-grpc-example/proto"
    "log"
)

var client proto.ServiceSearchClient

func main() {
    app := iris.New()
    app.Logger().SetLevel("debug") //debug
    app.Handle("GET", "/testSaveUser", saveUser)
    app.Handle("GET", "/testUserInfo", userInfo)
    app.Run(iris.Addr("127.0.0.1:8080"))
}

func saveUser(ctx iris.Context) {
    params := proto.UserParams{}
    params.Age = &proto.Age{Age: 31}
    params.Name = &proto.Name{Name: "test"}
    res, err := client.SaveUser(context.Background(), &params)
    if err != nil {
        log.Fatalf("client.SaveUser err: %v", err)
    }
    ctx.JSON(res)
}
func userInfo(ctx iris.Context) {
    res, err := client.UserInfo(context.Background(), &proto.Id{Id: 1})
    if err != nil {
        log.Fatalf("client.userInfo err: %v", err)
    }
    ctx.JSON(res)
}

func init() {
    connect, err := grpc.Dial("127.0.0.1:9527", grpc.WithInsecure())
    if err != nil {
        log.Fatalln(err)
    }
    client = proto.NewServiceSearchClient(connect)

}

这里使用了iris作为了一个client。与传统http的区别主要是在

func init() {
    connect, err := grpc.Dial("127.0.0.1:9527", grpc.WithInsecure())
    if err != nil {
        log.Fatalln(err)
    }
    client = proto.NewServiceSearchClient(connect)

}

这里创建了一个rpcclient。在使用的时候我们只需要调用services里面已经写好的函数即可。

测试

\iris-grpc-example>cd servers

开启服务:go run services.go
开启Client:go run main.go
浏览器访问
http://127.0.0.1:8080/testSaveUser
{
"id": 1
}
http://127.0.0.1:8080/testUserInfo
{
"id": 1,
"name": "test",
"age": 31
}

期待一起交流
短腿子猿

阅读 2.8k

逸梦
一个PHP程序猿的文章栏目

PHP、Golang

678 声望
76 粉丝
0 条评论

PHP、Golang

678 声望
76 粉丝
文章目录
宣传栏