// 上一篇:线性化(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. 控制结构(9) 管道(pipeline)

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

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

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

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

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

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

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

  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. nginx基础

    常见的能够提供web服务的程序有apache.IIS,nginx等,LLS是windows系统中的,nginx和apache是linux系统中的,nginx是一款高性能的http和反向代理的服务器. ...

  2. IIS配置HTTPS

    1,新建网站,选中类型为 https,然后更改SSL证书为你配置的SSL证书, 对于SSL证书的配置是这样的 点开第二步,然后点击 创建自签名证书 确定以后点开网站看到有个SSL, 双击进去,再选中 ...

  3. 【SpringBoot】拦截器使用@Autowired注入接口为null解决方法

    最近使用SpringBoot的自定义拦截器,在拦截器中注入了一个DAO,准备下面作相应操作,拦截器代码: public class TokenInterceptor implements Handle ...

  4. git 常用命令,上传,下载,更新线上代码

    git 常用命令以及推荐git新建上传个人博客 $ git clone  //本地如果无远程代码,先做这步,不然就忽略 $ git status //查看本地自己修改了多少文件 $ git add . ...

  5. pd_ds中的hash

    前言 在c++的STL中,提供了一种hash函数,其用法和map是几乎一样的,但是速度却能快接近一倍 使用方法 需要的头文件 #include<ext/pb_ds/assoc_container ...

  6. 驰骋工作流引擎JFlow与activiti的对比 -总结

    共同点: 1. 嵌入式的工作流引擎,降低集群复杂性. 2. 严格而灵活的流程版本控制 3. 支持多种数据库 4. 支持多种流程设计模式 5. 成熟度高的开源工作流,具有可靠的稳定性和性能. 区别: 1 ...

  7. 南京邮电大学java第三次实验报告

    实 验 报 告 ( 2017 / 2018学年 第2学期) 课程名称 JAVA语言程序设计 实验名称 Java集成开发环境的安装与使用. Java变量.表达式与控制结构 实验时间 2018 年 4 月 ...

  8. Python第四天 流程控制 if else条件判断 for循环 while循环

    Python第四天   流程控制   if else条件判断   for循环 while循环 目录 Pycharm使用技巧(转载) Python第一天  安装  shell  文件 Python第二天 ...

  9. Go 语言笔记

    Go 语言笔记 基本概念 综述 Go 语言将静态语言的安全性和高效性与动态语言的易开发性进行有机结合,达到完美平衡. 设计者通过 goroutine 这种轻量级线程的概念来实现这个目标,然后通过 ch ...

  10. C 语言 IO 缓存 相关

    必要了解函数的功能和使用场景: fflush, setbuf, setvbuf 了解的操作: setbuf(stdout,NULL); // 关闭输出缓冲区: libc 和 linux 内核IO缓存模 ...