go微服务框架go-micro深度学习(五) stream 调用过程详解
github 例子地址
上一篇写了一下rpc调用过程的实现方式,简单来说就是服务端把实现了接口的结构体对象进行反射,抽取方法,签名,保存,客户端调用的时候go-micro封请求数据,服务端接收到请求时,找到需要调用调用的对象和对应的方法,利用反射进行调用,返回数据。 但是没有说stream的实现方式,感觉单独写一篇帖子来说这个更好一些。上一篇帖子是基础,理解了上一篇,stream实现原理一点即破。先说一下使用方式,再说原理。
当前go-micro对 rpc 调用的方式大概如下:
普通的rpc调用 是这样:
1.连接服务器或者从缓存池得到连接
2.客户端 ->发送数据 -> 服务端接收
3.服务端 ->返回数据 -> 客户端处理数据
4.关闭连接或者把连接返回到缓存池
当前 rps stream的实现方式 是这样子:
1. 连接服务器
2. 客户端多次发送请求-> 服务端接收
3. 服务端多次返回数据-> 客户端处理数据
4. 关闭连接
当数据量比较大的时候我们可以用stream方式分批次传输数据。对于客户端还是服务端没有限制,我们可以根据自己的需要使用stream方式,使用方式也非常的简单,在定义接口的时候在参数或者返回值前面加上stream然后就可以多次进行传输了,使用的代码还是之前写的例子,代码都在github上:
比如我的例子中定义了两个使用stream的接口,一个只在返回值使用stream,另一个是在参数和返回值前都加上了stream,最终的使用方式没有区别
rpc Stream(model.SRequest) returns (stream model.SResponse) {}
rpc BidirectionalStream(stream model.SRequest) returns (stream model.SResponse) {}
看一下go-micro为我们生成的代码rpcapi.micro.go里,不要被吓到,生成了很多代码,但是没啥理解不了的
Server端
// Server API for Say service
type SayHandler interface {
// .... others
Stream(context.Context, *model.SRequest, Say_StreamStream) error
BidirectionalStream(context.Context, Say_BidirectionalStreamStream) error
}
type Say_StreamStream interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
Send(*model.SResponse) error
}
type Say_BidirectionalStreamStream interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
Send(*model.SResponse) error
Recv() (*model.SRequest, error)
}
// .... others
Client端
// Client API for Say service
type SayService interface {
//... others
Stream(ctx context.Context, in *model.SRequest, opts ...client.CallOption) (Say_StreamService, error)
BidirectionalStream(ctx context.Context, opts ...client.CallOption) (Say_BidirectionalStreamService, error)
}
type Say_StreamService interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
Recv() (*model.SResponse, error)
}
type Say_BidirectionalStreamService interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
Send(*model.SRequest) error
Recv() (*model.SResponse, error)
}
你会发现参数前面加了 Stream后,生成的代码会把你的参数变成一个接口,这个接口主要要的方法是
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
剩下的两个接口方法是根据你是发送还是接收生成的,如果有发送就会有Send(你的参数),如果有接收会生成Rev() (你的参数, error),但这两个方法只是为了让你使用时方便,里面调用的还是SendMsg(interface)和RecvMsg(interface)方法,但是他们是怎么工作的,如何多次发送和接收传输的数据,是不是感觉很神奇。
我就以TsBidirectionalStream 方法为例开始分析,上一篇和再早之前的帖子已经说了服务端启动的时候都做了哪些操作,这里就不再赘述,
服务端的实现,很简单,不断的获取客户端发过来的数据,再给客户端一次一次的返回一些数据。
/*
模拟数据
*/
func (s *Say) BidirectionalStream(ctx context.Context, stream rpcapi.Say_BidirectionalStreamStream) error {
for {
req, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
return err
}
for i := int64(0); i < req.Count; i++ {
if err := stream.Send(&model.SResponse{Value: []string {lib.RandomStr(lib.Random(3, 6))}}); err != nil {
return err
}
}
}
return nil
}
启动服务,服务开始监听客户端传过来的数据.....
客户端调用服务端方法:
// 调用
func TsBidirectionalStream(client rpcapi.SayService) {
rspStream, err := client.BidirectionalStream(context.Background())
if err != nil {
panic(err)
}
// send
go func() {
rspStream.Send(&model.SRequest{Count: 2})
rspStream.Send(&model.SRequest{Count: 5})
// close the stream
if err := rspStream.Close(); err != nil {
fmt.Println("stream close err:", err)
}
}()
// recv
idx := 1
for {
rsp, err := rspStream.Recv()
if err == io.EOF {
break
} else if err != nil {
panic(err)
}
fmt.Printf("test stream get idx %d data %v\n", idx, rsp)
idx++
}
fmt.Println("Read Value End")
}
当客户端在调用rpc的stream方法是要很得到stream
rspStream, err := client.BidirectionalStream(context.Background())
//
func (c *sayService) BidirectionalStream(ctx context.Context, opts ...client.CallOption) (Say_BidirectionalStreamService, error) {
req := c.c.NewRequest(c.name, "Say.BidirectionalStream", &model.SRequest{})
stream, err := c.c.Stream(ctx, req, opts...)
if err != nil {
return nil, err
}
return &sayServiceBidirectionalStream{stream}, nil
}
这个调用c.c.Stream(ctx, req, opts...)是关键,他的内部实现就是和服务器进行连接,然后返回一个stream,进行操作。
客户端:和服务端建立连接,返回Stream,进行接收和发送数据
服务端:接收客户端连接请求,利用反射找到相应的方法,组织Strem,传给方法,进行数据的发送和接收
建立连接的时候就是一次rpc调用,服务端接受连接,然后客户端发送一次调用,但是传输的是空数据,服务端利用反射找到具体的方法,组织stream,调用具体方法,利用这个连接,客户端和服务端进行多次通信。

