gRPC是一个高性能、开源和通用的RPC框架,面向移动和HTTP/2 设计。

它有很多特点,如双向流、流控、头部压缩、单TCP连接上的多复用请求,节省带宽、降低TCP链接次数、节省CPU使用和延长电池寿命等。强大的IDL,默认采用Protocol Buffers数据序列化协议,支持多种开发语言以及移动端。gRPC既能够在客户端应用,也能够在服务器端应用,从而以透明的方式实现客户端和服务器端的通信和简化通信系统的构建,很容易去构建分布式应用和微服务架构模式。

93622942-585a6adf5042f_articlex

各大主流的RPC框架的性能比较可以参考这一篇《流行的rpc框架benchmark 2018新春版》 我很久前写过一篇《Thrift 应用总结》

安装protobuf编译器

官方推荐使用protobuf 3,我用的是Mac,所以可以直接用brew安装 3.X 版本

brew info protobuf
brew install protobuf

安装完可以看下版本号

protoc --version
libprotoc 3.5.1

当然也可以通过编译安装

#去下载Protocol Buffers v3.5.1的release包
brew info automake
brew info libtool
# 没有这两个就安装
./autogen.sh
# 检查没问题了
./configure
make -j8
make check
make install

安装Golang for protobuf插件

go get -u -v github.com/golang/protobuf/proto
go get -u -v github.com/golang/protobuf/protoc-gen-go

安装grpc-go

go get -u -v google.golang.org/grpc

由于墙的原因,可能你会看到下面的错误:

Fetching https://google.golang.org/grpc?go-get=1 https fetch failed: Get https://google.golang.org/grpc?go-get=1: dial tcp 216.239.37.1:443: i/o timeout 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的项目中clone下来后install

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/google/go-genproto.git $GOPATH/src/google.golang.org/genproto  
  
cd $GOPATH/src/  
go install google.golang.org/grpc  

或者通过 https://golangtc.com/download/package 安装

protoc编译器使用

通过 protoc命令来编译.protpprotocol buffer定义文件

protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto        

参数使用:

  • -I 指定import路径,可以指定多个路径
  • –go_out golang编译支持,支持参数:
    • plugins 指定插件
    • M 指定.proto文件编译后对应的golang报名
    • import_prefix 为所有import路径添加前缀
    • import_path 指定未声明package或go_package的文件的包名,最右面的斜线前的字符会被忽略
    • 末尾 :编译文件路径 .proto文件路径(支持通配符)

具体使用可以参考官方的 Language Guide (proto3) 中文 proto3语言指南

运行一个简单的示例

我们写一个示例,其实就是 https://github.com/grpc/grpc-go/tree/master/examples helloworld示例

1.编写proto文件来定义服务

用 protocol buffers IDL 定义。Greeter 服务有一个方法 SayHello ,可以让服务端从远程客户端接收一个包含用户名的 HelloRequest 消息后,在一个 HelloReply 里发送回一个 Greeter。protos/helloworld.proto

syntax = "proto3";

package helloworld;
// 定义 Greeter 服务
service Greeter{
    //发送一个greeter
    rpc SayHello (HelloRequest) returns (HelloReply){}
}

message HelloRequest{
    string name = 1;
}

message HelloReply{
    string message = 1;
}

2.生成 gRPC 代码

使用 protocol buffer 编译器 protoc 来生成创建应用所需的特定客户端和服务端的代码,你可以生成任意 gRPC 支持的语言的代码,生成的代码同时包括客户端的存根和服务端要实现的抽象接口,均包含 Greeter 所定义的方法。

protoc -I ./protos ./protos/helloworld.proto --go_out=plugins=grpc:helloworld

3.编写服务端代码

greeter_server/main.go

package main

import (
	"context"
	pb "grpc/helloworld"
	"net"
	"log"
	"google.golang.org/grpc"
	"google.golang.org/grpc/reflection"
)

const port = ":50051"

//server 用于实现从proto 服务定义生成的 helloworld.GreeterServer接口.
type server struct {}

// SayHello 实现 helloworld.GreeterServer接口.
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	return &pb.HelloReply{Message: "hello " + in.Name}, nil
}

func main() {
	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	//创建gRPC 服务器,将我们实现的Greeter服务绑定到一个端口
	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, &server{})
	reflection.Register(s)
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to server: %v", err)
	}
}

4.编写客户端代码

greeter_client/main.go

package main

import (
	"google.golang.org/grpc"
	"log"
	pb "grpc/helloworld"
	"os"
	"context"
	"time"
)

const (
	address     = "localhost:50051"
	defaultName = "world"
)

func main() {
	//创建一个gRPC频道,指定连接的主机名和服务器端口
	conn, err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	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()

	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})

	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}

	log.Printf("Greeting %s", r.Message)

}

5.运行

其中一个终端运行

 go run greeter_server/main.go

另外一个终端运行

go run greeter_client/main.go  
#会看到打印出调用的结果
Greeting helloworld

转载请注明: 转载自Ryan是菜鸟 | LNMP技术栈笔记

如果觉得本篇文章对您十分有益,何不 打赏一下

本文链接地址: Go实践微服务 – gRPC配置和使用