使用 DataSource 时为角色获取太多连接

Getting too many connection for role when using DataSource

我有一个休息服务,当它获得时,它必须对近 25 个数据库进行一些插入和更新。所以当我像下面的代码一样尝试时,它在我的本地主机上工作但是当我部署到我的登台服务器时我得到 FATAL: too many connections for role "user123"

List<String> databaseUrls = null;
databaseUrls.forEach( databaseUrl -> {
    DataSource dataSource = DataSourceBuilder.create()
            .driverClassName("org.postgresql.Driver")
            .url(databaseUrl)
            .username("user123")
            .password("some-password")
            .build();
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    jdbcTemplate.update("Some...Update...Query");
});

根据我的理解 DataSource 不需要关闭,因为它从未打开过。

注:

A DataSource implementation need not be closed, because it is never “opened”. A DataSource is not a resource, is not connected to the database, so it is not holding networking connections nor resources on the database server. A DataSource is simply information needed when making a connection to the database, with the database server's network name or address, the user name, user password, and various options you want specified when a connection is eventually made.

谁能告诉我为什么会遇到这个问题

问题出在 DataSourceBuilder 中,它实际上创建了连接池,该连接池产生了一定数量的连接并保留它们 运行:

private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] {
            "org.apache.tomcat.jdbc.pool.DataSource",
            "com.zaxxer.hikari.HikariDataSource",
            "org.apache.commons.dbcp.BasicDataSource" };

Javadoc 说:

/**
 * Convenience class for building a {@link DataSource} with common implementations and
 * properties. If Tomcat, HikariCP or Commons DBCP are on the classpath one of them will
 * be selected (in that order with Tomcat first). In the interest of a uniform interface,
 * and so that there can be a fallback to an embedded database if one can be detected on
 * the classpath, only a small set of common configuration properties are supported. To
 * inject additional properties into the result you can downcast it, or use
 * <code>@ConfigurationProperties</code>.
 */

尝试使用例如SingleConnectionDataSource,那么你的问题就解决了:

List<String> databaseUrls = null;
Class.forName("org.postgresql.Driver");
databaseUrls.forEach( databaseUrl -> {
    SingleConnectionDataSource dataSource;
    try {
        dataSource = new SingleConnectionDataSource(
                databaseUrl, "user123", "some-password", true /*suppressClose*/);
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        jdbcTemplate.update("Some...Update...Query");
    } catch (Exception e) {
        log.error("Failed to run queries for {}", databaseUrl, e);
    } finally {
        // release resources
        if (dataSource != null) {
            dataSource.destroy();
        }
    }
});

首先,让单个应用程序管理 50 个数据库是非常糟糕的架构决策。无论如何,您应该使用工厂设计模式为每个数据库创建数据源,而不是在 for 循环中创建数据源。您应该向您的系统添加一些连接池机制。 HijariCP 和 TomcatPool 使用最广泛。分析失败线程的日志以查找任何其他问题。