ADF BC 框架中提供了开箱即用的应用‘状态管理’的功能。所谓状态管理,英文就是State Management,指的是维护用户在使用应用系统时的会话状态。概念上可能会和Http Session对象混淆。ADF BC提供状态管理是独立于Http Session机制的,自成体系的一个机制。
- 状态管理基本概念
首先来说,为什么需要状态管理?在一个应用被用户使用过程中,每个用户会有很多不需要立即存放到数据库中的信息,比如说一个购物车中的商品,一笔未完成录入的订单,一个查询输入的参数等等,这些信息只有在最后确定后才会提交到数据库事务处理。
然而,做过web应用开发的童鞋应该都很清楚,我们天天玩的这个HTTP 协议是一个无状态的协议,它不会和服务器保持一个连接,因此也无法在协议层来提供对连接状态的维持机制。 因此,开发人员为了维持用户使用系统过程中的状态,就需要通过其他方式来实现。最常见的就是利用浏览器的cookie,其机制如下:
在一个新的http 请求发送到服务器时,服务器会查看http包中的头部信息是否包含cookie信息,如果没有则认为该用户request是一个全新的会话开始。服务器会在返回结果response中写入该服务器所在domain的一个cookie。Cookie里面会有一个字符串来唯一的标示一个用户的会话(浏览器窗口)。Server端会根据这个字符串token,来将系统状态信息唯一对应到一个HttpSession对象中(JEE Container)。HttpSession对象中存放的数据是一个个key/value对,应用可以将在一个会话期间保留的状态信息都会存放在该对象中。这是JEE标准的容器都会应用的一种模式。这种模式在需要高可用性的情况下,就需要在服务器之间传递Session对象,以保证Session不会因为一台服务器故障而丢失。在网络上传递Session对象需要将Session序列化,并且要求Session包含的对象也都是可序列化的。同时,频繁的Session对象更新会增加很大的网络流量,降低系统的整体性能。当然,最近版本的Weblogic服务器已经提供了对Coherence的支持,可以通过Coherence数据网格来维护Session在一个集群中的高可用性。
- ADF的状态管理和HttpSession有何不同
通常,我们在HttpSession中存放的是业务数据。在一个充分设计分层的架构下,通常存放的是一些POJO对象。这些POJO不会记录底层数据访问层的状态(比如:一个查询的条件,查询结果的当前的游标位置,查询返回的结果集等等)。这些技术类的状态,在一般的架构下,是不会作为系统的状态来进行管理的并存放在HttpSession中的。ADF BC实现了一套独特的数据访问层的状态管理,可以使一个用户Session的多次请求都使用相同的Application Module的实例及其保持的状态来进行数据访问。从而,提高了数据访问的性能。当然,提高性能是相对其本身没有这套机制而言的,并非和其他持久化框架做比较。可以讲,如果ADF BC没有这样的机制会大大降低其运行的效率。
- ADF BC状态管理原理。
原理上ADF BC对AM的管理如下图所示:
- AM池 默认情况下,ADF的运行时会维护一个或者一些AM的实例池,每一个根节点的AM都会有一个独立的池。如果有新的用户访问,ADF框架运行时会从AM池中返回一个实例给当前线程中的Data Control。Data Control的实例会被一个ADF Binding Context实例所引用,该ADFBinding Context则会保存在HttpSession中。保存在HttpSession中的这些对象,都是轻量级的对象,占用很少的内存不会造成大的性能的影响。 同时,AM的实例池会维护每个session cookie token和AM实例之间的关系。
- 重复使用AM实例 当下次有相同用户的request来的时候,会从AM池中返回上次其使用的AM实例以及相关的状态(Entity对象,View对象的缓冲,View对象的查询条件,绑定的参数等信息)。如此,避免了重新建立AM状态的开销。
- Passiviation/Acitvation在一个高并发的场景下,AM池可能不得不将以及标签为有用户使用的AM实例提供给新的用户request,每个AM实例可能无法每次都能够等到上次服务的用户request(一般来说我们不会将AM的池设置为和最高并发数一样多,可能是最高并发数的20%)。在这样情况下,AM就会将其状态passivate到一个数据表中。这些状态信息会以一个XML的形式存放。 通常在ADF的应用schema下会发现这样的一个表:PS_TXN。可以设定一些数据JOB来定期清理其中的数据。当原来的用户再次访问时,AM池就会将原来AM实例的passivation的数据从数据库中读回并装载到一个AM实例,这个过程称为Activation。通过这种方式,实现了AM的状态恢复。
- 在集群环境下 我们会将jbo.dofailover参数设为true。这样为了保证AM状态的高可用性,在每次request结束时都会将AM状态做passivation。
稍微总结下:
AM实例可能的状态:
- buzy: 被一个user request使用中。
- referenced: AM的状态被维护中,等待下次相同的用户request。也可以被任何其他request使用,如果必须的话。
- unreferenced: AM在pool中,可以被任何request使用。
AM状态管理的持久化方式:
- Passivation = EO/VO的Snapshot被存放到数据库中。
- Recycle = reset:实例的状态被消除,并准备服务一个新的client
- Activation=将EO/VO的数据从数据库读回到AM。
- 在HA模式下passivation在每次request结束时进行,但recycle/activation并不会在每次request开始就进行。只有当服务器负载要求一个实例为新的客户端服务时才进行。换句话说,每次做passivation 并不会马上对AM坐recycle。Recycle只有在需要的时候才会进行。在非HA模式下,passivation会在负载需要的时候进行,并且紧接着会进行recycle和acivation。
- 默认情况下,每个AM都会hold住J 一个数据库的连接,直到这个AM被destroy。因此在进行数据库连接池的配置时要确保每个AM会有足够的连接可用,因为默认情况下AM拿到连接后不会在每次request结束时释放的。ADF BC也提供了参数来改变这一默认行为,具体配置方式请参考开发手册,以后有空的时候也会继续总结。