github 例子地址

上一篇写了一下rpc调用过程的实现方式,简单来说就是服务端把实现了接口的结构体对象进行反射,抽取方法,签名,保存,客户端调用的时候go-micro封请求数据,服务端接收到请求时,找到需要调用调用的对象和对应的方法,利用反射进行调用,返回数据。 但是没有说stream的实现方式,感觉单独写一篇帖子来说这个更好一些。上一篇帖子是基础,理解了上一篇,stream实现原理一点即破。先说一下使用方式,再说原理。

当前go-micro对 rpc 调用的方式大概如下:

普通的rpc调用 是这样:

  1. 1.连接服务器或者从缓存池得到连接
  2. 2.客户端 ->发送数据 -> 服务端接收
  3. 3.服务端 ->返回数据 -> 客户端处理数据
  4. 4.关闭连接或者把连接返回到缓存池

当前 rps stream的实现方式 是这样子:

  1. 1. 连接服务器
  2. 2. 客户端多次发送请求-> 服务端接收
  3. 3. 服务端多次返回数据-> 客户端处理数据
  4. 4. 关闭连接

    当数据量比较大的时候我们可以用stream方式分批次传输数据。对于客户端还是服务端没有限制,我们可以根据自己的需要使用stream方式,使用方式也非常的简单,在定义接口的时候在参数或者返回值前面加上stream然后就可以多次进行传输了,使用的代码还是之前写的例子,代码都在github上:

    比如我的例子中定义了两个使用stream的接口,一个只在返回值使用stream,另一个是在参数和返回值前都加上了stream,最终的使用方式没有区别

  1. rpc Stream(model.SRequest) returns (stream model.SResponse) {}
  2. rpc BidirectionalStream(stream model.SRequest) returns (stream model.SResponse) {}

看一下go-micro为我们生成的代码rpcapi.micro.go里,不要被吓到,生成了很多代码,但是没啥理解不了的

Server端

  1. // Server API for Say service
  2. type SayHandler interface {
  3. // .... others
  4. Stream(context.Context, *model.SRequest, Say_StreamStream) error
  5. BidirectionalStream(context.Context, Say_BidirectionalStreamStream) error
  6. }
  7. type Say_StreamStream interface {
  8. SendMsg(interface{}) error
  9. RecvMsg(interface{}) error
  10. Close() error
  11. Send(*model.SResponse) error
  12. }
  13. type Say_BidirectionalStreamStream interface {
  14. SendMsg(interface{}) error
  15. RecvMsg(interface{}) error
  16. Close() error
  17. Send(*model.SResponse) error
  18. Recv() (*model.SRequest, error)
  19. }
  20. // .... others

Client端

  1. // Client API for Say service
  2. type SayService interface {
  3. //... others
  4. Stream(ctx context.Context, in *model.SRequest, opts ...client.CallOption) (Say_StreamService, error)
  5. BidirectionalStream(ctx context.Context, opts ...client.CallOption) (Say_BidirectionalStreamService, error)
  6. }
  7. type Say_StreamService interface {
  8. SendMsg(interface{}) error
  9. RecvMsg(interface{}) error
  10. Close() error
  11. Recv() (*model.SResponse, error)
  12. }
  13. type Say_BidirectionalStreamService interface {
  14. SendMsg(interface{}) error
  15. RecvMsg(interface{}) error
  16. Close() error
  17. Send(*model.SRequest) error
  18. Recv() (*model.SResponse, error)
  19. }

    你会发现参数前面加了 Stream后,生成的代码会把你的参数变成一个接口,这个接口主要要的方法是

  1. SendMsg(interface{}) error
  2. RecvMsg(interface{}) error
  3. Close() error

剩下的两个接口方法是根据你是发送还是接收生成的,如果有发送就会有Send(你的参数),如果有接收会生成Rev() (你的参数, error),但这两个方法只是为了让你使用时方便,里面调用的还是SendMsg(interface)和RecvMsg(interface)方法,但是他们是怎么工作的,如何多次发送和接收传输的数据,是不是感觉很神奇。

