最近别人的项目,因为经常获取不到链接出错,我好奇也就跟着摆弄了一把,使用的插件是:c3p0+spring+ibatiS,当然事务管理部分也配置上了配置如下:

 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="minPoolSize" value="${datasource.minPoolSize}" />
<property name="maxPoolSize" value="${datasource.maxPoolSize}" />
<property name="initialPoolSize" value="${datasource.initialPoolSize}" />
<property name="maxIdleTime" value="${datasource.maxIdleTime}" />
<property name="maxStatements" value="${datasource.maxStatements}" />
<property name="idleConnectionTestPeriod" value="${datasource.idleConnectionTestPeriod}" />
<property name="acquireIncrement" value="${datasource.acquireIncrement}" />
<property name="acquireRetryAttempts" value="${datasource.acquireRetryAttempts}" />
<property name="breakAfterAcquireFailure" value="${datasource.breakAfterAcquireFailure}" />
<property name="checkoutTimeout" value="${datasource.checkoutTimeout}" />
<property name="numHelperThreads" value="${datasource.numHelperThreads}" />
</bean> <!-- 事务管理 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean> <!-- 事务拦截器 -->
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributes">
<!-- 下面定义事务传播属性:key表示 service中的方法 -->
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="select*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="query*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="pageQuery*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!-- BeanName auto proxy to define the interceptor --> <bean
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<!-- 配置需要事务管理的service -->
<value>*Service</value>
<value>*ServiceImpl</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>

上面的配置有点长,捡几个比较重要的项说明吧:

datasource.minPoolSize=30
datasource.maxPoolSize=80
datasource.initialPoolSize=40
datasource.maxIdleTime=60
datasource.acquireIncrement=5 datasource.idleConnectionTestPeriod=60
datasource.maxStatements=0
datasource.acquireRetryAttempts=30
datasource.breakAfterAcquireFailure=false
datasource.testConnectionOnCheckout=false
datasource.checkoutTimeout=20000
datasource.numHelperThreads=10

配置都很常规,任务线程数从默认的3加大到10,这也比较可取,因为minpoolSize是30,初始化的时候建的pool是40,每次增加5个链接数。

