Webx学习笔记(六)Pipeline服务

1. Pipeline工作原理

Pipeline的意思是管道,管道中有许多阀门(Valve),阀门可以控制水流的走向。在Webx中,pipeline的作用就是控制应用程序流程的走向。

图 6.4. Pipeline和Valves

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对象而已。

图 6.5. 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>类似的其它选项:levelstoLabel。和<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,又想要控制流程;
  • 如果你希望独立于任何框架。
时间: 2024-11-03 05:44:01

Webx学习笔记(六)Pipeline服务的相关文章

webx学习笔记

Webx学习笔记周建旭 2014-08-01 Webx工作流程 图 3.2. Webx Framework如何响应请求 当Webx Framework接收到一个来自WEB的请求以后,实际上它主要做了两件事: 1. 首先,它会增强request.response.session的功能,并把它们打包成更易使用 的RequestContext对象. #macro (registerMessage $field) #if (!$field.valid) $field.message #end #end

python之raw_input()(学习笔记六)

python之raw_input()(学习笔记六) 我们经常使用raw_input()读取用户的输入,如下例子所示: >>> name = raw_input('please input your name:'),截图如下: 下面简单说下,raw_input()与if搭配使用,脚本如下: #!/usr/bin/env python # -*- coding:utf-8 -*- birth = raw_input('birth:') if birth < 2000: print '0

swift学习笔记(六)析构过程和使用闭包对属性进行默认值赋值

一.通过闭包和函数实现属性的默认值 当某个存储属性的默认值需要定制时,可以通过闭包或全局函数来为其提供定制的默认值. 注:全局函数结构体和枚举使用关键字static标注    函数则使用class关键字标注 当对一个属性使用闭包函数进行赋值时,每当此属性所述的类型被创建实例时,对应的闭包或函数会被调用,而他们的返回值会被作为属性的默认值. ESC: Class SomeCLass{ let someProperty:SomeType={ //给someProperty赋一个默认值 //返回一个与

java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessController的checkPerssiom方法,访问控制器AccessController的栈检查机制又遍历整个 PerssiomCollection来判断具体拥有什么权限一旦发现栈中一个权限不允许的时候抛出异常否则简单的返回,这个过程实际上比我的描述要复杂 得多,这里我只是简单的一句带过,因为这

初探swift语言的学习笔记六(ARC-自动引用计数,内存管理)

Swift使用自动引用计数(ARC)来管理应用程序的内存使用.这表示内存管理已经是Swift的一部分,在大多数情况下,你并不需要考虑内存的管理.当实例并不再被需要时,ARC会自动释放这些实例所使用的内存. 另外需要注意的: 引用计数仅仅作用于类实例上.结构和枚举是值类型,而非引用类型,所以不能被引用存储和传递. swift的ARC工作过程 每当创建一个类的实例,ARC分配一个内存块来存储这个实例的信息,包含了类型信息和实例的属性值信息. 另外当实例不再被使用时,ARC会释放实例所占用的内存,这些

Linux System Programming 学习笔记(六) 进程调度

1. 进程调度 the process scheduler is the component of a kernel that selects which process to run next. 进程调度器需要使 处理器使用率最大化,并且提供 使多个进程并发执行的虚拟 Deciding which processes run, when, and for how long is the process scheduler's fundamental responsibility. 时间片:th

马哥Linux学习笔记之五——邮件服务

1.SMTP只负责将邮件发送到服务器,其他的,像身份认证检测邮件,都不能完成.(25/tcp) ESMTP:Extended SMTP POP3:Post Office Protocol IMAP4:Internet Mail Access Protocol 2.Open Relay:开放式中继 就是人家的邮件从这个邮件服务器过,你都帮忙转发,这样就会成为产生垃圾邮件的根源.所以都要关闭Open Relay 3.SASL,Simple Authintication Secure Layer,简单

Lua学习笔记(六):函数-续

Lua中的函数是带有词法定界(lexical scoping)的第一类值(first-class values).第一类值指:在Lua中函数和其他值(数值.字符串)一样,函数可以被存放在变量中,也可以存放在表中,可以作为函数的参数,还可以作为函数的返回值.词法定界指:嵌套的函数可以访问他外部函数中的变量.这一特性给Lua提供了强大的编程能力. Lua中关于函数稍微难以理解的是函数也可以没有名字,匿名的.当我们提到函数名(比如print),实际上是说一个指向函数的变量,像持有其他类型的变量一样:

laravel3学习笔记(六)

原作者博客:ieqi.net ==================================================================================================== ORM Laravel3中MVC体系中Model里最重要的组成部分无疑是ORM了,ORM — object-relational mapper — 将数据操作面向对象化,使得整个web框架的核心风格统一,降低整体复杂度,为开发者提供便利. Laravel3中的ORM叫