领略架构之美:tomcat 连接池 tomee 数据源 jta 支持 详细架构剖析

连接池核心机制


ProxyConnection:

最底部一个代理,负责:
1.路由xa和非xa的方法到正确的真实数据库上;
2.调用close方法之后将逻辑池化的逻辑连接对象放回池中;
3.在close之后拦截继续对真实连接的调用;
4.其它池化逻辑支持...


DisposableConnectionFacade:

最头部的一个代理,负责:
在调用close方法之后,切断该对象到后面各层代理的引用,好处是可以避免继续反射使用池中的资源以造成管理混乱,同时可以使得相应的资源得以及时被GC

动态代理:
根据真实数据库连接是否是 XA 的,选择创建出 XAConnection 或者 Connection 接口的动态代理对象,代理的执行对象正是最头部的代理:DisposableConnectionFacade
如此:客户端默认拿到一个 Connection 类型的连接后,可以根据实际情况 强制转换 为XAConnection,之所以可以转换成功,是因为在创建这个代理对象之初根据真实的xa连接选用了 XAConnection 接口,
最后的调用都会路由到 ProxyConnection,它会选择将xa的操作交由真实的 xa 连接来执行。


对外数据源接口设计


pool.DataSourceProxy:

注意:DataSourceProxy 即没有实现 JDK 标准的 DataSource 接口,也没有实现 JDK 标准的 XADataSource 接口,在从它的 getConnection 方法或者 getXAConnection 方法获取连接的时候都是是从 ConnectionPool 中获取后强制转换得到的,由“连接池核心机制”可得知“确实可以转换成功”!
注意:其子类 pool.DataSource 和 pool.XADataSource 并没有实质的逻辑实现,只是自己实现了标准的 JDK 接口,这样做的目的就是可以让客户端强制转换为所需要的 JDK 标准接口 DataSource 或者 XADataSource,也就是给客户开放标准接口,这样做的好处是可以兼容不同版本的jdk,比如新版本的添加了新的接口,pool.DataSourceProxy 也已经实现了,但是使用的旧版本的 JDK 无法编译,所以只能 抹去 新版本 JDK 接口的声明(比如抹去 @Override、extends、implements 等),然后在不同的子类中去开放这些接口,注意这里 子类要单独使用高版本 JDK 编译(因为要依赖高版本的api),这样的子类不用写任何具体的方法实现,主要声明一下自己是高版本的类型就可以了,具体的实现 父类已经都做过了(只不过没有暴漏类型而已)。


Tomee数据源扩展



Tomee数据源的创建是通过 DataSourceFactory 这个控制类结合数据源建造器 TomEEDataSourceCreator 来实现的,DataSourceFactory 控制构造的类型,比如是否需要 pool,是否需要 jta 等,TomEEDataSourceCreator 来完成具体的工作,而具体的操作就是对 tomcat 连接池进行扩展,主要是扩展 pool.DataSource 类和 ConnectionPool 类等。
TomEEDataSource:是对tomcat连接池的简单封装;
ManagedDataSource:则是对 TomEEDataSource 再次封装,目的是添加 JTA 职责,它是通过拦截创建连接方法来完成的,对创建出的连接进行了 JTA 业务栈包装,包装后的代理为:ManagedConnection(它是一个业务栈代理,并不是一个标准的 JDK Connection 类型的对象)


ManagedDataSource ManagedConnection :

ManagedDataSource 是 TomEEDataSource 的一个代理,其职责在于从TomEEDataSource中获取连接后对其进行 jta 逻辑包装,这些包装是通过 ManagedConnection 这个 连接代理来完成的,连接代理的逻辑:在调用连接相关的事务性方法前将当前的 xa resource 加入到当前 JTA 事务。


TomEEDataSource TomEEConnectionPool:

TomEEDataSource(继承自pool.DataSource),TomEEDataSource添加了自己的封装逻辑:
1.动态代理实现不允许父类对配置进行修改;
2.注册和解注册 jmx;
3.如果是数据源创建则使用tomcat-pool自己的连接池,如果是驱动创建则换成使用TomEEConnectionPool(继承自tomcat-pool,主要增强的逻辑是在建立PooledConnection的时候每次都改用尽量使用tomcat自己的类加载器来加载driver实现类)


ManagedConnection:

=== JTA 逻辑 ===
1.根据调用的Connection的方法名称,决定是否进行 JTA 逻辑,不需要则直接交给真实连接执行;
2.若上述判断需要 JTA 逻辑,则获取当前线程 JTA 事务,如果没有获取到则说明当前无事务,则直接交给真实连接执行;
3.到这里说明当前线程存在全局事务,进而先检查下该代理连接上是否已经绑定过事务,如果已经绑定过,且事务仍然处于活跃状态,且绑定的事务与当前线程事务一致,则直接在事务模式下代理执行方法,如果绑定的事务与当前线程事务不一致则抛出"该连接已经加入到其它事务"的异常;
4.若当前没有绑定事务,或者事务不处于活跃状态(已提交或回滚等),则继续检查当前线程事务是否处于活跃状态,不活跃则直接交给真实连接执行,否则进行 JTA 逻辑封装;
5.JTA 逻辑封装,首先在当前事务上检查该数据源是否已经绑定过 JTA 连接,如果绑定了则直接使用(比如是在一个ejb中进入 其它ejb,或第二次从池中 get 到一个连接,但都在同一个事务里)不再加入新的事务分支。
6.如果该数据源是第一次来加入事务(即没有在当前事务上找到该数据源的绑定连接),则获取到真实数据库的 XAResource,并通过 transaction.enlistResource 加入到当前事务,加入成功后则将当前数据源绑定到当前事务(下次如果是同一个数据源就还使用同一个连接,而不是新建立事务分支,这样即可以提高性能也可以避免一些问题:如oracle一个xa连接为关闭或者其对应的XAResource没有end,则不能使用另一个xa连接的XAResource来提交前一个xa连接的事务id),最后,向事务中注册同步逻辑(JTA规范):在事务结束后调用真实数据库连接的close方法(其实这里的真实数据库连接也只是一个代理,只是调用方并不关系);
7.另外,请注意:Tomee的JTA逻辑允许底层数据不是 XA 类型的,对于非 XA类型的数据源创建出来的连接进行了本地形式化的封装,即“LocalXAResource”,LocalXAResource表面上和标准 JTA XAResource提供了相同的接口,可以遵循 JTA 事务管理器的调度,但并没有真实地实现 XA 规范,在两阶段提交出错后不能保证数据的一致性,所以应该尽量保证参与 JTA 的数据源都是真实的 XA 数据源。



PS:以上分析源自 Apache 开源社区 Tomee Tomcat 源码,tomcat:8.5.6 版本 和 tomee:7.0.2 版本,其它版本的也没有太大变化。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值