这个用例的逻辑很简单, 服务器运行一个管理个人信息的服务, 提供如下的四个服务:

(1) 添加一个个人信息  

注: 对应于Unary RPCs, 客户端发送单一消息给服务器, 服务器返回单一消息

(2) 添加多个个人信息  

注: 对应于Client streaming RPCs, 客户端使用提供的stream发送多个消息给服务端, 等客户端写完了所有的消息, 就会等待服务器读取这些消息, 然后返回响应消息. gRPC保证在一次RPC调用中, 消息是顺序的.

(3) 获取最多N个个人信息

注: 对应于Server streaming RPCs, 客户端发送一条消息给服务端, 然后获取一个stream来读取一系列的返回消息. 客户端会一直读取消息, 知道没有消息可读为止, gRPC保证在一次RPC调用中,消息是顺序的.

(4) 获取指定名字的所有个人信息

注: 对应于Bidirectional streaming RPCs, 这种rcp, 客户端和服务端通过一个read-write stream来发送一系列的消息. 这两个消息流可以独立操作, 就是说, 客户端和服务端可以以任意它们所想的顺序操作这两个消息流. 例如, 服务器可以等待接收到所有的客户端消息时,才开始向客户端发送消息, 或者它可以读一条消息, 然后给客户端发送一条消息, 或者别的想要的方式.  在两个消息流的其中一个中, 消息是顺序的.

在给出代码之前, 先说明一件事, 在grpc中, 请求参数和返回值类型都需要是message类型, 而不能是string, int32等类型.下面给出proto文件的定义:

// [START declaration]
syntax = "proto3";
package tutorial; import "google/protobuf/timestamp.proto";
// [END declaration] // [START messages]
message Person {
string name = 1;
int32 id = 2; // Unique ID number for this person.
string email = 3; enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
} message PhoneNumber {
string number = 1;
PhoneType type = 2;
} repeated PhoneNumber phones = 4; google.protobuf.Timestamp last_updated = 5;
} // Our address book file is just one of these.
message AddressBook {
repeated Person people = 1;
} // rpc调用的结果
message Result {
bool success = 1;
} // rpc请求的个数
message ReqNum {
int32 num = 1;
} message ReqName {
string name = 1;
} // [END messages] // Interface exported by the server.
service Manage {
// 添加一个人
rpc AddPerson(Person) returns (Result) {}
// 添加很多人
rpc AddPersons(stream Person) returns (Result) {}
// 获取指定数目的个人列表
rpc GetPersonsLimit(ReqNum) returns (stream Person) {}
// 获取名字为输入的个人列表
rpc GetPersons(stream ReqName) returns (stream Person) {}
}

Person的定义和之前的protobuf中一致, 新加了一些用于grpc调用的结构体, 这些结构体很简单, 就不讲了. service Manage中定义的是这个服务提供的rpc调用接口.

(1) 添加一个个人信息 对应的是  AddPerson

(2) 添加多个个人信息 对应的是 AddPersons

(3) 获取最多N个个人信息 对应的是 GetPersonsLimit

(4) 获取指定名字的所有个人信息 对应的是 GetPersons

rpc定义很直观, 应该可以参照写出需要的rpc, 按照我了解的, 每个rpc有一个输入参数和一个输出参数, 这个需要注意.

下面给出服务端实现proto的Manage服务的代码:

package main

import (
"context"
"fmt"
"io"
"log"
"net"
"sync" pb "personservice/tutorial" "google.golang.org/grpc"
) // 个人信息服务端
type personServer struct {
persons sync.Map
} // AddPerson 添加一个个人信息
func (s *personServer) AddPerson(ctx context.Context, person *pb.Person) (*pb.Result, error) {
s.persons.LoadOrStore(person.Name, person)
return &pb.Result{
Success: true,
}, nil
} // AddPersons 添加多个个人信息
func (s *personServer) AddPersons(stream pb.Manage_AddPersonsServer) error {
for {
person, err := stream.Recv()
if err == io.EOF {
return stream.SendAndClose(&pb.Result{
Success: true,
})
} if err != nil {
return err
} s.persons.LoadOrStore(person.Name, person)
}
} // GetPersonsLimit 获取限定数目的个人信息
func (s *personServer) GetPersonsLimit(limitNum *pb.ReqNum, stream pb.Manage_GetPersonsLimitServer) error {
var err error
var i int32
s.persons.Range(func(key, value interface{}) bool {
person, ok := value.(*pb.Person)
if !ok {
return false
}
err = stream.Send(person)
if err != nil {
return false
}
i++
if i >= (limitNum.Num) {
return false
}
return true
})
return err
} // GetPersons 获取给定名字的所有个人信息
func (s *personServer) GetPersons(stream pb.Manage_GetPersonsServer) error {
for {
in, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
value, ok := s.persons.Load(in.Name)
if !ok {
continue
}
person, ok := value.(*pb.Person)
if !ok {
continue
}
err = stream.Send(person)
if err != nil {
return err
}
}
} func newServer() *personServer {
s := &personServer{}
return s
} func main() {
address := "localhost:50001"
lis, err := net.Listen("tcp", address)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
var opts []grpc.ServerOption
grpcServer := grpc.NewServer(opts...)
pb.RegisterManageServer(grpcServer, newServer())
fmt.Println("Server listening on:", address)
grpcServer.Serve(lis)
}

