golang基础-RPC结合Protobuf实例、GRPC实例

golang基础-httprpc、jsonrpc
golang基础-protobuf使用
以上2篇博客是了解httprpc、jsonrpc和protobuf的简单使用,今天就结合起来学习下

RPC、Protobuf相结合的例子

在简单的学习了RPC、Protobuf的规则之后,我们通过一个demo来学习下RPC、Protobuf是如何结合使用的
首先来看下这个demo的目录结构:

这里写图片描述

先来看下rpc.proto是如何定义数据的
数据很简单就一个string类型的value值

syntax = "proto3";
package go_protoc;
message String {
    string value = 1;
}

然后通过protoc ./rpc.proto --go_out=./进行编译为rpc.pb.go
在命令行执行如下命令即可

zhiliaodeMBP:go_protoc zhiliao$ protoc ./rpc.proto --go_out=./
zhiliaodeMBP:go_protoc zhiliao$ pwd
/Users/zhiliao/zhiliao/go/src/go_protoc
zhiliaodeMBP:go_protoc zhiliao$ 

然后基本数据类型有了,那么就着手去写服务端的代码server.go

我们先构造一个HelloService类型,其中的Hello方法用于实现打印功能:
其中Hello方法必须满足Go语言的RPC规则:
1、方法只能有两个可序列化的参数
2、其中第二个参数是指针类型
3、并且返回一个error类型
4、同时必须是公开的方法。

package main

import (
    "go_protoc"
    "log"
    "net"
    "net/rpc"
)

type HelloService struct {}

func (p *HelloService) Hello(request *go_protoc.String, reply *go_protoc.String) error {
    reply.Value = "hello:" + request.GetValue()
    return nil
}

然后就可以将HelloService类型的对象注册为一个RPC服务


func main() {
    rpc.RegisterName("HelloService", new(HelloService))

    listener, err := net.Listen("tcp", ":1234")
    if err != nil {
        log.Fatal("ListenTCP error:", err)
    }

    conn, err := listener.Accept()
    if err != nil {
        log.Fatal("Accept error:", err)
    }

    rpc.ServeConn(conn)
}

其中rpc.Register函数调用会将对象类型中所有满足RPC规则的对象方法注册为RPC函数,所有注册的方法会放在“HelloService”服务空间之下。然后我们建立一个唯一的TCP链接,并且通过rpc.ServeConn函数在该TCP链接上为对方提供RPC服务

下面是客户端请求HelloService服务的代码server.go

package main
import (
    "fmt"
    "go_protoc"
    "log"
    "net/rpc"
)

func main() {
    client, err := rpc.Dial("tcp", "localhost:1234")
    if err != nil {
        log.Fatal("dialing:", err)
    }

    var reply = &go_protoc.String{}
    var param = &go_protoc.String{
        Value:"hello",
    }

    err = client.Call("HelloService.Hello", &param, &reply)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(reply)
}

首选是通过rpc.Dial拨号RPC服务,然后通过client.Call调用具体的RPC方法。在调用client.Call时,第一个参数是用点号链接的RPC服务名字和方法名字,第二和第三个参数分别我们定义RPC方法的两个参数。

然后我们启动server.go,然后在启动client.go,输出结果如下:
这里写图片描述

GRPC插件由来

回顾我们之前的RPC接口部分的内容,当时我们花费了极大的力气去给RPC服务增加安全的保障。最终得到的更安全的RPC接口的代码本身就非常繁琐的使用手工维护,同时全部安全相关的代码只适用于Go语言环境!既然使用了Protobuf定义的输入和输出参数,那么RPC服务接口是否也可以通过Protobuf定义呢?其实用Protobuf定义语言无关的RPC服务接口才是它真正的价值所在!

下面看下rpc.proto文件,通过Protobuf来定义HelloService服务:

syntax = "proto3";

package go_protoc;

message String {
    string value = 1;
}

service HelloService {
    rpc Hello (String) returns (String);
}

但是重新生成的Go代码并没有发生变化。这是因为世界上的RPC实现有千万种,protoc编译器并不知道该如何为HelloService服务生成代码。

不过在protoc-gen-go内部已经集成了一个叫grpc的插件,可以针对grpc生成代码

$ protoc --go_out=plugins=grpc:. hello.proto

