Golang之远程过程调用--gRPC-go库的使用

      之前我在做自己的项目的时候只是做简单的socket编程或者是http编程,做的基本是直接发起一个网络请求,把请求的数据发送到后端,然后后端接收到这些数据并进行处理,再将结果封装成响应数据返回给前端去接收并解析、渲染等。但远程过程调用确是另一回事,它不仅仅是发送数据给远端,而是直接调用远端的代码来执行。举个例子:网络中存在A和B两台不同的主机,A主机跑的代码a调用某个名为abc的方法,这时候abc方法通过网络请求访问B主机跑的代码中某个名为abc的方法(我们称之为“服务”),B主机的abc方法在接收到方法参数之后进行解析处理,然后返回给A主机,这时候A主机接收到内容之后又再次进行处理,并完成功能,这个过程就完成了A主机访问B主机的代码。虽然看起来还是跟上面一开始提到的socket编程或者http编程,但这里面是有实质性区别的:socket编程和http编程是客户端单纯传递数据给服务端,服务端接收并处理,最后返回响应数据给客户端。远程过程调用(Remote Procedure Call)是客户端通过网络直接调用服务端的服务(代码)直接处理。
      在正式写代码之前我们需要安装一些扩展,也就是go语言开发所需的依赖包以及开发过程中所需要的名为protoc的工具。
      为什么在这里要提到protoc工具呢?先说说Protocol Buffer,它和Json、Xml一样都是一种数据格式,不过Protocol Buffer会编译成二进制数据流,也就说它作为数据传输的实质是二进制数据流,众所周知计算机在处理数据的时候处理二进制数据流的效率是最高的,因此使用它来做数据交换是一种高效的方案。protoc就是一个编译工具,可以将.proto文件进行编译成其他编程语言的文件,具体是什么要看你下的什么版本的工具了。
      protoc下载地址
Golang之远程过程调用--gRPC-go库的使用_第1张图片
下载完整个protoc的压缩文件后就解压到指定的目录,如下:
Golang之远程过程调用--gRPC-go库的使用_第2张图片
接着进入bin目录可以看到一个可执行文件:
Golang之远程过程调用--gRPC-go库的使用_第3张图片
这个工具就是我们用来编译.proto文件的工具,为了方便后面的命令操作,直接将该bin目录所在路径添加进环境变量path中。然后在命令行中输入protoc --verison,若可以查看protoc工具的版本则表示安装成功。
在这里插入图片描述
因为我们需要用该工具去编译.proto文件成go文件,因此需要安装插件。(为避免包冲突,我们先安装gRpc-go依赖库的包)

gRPC-go依赖库安装:

如果你的网络能够访问google官网的话可以用:
go get -­u ­-v google.golang.org/grpc

如果被墙了的话就用下面的方法一个一个下载包:($GOPATH只的是你的gopath所指定的目录)
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 
git clone https://github.com/golang/sys.git $GOPATH/src/golang.org/x/sys
go get -u github.com/golang/protobuf/proto
go get -u github.com/golang/protobuf/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
这些安装的都是一些关于grpc的和对protobuf的支持。

前面protoc工具已经安装完成了,但是现在它还不能编译成go文件,所以我们需要安装一个go插件:

如果可以访问的话直接安装:
go get ­-u github.com/golang/protobuf/protoc­-gen-­go
如果访问不了则:(src是gopath下的src目录)
mkdir ­p src/google.golang.org/
cd src/google.golang.org
git clone https://github.com/google/go-­genproto
git clone https://github.com/google/genproto

