aop程序锁_使用AOP进行应用程序故障转移

aop程序锁

AOP最近一直处于争议之中,Gavin King将其视作完全过分炒作,失败的技术,而Cedric Beust也对使其成为主流编程的各个方面表示严重怀疑, AOP 品牌 化为一个好主意,将仍然是 AOP 的特权。专家开发很少 。 本文不会尝试创建面向方面编程(AOP)的狂热症候群 -它描述了该技术如何为大型金融机构的现实Java EE项目提供了很多好处,从而解决了一些关键的交叉问题作为项目需求的最后一刻变化。 所描述的方案和实现的解决方案很好地描绘了AOP如何解决面向业务应用程序建模的正交问题时对OOP的补充。

问题

我们一直在为使用Java EE技术,以Oracle 10g RAC作为数据库集群和Websphere MQ作为消息传递中间件的大型金融组织开发证券交易后台解决方案。 该项目当前部署在UAT阶段,当时管理层决定我们需要在集群体系结构上实现透明的应用程序故障转移服务

Oracle 10g RAC支持FCF(快速连接故障转移),这为JDBC应用程序提供了一种绝佳的方式来利用连接故障转移工具。 但是,这里真正的挑战是在应用程序层处理故障转移,并通过重试和恢复方案对用户透明。

如果发生Oracle节点故障转移,则会发生以下事件序列:

  • 数据库实例失败,从而在连接缓存中保留了几个陈旧的连接。
  • 数据库中的RAC机制生成一个RAC事件,该事件被发送到包含JDBC的JVM。
  • JVM中的守护程序线程查找受RAC事件影响的所有连接,并通过SQL异常通知它们已关闭的连接,并回滚所有打开的事务。 当RAC服务故障传播到JDBC应用程序时,数据库已经回滚了本地事务。

如果启用了快速连接故障转移(FCF),则当一个RAC节点发生故障时,连接缓存将自动失效,并使用新节点重新建立所有未使用的连接。 但是,对于应用程序已在使用的连接,情况并非如此。 在这种情况下,当应用程序尝试使用在节点故障转移之前已连接的连接时,将引发SQLException(ORA-17008,关闭的连接)。 应用程序必须手动重试连接,并且FCF保证下一次连接尝试成功。

蛮力解决方案

上述问题的解决方案需要通过适当的重试-恢复方案来解决应用程序的手动重试问题。 我们意识到,在应用程序级别上,我们需要特别处理ORA-17008,并加入特殊的处理程序以启用自动重试。 问题在于,在过去两年中不断发展的代码库中,有超过200万行Java和JSP,包括6000多个类和500个数据库表,并且有无数的实例,如下所示:

long id = ...;
try {
Instrument instr = new Instrument(id, conn); } catch(SQLException ex) {
throw new KeyedException("cam.error.failed.retrieve.instrument",
ex);
}
...

对于上述所有代码段,SQLException是所有数据库相关故障的通用已检查异常,必须在代码库中对其进行捕获和处理(糟糕..已检查异常的痛苦:-( ..应该包裹在未检查的异常中)像Spring一样)。一种蛮力的方法是在捕获SQLException的所有站点上都加入专门的处理程序。这种方案必须排除在外,要记住这是UAT季节,这会对整个代码产生巨大的影响。基地-客户一定不会被逗乐!

输入方面

在对代码库进行仔细分析之后,我们发现影响的主要领域是大量服务组件和控制台应用程序,需要在其中实施重试和恢复方案。 由于历史原因,我们不使用EJB-而是所有服务组件和控制台应用程序都将启动器基类作为驱动程序。 我们仍然需要解决的全部功能仍然覆盖了我们总代码库的一半以上,这是一个真正的跨领域关注点。

那时我们考虑合并方面来解决此问题。 提出并实施了以下解决方案:

  1. 定义一个切入点以处理SQLException
  2. 定义要在切入点执行的建议,该切入点将拦截异常并引发类型化异常
  3. 该错误只能在2个基类中进行处理-一个针对服务组件,另一个针对控制台应用程序。 这两个类在处理程序中实现了此错误的重试和恢复功能。

以下方面实现了以上方案的框架:

public aspect AspectFastConnFailOver
{
pointcut sqlHandler(SQLException exception):
handler(SQLException+) && args(exception);

// advice to be executed as the handler of SQLException
// its derived exception
before(SQLException exception): sqlHandler(exception){

...

// handle only if non-UI
if (!Application.getInstance()
.getContext()
.getCallerIdentity()
.isInteractiveUser()) {
if(exception.getErrorCode() == Globals.FCF_SQLEX_ERRORCODE)
{
throw new DatabaseNotAvailableError();
} } }
... }

实施MQ故障转移

一旦为数据库服务器实现了透明的应用程序故障转移,我们决定为部署在使用Veritas的Storage Foundation 4.0 / HA和Veritas的MQ代理服务器框架配置的集群上的Websphere MQ Services尝试一种类似的实现方案。

如果使用Oracle 10g RAC进行故障转移,则每当发生故障转移时,都会生成适当的事件,并使用特定的错误代码将其传播到应用程序的JDBC层。 根据错误代码,JDBC使池中所有未使用的连接无效,并回滚与当前连接关联的事务。 在应用程序级别,我们需要捕获错误代码,然后重试以获取新的连接来处理这种情况。 下一次获取连接的调用将创建一个新连接,如果在该时间之前已经进行了故障转移,则该连接应该成功。

由于Veritas群集不会本质上处理故障转移,因此没有事件发送回应用程序级别的事实使MQ故障转移的情况变得复杂。 因此,应用程序需要检测故障转移,使池中的所有连接和会话无效并回滚事务。 像SQLException一样,按照JMS规范,在消息处理中,可能会从所有接口(例如Connection,Session,Receiver,Sender和Browser)的每个方法引发JMSException。 因此,需要通过适当的切入点在集中式处理程序中实现重试和恢复。

这是进行拦截的相似方面片段:

public aspect AspectFailOver
{
pointcut jmsHandler(JMSException exception):
handler(JMSException+) && args(exception)
&& !within(...)
&& !withincode(...));

// advice to be executed as the handler of JMSException
// its derived exception

before(JMSException exception): jmsHandler(exception){
...
if (!Application.getInstance()
.getContext()
.getCallerIdentity()
.isInteractiveUser()) {
if(isMQFailoverException(exception)) {
throw new MQNotAvailableError();
}
}
} ...
}
}

与Oracle故障转移类似,错误MQNotAvailableError已捕获在服务组件和控制台应用程序启动器的几个基类中,用于实现重试和恢复循环。

最小的影响,最大的影响

结果很棒! 借助AOP的强大功能,我们实现了对现有代码库影响最小的目标。 我们使用AspectJ并编译了时间编织-构建时间增加了,但是使用AOP作为启动器技术来防止对代码库造成重大影响的客户却面带微笑。

翻译自: https://www.infoq.com/articles/Application-Failover-using-AOP/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

aop程序锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值