频道直达 - 专题 - 新闻 - 技巧 - 组网 - 开发 - 安全 - web编程 - 图像 - 操作系统 - 数据库 - 教育 - 旅游 - 健康 - 时尚 - 驱动 - 软件 - 游戏 - 多媒体 - ERP - 讨论组

Informix Dynamic Server 中的分布式事务

来源:互连网 作者: 出处:巧巧读书 2007-12-15 进入讨论组
上一页 1 2 

讨论组http://group.qqread.com

正如上面所提到的,如果存在应用程序服务器(Application Server),还可以由它来进行这一初始化。

在用正确的参数初始化 XADataSource 之后,就将 XADataSource 返回给方法调用者。


            85         db2xacon = initDB2XAConnection (db2ds);
            86         ifxxacon = initIfxXAConnection (ifxds);
            

在第 85 和 86 行的代码中,创建了到数据库的 XA Connection。下面描述了如何初始化这些 XA Connection。


            329     XAConnection initIfxXAConnection (IfxXADataSource ifxdatasource) {
            330
            331         XAConnection xacon = null;
            332
            333
            334         try {
            335             System.out.print ("Set up IDS XA connection: ");
            336             xacon = ifxdatasource.getXAConnection (
            337                 props.getProperty ("ifx.connection.username"),
            338                 props.getProperty ("ifx.connection.password"));
            339
            340             System.out.println ("Okay.");
            341         }
            342         catch (SQLException e) {
            343             sqlerr (e);
            344         }
            345
            346         return xacon;
            347     }
            

为了设置 XAConnection,要使用前面初始化的 DataSource 对象。第 336 行使用 XADataSource 创建了 XAConnection。为了完成 XAConnection,只需要将身份验证信息传递给该对象。


            87         db2xares = initXAResource (db2xacon);
            88         ifxxares = initXAResource (ifxxacon);
            

现在,您准备创建 XAResource 对象了。这些对象将允许您操作两阶段提交(Two-Phase-Commit)。


            388     XAResource initXAResource (XAConnection xacon) {
            389
            390         XAResource xares = null;
            391
            392
            393         try {
            394             System.out.print ("Setting up a XA resource: ");
            395             xares = xacon.getXAResource ();
            396             System.out.println ("Okay.");
            397         }
            398         catch (SQLException e) {
            399             sqlerr (e);
            400         }
            401
            402         return xares;
            403     }
            

XAResource 对象的安装没有什么特别的。该对象是通过调用 XAConnection 中的 getXAResource() 来创建的。

在完成所有关于 XA 的准备之后,就创建到数据库的 JDBC 连接。


            89         db2con = getDatabaseConnection (db2xacon);
            90         ifxcon = getDatabaseConnection (ifxxacon);
            

getDatabaseConnection() 方法中,建立了一个 JDBC 数据库连接


            250     Connection getDatabaseConnection (XAConnection xacon) {
            251
            252         Connection con = null;
            253
            254         try {
            255             System.out.print ("Establish database connection: ");
            256             con = xacon.getConnection ();
            257             System.out.println ("Okay.");
            258         }
            259         catch (SQLException e) {
            260             sqlerr (e);
            261         }
            262
            263         return con;
            264     }
            

这看上去有些混乱。既然已经在第 336 行中设置了 XAConnection,我们为何还需要 JDBC 连接呢?我们为何仍然需要一个“传统”连接的理由是所有其他 JDBC 操作和类(Statement、ResultSet ...)都基于或使用 Connection 对象。如果您看一看 JDBC 类的层次结构图,将会发现 XAConnection 并非是 Connection,反之亦然。XAConnection(实际上,它是 ConnectionPool 的子类)使用 Connection(层次化)。


            93         db2xid = createDB2XID ();
            94         ifxxid = createIfxXID ();
            

启动 XA 事务之前的最后一步就是为数据库创建 XA ID 对象。在分布式事务(Distributed Transaction)中进行操作时,总是要使用这个 xid


            183     Xid createIfxXID () {
            184
            185         Xid xid = null;
            186
            187         byte [] gid = new byte[1];
            188         byte [] bid = new byte[1];
            189
            190         gid[0] =
            191             (Byte.decode (props.getProperty ("xid.global"))).byteValue ();
            192         bid[0] =
            193             (Byte.decode (props.getProperty ("xid.branch.ifx"))).byteValue ();
            194
            195         System.out.print ("Creating an XID (" + Byte.toString (gid[0]) + ", " +
            196                           Byte.toString (bid[0]) + ") for Informix: ");
            197
            198         xid = new IfxXid (0, gid, bid);
            199         System.out.println ("Okay.");
            200         return xid;
            201     }
            