go微服务框架go-micro深度学习(五) stream 调用过程详解的更多相关文章
- go微服务框架go-micro深度学习 rpc方法调用过程详解
摘要: 上一篇帖子go微服务框架go-micro深度学习(三) Registry服务的注册和发现详细解释了go-micro是如何做服务注册和发现在,服务端注册server信息,client获取serv ...
- RPC框架调用过程详解
RPC框架调用过程详解 2017年09月16日 21:14:08 荷叶清泉 阅读数 6275 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. ...
- go微服务框架go-micro深度学习(四) rpc方法调用过程详解
上一篇帖子go微服务框架go-micro深度学习(三) Registry服务的注册和发现详细解释了go-micro是如何做服务注册和发现在,服务端注册server信息,client获取server的地 ...
- 微服务框架SpringCloud(Dalston版)学习 (一):Eureka服务注册与发现
eureka-server eureka服务端,提供服务的注册与发现,类似于zookeeper 新建spring-boot工程,pom依赖: <dependency> <groupI ...
- 深度学习——优化器算法Optimizer详解(BGD、SGD、MBGD、Momentum、NAG、Adagrad、Adadelta、RMSprop、Adam)
在机器学习.深度学习中使用的优化算法除了常见的梯度下降,还有 Adadelta,Adagrad,RMSProp 等几种优化器,都是什么呢,又该怎么选择呢? 在 Sebastian Ruder 的这篇论 ...
- 深度学习之卷积神经网络(CNN)详解与代码实现(一)
卷积神经网络(CNN)详解与代码实现 本文系作者原创,转载请注明出处:https://www.cnblogs.com/further-further-further/p/10430073.html 目 ...
- 【转载】 深度学习之卷积神经网络(CNN)详解与代码实现(一)
原文地址: https://www.cnblogs.com/further-further-further/p/10430073.html ------------------------------ ...
- faceswap深度学习AI实现视频换脸详解
给大家介绍最近超级火的黑科技应用deepfake,这是一个实现图片和视频换脸的app.前段时间神奇女侠加尔盖朵的脸被换到了爱情动作片上,233333.我们这里将会从github项目faceswap开始 ...
- Java数据持久层框架 MyBatis之API学习八(Java API详解)
对于MyBatis的学习而言,最好去MyBatis的官方文档:http://www.mybatis.org/mybatis-3/zh/index.html 对于语言的学习而言,马上上手去编程,多多练习 ...
随机推荐
- java反射对实体类取值和赋值
public static void checkDesignerEdit(Object dtos) throws Exception { Class dtosClass = dtos.getClass ...
- Mysql 查询实现成绩排名
Mysql 查询实现成绩排名,相同分数名次相同,类似于rank()函数 近日系统要实现总分成绩排名,而且相同分数的学生排名要一样,在网上搜了一圈,没有找到合适的方法,只能靠自己实现了,这里提供两种方法 ...
- B2B、B2C、C2C、O2O
B2B:企业对企业 B2B (也有写成 BTB)是指企业对企业之间的营销关系,它将企业内部网,通过 B2B 网站与客户紧密结合起来,通过网络的快速反应,为客户提供更好的服务,从而促进企业的业务发展(B ...
- 2200: [Usaco2011 Jan]道路和航线 (拓扑排序+dijstra)
Description Farmer John正在一个新的销售区域对他的牛奶销售方案进行调查.他想把牛奶送到T个城镇 (1 <= T <= 25,000),编号为1T.这些城镇之间通过R条 ...
- UVA 796 Critical Links(模板题)(无向图求桥)
<题目链接> 题目大意: 无向连通图求桥,并将桥按顺序输出. 解题分析: 无向图求桥的模板题,下面用了kuangbin的模板. #include <cstdio> #inclu ...
- hdu 2553 n皇后问题【DFS递归解法】
<题目链接> 题目大意: Problem Description 在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45 ...
- 从小白到区块链工程师:第一阶段:Go语言中的函数学习(6)
一. 为什么要有函数 我们在以后的编码过程中,有很多代码会重复出现,这些重复实现的代码,我们不需要每次需要用到的时候都编写,我们将重复的代码封装起来.比如在一个网站中,无论是消费的金额还是积分的积累等 ...
- IDEA快速入门(Mac版)
[持续更新]一篇今年年头的老文章顺道发布了,大家有任何问题可以留言沟通.当时刚刚加入团团,愿大家有机会还是购买一台MAC,确实能给大家的效率赋能,虽然在一开始会有一些艰难!⛽️ 望借着换工作的东风,好 ...
- python & MySQLdb(one)
python开发过程中用到数据库无外乎MYSQL,Mangodb,redis三种,三者数据库使用可能存在差异,但在一些基础的语句使用时都是大同小异的,这阶段学习了一些基础操作,记录下 add: # - ...
- Maven设置utf8编码格式
在pom.xml添加如下配置即可 <properties> <project.build.sourceEncoding>UTF-8</project.build.sour ...