// 上一篇:线性化(linearization)

// 下一篇:指令序列(opcode)


最近阅读了酷壳上的一篇深度好文:LINUX PID 1 和 SYSTEMD。这篇文章介绍了systemd干掉sysvinit和UpStart的故事,作者在自己对三者深入理解的基础上对systemd所解决的问题做了精彩的解说。我也觉的systemd干的漂亮,把系统初始化过程中能并发的过程尽一切可能并发了,而且还能保持构架上的优雅,可见往往复杂事物的最佳解法本身也会是一个漂亮的结构。我也去阅读了下systemd作者对各种质疑的答疑文章:The Biggest Myths,从文章中的描述来看,systemd非但没有违背Unix的管道(pipeline)思想,如果说一个组件是一个齿轮,那么它把那么多的齿轮完美的拼接在一起,不正是管道思想的最佳体现么?

In Unix-like computer operating systems, a pipeline is a sequence of processes chained together by their standard streams, so that the output of each process (stdout) feeds directly as input (stdin) to the next one.

现在的工作中,在工程实践上,我会比较注意去思考那些看上去随意的工作怎样用经过设计(解决方案)的脚本系统/命令行工具统一解决,使得一个团队工作中日常运行每天都要流转的那些部分被经过设计的机器逻辑替代,从而规避低水平人工操作,消除低水平人工操作,通过解决问题进而提高效率。当然,不是所有人都看得到这些部分,有时候,你需要把东西做出来,切实改变了工程师团队的痛点和效率,工程师们才会体会到经过设计的工具所带来的好处。积累多了之后,以后可以慢慢再展现这些过程。

日常工作代码里,实际上也有许多在函数级别可以体现的管道思想,例如下面这段改造自实际代码的示例(原始代码在需求变更,BUG解决中琐碎的多,此处简化示例,也跟我挺长时间没写非C++代码,导致有些地方重复出现以前犯过的低阶错误有关):

function onResponse(err, obj){
if(this.bindable()){
if(err){
this.unCommitBindInfo(obj.bindInfo);
this.unCommitInfo(obj.info);
doComplete(err);
return;
} this.commitBindInfo(obj.bindInfo,(err)=>{
if(err){
this.unCommitBindInfo(obj.bindInfo);
doComplete(err);
return;
} this.commitInfo(obj.info, (err)=>{
if(err){
this.unCommitInfo(obj.info);
}
doComplete(err);
});
});
}else{
if(err){
this.unCommitInfo(obj.info);
doComplete(err);
return;
}
this.commitInfo(obj.info, (err)=>{
if(err){
this.unCommitInfo(obj.info);
}
doComplete(err);
});
}
} function doComplete(err, obj){
if(err){
this.m_complete({result:err});
}else{
this.m_complete({result:RESULT.SUCCESS, obj:obj})
}
}

在切换到动态语言的过程中,我发现一个意思的事情。就是很长一段时间写C++代码的程序员,在切到动态语言的时候,会经历一个返祖现象。以前在静态语言里锻炼的模块化/代码洁癖会在一段时间内因为动态语言带来的便利而出现暂时性丢失,例如:不用class,随手写出一个充满全局函数的能工作的“脚本”;随处使用魔数,拼接路径而不是集中配置管理...;当然,毕竟不是新手,在大家发现问题的时候,及时同步了下共通的问题后,大部分人还是能及时纠正过来。

上述代码是一种常见的逻辑分枝:在一段处理逻辑里有两种可能的大分枝,例如此处是被if(this.bindable())所分开的,第一个分支里会处理obj.bindinfo相关的逻辑后再处理obj.info相关的逻辑;第二个分支里则只处理obj.info相关的逻辑,那么其实两个分支处理obj.info部分的逻辑是重复的。一不小心就会直接用if-else把两个分支分开写,然后随着需求变更,两个分支里的碎片代码会变的繁杂,一段时间之后自己会觉得不可维护。

想清楚这点后,我们可以改变控制结构,使用管道(pipeline)的思想来改进。那就是,在抽象概念上,让这个流程变成一定先处理obj.bindinfo,再处理obj.info的流式处理。如果有更多的流程,可以继续串下去,这样它们就构成一个管道。

改进后的代码如下:

function onResponse(err, obj){
this.processBindInfo(err,obj,(err)=>{
this.processInfo(err,obj,(err)=>{
this.doComplete(err);
});
})
} function processBindInfo(err,obj,callback){
this.tryCommitBindInfo(err,obj,(err)=>{
if(err){
this.unCommitBindInfo(obj.bindInof);
}
callback(err);
})
} function processInfo(err,obj,callback){
this.tryCommitInfo(err,obj,(err)=>{
if(err){
this.unCommitInfo(obj.info);
}
callback(err);
});
} function tryCommitBindInfo(err, obj, callback){
if(!this.bindable()){ // 1. 消灭在这里
callback(err);
return;
} if(err){ // 2. 错误处理
callback(err);
return;
} this.commitBindInfo(obj.bindInfo,(err)=>{
callback(err);
});
} function tryCommitInfo(err, obj, callback){
if(err){ // 2‘. 错误处理
callback(err);
return;
} this.commitInfo(obj.info,(err)=>{
callback(err);
});
} function doComplete(err, obj){
if(err){
this.m_complete({result:err});
}else{
this.m_complete({result:RESULT.SUCCESS, obj:obj})
}
}