createIfxXID 方法创建一个 XID(这里:用于 IDS 连接)。正如“两阶段提交协议简介”小节中提到的,XA 事务包含定义该事务的两个元素。上面例子中的重要部分在第 198 行中。IDS XID 是同三个参数创建的。第一个参数是 format ID,它描述在什么格式中构建分布式事务(Distributed Transaction)。您可以省略这一格式信息。第二个参数定义了全局事务 ID(global transaction ID)。该 ID 对于所有参与数据库来说是惟一的。第三个参数表示该全局事务中的事务分支。

在(为 DB2 和 IDS)构建 XID 之后,我们可以使用它们来修改单个事务中的数据。


            98         execBranch (db2con, db2xares, db2xid);
            99         execBranch (ifxcon, ifxxares, ifxxid);
            

execBranch() 方法包含了上面为每个连接所定义的 XA 事务中的修改。


            215     void execBranch (Connection con, XAResource xares, Xid xid) {
            216
            217         String sql = props.getProperty ("sql.statement");
            218
            219         try {
            220             xares.start (xid, javax.transaction.xa.XAResource.TMNOFLAGS);
            221
            222                 Statement stmt = con.createStatement ();
            223                 stmt.executeUpdate (sql);
            224
            225             xares.end (xid, javax.transaction.xa.XAResource.TMSUCCESS);
            226         }
            227         catch (XAException e) {
            228             System.err.println ("XA exception caught:");
            229             System.err.println ("Cause  : " + e.getCause ());
            230             System.err.println ("Message: " + e.getMessage ());
            231             e.printStackTrace ();
            232         }
            233         catch (SQLException e) {
            234             sqlerr (e);
            235         }
            236     }
            

第 219-226 行代码包含了分布式事务(Distributed Transaction)中为相应分支所使用的真正 SQL 语句。分支边界在第 220 行中以 start 方法开始。传递给该方法的参数就是我们已经知道的事务 ID,而第二个参数包含了用于该 XA 事务的一些附加信息。因为这是第一个两阶段提交(Two-Phase-Commit)协议操作,所以不需要向该方法传递任何特殊信息。TMNOFLAGS 说明了这一事实。分支边界终止于第 225 行。标志 TMSUCCESS 描述所有操作都成功。

在 IDS 和 DB2 的分支都执行之后,全局事务就准备提交这些修改。当然,在可以向数据库传送最后的提交之前,必须询问数据库是否准备进行提交。


            104         if (prepareCommit (db2xares, db2xid) == XAResource.XA_OK &&
            105             prepareCommit (ifxxares, ifxxid) == XAResource.XA_OK) {
            106             // both branches are ready to commit
            107             commitBranch (db2xares, db2xid);
            108             commitBranch (ifxxares, ifxxid);
            109         }
            110         else {
            111             // a resource reported an error
            112             rollbackBranch (db2xares, db2xid);
            113             rollbackBranch (ifxxares, ifxxid);
            114         }
            116     } // end of constructor
            

第 104 和 105 行通知数据库准备提交。如果数据库报告 XAResource.XA_OK,就可以提交整个事务。否则,该事务就将被 ROLLBACK 中止。


            417     int prepareCommit (XAResource xares, Xid xid) {
            418
            419         int rc = 0;
            420
            421         System.out.print ("Prepare XA branch (" +
            422             Byte.toString ((xid.getGlobalTransactionId ())[0]) + ", " +
            423             Byte.toString ((xid.getBranchQualifier ())[0]) + "): ");
            424
            425         try {
            426             xares.prepare (xid);
            427         }
            428         catch (XAException e) {
            429             xaerr (e);
            430         }
            431
            432         System.out.println ("Okay.");
            433         return rc;
            434     }
            

prepareCommit() 方法中最重要的一行在第 426 行中。prepare 方法引起数据库调用两阶段提交协议(Two-Phase-Commit)的“第 1 阶段”。

根据“第 1 阶段”的结果,将提交或中止该分布式事务(Distributed Transaction)。下面是将用于发出这些必要操作的两个方法。


            128     void commitBranch (XAResource xares, Xid xid) {
            129
            130         System.out.print ("Commit XA branch (" +
            131             Byte.toString ((xid.getGlobalTransactionId ())[0]) + ", " +
            132             Byte.toString ((xid.getBranchQualifier ())[0]) + "): ");
            133
            134         try {
            135             // second parameter is 'false' since we have a two phase commit
            136             xares.commit (xid, false);
            137         }
            138         catch (XAException e) {
            139             xaerr (e);
            140         }
            141
            142         System.out.println ("Okay.");
            143     }
            

