go语言 grpc入门

what grpc

gRPC is a modern open source high performance RPC framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services.


go获取grpc依赖

go get google.golang.org/grpc

入门示例

1.编写.proto描述文件
hello.proto

syntax = "proto3";
package proto;

message String {
    string value = 1;
}

service HelloService {
    rpc Hello (String) returns (String);
}

2.使用protoc工具生成相应的go代码
生成的代码与porto描述文件在同一个目录下,包路径与package对应,文件名为hello.pb.go

3.服务提供者实现定义的rpc方法

package proto

import (
	"context"
	"fmt"
)

type HelloServiceImpl struct {

}

func (p *HelloServiceImpl) Hello(ctx context.Context, args *String) (*String, error) {
	reply := &String{Value: "hello grpc"}

	return reply, nil
}

4.注册grpc服务

package main

import (
	"google.golang.org/grpc"
	proto "let_me_go/protobuf"
	"log"
	"net"
)

func main() {
    // 启动一个grpc server
	grpcServer := grpc.NewServer()
	// 绑定服务实现,RegisterHelloServiceServer由protoc根据描述文件生成
	proto.RegisterHelloServiceServer(grpcServer, new(proto.HelloServiceImpl))

	listen, e := net.Listen("tcp", "localhost:8888")

	if e != nil {
		log.Fatal(e)
	}

    // 绑定监听端口
	grpcServer.Serve(listen)
}

5.服务端调用grpc服务

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	proto "let_me_go/protobuf"
)

func main() {
    // 监听服务提供端口
	dialConn, _ := grpc.Dial("localhost:8888", grpc.WithInsecure())

	defer dialConn.Close()
    
    // 在监听端口的基础上创建grpc client,NewHelloServiceClient由protoc生成
	client := proto.NewHelloServiceClient(dialConn)
	// 调用rpc方法
	result, _ := client.Hello(context.Background(), &proto.String{Value: "Hello GRPC"})

	fmt.Println(result.Value)
}

grpc流

传统的rpc属于典型的B/S模式,客户端和服务端不具备双向通信的能力。而grpc基于http2构建,对于这种场景提供了良好的支持。

1.修改.proto描述文件

syntax = "proto3";
package proto;

message String {
    string value = 1;
}

service HelloService {
    rpc Hello (String) returns (String);

    // 使用stream关键字支持流
    rpc Channel (stream String) returns (stream String);
}

2.使用protoc工具生成相应的go代码

3,服务提供者实现Channel方法

package proto

import (
	"context"
	"fmt"
)

type HelloServiceImpl struct {

}

func (p *HelloServiceImpl) Hello(ctx context.Context, args *String) (*String, error) {
	reply := &String{Value: "hello grpc"}

	return reply, nil
}

func (p *HelloServiceImpl) Channel(stream HelloService_ChannelServer) error {
	for  {
		args, _ := stream.Recv()

		if args.Value == "end" {
			fmt.Println("stop connection")
			return nil
		} else {
			fmt.Println(args.Value)
		}

		e := stream.Send(&String{Value: "你说我听着呢"})

		if e != nil {
			return e
		}
	}
}

这里HelloService_ChannelServer类型的stream由protoc生成,我们可以使用它轻松的发送和接收数据
HelloService_ChannelServer

type HelloService_ChannelServer interface {
	Send(*String) error
	Recv() (*String, error)
	grpc.ServerStream
}
  • Recv
    接收流中的数据
  • Send
    向流中写数据

4.注册grpc服务
grpc服务的注册没有任何区别

package main

import (
	"google.golang.org/grpc"
	proto "let_me_go/protobuf"
	"log"
	"net"
)

func main() {
	grpcServer := grpc.NewServer()
	proto.RegisterHelloServiceServer(grpcServer, new(proto.HelloServiceImpl))

	listen, e := net.Listen("tcp", "localhost:8888")

	if e != nil {
		log.Fatal(e)
	}

	grpcServer.Serve(listen)
}

5.客户端调用grpc服务

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"io"
	proto "let_me_go/protobuf"
	"time"
)

func main() {
	dialConn, _ := grpc.Dial("localhost:8888", grpc.WithInsecure())

	defer dialConn.Close()

	client := proto.NewHelloServiceClient(dialConn)

	stream, _ := client.Channel(context.Background())

    // 使用一个新的grountine定时发送数据
	go func() {
		for  {
			stream.Send(&proto.String{Value:"诶呀我说呀"})
			time.Sleep(time.Duration(2) * time.Second)
		}
	}()

	for  {
	    // 不断从流中接收新数据
		recv, e := stream.Recv()
		if e != nil {
			if e == io.EOF {
				break
			}
		}

		fmt.Println(recv.Value)
	}
}

6.调用结果
server

诶呀我说呀
诶呀我说呀
诶呀我说呀
诶呀我说呀

client

你说我听着呢
你说我听着呢
你说我听着呢
你说我听着呢

使用grpc实现发布订阅服务

1.docker包依赖引入
由于发布订阅模型使用了docker的pubsub包,所以要先引入docker包

go get github.com/docker/docker

2.编写.proto描述文件

// 发布订阅接口
service PubsubService {
    rpc publish(String) returns (String);

    rpc Subscribe(String) returns (stream String);
}

使用grpc流接收订阅数据

3.服务提供者实现publish/subscribe方法

package proto

import (
	"context"
	"github.com/docker/docker/pkg/pubsub"
	"strings"
	"time"
)

type PubsubService struct {
	pub *pubsub.Publisher
}

func NewPubsubService() *PubsubService {
	return &PubsubService{pub:pubsub.NewPublisher(2 * time.Second, 10)}
}

func (pub *PubsubService)Publish(ctx context.Context, in *String) (*String, error) {
	pub.pub.Publish(in.Value)
	return &String{}, nil
}

func (pub *PubsubService) Subscribe(args *String, stream PubsubService_SubscribeServer) error {
	ch := pub.pub.SubscribeTopic(func(v interface{}) bool {
		if s, ok := v.(string); ok {
			// 只订阅以args入参传入的值开头的消息
			if strings.HasPrefix(s, args.Value) {
				return true
			}
		}

		return false
	})

	// 将接收的到的订阅结果发送到订阅客户端
	for v := range ch {
		stream.Send(&String{Value:v.(string)})
	}

	return nil
}

4.注册grpc服务

package main

import (
	"google.golang.org/grpc"
	proto "let_me_go/protobuf"
	"log"
	"net"
)

func main() {
	grpcServer := grpc.NewServer()
	proto.RegisterPubsubServiceServer(grpcServer, proto.NewPubsubService())

	listen, e := net.Listen("tcp", "localhost:8888")

	if e != nil {
		log.Fatal(e)
	}

	grpcServer.Serve(listen)
}

5.内容发布客户端调用publish方法发布内容

package main

import (
	"context"
	"google.golang.org/grpc"
	proto "let_me_go/protobuf"
	"time"
)

func main() {
	dialConn, _ := grpc.Dial("localhost:8888", grpc.WithInsecure())

	defer dialConn.Close()

	client := proto.NewPubsubServiceClient(dialConn)

	time.Sleep(time.Duration(5) * time.Second)

	client.Publish(context.Background(), &proto.String{Value: "安卓也不错"})

	client.Publish(context.Background(), &proto.String{Value: "ios是最棒滴"})

}

6.内容订阅客户端调用subscribe方法订阅内容

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	proto "let_me_go/protobuf"
)

func main() {
	dialConn, _ := grpc.Dial("localhost:8888", grpc.WithInsecure())

	defer dialConn.Close()

	client := proto.NewPubsubServiceClient(dialConn)

	stream, _ := client.Subscribe(context.Background(), &proto.String{Value: "ios"})

	for  {
		recv, _ := stream.Recv()
		fmt.Println("接收到订阅消息:" + recv.Value)
	}
}

7.调用结果

接收到订阅消息:ios是最棒滴

你可能感兴趣的:(go)