// 上一篇:线性化(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. C#生成随机数的三种方法

    随机数的定义为:产生的所有数字毫无关系. 在实际应用中很多地方会用到随机数,比如需要生成唯一的订单号. 在C#中获取随机数有三种方法: 一.Random 类 Random类默认的无参构造函数可以根据当 ...

  2. JavaScript 文档对象模型(DOM)

    文档对象模型     通过 JavaScript,您可以重构整个 HTML 文档.您可以添加.移除.改变或重排页面上的项目.要改变页面的某个东西,JavaScript 就需要获得对 HTML 文档中所 ...

  3. ASP.NET MVC Session 过期验证跳转至登入页面

    一.在要检查登入的控制器上继承 CheckLoginController 类 2. CheckLoginController 类的写法 using System; using System.Colle ...

  4. [Linux] scp本地服务器和远程服务器拷贝文件

    上传本地文件到服务器scp 本地路径 用户名@远程服务器ip:远程路径 下载文件 scp 用户名@远程服务器ip:远程路径 本地路径-r 是上传下载本地目录到远程 远程文件

  5. Leetcode 807 Max Increase to Keep City Skyline 不变天际线

    Max Increase to Keep City Skyline In a 2 dimensional array grid, each value grid[i][j] represents th ...

  6. python3.6 pip 出现locations that require TLS/SSL异常解决方案

    在给CentOS服务器安装完Python3.6后,使用pip命令出现问题,提示说无法找到ssl模块. 上网查询后发现在安装Python3.6时没有安装openssl-devel依赖库,解决方案如下: ...

  7. java多线程中的三种特性

    java多线程中的三种特性 原子性(Atomicity) 原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行. 如果一个操作时原子性的,那么多线程并 ...

  8. XSS 漏洞介绍

    概念: XSS 攻击:跨站脚本攻击 (Cross Site Scripting),为不和层叠样式表 (Cascading Style Sheets, CSS) 的缩写混淆.故将跨站脚本攻击缩写为 XS ...

  9. Nodejs 操作 Sql Server

    Nodejs 操作 Sql Server Intro 最近项目需要爬取一些数据,数据有加密,前端的js又被混淆了,ajax请求被 hook 了,有些复杂,最后打算使用 puppeteer 来爬取数据. ...

  10. kafka环境搭建

    kafka环境搭建 for mac 对应qq群号:616961231 在之前的文章中, 有学习能力和兴趣爱好的同学,自己动手维护测试环境,丰衣足食是最好的办法,今天我们来讲讲kafka在mac上的安装 ...