Golang-GRPC demo

概念

RPC(Remote Procedure Call),即远程过程调用,是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员不用额外地为这个交互作用编程服务端实现了一个函数,客户端使用 RPC 框架提供的接口,像调用本地函数一样调用这个函数,并获取返回值。RPC 屏蔽了底层的网络通信细节,使得开发人员无需关注网络编程的细节,可以将更多的时间和精力放在业务逻辑本身的实现上,从而提高开发效率。

GRPC 介绍

gRPC 是由 Google 开发的高性能、开源、跨多种编程语言的通用 RPC 框架,基于 HTTP 2.0 协议开发,默认采用 Protocol Buffers 数据序列化协议。gRPC 具有如下特性:支持多种语言,例如 Go、Java、C、C++、C#、Node.js、PHP、Python、Ruby 等。基于 IDL(Interface Definition Language)文件定义服务,通过 proto3 工具生成指定语言的数据结构、服务端接口以及客户端 Stub。通过这种方式,也可以将服务端和客户端解耦,使客户端和服务端可以并行开发。通信协议基于标准的 HTTP/2 设计,支持双向流、消息头压缩、单 TCP 的多路复用、服务端推送等特性。支持 Protobuf 和 JSON 序列化数据格式。Protobuf 是一种语言无关的高性能序列化框架,可以减少网络传输流量,提高通信效率

在 gRPC 中,客户端可以直接调用部署在不同机器上的 gRPC 服务所提供的方法,调用远端的 gRPC 方法就像调用本地的方法一样,非常简单方便,通过 gRPC 调用,我们可以非常容易地构建出一个分布式应用。像很多其他的 RPC 服务一样,gRPC 也是通过 IDL 语言,预先定义好接口(接口的名字、传入参数和返回参数等)。在服务端,gRPC 服务实现我们所定义的接口。在客户端,gRPC 存根提供了跟服务端相同的方法。gRPC 支持多种语言,比如我们可以用 Go 语言实现 gRPC 服务,并通过 Java 语言客户端调用 gRPC 服务所提供的方法。通过多语言支持,我们编写的 gRPC 服务能满足客户端多语言的需求。gRPC API 接口通常使用的数据传输格式是 Protocol Buffers

gRPC 支持定义 4 种类型的服务方法,分别是简单模式、服务端数据流模式、客户端数据流模式和双向数据流模式。简单模式(Simple RPC):是最简单的 gRPC 模式。客户端发起一次请求,服务端响应一个数据。定义格式为 rpc SayHello (HelloRequest) returns (HelloReply) {}。服务端数据流模式(Server-side streaming RPC):客户端发送一个请求,服务器返回数据流响应,客户端从流中读取数据直到为空。定义格式为 rpc SayHello (HelloRequest) returns (stream HelloReply) {}。客户端数据流模式(Client-side streaming RPC):客户端将消息以流的方式发送给服务器,服务器全部处理完成之后返回一次响应。定义格式为 rpc SayHello (stream HelloRequest) returns (HelloReply) {}。双向数据流模式(Bidirectional streaming RPC):客户端和服务端都可以向对方发送数据流,这个时候双方的数据可以同时互相发送,也就是可以实现实时交互 RPC 框架原理。定义格式为 rpc SayHello (stream HelloRequest) returns (stream HelloReply) {}。

[going@dev apistyle]$ tree
.
└── greeter
    ├── clinet
    │   └── main.go
    ├── go.mod
    ├── go.sum
    ├── helloworld
    │   ├── helloworld.pb.go
    │   └── helloworld.proto
    └── server
        └── main.go

首先定义一个RPC服务 helloworld.proto

syntax ="proto3";

option go_package ="greeter/helloworld";

package helloworld;
//The greeting service definition.
service Greeter{
// Sends a greeting
    rpc SayHello (HelloRequest) returns (HelloReply){}
}
//The request message containing the user's name.
message HelloRequest {
    string name =1;
}
// The response message containing the greetings
message HelloReply {
     string message =1;
}

接下来使用protoc 生成pb.go
进入helloword 的目录来生成

protoc -I. --go_out=plugins=grpc:$GOPATH/src/github.com/marmotedu/grpcdemo/apistyle     helloworld.proto

// 此处 I. 是在当前目录下查询helloworld.proto 的位置 
// go_out 则是生成pb.go 的位置 他的生成是plugins=grpc:$GOPATH/src/github.com/marmotedu/grpcdemo/apistyle + proto文件中的go_package 的位置
// 这样生成出来的go 文件就会放在和protoc 文件一致的路径

接下来创建server端进入客户端创建server.go

package main

import (
	"context"
	"log"
	"net"

	pb "github.com/marmotedu/grpcdemo/apistyle/greeter/helloworld"
	"google.golang.org/grpc"
)

const (
	port = ":50051"
)

type server struct {
	pb.UnimplementedGreeterServer
}

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.Fatal("failed to listen:%v", err)
	}
	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, &server{})
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

接下来创建client端 我们可以指定不同的选项,用来控制创建连接的方式,例如 grpc.WithInsecure()、grpc.WithBlock() 等。gRPC 支持很多选项,更多的选项可以参考 grpc 仓库下dialoptions.go文件中以 With 开头的函数。连接建立起来之后,我们需要创建一个客户端 stub,用来执行 RPC 请求c := pb.NewGreeterClient(conn)。创建完成之后,我们就可以像调用本地函数一样,调用远程的方法了。通过 c.SayHello 这种本地式调用方式调用了远端的 SayHello 接口:

package main

import (
	"context"
	"log"
	"os"
	"time"

	pb "github.com/marmotedu/grpcdemo/apistyle/greeter/helloworld"
	"google.golang.org/grpc"
)

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

func main() {
	//set ip a connection to server
	conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewGreeterClient(conn)
	// contact the server and print out its reponse
	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)
}

创建完 main.go 文件后,在当前目录下,执行 go run main.go 发起 RPC 调用
返回Greeting: Hello world

你可能感兴趣的:(golang,网络,rpc)