所属文章:池化技术(一)Druid是如何管理数据库连接的?

本代码段对应流程1.2,真正获取连接的执行:


  1. private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException {
  2. //可用性判断
  3. if (closed) {
  4. connectErrorCountUpdater.incrementAndGet(this);
  5. throw new DataSourceClosedException("dataSource already closed at " + new Date(closeTimeMillis));
  6. }
  7. if (!enable) {
  8. connectErrorCountUpdater.incrementAndGet(this);
  9. throw new DataSourceDisableException();
  10. }
  11. final long nanos = TimeUnit.MILLISECONDS.toNanos(maxWait); //纳秒
  12. final int maxWaitThreadCount = this.maxWaitThreadCount; //目前因为拿不到连接而发生阻塞的业务线程数
  13. DruidConnectionHolder holder;
  14. for (boolean createDirect = false;;) {
  15. if (createDirect) { //模式未启用,恒等false,下面的逻辑不会触发,所以为了方便阅读,隐藏这部分代码
  16. //代码隐藏
  17. }
  18. try {
  19. lock.lockInterruptibly(); //锁获取
  20. } catch (InterruptedException e) {
  21. connectErrorCountUpdater.incrementAndGet(this);
  22. throw new SQLException("interrupt", e);
  23. }
  24. try {
  25. if (maxWaitThreadCount > 0
  26. && notEmptyWaitThreadCount >= maxWaitThreadCount) { //如果因为拿不到连接而阻塞的业务线程数达到阈值,则直接抛异常
  27. connectErrorCountUpdater.incrementAndGet(this);
  28. throw new SQLException("maxWaitThreadCount " + maxWaitThreadCount + ", current wait Thread count "
  29. + lock.getQueueLength());
  30. }
  31. if (onFatalError
  32. && onFatalErrorMaxActive > 0
  33. && activeCount >= onFatalErrorMaxActive) {
  34. connectErrorCountUpdater.incrementAndGet(this);
  35. StringBuilder errorMsg = new StringBuilder();
  36. errorMsg.append("onFatalError, activeCount ")
  37. .append(activeCount)
  38. .append(", onFatalErrorMaxActive ")
  39. .append(onFatalErrorMaxActive);
  40. if (lastFatalErrorTimeMillis > 0) {
  41. errorMsg.append(", time '")
  42. .append(StringUtils.formatDateTime19(
  43. lastFatalErrorTimeMillis, TimeZone.getDefault()))
  44. .append("'");
  45. }
  46. if (lastFatalErrorSql != null) {
  47. errorMsg.append(", sql \n")
  48. .append(lastFatalErrorSql);
  49. }
  50. throw new SQLException(
  51. errorMsg.toString(), lastFatalError);
  52. }
  53. connectCount++; //连接数累加
  54. if (createScheduler != null
  55. && poolingCount == 0
  56. && activeCount < maxActive
  57. && creatingCountUpdater.get(this) == 0
  58. && createScheduler instanceof ScheduledThreadPoolExecutor) {
  59. ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) createScheduler;
  60. if (executor.getQueue().size() > 0) {
  61. createDirect = true; //createScheduler这种异步添加模式不开启(默认不开启,本文也不是基于该模式的),createDirect永远不等于true,所以上面createDirect==true的代码不会被触发
  62. continue;
  63. }
  64. }
  65. if (maxWait > 0) {
  66. holder = pollLast(nanos); //尝试从池子里获取连接
  67. } else {
  68. holder = takeLast();
  69. }
  70. if (holder != null) {
  71. activeCount++; //拿到连接,activeCount累加
  72. if (activeCount > activePeak) {
  73. activePeak = activeCount;
  74. activePeakTime = System.currentTimeMillis();
  75. }
  76. }
  77. } catch (InterruptedException e) {
  78. connectErrorCountUpdater.incrementAndGet(this);
  79. throw new SQLException(e.getMessage(), e);
  80. } catch (SQLException e) {
  81. connectErrorCountUpdater.incrementAndGet(this);
  82. throw e;
  83. } finally {
  84. lock.unlock();
  85. }
  86. break;
  87. }
  88. if (holder == null) { //没有获取到连接,整理错误信息,抛出错误
  89. long waitNanos = waitNanosLocal.get();
  90. StringBuilder buf = new StringBuilder(128);
  91. buf.append("wait millis ")//
  92. .append(waitNanos / (1000 * 1000))//
  93. .append(", active ").append(activeCount)//
  94. .append(", maxActive ").append(maxActive)//
  95. .append(", creating ").append(creatingCount)//
  96. ;
  97. if (creatingCount > 0 && createStartNanos > 0) {
  98. long createElapseMillis = (System.nanoTime() - createStartNanos) / (1000 * 1000);
  99. if (createElapseMillis > 0) {
  100. buf.append(", createElapseMillis ").append(createElapseMillis);
  101. }
  102. }
  103. if (createErrorCount > 0) {
  104. buf.append(", createErrorCount ").append(createErrorCount);
  105. }
  106. List sqlList = this.getDataSourceStat().getRuningSqlList();
  107. for (int i = 0; i < sqlList.size(); ++i) {
  108. if (i != 0) {
  109. buf.append('\n');
  110. } else {
  111. buf.append(", ");
  112. }
  113. JdbcSqlStatValue sql = sqlList.get(i);
  114. buf.append("runningSqlCount ").append(sql.getRunningCount());
  115. buf.append(" : ");
  116. buf.append(sql.getSql());
  117. }
  118. String errorMessage = buf.toString();
  119. if (this.createError != null) {
  120. throw new GetConnectionTimeoutException(errorMessage, createError);
  121. } else {
  122. throw new GetConnectionTimeoutException(errorMessage);
  123. }
  124. }
  125. holder.incrementUseCount();
  126. DruidPooledConnection poolalbeConnection = new DruidPooledConnection(holder); //包装成目标对象
  127. return poolalbeConnection; //返回
  128. }
  129. //尝试从池子里获取连接
  130. private DruidConnectionHolder pollLast(long nanos) throws InterruptedException, SQLException {
  131. long estimate = nanos;
  132. for (;;) {
  133. if (poolingCount == 0) { //池子里的空闲连接为0,说明需要通知主流程3新增连接了
  134. emptySignal(); // empty.signal,唤起主流程3新增连接
  135. if (failFast && isFailContinuous()) { //如果置为快速结束,则不阻塞业务线程,直接抛出异常
  136. throw new DataSourceNotAvailableException(createError);
  137. }
  138. if (estimate <= 0) {
  139. waitNanosLocal.set(nanos - estimate);
  140. return null;
  141. }
  142. notEmptyWaitThreadCount++; //因为获取不到连接而陷入阻塞状态的业务线程数+1
  143. if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) {
  144. notEmptyWaitThreadPeak = notEmptyWaitThreadCount;
  145. }
  146. try {
  147. long startEstimate = estimate;
  148. estimate = notEmpty.awaitNanos(estimate); // 阻塞(挂起)estimate这么长的世界,期间如果被唤醒,则estimate就会被刷新成剩余等待时间
  149. // recycle or
  150. // creator
  151. notEmptyWaitCount++;
  152. notEmptyWaitNanos += (startEstimate - estimate);
  153. if (!enable) {
  154. connectErrorCountUpdater.incrementAndGet(this);
  155. throw new DataSourceDisableException();
  156. }
  157. } catch (InterruptedException ie) {
  158. notEmpty.signal(); // 期间线程被中断,则唤起一次其他处于阻塞状态的业务线程
  159. notEmptySignalCount++;
  160. throw ie;
  161. } finally {
  162. notEmptyWaitThreadCount--;
  163. }
  164. if (poolingCount == 0) { //依然没有竞争到
  165. if (estimate > 0) { //如果目标阻塞时间(maxWait)还没有用完,则继续尝试获取
  166. continue;
  167. }
  168. waitNanosLocal.set(nanos - estimate);
  169. return null;
  170. }
  171. }
  172. decrementPoolingCount(); //poolingCount--
  173. DruidConnectionHolder last = connections[poolingCount]; //直接获取
  174. connections[poolingCount] = null; //获取后意味着连接已被借出,原有位置置空
  175. long waitNanos = nanos - estimate; //标记这次获取连接花了多长时间,连接够用时便为0
  176. last.setLastNotEmptyWaitNanos(waitNanos);
  177. return last; //返回
  178. }
  179. }