趁此机会,又看了把c3p0的源码,我写了个很简单的测试,当然配置完全一模一样,不过结构上使用的是mybatis,然后只是使用了get方法,假定所有的业务逻辑都走了正常的transaction拦截器:即所有的链接在用完都把链接还回去了。

  1. 先是从100线程数,并发调用使用c3p0链接的service方法,发现毫无压力。显然100太小儿科了。
  2. 大力一把,将线程数加到1000,这样一来,就有一些(为数不多的几个线程获取不到connection了),即c3p0内部获取链接的方法:
     private synchronized Object prelimCheckoutResource( long timeout )
    throws TimeoutException, ResourcePoolException, InterruptedException
    {
    try
    {
    ensureNotBroken(); int available = unused.size();
    if (available == 0)
    {
    int msz = managed.size(); if (msz < max)
    {
    // to cover all the load, we need the current size, plus those waiting already for acquisition,
    // plus the current client
    int desired_target = msz + acquireWaiters.size() + 1; if (logger.isLoggable(MLevel.FINER))
    logger.log(MLevel.FINER, "acquire test -- pool size: " + msz + "; target_pool_size: " + target_pool_size + "; desired target? " + desired_target); if (desired_target >= target_pool_size)
    {
    //make sure we don't grab less than inc Connections at a time, if we can help it.
    desired_target = Math.max(desired_target, target_pool_size + inc); //make sure our target is within its bounds
    target_pool_size = Math.max( Math.min( max, desired_target ), min ); _recheckResizePool();
    }
    }
    else
    {
    if (logger.isLoggable(MLevel.FINER))
    logger.log(MLevel.FINER, "acquire test -- pool is already maxed out. [managed: " + msz + "; max: " + max + "]");
    } awaitAvailable(timeout); //throws timeout exception
    }

    直接到39行进行等待,其实也不难看出,available==0了,然后不管是否进行 _recheckResizePool();这个操作都需要等着。这里的等待时间,是配置的checkoutTimeout=2000,这么长时间的等待都木有资源可用。按照以往的理解,c3p0的实际上有进行连接池的扩展和收缩链接数的操作的对不,往下看,其实能看出来在_recheckResizePool()方法中就有进行扩容的判断的操作的:

       int desired_target = msz + acquireWaiters.size() + 1;
    
                         if (logger.isLoggable(MLevel.FINER))
    logger.log(MLevel.FINER, "acquire test -- pool size: " + msz + "; target_pool_size: " + target_pool_size + "; desired target? " + desired_target); if (desired_target >= target_pool_size)
    {
    //make sure we don't grab less than inc Connections at a time, if we can help it.
    desired_target = Math.max(desired_target, target_pool_size + inc); //make sure our target is within its bounds
    target_pool_size = Math.max( Math.min( max, desired_target ), min ); _recheckResizePool();
    }

    当等待的任务过大时,target_pool_size = Math.max( Math.min( max, desired_target ), min );会进行将target_pool_size置为max值。

       if ((shrink_count = msz - pending_removes - target_pool_size) > 0)
    shrinkPool( shrink_count );
    else if ((expand_count = target_pool_size - (msz + pending_acquires)) > 0)
    expandPool( expand_count );

    这里的target_pool_size是业务当前需要的目标size,这里会有歧义,msz是当前的poolsize,

    pending_acquires是当前等待获取资源的任务数,有可能很大,expand_count = target_pool_size - (msz + pending_acquires)) > 0这个条件的结果:expand_count= 80 - (40+ 40) <=0 ,所以当等待资源过大,但是当业务线程数过大时,链接并未被扩容,原因就在这里。瞬间并发到超峰值。所以当等待资源都timeout到很小值或者有资源使用完并还回链接后,新的请求进来时,该判断条件满足后,会进行资源的重新添加任务。至于如何添加任务,也是很常规的,即一个链接资源的新建需要一个线程,所以上面的配置10个线程,同时间内(理论上)只能创建10个链接,不过task的run方法都很瞬时,也能满足一定量的创建。

    而在创建资源的线程中,有一个竞争条件:synchronized ( ThreadPoolAsynchronousRunner.this ),所以这里可以说是并发创建资源的瓶颈,而当线程池都满了后,c3p0的expand就没什么作用了,它没有expand的作用,这时候只能通过回收现有资源重复使用,或者某个链接坏了,重新创建

c3p0----获取不到链接的更多相关文章

  1. 如何获取Flickr图片链接地址作为外链图片

    Flickr,雅虎旗下图片分享网站.为一家提供免费及付费数位照片储存.分享方案之线上服务,也提供网络社群服务的平台.其重要特点就是基于社会网络的人际关系的拓展与内容的组织.这个网站的功能之强大,已超出 ...

  2. c3p0获取连接Connection后的Close()---释疑

    论题: java c3p0获取连接Connnection 之后, 调用 con.close( ) 是否真的关闭了物理连接 ? 简答: c3p0采用连接池, 目的就是提前预置一定数量的连接, 在使用时候 ...

  3. wordpress获取当前页面链接

    我们知道wordpress的<?php the_permalink(); ?>和<?php echo get_permalink(); ?>可以获取页面链接,但是有些比较复杂的 ...

  4. java正则 读取html 获取标题/超链接/链接文本/内容

    java正则 读取html 获取标题/超链接/链接文本/内容 参考链接:http://yijianfengvip.blog.163.com/blog/static/175273432201142785 ...

  5. js获取带#号链接后的参数

    现在许多的主流网站都将'#'大规模用于重要URL中,我们通过正则表达式和window.location.search获取参数已经行不通了. 一.'#'号是什么 1.#代表网页中的一个位置.其后面的字符 ...

  6. WordPress主题开发实例:get_term_by()获取指定分类链接

    根据名称获取链接 <?php //根据名称获取对应的id $term=get_term_by('name','新闻动态','category'); $term_id=$term->term ...

  7. WordPress获取首页网站链接和站点名称

    利用bloginfo 获取WordPress网站名称和主页链接 用法一: $blog_title = get_bloginfo('name'); //获取站点名称 $linkzmki = get_bl ...

  8. 获取与Url链接相关的信息

    以下结果的值以此示例为基础:http://www.x2y2.com:80/fisker/post/0703/window.location.html?ver=1.0&id=6#imhere j ...

  9. jQuery获取浏览器URL链接的值

    代码: 方法一: $.extend({ getUrlVars: function () { var vars = [], hash; ).split('&'); ; i < hashes ...

  10. js 获取页面内链接

    今天有同学问如何用 JS 正则表达式获取一段文本中的超链接,并对超链接进行处理,想了几分钟,写了下面的代码: var re = /https?:\/\/[\w\.:~\-\d\/]+(?:\?[\w\ ...

随机推荐

  1. 利用R产生随机数

    生成随机数有两个函数runif()和rnorm(),其中r表示的是random随机的意思,unif表示的是均匀分布,而norm表示的是正态分布. 1)生成10个2到3之间的,服从均匀分布的随机数:ru ...

  2. grep匹配字符串出现的次数

    背景:想要匹配一个html页面中某个字符出现的次数   遇到的问题: 用grep -c “xxx字符”得到的是行数,如果一行中有多个匹配到的字符,只会算作一个       解决方法: 使用grep - ...

  3. resin-pro-4.0.53报错java.lang.Error: java.lang.ClassNotFoundException: com.caucho.loader.SystemClassLoader

    最初并未发现,笔者的系统环境变量JAVA_HOME变量设置错误 D:\develop\Java\x64\jdk1.8.0_144 #最初使用了阉割版的JDK 改成完整安装的JDK就可以 D:\deve ...

  4. 源代码安装grub-customizer

    wget https://launchpad.net/grub-customizer/5.0/5.0.6/+download/grub-customizer_5.0.6.tar.gztar zxvf ...

  5. 【转】MEF程序设计指南一:在应用程序中宿主MEF

    在应用程序中宿主MEF其实非常简单,只需要创建一个组合容器对象(CompositionContainer)的实例,然后将需要组合的部件(Parts)和当前宿主程序添加到容器中即可.首先需要添加MEF框 ...

  6. 不要用for in语句对数组进行遍历

    for...in主要用于对数组和对象的属性进行遍历.for ... in 循环中的代码每执行一次,就会对数组的元素或者对象的属性进行一次操作. 语法:for (variable in object) ...

  7. [python] can not find app ,module

    can not find module 1 startapp appname 而不是 startproject 2 不要自己创建项目根目录,要用mamage.py生成 can not find app ...

  8. 02 Maven 入门使用

    Maven 入门使用 1. Maven 项目工程目录约定 Project |-src | |-main | | |-java -- 存放项目的 .java 文件 | | |-resources -- ...

  9. Android系统root破解原理分析

    http://dengzhangtao.iteye.com/blog/1543494 root破解过程的终极目标是替换掉系统中的su程序.但是要想替换掉系统中su程序本身就是需要root权限的,怎样在 ...

  10. 2018.10.01 NOIP模拟 偷书(状压dp)

    传送门 状压dp经典题. 令f[i][j]f[i][j]f[i][j]表示到第i个,第i−k+1i-k+1i−k+1~iii个物品的状态是j时的最大总和. 然后简单维护一下转移就行了. 由于想皮一下果 ...