35

程序员如何用gRPC谈一场恋爱

 4 years ago
source link: https://www.tuicool.com/articles/zAZRfeB
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

ARFBnqu.gif

导语: 本文以幽默诙谐的方式,介绍gRPC的4种client-server服务模式的开发实践及应用场景

前言:为什么要写这篇文章?

  • The best way to learn is to teach. gRPC的examples里的例子是一个简易的router,琐碎的业务代码很多,看起来比较绕。于是就自己写一个例子,看看自己是否都将这些知识点掌握了。

  • 谈恋爱的过程,其实跟client-server服务模式非常像。单独讲这4种模式有些无聊,所以就尝试用一种尽量有趣的方式去介绍,顺便也可以作为某些男生的一份简单的恋爱指南。

gRPC client-server服务模式

这里先将重要的结论写出来,方便以后查阅,具体介绍见下文。找不到准确的中文来翻译这几种模式,就保留了英文。

  • 0x1: A simple RPC

    最简单的一发一收的client-server模型。这就是我们用得最多的模式

  •  0x2: A client-to-server streaming RPC

    client先建立长连接,然后发送多个request给server,server最后统一回一个rsp

应用场景:

  1. agent上报CPU,内存等数据到server

  2. 客户端心跳

  3. 客户端并发调用细小粒度的接口。比如有5个后台接口A B C D E,客户端在不同页面,可以调用不同的接口组合。比如在个人页,就调用ABC;在动态页面,就调用CDE,后台都只会有一个rsp。这种模式的好处就是让后台可以将接口的粒度细化,客户端调用灵活,减少重复代码,提高复用率

  • 0x3: A server-to-client streaming RPC

    client先发一个请求到server,然后server不停的回包

应用场景:

  1. 股票app。客户端向服务端发送一个股票代码,服务端就把该股票的实时数据源源不断的返回给客户端

  2. app的在线push。client先发请求到server注册,然后server就可以发在线push了

  • 0x4: A Bidirectional streaming RPC

    建立一个长连接,然后client和server可以随意收发数据

应用场景:

  1. 聊天机器人

  2. 有状态的游戏服务器进行数据交换。比如LOL,王者荣耀等竞技游戏,client和server之间需要非常频繁地交换数据

  • 总结:除了一发一收的模式,client-to-server与server-to-client这两种模式,其实用双向RPC都可以做到。但是双向RPC的编码难度更高(后面例子中可以看到),异常处理也需要更仔细,所以具体使用哪种模式需要根据具体需求来分析

接下来就让我用一个男女生之间的交往故事来说明这4种服务模式。内容纯属虚构,并故意写得比较搞笑。如果有不恰当的描述,请告诉我。

男女生交往之gRPC的4种模式

一些说明

  • Selina:女生名字,client

  • John:男生名字,server

  • 等等,女生是client,男生是server。那么,女生给男生提要求,是不是就是client向server请求数据一样自然!!!!噢噢噢,这应该是我学编程以来,领悟到的最重要的道理吧!!!

基础代码说明

  • client

// 建立跟server的连接

conn, err := grpc.Dial(love_const.Address, grpc.WithInsecure())

(左滑可查看完整代码,下同)

  • server

// 设置监听协议与端口

lis, err := net.Listen("tcp", love_const.Address)


// 初始化gRPC server实例

grpcServer := grpc.NewServer()

// 注册命令字与处理函数

love_proto.RegisterBehaviorServer(grpcServer, newServer())


// 启动服务

grpcServer.Serve(lis)

0x1: A simple RPC

  • Selina职场工作不顺,刚被leader说了一顿,又恰好来大姨妈了,心情十分不好,问John:在哪

  • John正在打游戏,非常忙,未察觉出Selina的语气有异样,说:刚起床,在打游戏呢

Selina: "在哪"

Jhon: "刚起床,在打游戏呢"

这种模式就是我们用得最多的模式,一发一收

  • client代码

response, err := client.WhereAreYou(ctx, message)
  • server代码

func (s *loveServer) WhereAreYou(ctx context.Context, message *love_proto.Message) (*love_proto.Response, error) {

rsp := new(love_proto.Response)

rsp.Words = "刚起床,在打游戏呢"

return rsp, nil

}

0x2: A client-to-server streaming RPC

  • Selina开始向John诉苦,说了很多话

  • John忙着打游戏,只看清“我来大姨妈了”,就只回了一句:哦,多喝热水

Selina: "你在干嘛"

Selina: "我不开心"

Selina: "我要和你说话"

Selina: "你怎么还不打电话过来"

Selina: "我来大姨妈了"

Jhon: "哦,多喝热水"

这种模式是client先建立长连接,然后发送多个request给server,server最后统一回一个rsp。

应用场景

  1. agent收集CPU,内存等数据到server

  2. 客户端心跳

  3. 客户端并发调用细小粒度的接口。比如有5个后台接口A B C D E,客户端在不同页面,可以调用不同的接口组合,比如在个人页,就调用ABC;在动态页面,就调用CDE,后台都只会有一个rsp。这种模式的好处就是让后台可以将接口的粒度细化,客户端调用灵活,减少重复代码,提高复用率

  • client代码