我就以TsBidirectionalStream 方法为例开始分析,上一篇和再早之前的帖子已经说了服务端启动的时候都做了哪些操作,这里就不再赘述,

服务端的实现,很简单,不断的获取客户端发过来的数据,再给客户端一次一次的返回一些数据。

  1. /*
  2. 模拟数据
  3. */
  4. func (s *Say) BidirectionalStream(ctx context.Context, stream rpcapi.Say_BidirectionalStreamStream) error {
  5. for {
  6. req, err := stream.Recv()
  7. if err == io.EOF {
  8. break
  9. }
  10. if err != nil {
  11. return err
  12. }
  13. for i := int64(0); i < req.Count; i++ {
  14. if err := stream.Send(&model.SResponse{Value: []string {lib.RandomStr(lib.Random(3, 6))}}); err != nil {
  15. return err
  16. }
  17. }
  18. }
  19. return nil
  20. }

启动服务,服务开始监听客户端传过来的数据.....

客户端调用服务端方法:

  1. // 调用
  2. func TsBidirectionalStream(client rpcapi.SayService) {
  3. rspStream, err := client.BidirectionalStream(context.Background())
  4. if err != nil {
  5. panic(err)
  6. }
  7. // send
  8. go func() {
  9. rspStream.Send(&model.SRequest{Count: 2})
  10. rspStream.Send(&model.SRequest{Count: 5})
  11. // close the stream
  12. if err := rspStream.Close(); err != nil {
  13. fmt.Println("stream close err:", err)
  14. }
  15. }()
  16. // recv
  17. idx := 1
  18. for {
  19. rsp, err := rspStream.Recv()
  20. if err == io.EOF {
  21. break
  22. } else if err != nil {
  23. panic(err)
  24. }
  25. fmt.Printf("test stream get idx %d data %v\n", idx, rsp)
  26. idx++
  27. }
  28. fmt.Println("Read Value End")
  29. }

当客户端在调用rpc的stream方法是要很得到stream

  1. rspStream, err := client.BidirectionalStream(context.Background())
  2. //
  3. func (c *sayService) BidirectionalStream(ctx context.Context, opts ...client.CallOption) (Say_BidirectionalStreamService, error) {
  4. req := c.c.NewRequest(c.name, "Say.BidirectionalStream", &model.SRequest{})
  5. stream, err := c.c.Stream(ctx, req, opts...)
  6. if err != nil {
  7. return nil, err
  8. }
  9. return &sayServiceBidirectionalStream{stream}, nil
  10. }

这个调用c.c.Stream(ctx, req, opts...)是关键,他的内部实现就是和服务器进行连接,然后返回一个stream,进行操作。

  1. 客户端:和服务端建立连接,返回Stream,进行接收和发送数据
  2. 服务端:接收客户端连接请求,利用反射找到相应的方法,组织Strem,传给方法,进行数据的发送和接收

建立连接的时候就是一次rpc调用,服务端接受连接,然后客户端发送一次调用,但是传输的是空数据,服务端利用反射找到具体的方法,组织stream,调用具体方法,利用这个连接,客户端和服务端进行多次通信。

