池化技术详解-对象池,连接池,线程池

在应用系统开发过程中,经常会使用到池化技术,如对象池,连接池,线程池等,通过复用技术来减少一些消耗,以提升性能。
1.对象池通过复用对象减少创建对象,垃圾回收的开销;
注,池不能太大,太大会影响GC时的扫描时间
2.连接池,如数据库连接池/Redis连接池/HTTP连接池,通过复用TCP连接来减少创建和释放连接的时间来提升性能
3.线程池,通过复用线程来提升性能

池化技术可以使用Apache-commons-pool2来实现,例如DBCP,Jedis连接池都是使用commons-pool2技术实现,不建议使用commons-pool1。

*****************************数据库连接池*****************************

数据库连接池有很多实现,如C3P0,DBCP,Druid等等,KT用的最多的是Durid和DBCP。
示例:commons-dbcp2 2.1.1
1.DBCP连接池配置
     
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
         
   
 
 
            destroy-method="close">
       
         
       
       
         
         
         
       
       
       
       
       
       
       
       
       
       
       
       
       
         
         
         
         
         
         
         
       
       
       
       
       
       
       
       
       
       
         
       
         
       
       
       
       
       
       
       
       
   
 

说明:

【数据库连接配置】
备注一点,也可以在JDBC URL ?proName=prop Value配置这些属性

【池配置】

【验证数据库连接有效性】
三种方法:主动测试,定时测试,关闭孤儿连接

【关闭孤儿连接】



2.DBCP配置建议
2.1在并发量大的情况下:
(1)几个池大小设置为一样
(2)禁用关闭孤儿连接
(3)禁用定时器
如下:
     
            destroy-method="close">     
       
       
       
       
       
       
       
               
       
       
       
         
         
       
       
         
   
 

2.2并发量不大时建议:
(1)按需设置池大小
(2)禁用关闭孤儿连接
(3)启用定时器(注意MYSQL空闲8小时自动断开)
如下:
     
            destroy-method="close">     
       
       
       
       
       
       
       
               
       
       
       
         
         
       
   


总结:
(1)无论何种方式,都必须设置超时时间
(2)在JVM关闭/重启时一定要销毁连接池(bean设置为destory-method="close")
   

3.数据库驱动超时 的 实现

4.连接池使用的一些建议
(1)注意网络阻塞/不稳定时出现的级联效应,还有当前等待连接池的人数过多,比如1000,那么接下来的等到就没有意义了,还会造成滚需求效应。
建议:
使用熔断和快速失败机制,即在连接池内部应该根据当前网络状态(例如超时次数过多),将一定时间内(比如100ms)的请求全部timeout,根本不进行await(maxAwait)

(2)DBCP比较容易出现的问题是:设置超时时间过长,造成大量TIMED_WAIT和线程阻塞,而且像滚雪球,一旦出现问题很难理解恢复,可以通过上下文方案解决。
等待超时时间应尽可能小些,即使返回错误页,也比等待并阻塞强

1. 数据库连接池应用中数据库服务器断开超时连接的问题
要点:应用中数据库连接池中会保存指定数量的数据库连接实例,而这些连接实例并没有定时地检测其到数据库服务器连接是否正常;
与此同时,数据库服务器存在数据库连接实例的超时时间,超过时间后它会自动断开连接。
也就是,被断开的那个连接此时仍然保存在应用的数据库连接池内,下次被使用的时候就会发生数据库连接断开而导致一次访问失败。

推荐解决方案:
1)如果能够提供这样一种检测机制,在应用的连接池管理中定时地检测连接池中连接的有效性,就完全可以避免上面描述的问题。
2)在应用代码中通过异常处理机制,来实现该次业务的重新处理,也可以很好地避免。
3)C3P0,Proxool,BoneCP,Druid等连接池的断开自动重联功能
参考:https://www.oschina.net/question/59889_44927
参考:http://blog.csdn.net/shirdrn/article/details/8248814

com.mysql.jdbc.Driver

http://blog.sina.com.cn/s/blog_85d71fb70101ab99.html

Spring配C3P0
http://blog.csdn.net/vebasan/article/details/5059000

Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource卡住
解决方案:肯定是网络,用户名密码的错误导致



*****************************HttpClient连接池**********************************
见 《HttpClient使用详解》



*****************************Java线程池****************************************
总的:线程池,通过复用线程来提升性能;

背景:
线程是一个操作系统概念。操作系统负责这个线程的创建、挂起、运行、阻塞和终结操作。而操作系统创建线程、切换线程状态、终结线程都要进行CPU调度,这是一个耗费时间和系统资源的事情。

在如下场景下:
处理某一次请求的时间是非常短暂的,但是请求数量是巨大的。如果为每个请求都单独创建一个线程,那么物理机的所有资源基本上都被操作系统创建线程、切换线程状态、销毁线程这些操作所占用,用于业务请求处理的资源反而减少了。
另外,一些操作系统是有最大线程数量限制的。当运行的线程数量逼近这个值的时候,操作系统会变得不稳定。这也是我们要限制线程数量的原因。每个线程都需要一个内存栈,用于存储局部变量,操作栈等信息,通过-Xss参数来调整每个线程栈大小(64位系统默认1024kb,可根据实际需要调小,比如256KB)
通过调整该参数可以创建更多的线程,不过JVM不能无限制地创建线程

最理想的处理方式:将处理请求的线程数量控制在一个范围,既保证后续的请求不会等待太长时间,又保证物理机将足够的资源用于请求处理本身。
使用线程池:线程池会限制创建的线程数,从而保护系统;线程池配合队列工作,限制并发处理的任务数量,当任务超限时,通过一定的策略来处理,可避免系统因为大流量
而导致崩溃-只是部分拒绝服务,还是有一部分是正常服务的。
 
线程池分为:核心线程池和最大数量线程池,线程池中线程空闲一段时间会被回收,核心线程是不会被回收的。

合适的线程数:
(1)建议根据实际业务情况来压测决定
(2)利特尔法则:在一个稳定系统内,长时间观察到的平均用户数量L = 长时间观察到的有效达到率 * 平均每个用户在系统花费的时间。
针对(2)实际情况更复杂,如:在处理超时,网络抖动会导致线程花费时间不一样。

鉴于在处理超时,网络抖动会导致线程花费时间不一样,可能造成的线程数不合理,需要考虑:超时机制,线程隔离机制,快速失败机制等来保护系统

Java语言为我们提供了两种基础线程池的选择:ScheduledThreadPoolExecutor和ThreadPoolExecutor。它们都实现了ExecutorService接口
(注意,ExecutorService接口本身和“线程池”并没有直接关系,它的定义更接近“执行器”,而“使用线程管理的方式进行实现”只是其中的一种实现方式)。
这篇文章中,我们主要围绕ThreadPoolExecutor类进行讲解。


Java提供了ExecutorService三种实现
(1)ThreadPoolExecutor:标准线程池
(2)ScheduledThreadPoolExecutor:支持延迟任务的线程池
(3)ForkJoinPool:
类似于ThreadPoolExecutor,但是使用work-stealing模式,其会为线程池中的每个线程创建一个队列,从而用work-stealing(任务窃取)算法使得线程可以从其他
线程队列里窃取任务来执行。即如果自己的任务处理完成了,可以去忙碌的工作线程哪里窃取任务执行。


*****************************Tomcat线程池**************************************
见<> 【2.1.5 连接池配置】

你可能感兴趣的:(池化技术)