这样一来所有的go开发所需要的的依赖包、protoc工具插件go扩展就全部安装完毕了,接下来进入开发环节。(Tip:上面的命令有一些是重复的,不用管,直接这么走就可以了,如果有提示已经安装了的话就跳过该命令,上面的这些命令所安装的依赖包和扩展是足够做接下来的开发的

接下来进入开发环节:
gRPC这个做的就是远程过程调用的开发,因此肯定少不了client和server,但我们先不写这两个,我们需要先写一份.proto文件,然后使用protoc工具用命令将.proto文件编译成.pb.go的go语言文件(这个文件会将我们在.proto中定义好的message转换成结构体将service转换成服务)。.proto文件的编写模式如下:
Golang之远程过程调用--gRPC-go库的使用_第4张图片
从图中可以看出,我们客户端发起请求所携带的数据是封装成一个结构体的,因此需要实现定义一个作为请求参数的结构体。服务端响应数据给客户端同样需要将响应数据封装成一个结构体,因此响应参数的结构体也需要定义。
上面的.proto写完之后就可以借助protoc工具使用下面命令进行编译了:

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

protoc表示protoc工具的可执行文件,我们肯定是用这个工具来编译的(这里可以直接使用protoc是因为前面配置过环境变量path了)
--go_out表示输出成go文件
plugins=grpc指定插件grpc,使生成的go文件能够兼容grpc
冒号之后的第一个参数表示输出路径,即生成的.pb.go文件的存放位置,.表示当前目录
最后一个参数表示需要编译的.proto文件

编译后的.pb.go文件的内容全部自动生成,即使后续开发也无需改动

Golang之远程过程调用--gRPC-go库的使用_第5张图片
客户端对象创建:
.pb.go中会根据我们定义好的服务,创建一个对应的客户端,在我们访问服务端代码中调用该函数即可完成客户端对象的创建
在这里插入图片描述
在.proto中所定义的服务中,我们定义了一个服务所提供的操作,这个操作虽然由服务器端实现,但是并不代表客户端这边就直接调用服务器端的代码了,客户端这边同样需要实现,不过这个客户端实现在生成.pb.go文件中已经帮我们实现好了,如下:
Golang之远程过程调用--gRPC-go库的使用_第6张图片
我们在客户端调用的服务其实是来到了本地的.pb.go中调用这个方法,然后方法内的Invoke方法才是真正地做网络请求,在服务端处理完毕之后会返回内容给我们客户端调用处,接下来才做进一步的处理。

服务端代码:

package main

import (
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/reflection"
	"log"
	"net"
	"proto"
	"strconv"
)

//服务提供者,可以理解为我们具体的服务操作需要作为一个对象的方法,所以才定义这个作为服务对象的结构体
type serviceProvicder struct {

}

//方法名同客户端调用的服务名,方法参数列表和返回值列表固定写法,至于Request和Response是在.proto中定义
//好的2个结构体,Request接收的是请求参数构成的结构体,Response返回的是响应参数构成的结构体
func (sp *serviceProvicder)DealMsg(ctx context.Context,request *proto.Request)(*proto.Response,error){

	s := "My name is " + request.Name + ".I'm " + strconv.Itoa(int(request.Age)) + " years old and I'm " + request.Sex

	return &proto.Response{Msg:s},nil
}

func main(){

	//监听本地8888端口
	listener,err := net.Listen("tcp",":8888")

	if err != nil {
		log.Fatal(err.Error())
	}
	//grpc已经开发者提供了服务端对象的创建方法
	server := grpc.NewServer()
	//服务端对象注册指定服务,下面的方法是.pb.go中自动生成的,第二个参数是服务提供者
	proto.RegisterCommcsServer(server,&serviceProvicder{})
	//将服务注册到grpc上
	reflection.Register(server)
	//server.Serve(listener)表示启动服务端,listener表示指定监听的地址和端口
	if err := server.Serve(listener);err != nil {
		log.Fatal(err.Error())
	}
}

客户端的代码就好写多了:

package main

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

func main(){
	//创建连接,第一个参数指定连接地址和端口,第二个参数是配置信息,基本上统一写这个就可以了
	conn,err := grpc.Dial("localhost:8888",grpc.WithInsecure())

	if err != nil {
		log.Fatal(err.Error())
	}

	defer conn.Close()

	//创建操作指定服务的客户端对象
	client := proto.NewCommcsClient(conn)

	//客户端调用服务,参数列表和返回值列表与服务端定义的一致
	response,err := client.DealMsg(context.Background(),&proto.Request{Name:"Alex",Age:23,Sex:"fale"})

	if err != nil {
		log.Fatal(err.Error())
	}

	fmt.Println(response.Msg)
}

至此代码就编写完毕了,直接编译客户端和服务端的程序成可执行文件,然后可以看到我们的功能实现了,从上面的代码上看,就是客户端把含有姓名、年龄、性别的信息封装成一个Request对象作为请求参数,通过调用服务直接传递给服务端处理,服务端将这些数据组装成一句话再返回给客户端打印。结果如图:
运行服务端程序:此时处于监听状态
在这里插入图片描述
运行客户端程序:
在这里插入图片描述
可以看到客户端成功接收到来自服务端的组装结果,由此使用gRPC-go库的go版本的远程过程调用就实现了

你可能感兴趣的:(Golang)