// 上一篇:线性化(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. 【.NET Core项目实战-统一认证平台】第十四章 授权篇-自定义授权方式

    [.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章我介绍了如何强制令牌过期的实现,相信大家对IdentityServer4的验证流程有了更深的了解,本篇我将介绍如何使用自定义的授权方 ...

  2. .Net语言 APP开发平台——Smobiler学习日志:如何设置页面的title

    1.修改Mobile Form的TitleText的属性 输入需要显示标题,如图1: 2.修改Mobile Form的TitleStyle属性 其中包括Image属性(窗体图标).BackColor属 ...

  3. 第一册:lesson 107.

    第一册: It's too small. Do you like this dress,madam? I like the colour very much.It's a lovely dress,b ...

  4. WPF 可触摸移动的ScrollViewer控件

    ListBox支持触摸滑动,而ScrollViewer默认不支持.需要设置PanningMode属性,如果不设置PanningMode,如何自定义触摸滑动? ScrollViewer如需要添加上下/左 ...

  5. ecstore 安装后提示require function does not exist in....

    解决: 安装好后,修改config.php里的TMP_DIR,指向网站目录下的data目录(用绝对路径) // define('TMP_DIR', '/data/www/data/tmp/');  先 ...

  6. 22 , CSS 构造颜色、背景与图像

    1. 设定颜色 2. 背景使用 3. 图像使用 1.设定颜色 红色的几种合法定义; #f00; #ff0000; Red; Rgb(255,0,0); Rgb(100%,0%,0%); 2.十六进制三 ...

  7. 快速使用CSS Grid布局,实现响应式设计

    常用Grid布局属性介绍 下面从一个简单Grid布局例子说起. CSS Grid 布局由两个核心组成部分是 wrapper(父元素)和 items(子元素). wrapper 是实际的 grid(网格 ...

  8. 关于ajax用户名验证和jquery实现简单表单验证

    首先来说用户名验证: 前台: <tr> <td class="tableleft">教师编号</td> <td><input ...

  9. MongoDB在已有账号的实例下还原数据库报错的分析(error applying oplog)

    一. 背景 今天在MongoDB 4.0.4版本下,在还原恢复数据库时报错. 主要错误为: Failed: restore error: error applying oplog: applyOps: ...

  10. LeetCode算法题-Base 7(Java实现)

    这是悦乐书的第247次更新,第260篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第114题(顺位题号是504).给定一个整数,返回其基数为7的字符串表示.例如: 输入: ...