如何通过流式编程打造围绕数据而非API的架构革新?
摘要:背景 在服务刚刚搭建时,通常的思维就是根据API编写业务逻辑:SendStream ... func (d *Svc) SendStream(stream MyApi_data.ProxyDialOut_SendStreamServ
背景
在服务刚刚搭建时,通常的思维就是根据API编写业务逻辑:
// SendStream ...
func (d *Svc) SendStream(stream MyApi_data.ProxyDialOut_SendStreamServer) error {
for {
...
data, err := stream.Recv()
if err != nil {
logrus.Errorf("recv error:%v", err)
return err
}
...
// 对data做相关的操作
}
}
在服务暴露出越来越多的API后,相似的操作会越来越多。此时会进行抽象和封装,提取公共操作,例如提取函数、建立工厂等。
比如,在已有的API中添加监控统计。虽然对统计器做了抽象(对象或者函数),但可能仍然需要侵入到所有不同的API实现中。
// SendStream ...
func (d *MyApiSvc) SendStream(stream MyApi_data.ProxyDialOut_SendStreamServer) error {
for {
...
data, err := stream.Recv()
if err != nil {
logrus.Errorf("recv error:%v", err)
return err
}
...
// 对data做相关的操作
...
// 添加一个共享的监控统计器,调用上报业务,每个api都需要改动
counter.Add("MyApi", 1)
}
}
这在简单项目中无可厚非,但长此以往,随着各种功能的加入,API的业务代码会迅速臃肿起来。
后续,会发现每个API都各不相同,却又有公共部分。所以不得不写出大量形容相似的代码。这在部门大部分项目中都屡见不鲜。
究其原因,这是因为抽象层次不够造成的。
摒除以API为中心的编程模式
在网络编程中,一般会引入中间件(比如trpc的filter)来处理共有逻辑,比如鉴权,日志,panic处理等。
但中间件一般太过于抽象并不直观,使得编写调试不易。但它的思路值得借鉴。
在对业务进行思考后,突发奇想。虽然对客户端(用户)而言,每个API都是服务(消费者)。但对于具体处理而言,每个API同时也是生产者。
将每个API看成data source,生产数据(data),就是对api最底层的抽象。
在这里,引入一个简单的流式编程包go-streams(github.com/reugn/go-streams),方便快速建立流式编程的架构。