如果“第 1 阶段”未报告任何错误,就在第 136 行中为 xid 所描述的事务分支提交“第 2 阶段”。方法 commit() 中的第二个参数区分单阶段或两阶段提交操作。因为我们具有一个两阶段提交操作,所以必须将该值设置为 false

下面的例子展示了如何为数据库回滚事务分支。


            446     void rollbackBranch (XAResource xares, Xid xid) {
            447
            448         System.out.print ("Rollback XA branch (" +
            449             Byte.toString ((xid.getGlobalTransactionId ())[0]) + ", " +
            450             Byte.toString ((xid.getBranchQualifier ())[0]) + "): ");
            451
            452         try {
            453             xares.rollback (xid);
            454         }
            455         catch (XAException e) {
            456             xaerr (e);
            457         }
            458
            459         System.out.println ("Okay.");
            460     }
            

问题解答
本文中的例子演示了如何在 Java 中使用 JTA 实现两阶段提交(Two-Phase-Commit)协议。在该应用程序中,如果一个事务分支报告了错误,您就要负责进行错误处理。但是“两阶段提交协议简介”小节中提到仍然存在一个问题,那就是如果第 2 阶段中一个事务分支发生故障,该怎么办呢?

如果再次查看程序代码,您可以看到在“第 1 阶段”和“第 2 阶段”之间有一个很小的时间间隔。在这一时间间隔中,出于某种理由,其中某一参与数据库可能崩溃。如果发生了,我们将陷入分布式事务已经部分提交的情形中。

假定下列情形:在“第 1 阶段”之后,您从 DB2 和 IDS 数据库中都收到了“okay”。在下一步中,应用程序成功提交了 DB2 的事务分支。接着,应用程序通知 DB2 事务分支提交事务。现在,在应用程序可以通知 IDS 事务分支提交它这一部分之前,IDS 引擎由于断电发生崩溃。这就是一种部分提交全局事务的情形。您现在该怎么办呢?

在重启之后,DB2 和 IDS 都将尝试恢复打开的事务分支。该引擎等待来自应用程序的提示如何做。如果应用程序没有准备重新发送“第 2 阶段”的提交,该事务分支将被引擎所启动的试探性回滚中止。这是非常糟糕的,因为这将使该全局事务处于不一致状态。

一种解决方案是用一个小型应用程序连接引擎中打开的事务分支,并通知引擎提交或回滚这一打开的事务。如果您使用 IDS 作为后端,那么还有一个隐藏的 onmode 标志,允许您结束打开的事务分支。(onmode -Z xid)。

在 DB2 UDB 中,您可以发出 LIST INDOUBT TRANSACTIONS 来获得打开的 XA 事务的有关信息。您必须查看 DB2 Information Center 中的描述来解决该问题。

上面描述的情形是一个很好的例子,也是使用应用程序服务器(Application Server)或事务监控器(Transaction Monitor)的理由。在使用一个中间层服务器时,就由该服务器负责保持事情正常。

备选方案
清单 1 演示了在应用程序中从数据库读取数据并处理结果的可行方法。如果您的应用程序是“只读”应用程序,IBM? 就提供了另一种解决方案,称作 WebSphere? Information Integrator。WebSphere Information Integrator 使用来自 DB2 UDB(或 DB2 Data Joiner、DB2 Relational Connect)的联邦数据库技术,以将多个数据库(通常:数据源)虚拟化(virtualize)到一个数据库中。不同的、非本地的数据库中的表都链接到 DB2 UDB 中。该操作对于客户机应用程序是完全透明的。客户机可以访问其他数据库中的所有远程表,就像它们是本地 DB2 UDB 表一样。正如 清单 1 中引用的,不再需要连接两个数据库。到 DB2 UDB 的单个连接就已经足够了,因为 DB2 中可以看到 IDS 数据库中的所有表。

目前,WebSphere Information Integrator 不支持两阶段提交,然而,将来的版本将支持两阶段提交协议;这将带来实现企业应用程序的新方法

更多文章 更多内容请看informix专题,或进入讨论组讨论。
上一页 1 2 
收藏此文】【 】【打印】【关闭
相关图文阅读
频道图文推荐
健 康 咨 询
时 尚 咨 询
巧巧读书宗旨
相关专题
讨论组问题推荐
站内各频道最新更新文档
站内最新制作专题
热门关键字导读
Photoshop教 程照片处理 照片制作 PS快捷键 抠图
计 算 机 故 障XP系统修复
艺 术 与 设 计设计 流媒体 设计欣赏 边框
计 算 机 安 全ARP
站内频道文章精选
巧巧电脑频道编辑信箱  告诉我们您想看的专题或文章