// 获取一个stream对象

stream, err := client.ContinuousCall(ctx)

if err != nil {

log.Fatalf("%v.ContinuousCall(_) = _, %v", client, err)

}

// 通过stream来发送多个message

for _, message := range messages {

fmt.Printf("message words: %s\n", message.Words)

if err := stream.Send(message); err != nil {

log.Fatalf("%v.Send(%v) = %v", stream, message, err)

}

}

// 发送EOF给server,然后收取server的回包

reply, err := stream.CloseAndRecv()

if err != nil {

log.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil)

}

  • server代码

for {

// 不断收取client的发包

message, err := stream.Recv()

// 当客户端发送EOF时说明要给回包了

if err == io.EOF {

rsp := new(love_proto.Response)

rsp.Words = "哦,多喝热水"

return stream.SendAndClose(rsp)

}

if err != nil {

return err

}

printGirlWords(message.Words)

}

0x3: A server-to-client streaming RPC

  • Selina生气了

  • John先一直想解决方案,然后发现没有回应,就要买礼物,安慰等措施:包治百病

Selina: "这么久不给我打电话,你是不是不爱我了?"

Jhon: "啊,宝贝你怎么了?"

Jhon: "我刚刚在玩游戏,那一局刚开,我走不开啊"

Jhon: "你不能不讲道理啊"

Jhon: "你都20分钟不回我了"

Jhon: "好了,宝贝,我错了,都是我的错"

Jhon: "你在家等我,我过去接你,带你去买包包"

这种模式是client先发一个请求到server,然后server不停的回包

应用场景

  1. 股票app。客户端向服务端发送一个股票代码,服务端就把该股票的实时数据源源不断的返回给客户端

  2. app的在线push。client先发请求到server注册,然后server就可以发在线push了

  • client代码

// 获取stream对象,并发送一个消息

stream, err := client.LoveOrNot(ctx, message)

if err != nil {

log.Fatalf("%v.LoveOrNot(_) = _, %v", client, err)

}

for {

// 不断收取server回包

response, err := stream.Recv()

// EOF表示server发包结束

if err == io.EOF {

break

}

if err != nil {

log.Fatalf("%v.LoveOrNot(_) = _, %v", client, err)

}

log.Printf("rsp: %s", response.Words)

}

  • server代码

for _, response := range responses {

// 不停向client发消息

if err := stream.Send(response); err != nil {

return err

}

}

0x4: A Bidirectional streaming RPC

  • John道歉,买礼物,Selina不再生气,于是开始聊天

Selina: "好呀好呀"

Jhon: "宝贝我很快就到啦"

Selina: "等我化妆"

Jhon: "宝贝我很快就到啦"

Selina: "亲爱的你最好啦"

Jhon: "宝贝我很快就到啦"

Selina: "么么哒"

Jhon: "宝贝我很快就到啦"

这种模式就是建立一个长连接,然后client和server可以随意收发数据

应用场景

  1. 聊天机器人

  2. 有状态的游戏服务器进行数据交换。比如LOL,王者荣耀等竞技游戏,client和server之间需要非常频繁地交换数据

  • client代码

// 获取stream对象

stream, err := client.LoveChat(ctx)

if err != nil {

log.Fatalf("%v.LoveChat(_) = _, %v", client, err)

}

// 这个管道没有太多实际意义,只是为了让client将rsp都收完整

waitc := make(chan struct{})

go func() {

for {

// 不停地收server的包

response, err := stream.Recv()

// server回包EOF,然后关闭管道

if err == io.EOF {

// read done.

close(waitc)

log.Printf("EOF return")

return

}

if err != nil {

log.Fatalf("Failed to receive a note : %v", err)

}

log.Printf("rsp: %s", response.Words)

}

}()

// 每隔一秒向server发一个消息,模拟聊天场景

for _, message := range messages {

time.Sleep(1 * time.Second)

if err := stream.Send(message); err != nil {

log.Fatalf("Failed to send a message: %v", err)

}

}

// 这里告诉server: client已经将数据发完了(其实就是给server发一个EOF),server会返回一个io.EOF

stream.CloseSend()

<-waitc

  • server代码

for {

// 不断收取client发包

message, err := stream.Recv()

if err == io.EOF {

return nil

}

if err != nil {

return err

}


for _, response := range responses {

// 给client发包

if err := stream.Send(response); err != nil {

return err

}

}

}

男生们看过来,看过来

  • 既然标题写了“用gRPC教程序员谈恋爱”,那么肯定就得给点干货。女生可能天生比男生更感性一些,所以当女生在倾诉问题的时候,千万不要只去想解决方案,而是要尽快地安慰女生,倾听她的吐槽,顺着她的思路去稍微吐槽下。可以换位思考,如果你自己心情不好的时候,肯定也想有人来安慰,而不是想听到”你要看开一点,这样工作效率才会高”之类的解决方案的话

  • 在倾听和安慰之后,然后把问题解决,或者帮助女生把问题解决。做和说同样重要。

q2QFRjB.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK