微服务组件之grpc
概述
grpc是一个高性能、开源、通用的rpc框架,面向移动和http/2设计,带来诸如双向流、流控、头部压缩、单tcp连接的多复用请求等特点,简单来说可以认为是一个比http好用的协议。
rpc
- 即远程过程调用协议,通过网络从远程计算机进行请求,和web请求差不多,但是web请求是使用的http高层协议,而rpc使用的多是tcp协议,是网络层的协议减少了一部分信息的包装,加快了网络处理的速度。golang自有rpc包,可以用于构建rpc服务。
rpc使用实例
- 服务端
package rpc
import (
"fmt"
"net"
"net/http"
"net/rpc"
)
type Panda int
/*
方法是可以导出的
方法要具有两个参数都是导出类型或者内建类型
方法的第二个参数必须是指针
方法只有一个返回值即error接口类型
*/
// Getinfo 函数关键字(对象)函数名(对端发送内容,返回对端内容)返回类型
func (this *Panda) Getinfo(argType int, replyType *int) error {
fmt.Println("打印对端端口发送内容:", argType)
*replyType = argType + 1
return nil
}
func Server() {
// 服务端必须注册对象 类实例化为对象
pd := new(Panda)
rpc.Register(pd)
// 连接到网络
rpc.HandleHTTP()
listener, err := net.Listen("tcp",":8888")
if err != nil {
fmt.Println("服务端启动失败err:",err)
return
}
http.Serve(listener,nil)
}
- 客户端
package rpc
import (
"fmt"
"net/rpc"
)
//rpc的客户端
func Client(){
//建立网络连接
cli, err := rpc.DialHTTP("tcp","127.0.0.1:8888")
if err != nil {
fmt.Println("网络连接失败...")
return
}
var pd int
err = cli.Call("Panda.Getinfo",1000,&pd)
if err!=nil{
fmt.Println("调用失败...")
return
}
fmt.Println("调用成功,pd:",pd)
}
grpc
- 在gprc中可以像调用本地对象一样直接调用例外一台机器在服务端的应用方法,能够更容易的创建分布式应用和服务,和其他rpc一样,也是差不多的思想。定义一个服务,指定其能够远程调用的方法(包括参数和返回类型),在服务端实现这个接口,并且运行一个grpc的服务器来处理客户的调用请求,在客户端拥有一个存根能够像服务器一样的方法。grpc的客户端和服务端可以在多种环境中进行运行和交互。比如,可以是哟个java创建一个grpc的服务端,可以用go、python、ruby等创建客户端,同时google的很多api也会有grpc的版本接口,可以很快地把google的功能集成到应用之中。
- 默认使用的就是protobuf,通常来说应该使用proto3,可以使用grpc范围内的全部语言,避免出现兼容性问题。
- 环境搭建
https://blog.youkuaiyun.com/sitebus/article/details/107481874
- 代码示例
- protobuf.proto
syntax = "proto3" ;
package gprc ;
//定义服务 protoc --go_out=./ *.proto 编译后不会有rpc的服务
// 添加插件 protoc --go_out=plugins=grpc:./ *.proto
service HelloServer{
// 打招呼的函数
rpc SayHello(HelloRequest)returns(HelloResponse){}
// 说名字的函数
rpc SayName(NameRequest)returns(NameResponse){}
}
message HelloRequest{
string name = 1;
}
message HelloResponse {
string msg = 1;
}
message NameRequest{
string name = 1;
}
message NameResponse{
string msg = 1;
}
- 服务端
package gprc
import (
"context"
"fmt"
"google.golang.org/grpc"
"net"
pb "unioj/utils/future/gprc/protobuf"
)
type server struct {
}
func (this *server)SayHello(ctx context.Context, in *pb.HelloRequest) (out *pb.HelloResponse,err error){
return &pb.HelloResponse{Msg: "hellooooooo"},err
}
func (this *server)SayName(ctx context.Context, in *pb.NameRequest) (out *pb.NameResponse,err error){
return &pb.NameResponse{Msg: in.Name+" good morning"},err
}
func Server() {
//grpc
listen,err := net.Listen("tcp",":8888")
defer func(){
err := recover();if err!=nil{
fmt.Println("网络建立失败",err)
}
}()
if err != nil{
panic(err)
}
srv := grpc.NewServer()
pb.RegisterHelloServerServer(srv,&server{})
err = srv.Serve(listen)
if err != nil {
panic(err)
return
}
}
- 客户端
package gprc
import (
"context"
"fmt"
"google.golang.org/grpc"
pb "unioj/utils/future/gprc/protobuf"
)
func Client() {
// 连接服务器
conn, err := grpc.Dial("127.0.0.1:8888", grpc.WithInsecure())
if err != nil {
fmt.Println(err)
return
}
defer conn.Close()
cli := pb.NewHelloServerClient(conn)
// 通过句柄调用函数
re,err := cli.SayHello(context.Background(),&pb.HelloRequest{Name: "ethan"})
if err != nil {
fmt.Println(err)
}
fmt.Println("调用结果:",re.GetMsg())
res,err := cli.SayName(context.Background(),&pb.NameRequest{
Name: "alban",
})
fmt.Println("第二次调用结果:",res.GetMsg())
}