《Go-micro微服务框架入门教程》学习笔记 | gRPC

Posted COCOgsta

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Go-micro微服务框架入门教程》学习笔记 | gRPC相关的知识,希望对你有一定的参考价值。

学习视频来源:Golang微服务框架go-micro教程

个人在学习的同时,也验证了视频中的实验部分,现将授课笔记和实验笔记整理下来。

gRPC是一个高性能、开源和通用的RPC框架,面向移动和HTTP/2设计。gRPC基于HTTP/2标准设计,带来诸如双向流、流控、头部压缩、单TCP连接上的多复用请求等待。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。

RPC

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 (
	"fmt"
	"io"
	"net"
	"net/http"
	"net/rpc"
)
type Panda int
func (this *Panda)Getinfo(argType int, replyType *int)error{
	fmt.Println("Print peer send message:", argType)
	*replyType = argType + 12306
	return nil
}
func pandatext(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "hello world hello panda")
}
func main() {
	http.HandleFunc("/panda", pandatext)
	pd := new(Panda)
	rpc.Register(pd)
	rpc.HandleHTTP()
	ln, err := net.Listen("tcp", ":10086")
	if err!=nil {
		fmt.Println("Network failure")
	}
	http.Serve(ln, nil)
}

客户端

package main
import (
	"fmt"
	"net/rpc"
)
func main() {
	cli, err := rpc.DialHTTP("tcp", "127.0.0.1:10086")
	if err != nil {
		fmt.Println("Network connect failure")
	}
	var pd int
	err = cli.Call("Panda.Getinfo", 10086, &pd)
	if err!=nil {
		fmt.Println("Call failure")
	}
	fmt.Println("Result is :", pd)
}

运行结果

服务端打印信息

客户端打印信息

GPRC是什么?

在gRPC里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多RPC系统类似,gRPC也是基于以下理念:定义一个服务,指定能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个gRPC服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。gRPC客户端和服务端可以在多种环境中运行和交互 - 从google内部的服务器到你自己的笔记本,并且可以用任何gRPC支持的语言来编写。所以,你可以很容易地用java创建一个gRPC服务端,用Go、Python、Ruby来创建客户端。此外,Google最新API将有gRPC版本的接口,使你很容易地将Google的功能集成到你的应用里。

GRPC使用protocol buffers

gRPC使用默认protobuf,这是Google开源的一套成熟的结构数据序列化机制(当然也可以使用其它数据格式如JSON)。正如你将在下方例子里所看到的,你用proto files创建gRPC服务,用protobuf消息类型来定义方法参数和返回类型。你可以在Protocol Buffers文档找到更多关于protoBuf的资料。虽然你可以使用proto2(当前默认的protocol buffers版本),我们通常建议你在gRPC里使用proto3,因为这样你可以使用gRPC支持全部范围的语言,并且能避免proto2客户端与proto3服务端交互时出现的兼容性问题,反之亦然。

环境搭建

git clone https://github.com/grpc/grpc-go.git

启动服务端

cd grpc/examples/helloworld/greeter_server
go run main.go

启动客户端

grpc/examples/helloworld/greeter_client
go run main.go

客户端代码介绍