Druid-代码段-1-3的更多相关文章

  1. WPF自定义RoutedEvent事件代码段

    今天在写东西的时候,发现常用的代码段里没有RoutedEvent的,因此,写了一个代码段,方便以后使用,顺便记录一下,如何做代码段. 1.在项目中新建一个XML文件,将扩展名修改为snippet. 2 ...

  2. JavaScript代码段整理笔记系列(二)

    上篇介绍了15个常用代码段,本篇将把剩余的15个补齐,希望对大家有所帮助!!! 16.检测Shift.Alt.Ctrl键: event.shiftKey; //检测Shift event.altKey ...

  3. 我们为什么要看《超实用的Node.JS代码段》

    不知道自己Node.JS水平如何?看这张图 如果一半以上的你都不会,必须看这本书,一线工程师用代码和功能页面来告诉你每一个技巧点. 都会一点,但不知道如何检验自己,看看本书提供的面试题: 1.     ...

  4. 《超实用的JavaScript代码段》—— 读后总结

    这本书全是代码,从头到尾跟着坐下来确实收获很多.比那些古板的教科书式的理解更多,不过书中并不是每个例子都做了,有的作者封装的太多,觉得看了收获不多,就没细看——比如模块渐变.有空好好学学这段的代码. ...

  5. Visual Studio常用小技巧一:代码段+快捷键+插件=效率

    用了visual studio 5年多,也该给自己做下备忘录了.每次进新的组换新的电脑,安装自己熟悉的环境又得重新配置,不做些备忘老会忘记一些东西.工具用的好,效率自然翻倍. 1,代码段 在Visua ...

  6. 使用eclipse开发Morphline的Java代码段

    背景:morphline是一个轻量级的etl工具.除了提供标准化的方法之外,还可以定制化的开发java片段.定制化的java片段会在加载时被作为一个独立的类编译,对源数据作处理. morphline关 ...

  7. 前端福利!10个短小却超实用的JavaScript 代码段

    JavaScript正变得越来越流行,它已经成为前端开发的第一选择,并且利用基于JavaScript语言的NodeJS,我们也可以开发出高 性能的后端服务,甚至我还看到在硬件编程领域也出现了JavaS ...

  8. Visual C# 代码段

    代码段是现成的代码段,您可以快速将其插入到您的代码中. 例如,for 代码段创建一个空的 for 循环. 有些代码段为外侧代码段,这些代码段允许您先选择代码行,然后选择要并入选定代码行的代码段. 例如 ...

  9. 十五个常用的jquery代码段【转】

    好的文章顶一个 回到顶部按钮 通过使用 jQuery 中的 animate 和 scrollTop 方法,你无需插件便可创建一个简单地回到顶部动画: 1 // Back to top 2 $('a.t ...

  10. 十五个常用的jquery代码段

    十五个常用的jquery代码段 回到顶部按钮 通过使用 jQuery 中的 animate 和 scrollTop 方法,你无需插件便可创建一个简单地回到顶部动画: 1 // Back to top ...

