BoneCP framework的整个框架设计,其实还是比较容易看懂的,大致用两天左右时间把源代码看了大概。
发现能够拿得出比较有意思的点应该是配置transactionRecoveryEnabled参数的意义,虽然在xml配置里面只是配置boolean类型true、false,但是内置的涵义挺有趣、挺cool。
了解transactionRecoveryEnabled参数首先需要把MemorizeTransactionProxy
public class MemorizeTransactionProxy implements InvocationHandler { /** Target of proxy. */ private Object target; /** Connection handle. Keep a WeakReference here because we want the GC to kick in if the application loses a handle on it.*/ private WeakReference<ConnectionHandle> connectionHandle; /** List of methods that will trigger a reset of the transaction. */ private static final ImmutableSet<String> clearLogConditions = ImmutableSet.of("rollback", "commit", "close"); /** Class logger. */ private static final Logger logger = LoggerFactory.getLogger(MemorizeTransactionProxy.class); /** * Default constructor. */ public MemorizeTransactionProxy(){ // not needed } /** Wrap connection with a proxy. * @param target connection handle * @param connectionHandle originating bonecp connection * @return Proxy to a connection. */ protected static Connection memorize(final Connection target, final ConnectionHandle connectionHandle) { return (Connection) Proxy.newProxyInstance( ConnectionProxy.class.getClassLoader(), new Class[] {ConnectionProxy.class}, new MemorizeTransactionProxy(target, connectionHandle)); } /** Wrap Statement with a proxy. * @param target statement handle * @param connectionHandle originating bonecp connection * @return Proxy to a statement. */ protected static Statement memorize(final Statement target, final ConnectionHandle connectionHandle) { return (Statement) Proxy.newProxyInstance( StatementProxy.class.getClassLoader(), new Class[] {StatementProxy.class}, new MemorizeTransactionProxy(target, connectionHandle)); } /** Wrap PreparedStatement with a proxy. * @param target statement handle * @param connectionHandle originating bonecp connection * @return Proxy to a Preparedstatement. */ protected static PreparedStatement memorize(final PreparedStatement target, final ConnectionHandle connectionHandle) { return (PreparedStatement) Proxy.newProxyInstance( PreparedStatementProxy.class.getClassLoader(), new Class[] {PreparedStatementProxy.class}, new MemorizeTransactionProxy(target, connectionHandle)); } /** Wrap CallableStatement with a proxy. * @param target statement handle * @param connectionHandle originating bonecp connection * @return Proxy to a Callablestatement. */ protected static CallableStatement memorize(final CallableStatement target, final ConnectionHandle connectionHandle) { return (CallableStatement) Proxy.newProxyInstance( CallableStatementProxy.class.getClassLoader(), new Class[] {CallableStatementProxy.class}, new MemorizeTransactionProxy(target, connectionHandle)); } /** Main constructor * @param target target to actual handle * @param connectionHandle bonecp ref */ private MemorizeTransactionProxy(Object target, ConnectionHandle connectionHandle) { this.target = target; this.connectionHandle = new WeakReference<ConnectionHandle>(connectionHandle); } // @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; ConnectionHandle con = this.connectionHandle.get(); if (con != null){ // safety! if (method.getName().equals("getProxyTarget")){ // special "fake" method to return our proxy target return this.target; } if (con.isInReplayMode()){ // go straight through when flagged as in playback (replay) mode. try{ return method.invoke(this.target, args); } catch (InvocationTargetException t){ throw t.getCause(); // we tried running it, but playback mode also blew up. Throw out the cause, not the // wrapped invocationtargetexception. } } if (con.recoveryResult != null){ // if we previously failed, do the mapping to the new connection/statements Object remap = con.recoveryResult.getReplaceTarget().get(this.target); if (remap != null){ this.target = remap; } remap = con.recoveryResult.getReplaceTarget().get(con); if (remap != null){ con = (ConnectionHandle) remap; } } // record this invocation if (!con.isInReplayMode() && !method.getName().equals("hashCode") && !method.getName().equals("equals") && !method.getName().equals("toString")){ con.getReplayLog().add(new ReplayLog(this.target, method, args)); } try{ // run and swap with proxies if we encounter prepareStatement calls result = runWithPossibleProxySwap(method, this.target, args); // when we commit/close/rollback, destroy our log. Does this work if we have nested transactions???? Fixme? if (!con.isInReplayMode() && (this.target instanceof Connection) && clearLogConditions.contains(method.getName())){ con.getReplayLog().clear(); // con.recoveryResult.getReplaceTarget().clear(); } } catch (Throwable t){ // if we encounter problems, grab a connection and replay back our log List<ReplayLog> oldReplayLog = con.getReplayLog(); con.setInReplayMode(true); // stop recording // this will possibly terminate all connections here if (t instanceof SQLException || (t.getCause() != null && t.getCause() instanceof SQLException)){ con.markPossiblyBroken((SQLException)t.getCause()); } if (!con.isPossiblyBroken()){ // connection is possibly recoverable... con.setInReplayMode(false); // start recording again con.getReplayLog().clear(); } else { // connection is possibly recoverable... logger.error("Connection failed. Attempting to recover transaction on Thread #"+ Thread.currentThread().getId()); // let's try and recover try{ con.recoveryResult = attemptRecovery(oldReplayLog); // this might also fail con.setReplayLog(oldReplayLog); // attemptRecovery will probably destroy our original connection handle con.setInReplayMode(false); // start recording again logger.error("Recovery succeeded on Thread #" + Thread.currentThread().getId()); con.possiblyBroken = false; // return the original result the application was expecting return con.recoveryResult.getResult(); } catch(Throwable t2){ con.setInReplayMode(false); // start recording again con.getReplayLog().clear(); /* #ifdef JDK6 throw new SQLException("Could not recover transaction.", t.getCause()); #endif JDK6 */ /* #ifdef JDK5 throw new SQLException("Could not recover transaction. Original exception follows." + t.getCause()); #endif JDK5 */ } } // it must some user-level error eg setting a preparedStatement parameter that is out of bounds. Just throw it back to the user. throw t.getCause(); } } return result; // normal state } /** Runs the given method with the specified arguments, substituting with proxies where necessary * @param method * @param target proxy target * @param args * @return Proxy-fied result for statements, actual call result otherwise * @throws IllegalAccessException * @throws InvocationTargetException */ private Object runWithPossibleProxySwap(Method method, Object target, Object[] args) throws IllegalAccessException, InvocationTargetException { Object result; // swap with proxies to these too. if (method.getName().equals("createStatement")){ result = memorize((Statement)method.invoke(target, args), this.connectionHandle.get()); } else if (method.getName().equals("prepareStatement")){ result = memorize((PreparedStatement)method.invoke(target, args), this.connectionHandle.get()); } else if (method.getName().equals("prepareCall")){ result = memorize((CallableStatement)method.invoke(target, args), this.connectionHandle.get()); } else result = method.invoke(target, args); return result; } /** Play back a transaction * @param oldReplayLog * @return map + result * @throws SQLException * */ private TransactionRecoveryResult attemptRecovery(List<ReplayLog> oldReplayLog) throws SQLException{ boolean tryAgain = false; Throwable failedThrowable = null; ConnectionHandle con = this.connectionHandle.get(); if (con == null){ // safety! throw PoolUtil.generateSQLException("ConnectionHandle is gone!", new IllegalStateException()); } TransactionRecoveryResult recoveryResult = con.recoveryResult; ConnectionHook connectionHook = con.getPool().getConfig().getConnectionHook(); int acquireRetryAttempts = con.getPool().getConfig().getAcquireRetryAttempts(); long acquireRetryDelay = con.getPool().getConfig().getAcquireRetryDelayInMs(); AcquireFailConfig acquireConfig = new AcquireFailConfig(); acquireConfig.setAcquireRetryAttempts(new AtomicInteger(acquireRetryAttempts)); acquireConfig.setAcquireRetryDelayInMs(acquireRetryDelay); acquireConfig.setLogMessage("Failed to replay transaction"); Map<Object, Object> replaceTarget = new HashMap<Object, Object>(); do{ replaceTarget.clear(); // make a copy for (Entry<Object, Object> entry: recoveryResult.getReplaceTarget().entrySet()){ replaceTarget.put(entry.getKey(), entry.getValue()); } List<PreparedStatement> prepStatementTarget = new ArrayList<PreparedStatement>(); List<CallableStatement> callableStatementTarget = new ArrayList<CallableStatement>(); List<Statement> statementTarget = new ArrayList<Statement>(); Object result = null; tryAgain = false; // this connection is dead con.setInReplayMode(true); // don't go in a loop of saving our saved log! try{ con.clearStatementCaches(true); con.getInternalConnection().close(); } catch(Throwable t){ // do nothing - also likely to fail here } try{ con.setInternalConnection(memorize(con.getPool().obtainInternalConnection(con), con)); } catch(SQLException e){ throw con.markPossiblyBroken(e); } con.getOriginatingPartition().trackConnectionFinalizer(con); // track this too. for (ReplayLog replay: oldReplayLog){ // we got new connections/statement handles so replace what we've got with the new ones if (replay.getTarget() instanceof Connection){ replaceTarget.put(replay.getTarget(), con.getInternalConnection()); } else if (replay.getTarget() instanceof CallableStatement){ if (replaceTarget.get(replay.getTarget()) == null){ replaceTarget.put(replay.getTarget(), callableStatementTarget.remove(0)); } } else if (replay.getTarget() instanceof PreparedStatement){ if (replaceTarget.get(replay.getTarget()) == null){ replaceTarget.put(replay.getTarget(), prepStatementTarget.remove(0)); } }else if (replay.getTarget() instanceof Statement){ if (replaceTarget.get(replay.getTarget()) == null){ replaceTarget.put(replay.getTarget(), statementTarget.remove(0)); } } try { // run again using the new connection/statement // result = replay.getMethod().invoke(, replay.getArgs()); result = runWithPossibleProxySwap(replay.getMethod(), replaceTarget.get(replay.getTarget()), replay.getArgs()); // remember what we've got last recoveryResult.setResult(result); // if we got a new statement (eg a prepareStatement call), save it, we'll use it for our search/replace if (result instanceof CallableStatement){ callableStatementTarget.add((CallableStatement)result); } else if (result instanceof PreparedStatement){ prepStatementTarget.add((PreparedStatement)result); } else if (result instanceof Statement){ statementTarget.add((Statement)result); } } catch (Throwable t) { // It blew up again, let's try a couple more times before giving up... // call the hook, if available. if (connectionHook != null){ tryAgain = connectionHook.onAcquireFail(t, acquireConfig); } else { logger.error("Failed to replay transaction. Sleeping for "+acquireRetryDelay+"ms and trying again. Attempts left: "+acquireRetryAttempts+". Exception: "+t.getCause() + " Message:"+t.getMessage()); try { Thread.sleep(acquireRetryDelay); if (acquireRetryAttempts > 0){ tryAgain = (--acquireRetryAttempts) != 0; } } catch (InterruptedException e) { tryAgain=false; } } if (!tryAgain){ failedThrowable = t; } break; } } } while (tryAgain); // fill last successful results for (Entry<Object, Object> entry: replaceTarget.entrySet()){ recoveryResult.getReplaceTarget().put(entry.getKey(), entry.getValue()); } for (ReplayLog replay: oldReplayLog){ replay.setTarget(replaceTarget.get(replay.getTarget())); // fix our log } if (failedThrowable != null){ throw PoolUtil.generateSQLException(failedThrowable.getMessage(), failedThrowable); } return recoveryResult; } }
其实就是类似于录影功能,通过代理方式,重新播放一次操作,有没有觉得其实我们可以把类似的想法放到我们开发中,诸如:短信邮件的发送、重复性动作的模仿操作......
相关推荐
BoneCP 数据源是一种高效、快速的数据连接池技术,它被设计用于提高应用程序处理数据库连接的性能和效率。在Java环境中,数据库连接池是管理数据库连接的关键组件,它减少了创建和销毁连接的开销,从而提升了整体...
BoneCP 是一个高效的开源连接池实现,主要用于Java应用程序中数据库连接的管理。它提供了一种高效、可配置的方式来管理和复用数据库连接,从而提高应用程序的性能和稳定性。在标题中提到的 "bonecp-0.8.0.RELEASE....
BoneCP是一个轻量级的Java数据库连接池,它在设计时考虑了性能和资源的高效利用,特别适合于高并发的Web应用环境。本篇将详细介绍BoneCP连接池的使用和核心概念,以及如何通过提供的示例代码进行配置和集成。 ...
BoneCP是一款高效的、开源的Java数据库连接池(JDBC Connection Pool)框架,它在性能上优于其他同类连接池,如C3P0、DBCP等。这个标题提及的是"bonecp 0.7.1 jar包以及源码",这意味着我们拥有 BoneCP 的特定版本...
BoneCP是一款轻量级的Java数据库连接池,它以其高效性能和简洁的API而受到开发者们的欢迎。在Java应用程序中,数据库连接池是至关重要的组件,它可以有效地管理和复用数据库连接,减少创建和销毁连接的开销,提高...
** BoneCP 连接池学习笔记 ** BoneCP 是一个高效、轻量级的 Java 数据库连接池(JDBC Connection Pool)实现。它在性能上优于其他同类连接池,如 C3P0 和 DBCP,尤其适用于高并发场景。在深入理解 BoneCP 之前,...
bonecp数据库连接池jar包0.7.1: bonecp-0.7.1.RELEASE.jar bonecp-provider-0.7.1-rc2.jar bonecp-spring-0.7.1.RELEASE.jar
BoneCP是一款高效的、开源的Java数据库连接池(JDBC Connection Pool)框架,它在性能上优于其他同类连接池,如C3P0和DBCP。本资料提供了使用BoneCP连接Oracle数据库的实例以及所需的jar包,帮助开发者快速理解和...
### BoneCP参数配置详解 BoneCP,全称Bone Connection Pool,是Java环境下一款高效、轻量级的数据库连接池解决方案,特别适用于高并发环境。它通过优化连接管理和资源分配策略,能够显著提升数据库访问效率,降低...
BoneCP 是一个高性能的 JDBC 连接池,它在 Java 应用中被广泛使用,以提高数据库连接的效率和管理。在这个压缩包中,包含了 BoneCP 的所有必需的 JAR 包,以及一个连接示例(demo),帮助开发者快速理解和使用 ...
BoneCP是一种高效的、开源的Java连接池实现,它旨在提供比其他常见的数据库连接池如C3P0和DBCP更高的性能。在这个实例中,我们将学习如何通过XML配置文件来使用BoneCP,以及如何在Java代码中加载这个配置。 首先,...
BoneCP是一种高效的、轻量级的Java数据库连接池(JDBC Connection Pool)实现,它旨在提供比其他连接池更快的性能。在这个“BoneCP所需依赖包”中,包含了使用BoneCP时必要的第三方库,让我们逐一解析这些依赖: 1....
BoneCP是一款轻量级的Java数据库连接池(JDBC Connection Pool)框架,它以其高效、易用性而受到开发者的欢迎。在这个压缩包文件中,包含了BoneCP运行所需的多个核心库,下面将对这些库进行详细解读。 1. **bonecp-...
bonecp-0.7.0.jar bonecp-0.7.0.jar bonecp-0.7.0.jar bonecp-0.7.0.jar bonecp-0.7.0.jar bonecp-0.7.0.jar bonecp-0.7.0.jar bonecp-0.7.0.jar
在用C3P0数据连接池的时候,一旦并发上来就坑不住了,因为C3P0存在...在Hibernate中使用BoneCP除了需要上面提到的jar包之外,还需要下载一个名为bonecp-provider-0.7.0.jar的bonecp-provider的jar包,它的下载位置是:...
《BoneCP:一款高效数据库连接池的深度解析》 BoneCP是一款开源的、高性能的Java数据库连接池。在深入理解 BoneCP之前,我们先要明白数据库连接池的基本概念。数据库连接池是在应用服务器启动时创建的,它可以提供...
bonecp-0.8.1-20131105.191813-1.jar bonecp-jdk-compat-0.8.1-20131105.191752-1.jar bonecp-provider-0.8.1-20131105.191842-1.jar slf4j-api-1.7.7.jar slf4j-log4j12-1.7.7.jar
骨CP(BoneCP)是一种高性能、轻量级的Java数据库连接池(JDBC Connection Pool)实现,被设计成能够提供比其他同类连接池更高的效率。它由Jeffrey Resnick开发,旨在解决传统连接池在并发处理上的性能问题。在本文...
BoneCP是一款高效的数据库连接池,它以其出色的性能和稳定性赢得了开发者们的青睐。相较于其他常见的连接池如c3p0,BoneCP宣称能提供超过25倍的性能提升,这对于高并发、大数据量的Web应用来说尤其重要。在本文中,...
BoneCP是一款高效、轻量级的数据库连接池实现,它以其优秀的性能和灵活的配置赢得了开发者们的青睐。这篇源码分析报告将深入探讨BoneCP的工作原理、核心功能以及其在实际应用中的优化策略。 首先,我们要理解...