Java连接池不限制打开到DB服务器的TCP连接数
我正在使用Hibernate属性定义连接池大小,随着Java连接池不限制打开到DB服务器的TCP连接数,java,spring,hibernate,orm,connection-pooling,Java,Spring,Hibernate,Orm,Connection Pooling,我正在使用Hibernate属性定义连接池大小,随着LocalSessionFactoryBean和DriverManagerDataSource创建org.hibernate.SessionFactory,我观察到有1000个TCP连接被打开到DB服务器,而不是连接池大小的上限100 设置连接休眠池的代码如下所示。请注意最大大小为100,但我观察到从工作站到DB服务器的连接数为1000个-我在Windows计算机上使用TCPView连接到由Vagrant(VirtualBox)管理的Cento
LocalSessionFactoryBean
和DriverManagerDataSource
创建org.hibernate.SessionFactory
,我观察到有1000个TCP连接被打开到DB服务器,而不是连接池大小的上限100
设置连接休眠池的代码如下所示。请注意最大大小为100,但我观察到从工作站到DB服务器的连接数为1000个-我在Windows计算机上使用TCPView连接到由Vagrant(VirtualBox)管理的Centos OS VM上的MariabDB实例
我希望看到这些连接在100处达到最大值,并在以后执行循环时重新使用,但我观察到1000多个连接在关闭前等待了大约一分钟。以下是TCPView的输出示例:
[System Process] 0 TCP localhost 62794 192.168.98.102 3306 TIME_WAIT
[System Process] 0 TCP localhost 62796 192.168.98.102 3306 TIME_WAIT
[System Process] 0 TCP localhost 62797 192.168.98.102 3306 TIME_WAIT
[System Process] 0 TCP localhost 62795 192.168.98.102 3306 TIME_WAIT
[System Process] 0 TCP localhost 62798 192.168.98.102 3306 TIME_WAIT
[System Process] 0 TCP localhost 62801 192.168.98.102 3306 TIME_WAIT
我显然做错了什么,但不确定是什么。我使用Spring来管理提供数据访问逻辑的DAO。DAO注册为原型Spring作用域,而SingletonSessionFactory
被注入DAO,如下所示:
@Bean(name="jobDao")
@Scope(SpringBeanScope.Prototype)
public JobDao jobDao(SessionFactory jobSessionFactory) { //...}
在DAO中,我调用sessionFactory.getCurrentSession()
来访问DB会话。以下是通用DAO库的摘录,演示了这一点:
protected Session currentSession() {
return sessionFactory.getCurrentSession();
}
@Transactional
@Override
public void Add(TEntity entity) {
currentSession().save(entity);
}
有人知道为什么连接池限制为100时会打开这么多TCP连接吗
更新
由于此问题发生在Windows dev计算机上,因此我创建了一个小型的.NET控制台应用程序,该应用程序使用并行for循环执行一个简单的SQL select语句,最大线程数为64,每次都创建一个新连接:
public void Test()
{
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = 64;
Parallel.For(0,
1000,
options,
(i, state) =>
{
ExecuteSql();
});
}
private void ExecuteSql()
{
SqlDataAdapter adapter = new SqlDataAdapter(sql, new SqlConnection(connectionString));
DataSet orders = new DataSet();
adapter.Fill(orders, "Order");
Console.WriteLine("Thread {0} returned {1} rows", Thread.CurrentThread.ManagedThreadId, orders.Tables[0].Rows.Count);
}
TCPView结果如下所示,这是我在连接池解决方案中所期望看到的结果,即,TCP连接被重用
在netstat输出中,它们都处于
TIME\u WAIT
状态,指示TCP套接字在实际关闭之前的状态。这并不意味着数据库连接处于活动状态,只是TCP正在关闭套接字
他们中有多少人处于已建立状态?这将告诉您在该时间点打开的DB连接的确切数量
您可以参考以下链接了解不同的TCP套接字状态 Hibernate配置正确,您应该使用C3P0。如果您为每个请求创建
SessionFactory
,则可以看到比最大池大小更多的连接
从您的配置中,我看到DAO是作为原型创建的,这是不寻常的,因为它们应该是单例的。确保
LocalSessionFactoryBean
也不使用原型作用域。问题是,当使用DriverManager数据源时,c3p0从未初始化,并且让hibernate通过在LocalSessionFactoryBean
上设置c3p0属性来管理池
因此,我切换到一个ComboPooledDataSource
,并使用此处可用的setter方法设置c3p0属性,而不是在LocalSessionFactoryBean
上设置,因此您实际上可以得到以下结果:
@Bean(name="dataSource")
public DataSource dataSource() throws PropertyVetoException {
Properties hibernateProperties = new Properties();
hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
hibernateProperties.put("hibernate.show_sql", false);
hibernateProperties.put("hibernate.generate_statistics", false);
hibernateProperties.put("hibernate.hbm2ddl.auto", "update");
hibernateProperties.put("hibernate.use_sql_comments", false);
//note the "hibernate.c3p0...." properties are no longer in use
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(jobDatabaseProperties.getJobDatabaseDriverClassName());
dataSource.setJdbcUrl(jobDatabaseProperties.getJobDatabaseUrl());
dataSource.setUser(jobDatabaseProperties.getJobDatabaseUsername());
dataSource.setPassword(jobDatabaseProperties.getJobDatabasePassword());
dataSource.setAcquireIncrement(1);
dataSource.setMinPoolSize(5);
dataSource.setMaxPoolSize(100);
dataSource.setMaxIdleTime(20);
return new dataSource;
}
我现在可以看到c3p0在日志中被初始化,连接的数量按预期由池控制
关于“让Spring管理池”一节的帽子提示我认为是这样的,但问题是要跨更多线程处理的项目数量更多,我正在耗尽机器上的端口,因为释放这些端口需要一分钟(windows上为72秒)。我是否错过了提前释放这些端口的hibernate设置,或者这是我需要更改的操作系统配置?几乎所有这些都是。正如我们预期的那样,在多达20个线程的线程池中,一次只有3-4个活动(已建立)线程,但时间为100秒,有时为1000秒。\u等待一分钟以上,我一定是做错了什么,因为每次调用session.save(或者session.getCurrentSession,不确定)时,似乎都会打开一个连接这在我看来是错误的,因为一个操作系统可以支持/打开2^16个端口(理论上),除非你看到上面的一些东西,比如25000个大数字,在TIME\u WAIT状态下的条目,我们不应该担心那些TIME\u WAIT状态。操作系统将接管它。另外,由于您只看到3-4个活动连接,您可能需要检查的一些指标是web/app服务器的线程池配置,或者GC是否导致了处理延迟,或者java进程是否渴望内存或资源。您可能还想进行一些线程转储,看看您的代码是否在同步上等待了太多时间。我创建了一个.NET测试应用程序(因为我在Windows上),它对SQL Server DB运行一个简单的SELECT语句。我在并行for循环中运行了1000次迭代,最多32个线程,结果与Java完全不同。请参阅我对以下问题的最新更新:工厂是单例的,DAO是原型,因此同一个会话不会在线程之间共享(这不是web应用程序)。我也尝试过singleton DAO,但也出现了会话关闭错误,并且在TIMRE_等待状态下观察到了相同的连接问题,因此基于此,我确信原型是正确的。
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61316 dev-database01 ms-sql-s ESTABLISHED 1 88 776 2,684,495 43,800 16
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61317 dev-database01 ms-sql-s ESTABLISHED 1 88 507 1,998,709 6,326 1
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61318 dev-database01 ms-sql-s ESTABLISHED 2 176 862 3,081,722 49,640 19
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61319 dev-database01 ms-sql-s ESTABLISHED 2 176 952 3,128,657 14,600 9
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61320 dev-database01 ms-sql-s ESTABLISHED 2 176 1,149 3,569,440 25,747 8
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61321 dev-database01 ms-sql-s ESTABLISHED 2 176 1,166 3,788,974
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61322 dev-database01 ms-sql-s ESTABLISHED 2 176 884 3,197,392 8,713 2
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61323 dev-database01 ms-sql-s ESTABLISHED 2 176 535 1,816,150
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61324 dev-database01 ms-sql-s ESTABLISHED 2 176 631 2,197,973
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61327 dev-database01 ms-sql-s ESTABLISHED 2 176 1,037 3,344,226 18,980 5
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61328 dev-database01 ms-sql-s ESTABLISHED 3 264 1,271 4,057,097 30,660 13
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61331 dev-database01 ms-sql-s ESTABLISHED 2 176 780 2,639,988 8,760 2
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61333 dev-database01 ms-sql-s ESTABLISHED 2 176 1,041 3,352,777 31,248 12
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61334 dev-database01 ms-sql-s ESTABLISHED 6 995 729 2,387,668
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61335 dev-database01 ms-sql-s ESTABLISHED 6 995 601 1,917,537 23,937 6
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61336 dev-database01 ms-sql-s ESTABLISHED
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61339 dev-database01 ms-sql-s ESTABLISHED
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61340 dev-database01 ms-sql-s ESTABLISHED
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61342 dev-database01 ms-sql-s ESTABLISHED
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61343 dev-database01 ms-sql-s ESTABLISHED
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61344 dev-database01 ms-sql-s ESTABLISHED
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61345 dev-database01 ms-sql-s ESTABLISHED
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61346 dev-database01 ms-sql-s ESTABLISHED
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61356 dev-database01 ms-sql-s ESTABLISHED
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61357 dev-database01 ms-sql-s ESTABLISHED
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61358 dev-database01 ms-sql-s ESTABLISHED
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61359 dev-database01 ms-sql-s ESTABLISHED
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61362 dev-database01 ms-sql-s ESTABLISHED
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61363 dev-database01 ms-sql-s ESTABLISHED
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61364 dev-database01 ms-sql-s ESTABLISHED
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61365 dev-database01 ms-sql-s ESTABLISHED
DatabaseTcpPortTester.vshost.exe 5036 TCP localhost 61369 dev-database01 ms-sql-s ESTABLISHED
[System Process] 0 TCP localhost 61395 dev-database01 epmap TIME_WAIT
@Bean(name="dataSource")
public DataSource dataSource() throws PropertyVetoException {
Properties hibernateProperties = new Properties();
hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
hibernateProperties.put("hibernate.show_sql", false);
hibernateProperties.put("hibernate.generate_statistics", false);
hibernateProperties.put("hibernate.hbm2ddl.auto", "update");
hibernateProperties.put("hibernate.use_sql_comments", false);
//note the "hibernate.c3p0...." properties are no longer in use
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(jobDatabaseProperties.getJobDatabaseDriverClassName());
dataSource.setJdbcUrl(jobDatabaseProperties.getJobDatabaseUrl());
dataSource.setUser(jobDatabaseProperties.getJobDatabaseUsername());
dataSource.setPassword(jobDatabaseProperties.getJobDatabasePassword());
dataSource.setAcquireIncrement(1);
dataSource.setMinPoolSize(5);
dataSource.setMaxPoolSize(100);
dataSource.setMaxIdleTime(20);
return new dataSource;
}