在生成的代码中多了一些类似HelloServiceServer、HelloServiceClient的新类型。这些类型是为grpc服务的,并不符合我们的RPC要求。

// HelloServiceServer is the server API for HelloService service.
type HelloServiceServer interface {
    Hello(context.Context, *String) (*String, error)
}

func RegisterHelloServiceServer(s *grpc.Server, srv HelloServiceServer) {
    s.RegisterService(&_HelloService_serviceDesc, srv)
}
type HelloServiceClient interface {
    Hello(ctx context.Context, in *String, opts ...grpc.CallOption) (*String, error)
}

type helloServiceClient struct {
    cc *grpc.ClientConn
}

func NewHelloServiceClient(cc *grpc.ClientConn) HelloServiceClient {
    return &helloServiceClient{cc}
}

GRPC库依赖下载

安装官方安装命令:
go get google.golang.org/grpc
是安装不起的,会报:

package google.golang.org/grpc: unrecognized import path "google.golang.org/grpc"(https fetch: Get https://google.golang.org/grpc?go-get=1: dial tcp 216.239.37.1:443: i/o timeout)

原因是这个代码已经转移到github上面了,但是代码里面的包依赖还是没有修改,还是google.golang.org这种,

所以不能使用go get的方式安装

git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc  
git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net  
git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text  
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}  
git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto  

cd $GOPATH/src/  
go install google.golang.org/grpc 

到这里还会报package golang.org/x/text: unrecognized import path “golang.org/x/text” (https fetch: Get https://golang.org/x/text?go-get=1: dial tcp 119.28.87.227:443: i/o timeout)错误

获取 golang.org/x/net 包,其实只需要以下步骤:

mkdir -p $GOPATH/src/golang.org/x
cd $GOPATH/src/golang.org/x
git clone https://github.com/golang/text.git

GRPC例子学习

上图一个项目目录结构
这里写图片描述

首先看下rpc.proto数据

syntax = "proto3";

package go_protoc;

message String {
    string value = 1;
}

service HelloService {
    rpc Hello (String) returns (String);
}

然后进行通过protoc --go_out=plugins=grpc:. rpc.proto进行编译

zhiliaodeMBP:go_protoc zhiliao$ protoc --go_out=plugins=grpc:. rpc.proto
zhiliaodeMBP:go_protoc zhiliao$ pwd
/Users/zhiliao/zhiliao/go/src/go_protoc
zhiliaodeMBP:go_protoc zhiliao$ 

接下来看服务端的代码server.go

package main

import (
    "context"
    "go_protoc"
    "google.golang.org/grpc"
    "log"
    "net"
)



type HelloServiceImpl struct{}

func (p *HelloServiceImpl) Hello(
    ctx context.Context, args *go_protoc.String,
) (*go_protoc.String, error) {
    reply := &go_protoc.String{Value: "hello:" + args.GetValue()}
    return reply, nil
}



func main() {

    grpcServer := grpc.NewServer()
    go_protoc.RegisterHelloServiceServer(grpcServer, new(HelloServiceImpl))

    lis, err := net.Listen("tcp", ":1234")
    if err != nil {
        log.Fatal(err)
    }
    grpcServer.Serve(lis)
}

首先是通过grpc.NewServer()构造一个GRPC服务对象,然后通过GRPC插件生成的RegisterHelloServiceServer函数注册我们实现的HelloServiceImpl服务。然后通过grpcServer.Serve(lis)在一个监听端口上提供GRPC服务。

接下来看客户端的代码client.go

package main

import (
    "fmt"
    "go_protoc"
    "google.golang.org/grpc"
    "log"
    "golang.org/x/net/context"
)

func main() {
    conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    client := go_protoc.NewHelloServiceClient(conn)
    reply, err := client.Hello(context.Background(), &go_protoc.String{Value: "hello-sadly"})
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(reply.GetValue())
}

其中grpc.Dial负责和GRPC服务建立链接,然后NewHelloServiceClient函数基于已经建立的链接构造HelloServiceClient对象。返回的client其实是一个HelloServiceClient接口对象,通过接口定义的方法就可以调用服务端对应的GRPC服务提供的方法。

启动服务端,然后启动客户端结果输出如下:
这里写图片描述

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页