Skip to content

Commit 9eebf48

Browse files
author
jiahaixin
committed
📝pipeline
1 parent f858647 commit 9eebf48

File tree

2 files changed

+27
-61
lines changed

2 files changed

+27
-61
lines changed

docs/.DS_Store

0 Bytes
Binary file not shown.

docs/design-pattern/Pipeline-Pattern.md

Lines changed: 27 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@ System.out.println(new StringBuilder(String.join("",helloStr)).reverse().toStrin
3434

3535
再假设我们上边的场景是在一个大型系统中,有这样的数据流需要多次进行复杂的逻辑处理,还是简单粗暴的把一系列流程像上边那样放在一个大组件中吗?
3636

37-
如果还是这么设计,那我们在增改,或者减少一些处理逻辑的时候,我们就必须对整个组件进行改动。可扩展性和可重用性几乎没有~~
37+
这样的设计完全违背了单一职责原则,我们在增改,或者减少一些处理逻辑的时候,就必须对整个组件进行改动。可扩展性和可重用性几乎没有~~
3838

39-
那有没有一种模式可以将整个处理流程进行详细划分,划分出的每个小模块互相独立且各自负责一小段逻辑处理,这些小模块可以按顺序连起来,前一模块的输出作为后一模块的输入,最后一个模块的输出为最终的处理结果。如此一来修改逻辑时只针对某个模块修改,添加或减少处理逻辑也可细化到某个模块颗粒度,并且每个模块可重复利用,可重用性大大增强。
39+
那有没有一种模式可以将整个处理流程进行详细划分,划分出的每个小模块互相独立且各自负责一小段逻辑处理,这些小模块可以按顺序连起来,前一模块的输出作为后一模块的输入,最后一个模块的输出为最终的处理结果呢?
4040

41-
恩,这就是管道模式
41+
如此一来修改逻辑时只针对某个模块修改,添加或减少处理逻辑也可细化到某个模块颗粒度,并且每个模块可重复利用,可重用性大大增强。
42+
43+
恩,这就是我们要说的管道模式
4244

4345
![](https://cdn.jsdelivr.net/gh/Jstarfish/picBed/design-pattern/pipeline-pattern-csharp-uml.png)
4446

@@ -50,9 +52,9 @@ System.out.println(new StringBuilder(String.join("",helloStr)).reverse().toStrin
5052

5153
管道模式(Pipeline Pattern) 是责任链模式(Chain of Responsibility Pattern)的常用变体之一。
5254

53-
顾名思义,管道模式就像一条管道把多个对象连接起来,整体看起来就像若干个阀门嵌套在管道中,而处理逻辑就放在阀门上,需要处理的对象进入管道后,分别经过各个阀门,每个阀门都会对进入的对象进行一些逻辑处理,经过一层层的处理后从管道尾处理,此时的对象就是已完成处理的目标对象。
55+
顾名思义,管道模式就像一条管道把多个对象连接起来,整体看起来就像若干个阀门嵌套在管道中,而处理逻辑就放在阀门上,需要处理的对象进入管道后,分别经过各个阀门,每个阀门都会对进入的对象进行一些逻辑处理,经过一层层的处理后从管道尾出来,此时的对象就是已完成处理的目标对象。
5456

55-
![img](https://cdn.jsdelivr.net/gh/Jstarfish/picBed/design-pattern/pipeline-filter-pattern-csharp-implementations2.jpg)
57+
![](https://cdn.jsdelivr.net/gh/Jstarfish/picBed/design-pattern/pipeline-filter-pattern-csharp-implementations2.jpg)
5658

5759
管道模式用于将复杂的进程分解成多个独立的子任务。每个独立的任务都是可复用的,因此这些任务可以被组合成复杂的进程。
5860

@@ -62,21 +64,17 @@ System.out.println(new StringBuilder(String.join("",helloStr)).reverse().toStrin
6264

6365
## 三、角色
6466

65-
管道模式:对于管道模式来说,有3个对象:管道,载荷,过滤器(阶段,阀门均可)。
66-
67-
- 阀门 处理数据的节点
68-
- 管道 组织各个阀门
69-
- 客户端 构造管道,并调用
70-
67+
管道模式:对于管道模式来说,有 3 个对象:
7168

72-
73-
Pipeline的类模型由Pipeline, Valve 和 Context 组成。 Pipeline代表一个执行流,Valve代表执行流中的一个节点,Context是执行时的上下文信息,它一般由两部分组成: request/response + 当前流的执行状态。
69+
- 阀门:处理数据的节点,或者叫过滤器、阶段
70+
- 管道:组织各个阀门
71+
- 客户端:构造管道,并调用
7472

7573

7674

7775
## 四、实例
7876

79-
我们用管道模式实现下文章开头的小需求
77+
程序员还是看代码消化才快些,我们用管道模式实现下文章开头的小需求
8078

8179
### 1、处理器(管道的各个阶段)
8280

@@ -86,8 +84,6 @@ public interface Handler<I,O> {
8684
}
8785
```
8886

89-
90-
9187
### 2、定义具体的处理器(阀门)
9288

9389
```java
@@ -196,64 +192,36 @@ Java 怎么搞,你应该很清晰了吧
196192

197193
## 五、优缺点
198194

199-
> Pipeline 模式的核心思想是将一个任务处理分解为若干个处理阶段(Stage),其中每个处理阶段的输出作为下一个处理阶段的输入,并且各个处理阶段都有相应的工作者线程去执行相应的计算。因此,处理一批任务时,各个任务的各个处理阶段是并行(Parallel)的。通过并行计算,Pipeline 模式使应用程序能够充分利用多核 CPU 资源,提高其计算效率。
195+
> Pipeline 模式的核心思想是将一个任务处理分解为若干个处理阶段(Stage),其中每个处理阶段的输出作为下一个处理阶段的输入,并且各个处理阶段都有相应的工作者线程去执行相应的计算。因此,处理一批任务时,各个任务的各个处理阶段是并行(Parallel)的。通过并行计算,Pipeline 模式使应用程序能够充分利用多核 CPU 资源,提高其计算效率。 ——《Java 多线程编程实战指南》
200196
>
201-
> ​ ——《Java 多线程编程实战指南》
202197
203198
#### 优点
204199

205200
- 将复杂的处理流程分解成独立的子任务,解耦上下游处理逻辑,也方便您对每个子任务的测试
206201
- 被分解的子任务还可以被不同的处理进程复用
207-
- 在复杂进程中添加、移除和替换子任务非常轻松,对已存在的进程没有任何影响
208-
209-
#### 缺点
210-
211-
- 虽然每个子任务变得简单了,但是当你再度尝试将这些子任务组合成完整进程时有一定复杂性;
212-
- 此外你还需要保证独立子任务测试通过后整体的流程能正常工作,这有一定的不确定性。
213-
- 当你看到的都是一个个子任务时,对理解整体流程带来困难(盲人摸象的故事想必大家很熟悉,正是此理)。
214-
215-
确保 了整体架构的可伸缩性和可扩展性
216-
217-
218-
219-
模式考量
220-
221-
Pipeline模式可以对有依赖关系的任务实现并行处理。并行和并发编程中,为了提高并发性我们往往需要将规模较大的任务分解撤柜若干个规模较小的子任务,这些子任务间通常没有依赖关系。而Pipeline模式则允许子任务间存在依赖关系的条件下实现并行运算。
222-
223-
Pipeline模式为用单线程模式编程提供了便利。多线程编程总的来说是复杂的,不仅代码编写比较复杂,出现问题也不好定位,多线程出现非预期结果是,开发人员不仅要考虑算法是否正确,还要考虑是否是多线程先关问题导致非预期的结果。相反,单线程编程就显得相对简单。Pipeline模式非常便于我们采用单线程模式实现对子任务的处理。
224-
225-
Pipeline模式中,每个Pipeline实例都是一个Pipe实例,因此,我们可以添加成其他实例,这就加大了该模式的扩展性和灵活性。
226-
227-
228-
229-
模式需要注意的东西
230-
231-
1.Pipeline的深度:Pipeline中Pipe的个数被称作Pipeline的深度。所以我们在用Pipeline的深度与JVM宿主机的CPU个数间的关系。如果Pipeline实例所处的任务多属于CPU密集行,那么深度最好不超过Ncpu。如果Pipeline所处理的任务多属于I/O密集型,那么Pipeline的深度最好不要超过2*Ncpu。
232-
233-
2.基于线程池的Pipe:如果Pipe实例使用线程池,由于有多个Pipe实例,更容易出现线程死锁的问题,需要仔细考虑。
234-
235-
3.错误处理:Pipe实例对其任务进行过程中跑出的异常可能需要相应Pipe实例之外进行处理。此时,处理方法通常有两种:一是各个Pipe实例捕获到异常后调用PipeContext实例的handleError进行错误处理。另一个是创建一个专门负责错我处理的Pipe实例,其他Pipe实例捕获异常后提交相关数据给该Pipe实例处理。
236-
237-
4.可配置的Pipeline:Pipeline模式可以用代码的方式将若干个Pipe实例添加,可以用配置文件的方式实现动态方式添加Pipe。
238-
239-
240-
202+
- 在复杂进程中添加、移除和替换子任务非常轻松,对已存在的进程没有任何影响,这就加大了该模式的扩展性和灵活性
241203
- 对于每个处理单元又可以打补丁,做监听。(这就是切面编程了)
242-
- 这些处理步骤的代码还可以被其他不同的解决方案复用;
243-
- 在复杂进程中添加、移除和替换子任务非常轻松
244204

245205

246206

247-
-
248-
207+
> 模式需要注意的东西
208+
>
209+
> 1. Pipeline的深度:Pipeline 中 Pipe 的个数被称作 Pipeline 的深度。所以我们在用 Pipeline 的深度与 JVM 宿主机的 CPU 个数间的关系。如果 Pipeline 实例所处的任务多属于 CPU 密集型,那么深度最好不超过 Ncpu。如果 Pipeline 所处理的任务多属于 I/O 密集型,那么 Pipeline 的深度最好不要超过 2*Ncpu。
210+
>
211+
> 2. 基于线程池的 Pipe:如果 Pipe 实例使用线程池,由于有多个 Pipe 实例,更容易出现线程死锁的问题,需要仔细考虑。
212+
>
213+
> 3. 错误处理:Pipe 实例对其任务进行过程中跑出的异常可能需要相应 Pipe 实例之外进行处理。
214+
>
215+
> 此时,处理方法通常有两种:一是各个 Pipe 实例捕获到异常后调用 PipeContext 实例的 handleError 进行错误处理。另一个是创建一个专门负责错我处理的 Pipe 实例,其他 Pipe 实例捕获异常后提交相关数据给该 Pipe 实例处理。
216+
>
217+
> 4. 可配置的 Pipeline:Pipeline 模式可以用代码的方式将若干个 Pipe 实例添加,也可以用配置文件的方式实现动态方式添加 Pipe。
249218
250219

251-
执行流须提供中断机制和异常处理机制,比如一个鉴权filter在发现用户未登录时需要立即返回403并中断流,当一个valve出现异常时这个异常需要被捕捉、处理。 如果Valve是嵌套执行的,这些机制会很方便实现: 鉴权filter发现用户未登录时,不调用nextValve.invoke()即可中断pipeline;
252220

253221

254222
## 六、Java Function
255223

256-
如果,你的管道逻辑真的很简单,那直接用 `Java8` 提供的 `Function` 就可以实现了
224+
如果,你的管道逻辑真的很简单,也直接用 `Java8` 提供的 `Function` 就,具体实现如下这样
257225

258226
```java
259227
File file = new File("/Users/apple/Documents/hello.txt");
@@ -290,9 +258,7 @@ Pipeline模式中,每个Pipeline实例都是一个Pipe实例,因此,我们
290258

291259
## 最后
292260

293-
但是,并不是一碰到这种类似流式处理的任务就需要用管道,Pipeline 模式中各个处理阶段所用的工作者线程或者线程池,表示各个阶段的输入/输出对象的创建和一定(进出队列)都有其自身的时间和空间开销,所以使用Pipeline模式的时候需要考虑它所付出的代价。建议处理规模较大的任务,否则可能得不偿失。
294-
295-
261+
但是,并不是一碰到这种类似流式处理的任务就需要用管道,Pipeline 模式中各个处理阶段所用的工作者线程或者线程池,表示各个阶段的输入/输出对象的创建和一定(进出队列)都有其自身的时间和空间开销,所以使用 Pipeline 模式的时候需要考虑它所付出的代价。建议处理规模较大的任务,否则可能得不偿失。
296262

297263

298264

0 commit comments

Comments
 (0)