/*
 *
 * Copyright 2015 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
// Package main implements a client for Greeter service.
package main
import (
        "context"
        "log"
        "os"
        "time"
        "google.golang.org/grpc"
        pb "google.golang.org/grpc/examples/helloworld/helloworld"
        //这是引用编译好的protobuf
)
const (
        address     = "localhost:50051"
        defaultName = "world"
)
func main() {
        // 建立到服务器的连接
        conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
        if err != nil {
                log.Fatalf("did not connect: %v", err)
        }
        //延迟关闭连接
        defer conn.Close()
        //调用protobuf的函数创建客户端连接句柄
        c := pb.NewGreeterClient(conn)
        //联系服务器并打印它的响应
        name := defaultName
        if len(os.Args) > 1 {
                name = os.Args[1]
        }
        ctx, cancel := context.WithTimeout(context.Background(), time.Second)
        defer cancel()
        //调用protobuf的sayhello函数
        r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
        if err != nil {
                log.Fatalf("could not greet: %v", err)
        }
        //打印结果
        log.Printf("Greeting: %s", r.GetMessage())
}

服务端代码介绍

/*
 *
 * Copyright 2015 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
// Package main implements a server for Greeter service.
package main
import (
        "context"
        "log"
        "net"
        "google.golang.org/grpc"
        pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
const (
        port = ":50051"
)
//服务器用于实现hellow.GreeterServer
type server struct {
        pb.UnimplementedGreeterServer
}
//SayHello实现helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
        log.Printf("Received: %v", in.GetName())
        return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func main() {
        //监听
        lis, err := net.Listen("tcp", port)
        if err != nil {
                log.Fatalf("failed to listen: %v", err)
        }
        //new服务对象
        s := grpc.NewServer()
        //注册服务
        pb.RegisterGreeterServer(s, &server{})
        //在gRPC服务器上注册反射服务
        log.Printf("server listening at %v", lis.Addr())
        if err := s.Serve(lis); err != nil {
                log.Fatalf("failed to serve: %v", err)
        }
}

go语言实现GRPC远程调用

准备环境

cd /home/guoliang/go/src/google.golang.org/grpc-go/cmd/protoc-gen-go-grpc
go build
cp protoc-gen-go-grpc /usr/local/bin
mkdir /usr/local/go/src/github.com/golang
cd /home/guoliang
git clone https://github.com/golang/net.git
cp -R net/ /usr/local/go/src/golang.org/x/
git clone https://github.com/golang/protobuf.git
cp -R protobuf/ /usr/local/go/src/google.golang.org/
git clone https://github.com/golang/sys.git
cp -R sys/ /usr/local/go/src/golang.org/x/
git clone https://github.com/googleapis/go-genproto.git
cp -R go-genproto/ /usr/local/go/src/google.golang.org/
mv go-genproto/ genproto

protobuf协议定义

创建proto文件

syntax = "proto3";
package myproto;
option go_package = ".;myproto";
service Helloserver{
  rpc Sayhello(HelloReq)returns(HelloRsp){}
  rpc Sayname(NameReq)returns(NameRsp){}
}
message HelloReq{
  string name = 1;
}
message HelloRsp{
  string msg = 1;
}
message NameReq{
  string name = 1;
}
message NameRsp{
  string msg = 1;
}

编译生成pb.go文件

protoc --go-grpc_out=. *.proto

gRPC-Server编写

package main
import (
	pd "awesomeProject/myproto"
	"context"
	"fmt"
	"google.golang.org/grpc"
	"net"
)
type server struct {
	pd.UnimplementedHelloserverServer
}
func (this *server)Sayhello(ctx context.Context, in *pd.HelloReq) (out * pd.HelloRsp, err error){
	return &pd.HelloRsp{Msg: "hello"+in.Name}, nil
}
func (this *server)Sayname(ctx context.Context, in *pd.NameReq) (out *pd.NameRsp, err error){
	return &pd.NameRsp{Msg:in.Name+"morning"}, nil
}
func main() {
	ln, err := net.Listen("tcp", ":10086")
	if err != nil {
		fmt.Println("Network failure", err)
	}
	srv := grpc.NewServer()
	pd.RegisterHelloserverServer(srv, &server{})
	err=srv.Serve(ln)
	if err!=nil {
		fmt.Println("Network failure", err)
	}
}

gRPC-Client编写

package main
import (
	pd "awesomeProject/myproto"
	"context"
	"fmt"
	"google.golang.org/grpc"
)
func main() {
	conn, err := grpc.Dial("127.0.0.1:10086", grpc.WithInsecure())
	if err!=nil {
		fmt.Println("Network failure", err)
	}
	defer conn.Close()
	c := pd.NewHelloserverClient(conn)
	re, err := c.Sayhello(context.Background(), &pd.HelloReq{Name: "Panda"})
	if err != nil {
		fmt.Println("sayhello service call failure")
	}
	fmt.Println("call sayhello returns", re.Msg)
	re1, err := c.Sayname(context.Background(), &pd.NameReq{Name: "Tony"})
	if err != nil{
		fmt.Println("sayname call failure")
	}
	fmt.Println("call sayname returns", re1.Msg)
}

运行结果

先运行server.go,再运行client.go

以上是关于《Go-micro微服务框架入门教程》学习笔记 | gRPC的主要内容,如果未能解决你的问题,请参考以下文章

go微服务框架go-micro深度学习-目录

Go-Micro框架入门教程---框架结构

go微服务框架go-micro深度学习 Registry服务的注册和发现

go微服务学习 go-micro框架

go微服务学习 go-micro框架

go微服务学习 go-micro框架