上述代码里,processBindInfoprocessInfo两个处理分支分别只处理自己哪个分支的事情,如果不需要处理,则让数据流(err)通过callback透传(pass-through)下去即可。可以看到在tryCommitBindInfo1这个地方,就把本来做大分枝用的bindable消灭掉了。而在tryCommitBindInfotryCommitInfo两个分支里的2位置以及2‘位置,则分别是两个处理流程里对错误的处理。

有时候,我觉的很多重构过程似成相识,应该是以前也干过同样的事情。所以,我想记录下来是比较重要的,记录下来常常review。

控制结构(9) 管道(pipeline)的更多相关文章

  1. [Linux] 流 ( Stream )、管道 ( Pipeline ) 、Filter - 笔记

    流 ( Stream ) 1. 流,是指可使用的数据元素一个序列. 2. 流,可以想象为是传送带上等待加工处理的物品,也可以想象为工厂流水线上的物品. 3. 流,可以是无限的数据. 4. 有一种功能, ...

  2. Android OpenGL ES(二)OpenGL ES管道(Pipeline) .

    大部分图形系统都可以比作工厂中的装配线(Assemble line)或者称为管道(Pipeline).前一道的输出作为下道工序的输入.主CPU发出一个绘图指令,然后可能由硬件部件完成坐标变换,裁剪,添 ...

  3. [并发并行]_[线程模型]_[Pthread线程使用模型之一管道Pipeline]

    场景 1.经常在Windows, MacOSX 开发C多线程程序的时候, 经常需要和线程打交道, 如果开发人员的数量不多时, 同时掌握Win32和pthread线程 并不是容易的事情, 而且使用Win ...

  4. 控制结构(9): 管道(pipeline)

    // 上一篇:线性化(linearization) // 下一篇:指令序列(opcode) 最近阅读了酷壳上的一篇深度好文:LINUX PID 1 和 SYSTEMD.这篇文章介绍了systemd干掉 ...

  5. redis使用管道pipeline提升批量操作性能(php演示)

    Redis是一个TCP服务器,支持请求/响应协议. 在Redis中,请求通过以下步骤完成: 客户端向服务器发送查询,并从套接字读取,通常以阻塞的方式,用于服务器响应. 服务器处理命令并将响应发送回客户 ...

  6. Redis 管道pipeline

    Redis是一个cs模式的tcp server,使用和http类似的请求响应协议. 一个client可以通过一个socket连接发起多个请求命令. 每个请求命令发出后client通常会阻塞并等待red ...

  7. jenkins~管道Pipeline的使用,再见jenkinsUI

    Pipeline在Jenkins里的作用 最近一直在使用jenkins进行自动化部署的工作,开始觉得很爽,省去了很多重复的工作,它帮助我自动拉服务器的代码,自动还原包包,自动编译项目,自动发布项目,自 ...

  8. jenkins~管道Pipeline里使用公用类库

    Pipeline使用了groovy语法,同时可以使用所有jenkins插件在groovy里进行调用,可以说通过UI可以实现的功能使用pipeline也可以实现,这一点我在上一篇文章里已经说明,今天主要 ...

  9. redis管道pipeline

    Jedis jedis = new Jedis("127.0.0.1",6379); Pipeline pipeline = jedis.pipelined(); for(int ...

随机推荐

  1. Java标识符的命名规则

    Java语言中,为各种变量.方法和类等起的名字称为标识符Java标识符的命名规则:应以字母.下划线.美元符开头后跟字母.下划线.美元符或数字Java标识符大小写敏感,长度无限制 1.java中能用作标 ...

  2. React Native 系列(八) -- 导航

    前言 本系列是基于React Native版本号0.44.3写的.我们都知道,一个App不可能只有一个不变的界面,而是通过多个界面间的跳转来呈现不同的内容.那么这篇文章将介绍RN中的导航. 导航 什么 ...

  3. php并发加锁示例

    在工作项目中,会遇到一些php并发访问去修改一个数据问题,如果这个数据不加锁,就会造成数据的错误.下面我将分析一个财务支付锁的问题.希望对大家有所帮助. 1 没有应用锁机制 1.1 财务支付简化版本代 ...

  4. python学习===计算字符串中子串出现的次数。

    if __name__ == '__main__': str1 = input('input a string: \n') str2 = input('input a sub string: \n') ...

  5. python学习===复制list

    """将一个列表的数据复制到另一个列表中.""" """ 使用[:] """ a ...

  6. JS进阶 ] 分析JS中的异步操作

    写在前面 JS因为是单线程的,所以在执行事务的时候,往往会因为某个事务的延迟,而导致服务器假死,这时候异步编程就显的格外重要,但是异步编程一般理解为回调函数callback,典型的就是node,回调函 ...

  7. 6个Linux chkconfig命令实例 - 增加,删除,查看和修改services的自动启动选项

    注意:service的安装目录在/etc/rc.d/init.d下,/etc/init.d 是/etc/rc.d/init.d的链接. chkconfig命令用来安装,查看或修改 services随系 ...

  8. Struts2.5的的环境搭建及跑通流程

    Struts2.5 struts是开源框架.使用Struts的目的是为了帮助我们减少在运用MVC设计模型来开发Web应用的时间.如果我们想混合使用Servlets和JSP的优点来建立可扩展的应用,st ...

  9. 每个Web开发人员应该知道的12个终端命令

    Tips 原文作者:Danny Markov 原文地址:12 Terminal Commands Every Web Developer Should Know About 终端是开发人员的武器库中最 ...

  10. 【Java学习笔记之二十五】初步认知Java内部类

    可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人到现在都没有怎么使用过内部类,对内部类也只是略知一二). 第一次见面 内部类我们从外面 ...