RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求
服务,而不需要了解底层网络技术的协议。
简单来说,就是跟远程访问或者web请求差不多,都是一个client向远端服务器请求服务返回结果,但是web请求
使用的网络协议是http高层协议,而rpc所使用的协议多为TCP,是网络层协议,减少了信息的包装,加快了处理速
度。
golang本身有rpc包,可以方便的使用,来构建自己的rpc服务,下边是一个简单是实例,可以加深我们的理解
1.调用客户端句柄;执行传送参数
2.调用本地系统内核发送网络消息
3.消息传送到远程主机
4.服务器句柄得到消息并取得参数
5.执行远程过程
6.执行的过程将结果返回服务器句柄
7.服务器句柄返回结果,调用远程系统内核
8.消息传回本地主机
9.客户句柄由内核接收消息
10.客户接收句柄返回的数据
服务端
package main
import (
"net/http"
"net/rpc"
"net"
"github.com/astaxie/beego"
"io"
) //- 方法是导出的
//- 方法有两个参数,都是导出类型或内建类型
//- 方法的第二个参数是指针
//- 方法只有一个error接口类型的返回值
//
//func (t *T) MethodName(argType T1, replyType *T2) error
type Panda int;
func (this *Panda)Getinfo(argType int, replyType *int) error {
beego.Info(argType)
*replyType =1 +argType
return nil
}
func main() {
//注册1个页面请求
http.HandleFunc("/panda",pandatext)
//new 一个对象
pd :=new(Panda)
//注册服务
//Register在默认服务中注册并公布 接收服务 pd对象 的方法
rpc.Register(pd)
rpc.HandleHTTP()
//建立网络监听
ln , err :=net.Listen("tcp","127.0.0.1:10086")
if err != nil{
beego.Info("网络连接失败")
}
beego.Info("正在监听10086")
//service接受侦听器l上传入的HTTP连接,
http.Serve(ln,nil)
} //用来现实网页的web函数
func pandatext(w http.ResponseWriter, r *http.Request) {
io.WriteString(w,"panda")
}
客户端
package main
import (
"net/rpc"
"github.com/astaxie/beego"
)
func main() {
//rpc的与服务端建立网络连接
cli,err := rpc.DialHTTP("tcp","127.0.0.1:10086")
if err !=nil {
beego.Info("网络连接失败")
}
var val int
//远程调用函数(被调用的方法,传入的参数 ,返回的参数)
err =cli.Call("Panda.Getinfo",123,&val)
if err!=nil{
beego.Info("打call失败")
}
beego.Info("返回结果",val)
}
GRPC
在 gRPC里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多 RPC系统类似, gRPC也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。 gRPC客户端和服务端可以在多种环境中运行和交互 -从 google内部的服务器到你自己的笔记本,并且可以用任何 gRPC支持的语言 来编写。所以,你可以很容易地用 Java创建一个gRPC服务端,用 Go、 Python、Ruby来创建客户端。此外, Google最新 API将有 gRPC版本的接口,使你很容易
地将 Google的功能集成到你的应用里。
gRPC默认使用protoBuf,这是 Google开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如JSON)。正如你将在下方例子里所看到的,你用 proto files创建 gRPC服务,用 protoBuf消息类型来定义方法参
数和返回类型。你可以在 Protocol Buffers文档找到更多关于 protoBuf的资料。 虽然你可以使用 proto2 (当前默认的 protocol buffers版本 ),我们通常建议你在 gRPC里使用 proto3,因为这样你可以使用 gRPC支持全部范围的
的语言,并且能避免 proto2客户端与 proto3服务端交互时出现的兼容性问题,反之亦然。
protobuf协议定义
创建一个 protobuf package,如: my_rpc_proto;
在$GOPATH/src/下创建 /my_grpc_proto/文件夹里面创建 protobuf协议文件 helloServer.proto
#到工作目录
$ CD $GOPATH/src/
#创建目录
$ mkdir grpc/myproto
#进入目录
$ cd grpc/myproto
#创建proto文件
$ vim helloServer.proto
syntax = "proto3";
package my_grpc_proto;
service HelloServer{
// 创建第一个接口
rpc SayHello(HelloRequest)returns(HelloReplay){}
// 创建第二个接口
rpc GetHelloMsg(HelloRequest)returns(HelloMessage){}
}
message HelloRequest{
string name = 1 ;
}
message HelloReplay{
string message = 1;
}
message HelloMessage{
string msg = 1;
}
在当前文件下,编译 helloServer.proto文件
$ protoc --go_out=./ *.proto #不加grpc插件
$ protoc --go_out=plugins=grpc:./ *.proto #添加grpc插件
#对比发现内容增加
#得到 helloServer.pb.go文件
gRPC-Server编写
package main
import (
"net"
"fmt"
"google.golang.org/grpc"
pt "demo/grpc/proto"
"context"
)
const (
post = "127.0.0.1:18881"
)
//对象要和proto内定义的服务一样
type server struct{}
//实现RPC SayHello 接口
func(this *server)SayHello(ctx context.Context,in *pt.HelloRequest)(*pt.HelloReplay, error){
return &pt.HelloReplay{Message:"hello"+in.Name},nil
}
//实现RPC GetHelloMsg 接口
func (this *server) GetHelloMsg(ctx context.Context, in *pt.HelloRequest)(*pt.HelloMessage, error) {
return &pt.HelloMessage{Msg: "this is from server HAHA!"}, nil
}
func main() {
//监听网络
ln ,err :=net.Listen("tcp",post)
if err!=nil {
fmt.Println("网络异常",err)
}
// 创建一个grpc的句柄
srv:= grpc.NewServer()
//将server结构体注册到 grpc服务中
pt.RegisterHelloServerServer(srv,&server{})
//监听grpc服务
err= srv.Serve(ln)
if err!=nil {
fmt.Println("网络启动异常",err)
}
}
gRPC-Client编写
package main
import (
"google.golang.org/grpc"
pt "demo/grpc/proto"
"fmt"
"context"
)
const (
post = "127.0.0.1:18881"
)
func main() {
// 客户端连接服务器
conn,err:=grpc.Dial(post,grpc.WithInsecure())
if err!=nil {
fmt.Println("连接服务器失败",err)
}
defer conn.Close()
//获得grpc句柄
c:=pt.NewHelloServerClient(conn)
// 远程调用 SayHello接口
//远程调用 SayHello接口
r1, err := c.SayHello(context.Background(), &pt.HelloRequest{Name: "panda"})
if err != nil {
fmt.Println("cloud not get Hello server ..", err)
return
}
fmt.Println("HelloServer resp: ", r1.Message)
//远程调用 GetHelloMsg接口
r2, err := c.GetHelloMsg(context.Background(), &pt.HelloRequest{Name: "panda"})
if err != nil {
fmt.Println("cloud not get hello msg ..", err)
return
}
fmt.Println("HelloServer resp: ", r2.Msg)
}
#先运行 server,后运行 client
#得到以下输出结果
HelloServer resp: hellopanda
HelloServer resp: this is from server HAHA!
#如果反之则会报错