go微服务框架go-micro深度学习(五) stream 调用过程详解的更多相关文章

  1. go微服务框架go-micro深度学习 rpc方法调用过程详解

    摘要: 上一篇帖子go微服务框架go-micro深度学习(三) Registry服务的注册和发现详细解释了go-micro是如何做服务注册和发现在,服务端注册server信息,client获取serv ...

  2. RPC框架调用过程详解

    RPC框架调用过程详解 2017年09月16日 21:14:08 荷叶清泉 阅读数 6275   版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. ...

  3. go微服务框架go-micro深度学习(四) rpc方法调用过程详解

    上一篇帖子go微服务框架go-micro深度学习(三) Registry服务的注册和发现详细解释了go-micro是如何做服务注册和发现在,服务端注册server信息,client获取server的地 ...

  4. 微服务框架SpringCloud(Dalston版)学习 (一):Eureka服务注册与发现

    eureka-server eureka服务端,提供服务的注册与发现,类似于zookeeper 新建spring-boot工程,pom依赖: <dependency> <groupI ...

  5. 深度学习——优化器算法Optimizer详解(BGD、SGD、MBGD、Momentum、NAG、Adagrad、Adadelta、RMSprop、Adam)

    在机器学习.深度学习中使用的优化算法除了常见的梯度下降,还有 Adadelta,Adagrad,RMSProp 等几种优化器,都是什么呢,又该怎么选择呢? 在 Sebastian Ruder 的这篇论 ...

  6. 深度学习之卷积神经网络(CNN)详解与代码实现(一)

    卷积神经网络(CNN)详解与代码实现 本文系作者原创,转载请注明出处:https://www.cnblogs.com/further-further-further/p/10430073.html 目 ...

  7. 【转载】 深度学习之卷积神经网络(CNN)详解与代码实现(一)

    原文地址: https://www.cnblogs.com/further-further-further/p/10430073.html ------------------------------ ...

  8. faceswap深度学习AI实现视频换脸详解

    给大家介绍最近超级火的黑科技应用deepfake,这是一个实现图片和视频换脸的app.前段时间神奇女侠加尔盖朵的脸被换到了爱情动作片上,233333.我们这里将会从github项目faceswap开始 ...

  9. Java数据持久层框架 MyBatis之API学习八(Java API详解)

    对于MyBatis的学习而言,最好去MyBatis的官方文档:http://www.mybatis.org/mybatis-3/zh/index.html 对于语言的学习而言,马上上手去编程,多多练习 ...

随机推荐

  1. VS项目启动后 提示ID为*******的进程当前未运行

    就是VS2015中的这种问题,启动调试时,右下角根本没有IISPress图标出现.我的工程是因为突然停电,就再也调试不了了! 解决办法: 用文本编辑器打开Web项目下的{X}.csproj文件,然后查 ...

  2. Bi-shoe and Phi-shoe (欧拉函数)

    题目描述: 题目大意:一个竹竿长度为p,它的score值就是比p长度小且与且与p互质的数字总数,比如9有1,2,4,5,7,8这六个数那它的score就是6.给你T组数据,每组n个学生,每个学生都有一 ...

  3. NN:实现BP神经网络的回归拟合,基于近红外光谱的汽油辛烷值含量预测结果对比—Jason niu

    load spectra_data.mat plot(NIR') title('Near infrared spectrum curve—Jason niu') temp = randperm(siz ...

  4. HDU-2177 取(2堆)石子游戏 (威佐夫博奕)

    Problem Description 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子:二是可以在两堆中同时取走相同 ...

  5. soul

    mark,数据使用gzip加密,因为公司网络卡,明天后天看.

  6. Flutter - Migrate to AndroidX

    一段时间没玩Flutter,今天打开一个项目编译了一下,突然发现不能编译了,出现 Launching lib\main.dart on Nokia X6 in debug mode... FAILUR ...

  7. codeforces723----C. Polycarp at the Radio

    //AC代码...表示很晕 #include <iostream> using namespace std; ],b[]; int main() { int n,m,cnt; cin &g ...

  8. 51nod 算法马拉松30

    题目链接 附一个代码地址 A,这个容斥一下就好了 C,rxd大爷给讲的,首先如果分三种情况(成环,正在比配环,未访问)讨论复杂度是\(3^n * n ^ 2\)的,但是对于每一个环,都可以直接枚举环的 ...

  9. WordPress UpdraftPlus插件 Google Drive 备份

    本文连接地址: http://blog.tuzhuke.info/?p=168 本文作者:tuzhuke 完成时间:2015-04-10 使用wordpress 搭建自己的博客网站,但是对于租用的服务 ...

  10. mac中安装 RabbitMQ

    1.brew install rabbitmq 2.安装后,进入/usr/local/Cellar/rabbitmq/3.7.7 ,输入:sbin/rabbitmq-server 出现下面日志,说明启 ...