下面代码实现了客户端对Manage服务的rpc调用:

package main

import (
"context"
"fmt"
"io"
"log"
pb "personservice/tutorial"
"time" "google.golang.org/grpc"
) const (
rpcTimeOut = 10
) // addPerson 用于添加个人信息
func addPerson(client pb.ManageClient, person *pb.Person) bool {
ctx, cancel := context.WithTimeout(context.Background(), rpcTimeOut*time.Second)
defer cancel()
res, err := client.AddPerson(ctx, person)
if err != nil {
log.Printf("client.AddPerson failed, error: %v\n", err)
return false
}
return res.Success } // addPersons 用来添加多个个人信息
func addPersons(client pb.ManageClient, persons []*pb.Person) bool {
ctx, cancel := context.WithTimeout(context.Background(), rpcTimeOut*time.Second)
defer cancel()
stream, err := client.AddPersons(ctx)
if err != nil {
log.Printf("client.AddPersons failed, error: %v\n", err)
return false
}
for _, person := range persons {
if err := stream.Send(person); err != nil {
log.Printf("stream.Send failed, error: %v\n", err)
return false
}
}
res, err := stream.CloseAndRecv()
if err != nil {
log.Printf("stream.CloseAndRecv failed, error: %v\n", err)
return false
}
return res.Success
} // getPersonsLimit 用来获取指定数目的个人信息
func getPersonsLimit(client pb.ManageClient, limitNum int32) ([]*pb.Person, error) {
var persons []*pb.Person
ctx, cancel := context.WithTimeout(context.Background(), rpcTimeOut*time.Second)
defer cancel()
num := pb.ReqNum{
Num: limitNum,
}
stream, err := client.GetPersonsLimit(ctx, &num)
if err != nil {
log.Printf("client.GetPersonsLimit failed, error: %v\n", err)
return persons, err
}
for {
person, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Printf("stream.Recv failed, error: %v\n", err)
return persons, err
}
persons = append(persons, person)
} return persons, nil
} // getPersons 用来获取指定名字的所有个人信息
func getPersons(client pb.ManageClient, personNames []string) ([]*pb.Person, error) {
ctx, cancel := context.WithTimeout(context.Background(), rpcTimeOut*time.Second)
defer cancel()
stream, err := client.GetPersons(ctx)
if err != nil {
log.Printf("client.GetPersons failed, error: %v\n", err)
return nil, err
}
waitc := make(chan struct{})
// 发送个人名字信息
go func() {
for _, personName := range personNames {
name := pb.ReqName{
Name: personName,
}
if err := stream.Send(&name); err != nil {
log.Printf("stream.Send failed, error: %v\n", err)
break
}
}
err := stream.CloseSend()
if err != nil {
log.Printf("stream.CloseSend failed, error: %v\n", err)
}
close(waitc)
}()
// 获取对应的所有个人信息
var persons []*pb.Person
var in *pb.Person
for {
in, err = stream.Recv()
if err != nil {
break
}
persons = append(persons, in)
} <-waitc
// 检查读取结果, err应该不会为nil
if err == io.EOF || err == nil {
return persons, nil
}
log.Fatalf("stream.Recv failed, error: %v\n", err)
return persons, err
} func makePerson(name string, id int32, email string) pb.Person {
return pb.Person{
Name: name,
Id: id,
Email: email,
}
} func printPersons(persons []*pb.Person) {
for _, person := range persons {
fmt.Printf("%+v\n", person)
}
fmt.Println("")
} func main() {
var opts []grpc.DialOption
opts = append(opts, grpc.WithInsecure())
conn, err := grpc.Dial("localhost:50001", opts...)
if err != nil {
log.Fatalf("grpc.Dial failed, error: %v\n", err)
}
defer conn.Close()
client := pb.NewManageClient(conn) person := makePerson("Tom", 1, "tom@gmail.com") suc := addPerson(client, &person)
if !suc {
log.Fatalf("addPerson failed.\n")
} person = makePerson("Lilly", 2, "lilly@gmail.com")
person2 := makePerson("Jim", 3, "jim@gmail.com") persons := []*pb.Person{&person, &person2}
suc = addPersons(client, persons)
if !suc {
log.Fatalf("addPersons failed.\n")
} resPersons, err := getPersonsLimit(client, 5)
if err != nil {
log.Fatalf("getPersonsLimit failed, error: %v\n", err)
}
fmt.Println("getPersonsLimit output:")
printPersons(resPersons) var personNames []string
for _, person := range persons {
personNames = append(personNames, person.GetName())
}
resPersons, err = getPersons(client, personNames)
if err != nil {
log.Fatalf("getPersons failed, error: %v\n", err)
}
fmt.Println("getPersons output:")
printPersons(resPersons)
}

这个我没有使用单元测试, 可能使用单元测试会更好, 不过根据客户端代码和输出, 也可以验证服务的正确性.

完整的代码参考: https://github.com/ss-torres/personservice.git

如果有什么建议或者提议, 欢迎提出

grpc的简单用例 (golang实现)的更多相关文章

  1. grpc的简单用例 (C++实现)

    这个用例的逻辑很简单, 服务器运行一个管理个人信息的服务, 提供如下的四个服务: (1) 添加一个个人信息 注: 对应于Unary RPCs, 客户端发送单一消息给服务器, 服务器返回单一消息 (2) ...

  2. gRPC的简单Go例子

    gRPC是一个高性能.通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于ProtoBuf(Protocol Buffers)序列化协议开发,且支持众多开发 ...

  3. extern外部方法使用C#简单样例

    外部方法使用C#简单样例 1.添加引用using System.Runtime.InteropServices; 2.声明和实现的连接[DllImport("kernel32", ...

  4. spring事务详解(二)简单样例

    系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...

  5. Docker Compose 创建yml 简单试例

    Docker Compose 创建yml 简单试例 Docker Compose 文件使用格式版本需要与Docker版本对应可在官网内查找 查找地址:https://docs.docker.com/c ...

  6. webpack -- 多页面简单小例

    有时单页面并不能满足我们的业务需求,就需要去构建多页面应用,以下为简单小例: entry:{ index:'./src/module/index/index.js', student:'./src/m ...

  7. 学习GRPC(一) 简单实现

    Grpc 实现流程图 资料 https://grpc.io/docs/quickstart/go/ https://studygolang.com/articles/16627 使用方法 make r ...

  8. velocity简单样例

    velocity简单样例整体实现须要三个步骤,详细例如以下: 1.创建一个Javaproject 2.导入须要的jar包 3.创建须要的文件 ============================= ...

  9. 自己定义隐式转换和显式转换c#简单样例

    自己定义隐式转换和显式转换c#简单样例 (出自朱朱家园http://blog.csdn.net/zhgl7688) 样例:对用户user中,usernamefirst name和last name进行 ...

随机推荐

  1. [转]JS - Promise使用详解2(ES6中的Promise)

    原文地址:https://www.hangge.com/blog/cache/detail_1638.html 2015年6月, ES2015(即 ECMAScript 6.ES6) 正式发布.其中  ...

  2. EasyNVR摄像机网页Chrome无插件视频播放功能二次开发之通道配置文件上传下载示例代码

    背景需求 熟悉EasyNVR产品的朋友们都知道,产品设计初期根据整个直播流程层级,我们将EasyNVR无插件直播系统划分为:硬件层.能力层.应用层,连接硬件与应用之间的桥梁,同时屏蔽各种厂家硬件的不同 ...

  3. [LeetCode] 326. Power of Three 3的次方数

    Given an integer, write a function to determine if it is a power of three. Follow up:Could you do it ...

  4. nuxt/eapress 安装报错Module build failed: ValidationError: PostCSS Loader Invalid OptionsModule build failed: ValidationError: PostCSS Loader Invalid Options options['useConfigFile'] is an invalid additi

    错误信息: Module build failed: ValidationError: PostCSS Loader Invalid Options options['useConfigFile'] ...

  5. Fineui 解决OnClientClick中无论是返回true或false,都依然执行后台代码的问题

    有时写js代码验证数据,需要在OnClientClick中执行,如果符合条件执行后台代码,不符合则不触发后台代码.刚开始的时候无论返回true或false都会执行后台代码(asp.net写法),看了h ...

  6. libevent实现TCP 客户端

    ibevent实现Tcp Client基于bufferevent实现 #include <stdio.h> #include <unistd.h> #include <s ...

  7. UML概念模型

    UML概念模型 UML(Unified Modeling Language):统一建模语言,为面向对象开发系统的产品进行说明.可视化.和编制文档的标准语言 面向对象程序设计 面向对象基本概念:对象.类 ...

  8. python实战项目 — selenium登陆豆瓣

    利用selenium 模仿浏览器,登陆豆瓣 重点: 1. 要设置好 chromedriver配置与使用, chromedriver.exe 和 Chrome的浏览器版本要对应, http://chro ...

  9. 深度学习-LSTM与GRU

    http://www.sohu.com/a/259957763_610300此篇文章绕开了数学公式,对LSTM与GRU采用图文并茂的方式进行说明,尤其是里面的动图,让人一目了然.https://zyb ...

  10. PHP网文

    1.php底层运行机制及原理 https://cloud.tencent.com/developer/article/1055801