1、数据库连接池
JDBC在JDK中有一整套API,也就是接口。对于不同的数据库都有自己的实现。但是这些实现都是单个的连接,如果一个应用只有一个连接,那不能支持并发,如果一个操作就有一个连接,那连接的开销又非常大。
最好的办法就是一个线程一个连接,或者是总共建立30个连接,你用完了它用。这就是JDBC连接池的做法。
连接池要取出连接,再放回去,当连接不够的时候怎么办,连接闲置的时候怎么办,不同的连接池算法不一样,实现方式不一样,那连接池的开销也就是效率不一样。这个需要你自己去选择,不过连接池的实现原理大致都是相同的,就是实现JDK中的javax.sql.DataSource接口,通过javax.sql.DataSource.getConnection(),获取一个连接java.sql.Connection,也就是从池子里取出一个连接,这个Connection也由连接池去实现,在Connection.close()的时候,并没有关闭连接,而是把连接放回到池子中,从而达到池子的效果。
所以连接池的配置关键是DataSource的配置,等DataSource配好了,对连接池的使用,就像基础的JDBC连接一样使用。一般DataSource的配置都大同小异,都是那几个参数,参数的含义,网上都可以搜的到,下面是阿里巴巴的Druid数据库连接池的配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <bean id="dataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc1.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="maxActive" value="${jdbc.maxActive}" /> <property name="initialSize" value="${jdbc.initialSize}" /> <property name="maxWait" value="${jdbc.maxWait}" /> <property name="minIdle" value="${jdbc.minIdle}" /> <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}" /> <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}" /> <property name="validationQuery" value="${jdbc.validationQuery}" /> <property name="testWhileIdle" value="${jdbc.testWhileIdle}" /> <property name="testOnBorrow" value="${jdbc.testOnBorrow}" /> <property name="testOnReturn" value="${jdbc.testOnReturn}" /> <property name="defaultAutoCommit" value="${jdbc.defaultAutoCommit}"/> </bean> |
2、HTTP连接池
HTTP连接,有一个keepalive的参数,只要你带了这个参数并且服务器支持这个参数,那么你就可以在timeout的时间内保持和服务器的连接,不断的发送请求,得到响应。这样你喝服务器的交互,就不用每次请求都去进行TCP的连接的三次握手等等,减少连接等待时间。所以如果你经常和一个服务器进行http交互的话,使用HTTP连接池是个不错的选择。
HTTP连接池的原理就是利用了keepalive的参数,保持几个或者多个服务器的连接,当你下次在有效的时间内去同一个服务器请求的时候,就会减少不小的开支。这就是为什么浏览器浏览一个网页,第一个请求可能很慢,后面的请求会很快,一方面可能是因为DNS初次解析的缘故,另一方面有可能就是keepalive参数的效果。
HTTP连接池的实现也有很多种,而且一般的HTTP包里的client都默认实现了连接池,你可以给它配置不同需求的连接池,这里示例apache的httpclient4X的连接池:org.apache.http.impl.conn.PoolingClientConnectionManager
1 2 3 4 5 6 7 8 9 10 | SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register( new Scheme("http", 80, PlainSocketFactory.getSocketFactory())); schemeRegistry.register( new Scheme("https", 443, SSLSocketFactory.getSocketFactory())); PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager(schemeRegistry); connectionManager.setMaxTotal(maxConnections); connectionManager.setDefaultMaxPerRoute(maxPerRoute); |
把连接池管理器配给client即可
1 2 | DefaultHttpClient httpClient = new DefaultHttpClient(connectionManager, params); |
其中params 是你要配的其它参数,例如连接超时时间,请求超时时间等等。
3、线程池
如果你的任务需要并发执行,那么你就需要建立新的线程去执行,但是线程的建立是需要开销的,而且系统的线程数是有限制的,你不可能起无数个线程去执行任务的。如果你的线程的任务需要很大的内存或者带宽,起很多线程也是有限制的,所以你要用到线程池的概念。JDK就有丰富多彩的线程池实现,你直接可以使用,也就是java.util.concurrent.ExecutorService的实现类。concurrent字面上就是并发的意思。
最常用的就是java.util.concurrent.ThreadPoolExecutor。
你可以直接new出一个来就可以
1 2 3 4 5 6 | RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy(); BlockingQueue queue= new LinkedBlockingQueue(queueCapacity); ThreadPoolExecutor threadPool = new ThreadPoolExecutor( corePoolSize, maxPoolSize, keepAliveSeconds, TimeUnit.SECONDS, queue, rejectedExecutionHandler); |
当maxPoolSize个线程都用完了的时候,新添加的任务就会放到queue中,如果queue中都满了得话
就会按rejectedExecutionHandler
你选择的策略进行处理。所以你要根据你的业务量,合理安排pool的大小和queue的大小。
在使用的时候,只要向threadPool提交你的任务就可以了:
1 2 3 4 5 6 | threadPool.submit(new Runable(){ public void run() { } }); |
4、普通对象池
如果你的一个对象的创建需要很大的开销,那么你需要使用对象池,创建一些对象放在池子里,用的时候取出来一个,不用的时候放回去。对象池也有现成的方案,那就是apache的commons-pool。
比较常用的是org.apache.commons.pool.impl.GenericObjectPool
你需要实现一个org.apache.commons.pool.PoolableObjectFactory接口中的makeObject,
activateObject, passivateObject, validateObject,
destroyObject方法。说明你的对象是如何实现的,是否是有效的对象,销毁这个对象需要说明操作等等。
然后new一个对象池就可以了,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | GenericObjectPool objectPool = new PoolableObjectFactory(objectFactory); //在使用的时候是这样的形式: MyClass borrowObject = null; try { borrowObject = objectPool.borrowObject(); //do any thing } finally { if(borrowObject != null) { objectPool.returnObject(borrowObject); } } |
除非注明,赵岩的博客文章均为原创,转载请以链接形式标明本文地址
本文地址:https://zhaoyanblog.com/archives/203.html