标签:
1. Pipeline工作原理
Pipeline的意思是管道,管道中有许多阀门(Valve),阀门可以控制水流的走向。在Webx中,pipeline的作用就是控制应用程序流程的走向。
Pipeline的设计和filter非常相似,也是击鼓传花式的流程控制。但是有几点不同:
Pipeline只能控制流程,不能改变request和response。
Pipeline是轻量级组件,它甚至不依赖于WEB环境。Pipeline既可以在程序中直接装配,也可以由spring和schema来配置。
Pipeline支持更复杂的流程结构,例如:子流程、条件分支、循环等。
2. Pipeline的用途
Pipeline可以说是Webx框架的核心功能之一。利用pipeline,你可以定制一个请求处理过程的每一步。
一个典型的Webx应用的pipeline配置文件(pipeline.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:services="http://www.alibaba.com/schema/services"
xmlns:pl-conditions="http://www.alibaba.com/schema/services/pipeline/conditions"
xmlns:pl-valves="http://www.alibaba.com/schema/services/pipeline/valves"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.alibaba.com/schema/services
http://localhost:8080/schema/services.xsd
http://www.alibaba.com/schema/services/pipeline/conditions
http://localhost:8080/schema/services-pipeline-conditions.xsd
http://www.alibaba.com/schema/services/pipeline/valves
http://localhost:8080/schema/services-pipeline-valves.xsd
http://www.springframework.org/schema/beans
http://localhost:8080/schema/www.springframework.org/schema/beans/spring-beans.xsd
">
<services:pipeline xmlns="http://www.alibaba.com/schema/services/pipeline/valves">
<!-- 初始化turbine rundata,并在pipelineContext中设置可能会用到的对象(如rundata、utils),以便valve取得。 -->
<prepareForTurbine />
<!-- 设置日志系统的上下文,支持把当前请求的详情打印在日志中。 -->
<setLoggingContext />
<!-- 分析URL,取得target。 -->
<analyzeURL homepage="homepage" />
<!-- 检查csrf token,防止csrf攻击和重复提交。 -->
<checkCsrfToken />
<loop>
<choose>
<when>
<!-- 执行带模板的screen,默认有layout。 -->
<pl-conditions:target-extension-condition extension="null, vm, jsp" />
<performAction />
<performTemplateScreen />
<renderTemplate />
</when>
<when>
<!-- 执行不带模板的screen,默认无layout。 -->
<pl-conditions:target-extension-condition extension="do" />
<performAction />
<performScreen />
</when>
<otherwise>
<!-- 将控制交还给servlet engine。 -->
<exit />
</otherwise>
</choose>
<!-- 假如rundata.setRedirectTarget()被设置,则循环,否则退出循环。 -->
<breakUnlessTargetRedirected />
</loop>
</services:pipeline>
</beans:beans>
3. Pipeline的使用
一个简单的valve实现
public class MyValve implements Valve {
public void invoke(PipelineContext pipelineContext) throws Exception {
System.out.println("valve started.");
pipelineContext.invokeNext(); // 调用后序valves
System.out.println("valve ended.");
}
}
配置(pipeline.xml)
<services:pipeline xmlns="http://www.alibaba.com/schema/services/pipeline/valves">
...
<valve class="com.alibaba.myapp.pipeline.MyValve" />
...
</services:pipeline>
上面的代码和配置创建了一个基本的valve ── 事实上,它只是打印了一些消息,然后把控制权传递给后序的valves。
在代码中执行pipeline
@Autowired
private Pipeline myPipeline;
public void invokePipeline() {
PipelineInvocationHandle invocation = myPipeline.newInvocation();
invocation.invoke();
System.out.println(invocation.isFinished());
System.out.println(invocation.isBroken());
}
从spring容器中取得一个pipeline对象以后(一般是通过注入取得),我们就可以执行它。上面代码中,PipelineInvocationHandle对象代表此次执行pipeline的状态。Pipeline执行结束以后,访问invocation对象就可以了解到pipeline的执行情况 ── 正常结束还是被中断?
Pipeline对象是线程安全的,可被所有线程所共享。但PipelineInvocationHandle对象不是线程安全的,每次执行pipeline时,均需要取得新的invocation对象。
调用子流程
Pipeline支持子流程。事实上,子流程不过是另一个pipeline对象而已。
子流程是从valve中发起的。下面的Valve代码启动了一个子流程。
public class MyNestableValve implements Valve {
private Pipeline subPipeline;
public void setSubPipeline(Pipeline subPipeline) {
this.subPipeline = subPipeline;
}
public void invoke(PipelineContext pipelineContext) throws Exception {
// 发起子流程,以当前流程的pipelineContext为参数
PipelineInvocationHandle subInvocation = subPipeline.newInvocation(pipelineContext);
subInvocation.invoke();
System.out.println(subInvocation.isFinished());
System.out.println(subInvocation.isBroken());
pipelineContext.invokeNext(); // 别忘了调用后序的valves
}
}
配置文件(pipeline.xml)
<services:pipeline xmlns="http://www.alibaba.com/schema/services/pipeline/valves">
...
<valve class="com.alibaba.myapp.pipeline.MyNestableValve" p:subPipeline-ref="subPipeline" />
...
</services:pipeline>
中断一个pipeline
Pipeline可以被中断。当有多级子pipeline时,你可以中断到任何一级pipeline。
pipelineContext.breakPipeline(0); // level=0,中断当前pipeline
pipelineContext.breakPipeline(1); // level=1,中断上一级pipeline
pipelineContext.breakPipeline("label"); // 中断到指定label的上级pipeline
// 以上调用相当于:
pipelineContext.breakPipeline(pipelineContext.findLabel("label"));
pipelineContext.breakPipeline(Pipeline.TOP_LABEL); // 终止所有pipelines
中断一个pipeline

图 6.6. 中断一个pipeline
下面的valve将子流程执行了至多10遍。如果子流程内部中断了流程,则循环终止
public class Loop10 implements Valve {
private Pipeline loopBody;
public void setLoopBody(Pipeline loopBody) {
this.loopBody = loopBody;
}
public void invoke(PipelineContext pipelineContext) throws Exception {
PipelineInvocationHandle handle = loopBody.newInvocation(pipelineContext);
for (int i = 0; i < 10 && !handle.isBroken(); i++) {
handle.invoke();
}
pipelineContext.invokeNext();
}
}
存取pipeline的状态
当一个pipeline在运行时,你可以通过PipelineContext取得一些上下文信息:
在valve中存取pipeline的状态
pipelineContext.index(); // 当前valve在pipeline中的序号 pipelineContext.level(); // 当前pipeline在所有子pipeline中的级别 pipelineContext.isBroken(); // 当前pipeline是否已经被中断 pipelineContext.isFinished(); // 当前pipeline的所有valves是否已经执行完 // 存取任意数据 pipelineContext.getAttribute(key); pipelineContext.setAttribute(key, value);
现成可用的valves
一般情况下,你并不需要写前面例子中的代码,因为Webx已经为你提供了一系列现成的valves来实现同样的功能。
<loop><services:pipeline>
<loop loopCounterName="count" maxLoopCount="10">
<valve />
<break-if test="..." />
</loop>
</services:pipeline>
定义循环变量loopCounterName,这个变量值将被保存在PipelineContext中,且可被其它的valve所访问。定义maxLoopCount=10最大循环圈数,以避免循环失控。无条件循环一定要和<break>、<break-if>或<break-unless>等valve相配合。
条件循环 - <while>
<services:pipeline>
<while loopCounterName="count" test="count <= 2">
<valve />
</while>
<while maxLoopCount="10">
<conditions:condition class="..." />
<valve />
</while>
</services:pipeline>
定义循环变量loopCounterName,这个变量值将被保存在PipelineContext中,且可被其它的valve所访问。通过判断循环变量“count <= 2”,循环2次。定义maxLoopCount=10,以避免循环失控。
单条件分支if
<services:pipeline>
<if test="1 == 2">
<valve />
</if>
<if>
<conditions:condition class="..." />
<valve />
</if>
</services:pipeline>
多条件分支 <choose><when><otherwise>
<services:pipeline>
<choose>
<when test="1 == 2">
<valve />
</when>
<when>
<conditions:condition class="..." />
<valve />
</when>
<otherwise>
<valve />
</otherwise>
</choose>
</services:pipeline>
无条件中断 - <break>
<services:pipeline>
<loop>
<valve />
<break />
<valve />
</loop>
<loop>
<valve />
<loop>
<break levels="1" />
</loop>
<valve />
</loop>
<loop label="MY_LOOP">
<valve />
<loop>
<break toLabel="MY_LOOP" />
</loop>
<valve />
</loop>
</services:pipeline>
无条件中止当前的pipeline(即loop循环),无条件中止上一层(levels=1)的pipeline(即loop循环),无条件中止指定label的pipeline(即loop循环)。
有条件中断 - <break-if>、<break-unless>
<services:pipeline>
<loop loopCounterName="count">
<valve />
<break-if test="count > 2" />
<valve />
</loop>
<loop label="MY_LOOP">
<valve />
<break-if toLabel="MY_LOOP">
<conditions:condition class="..." />
</break-if>
<valve />
</loop>
<loop loopCounterName="count">
<valve />
<break-unless test="count <= 2" />
<valve />
</loop>
</services:pipeline>
count>2时中断。<break-if>和<break-unless>均支持和<break>类似的其它选项:levels和toLabel。和<if>类似,也支持任意condition。<break-unless>和<break-if>的条件相反:除非count<=2,否则中断。
无条件退出整个pipeline - <exit>
<services:pipeline>
<loop>
<valve />
<loop>
<exit />
</loop>
<valve />
</loop>
</services:pipeline>
对于Webx而言,<exit>还有一层特殊的含义:放弃WebxFrameworkFilter的控制权,把它交还给servlet engine。以URL http://localhost:8081/myapp/myimage.jpg为例,把控制权交还给servlet engine,意味着让servlet engine去显示myapp应用目录下的静态图片:myimage.jpg。
异常捕获和finally处理 - <try-catch-finally>
<services:pipeline>
<try-catch-finally>
<try>
<valve />
</try>
<catch exceptionName="myexception">
<valve />
</catch>
<finally>
<valve />
</finally>
</try-catch-finally>
</services:pipeline>
<catch>标签可以将捕获的异常以指定名称保存在PipelineContext中,以便其它valve取得。
创建子流程 - <sub-pipeline>
<services:pipeline>
<valve />
<sub-pipeline label="mylabel">
<valve />
</sub-pipeline>
<valve />
</services:pipeline>
条件
在前文所述的各种条件valve(例如<if>、<when>、<while>、<break-if>、<break-unless>等)中,都用到一个共同的对象:condition。Condition是一个简单的接口。
Condition接口
public interface Condition {
/**
* 如满足条件,则返回<code>true</code>。
*/
boolean isSatisfied(PipelineStates pipelineStates);
}
为了方便起见,Webx默认提供了一个JexlCondtion。
<if>
<conditions:jexl-condition expr="loopCount == 2" />
<break />
</if>
//以上配置可以简化为:
<if test="loopCount == 2">
<break />
</if>
Webx还提供了三个组合式的条件。
//相当于java中&&
<all-of>
<condition1 />
<condition2 />
<condition3 />
</all-of>
//相当于java||
<any-of>
<condition1 />
<condition2 />
<condition3 />
</any-of>
//相当于java!
<none-of>
<condition1 />
<condition2 />
<condition3 />
</none-of>
Request Contexts和Pipeline是Webx框架中的两个核心服务。它们分别从两个方面实现了原本需要由Filter来实现的功能 ── Request Contexts提供了包装和修改request/response的机制,而pipeline则提供了流程控制的能力。Request contexts和pipeline组合起来的功能比servlet filter机制更加强大。因为它们是基于Spring的轻量组件,其性能、配置的方便性、扩展性都优于filter。
当然,Request Contexts和Pipeline并不想取代filter。在好几种场合,filter仍然是唯一的选择:
如果你既想要修改request/response,又想要控制流程;
如果你希望独立于任何框架。
标签:
原文地址:http://www.cnblogs.com/Vae98Scilence/p/4631113.html