随机推荐

  1. 如何将本地的项目推送到github

    一.创建密钥 1.本地终端命令行生成密钥 访问密钥创建的帮助文档:https://help.github.com/en/github/authenticating-to-github/generati ...

  2. pillow模块Image.crop()函数切割图片方法,参数说明

    使用Image.crop()方法对图片进行切割. 参数: Image.crop(left, up, right, below) left:与左边界的距离 up:与上边界的距离 right:还是与左边界 ...

  3. nltk的安装和简单使用

    使用python进行自然语言处理,有一些第三方库供大家使用: ·NLTK(Python自然语言工具包)用于诸如标记化.词形还原.词干化.解析.POS标注等任务.该库具有几乎所有NLP任务的工具. ·S ...

  4. 字段明明存在,用Web API使用该字段进行查询报错?

    我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...

  5. Zeus:1 Vulnhub Walkthrough

    主机层面扫描: ╰─ nmap -p1-65535 -sV -A 10.10.202.14 PORT STATE SERVICE VERSION21/tcp open ftp vsftpd 2.0.8 ...

  6. TCP/IP网络协议初识

    目录 一.什么是协议? 二.什么是TCP/IP协议? 三.TCP/IP为什么这么多协议? 四.TCP/IP协议为什么分层? 五.TCP/IP协议如何入门? 六.TCP/IP 的分层: 七.各协议层打包 ...

  7. Centos8_本地Yum源配置

    一.先将iso镜像文件挂载 mount /dev/cdrom /mnt 将cdrom挂载到/mnt下 二.编辑挂载文件 cp -a /mnt/media.repo /etc/yum.repos.d/ ...

  8. Web项目配置https

    在java安装目录下bin目录下keytool工具 keytool -genkey -storetype PKCS12 -keysize 2048 -alias tomcat -keyalg RSA ...

  9. Codefest19受虐记

    date: 2019-08-28 前言 比赛链接:Codefest 19 A题 思路: 这是一道水题.你对着样例递推打一个表出来,会发现结果三个一组循环. 例如:A = [3, 4, 7, 3, 4, ...

  10. NumPy 会自动检测并利用 GPU 吗?

    PyTorch 官网 60 分钟入门教程在介绍什么是 PyTorch 时有一句话:A replacement for NumPy to use the power of GPUs PyTorch 是 ...