当前位置: 首页 > 知识库问答 >
问题:

Java:HikariDataSource对象的一致性

常波鸿
2023-03-14

我有一个类,看起来像这样:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

public class ConnectionPool {
    private HikariDataSource hds;
    private final String propertyFileName;

    public ConnectionPool(String propertyFileName) {
        if (propertyFileName == null) {
            throw new IllegalArgumentException("propertyFileName can't be null");
        }

        this.propertyFileName = propertyFileName;
        reloadFile();
    }

    public void reloadFile() {
        if (hds != null) {
            hds.close();
        }

        hds = new HikariDataSource(new HikariConfig(propertyFileName));
    }

    public HikariDataSource getHikariDataSource() {
        return hds;
    }

    public String getPropertyFileName() {
        return propertyFileName;
    }

    public void executeQuery(final String sql, final CallBack<ResultSet, SQLException> callBack) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Connection connection = null;
                PreparedStatement preparedStatement = null;
                ResultSet resultSet = null;

                try {
                    connection = hds.getConnection();
                    preparedStatement = connection.prepareStatement(sql);
                    resultSet = preparedStatement.executeQuery();
                    callBack.call(resultSet, null);
                } catch (SQLException e) {
                    callBack.call(null, e);
                } finally {
                    if (resultSet != null) {
                        try {
                            resultSet.close();
                        } catch (SQLException ignored) {}
                    }

                    if (preparedStatement != null) {
                        try {
                            preparedStatement.close();
                        } catch (SQLException ignored) {}
                    }

                    if (connection != null) {
                        try {
                            connection.close();
                        } catch (SQLException ignored) {}
                    }
                }
            }
        }).start();
    }

    public void executeUpdate(final String sql, final CallBack<Integer, SQLException> callBack) {
        //TODO
    }

    public void execute(final String sql, final CallBack<Boolean, SQLException> callBack) {
        //TODO
    }

    public void connection(final String sql, final CallBack<Connection, SQLException> callBack) {
        //TODO
    }
}

问题是当使用hds时,可以从不同的线程调用reloadFile()方法。所以当我在另一个线程中使用它的连接对象时,hds可能是关闭的。解决这个问题的最好办法是什么?在创建新的HikariDataSource对象之后,是否应该在关闭旧对象之前等待几秒钟(直到查询完成)?

编辑:另一个问题:hds是否应该是易失性的,以便hds的更改对所有线程都可见?

共有3个答案

殷烨
2023-03-14

几个选项:

同步对数据源的所有访问,以便只有一个线程可以干扰它。不可扩展,但可行。

滚动您自己的连接池,例如ApacheCommons池,以便无论线程如何,每次访问都会请求一个数据源,并根据需要创建一个数据源。可能会弄乱数据,这取决于是否需要脏数据、何时刷新数据、事务性等。

每个线程还可以使用ThreadLocal拥有自己的数据源,以便每个线程彼此完全独立。同样,数据质量可能是一个问题,如果您有“很多”线程(取决于您的定义),那么资源可能是一个问题,并且太多打开的连接会导致客户机或服务器上的资源问题。

云和惬
2023-03-14

你到底为什么要让HikariCP重新加载?许多重要的池参数(最小化池最大池大小连接超时等)在运行时可以通过JMX bean进行控制,而无需重新启动池。

重新启动池是在连接关闭和重建时“挂起”应用程序几秒钟的好方法。如果你不能通过JMX接口做你需要的事情,阿德里安的建议似乎是一个相当合理的解决方案。

其他解决方案是可能的,但更为复杂。

编辑:只是为了我自己的娱乐,这里有一个更复杂的解决方案。。。

public class ConnectionPool {
   private AtomicReference<HikariDataSource> hds;

   public ConnectionPool(String propertyFileName) {
      hds = new AtomicReference<>();
      ...
   }

   public void reloadFile() {
      final HikariDataSource ds = hds.getAndSet(new HikariDataSource(new HikariConfig(propertyFileName)));
      if (ds != null) {
         new Thread(new Runnable() {
           public void run() {
              ObjectName poolName = new ObjectName("com.zaxxer.hikari:type=Pool (" + ds.getPoolName() + ")");
              MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
              HikariPoolMXBean poolProxy = JMX.newMXBeanProxy(mBeanServer, poolName, HikariPoolMXBean.class);

              poolProxy.softEvictConnections();
              do {
                 Thread.sleep(500);
              } while (poolProxy.getActiveConnections() > 0);
              ds.close();
           }
         }).start();
       }
   }

   public HikariDataSource getHikariDataSource() {
      return hds.get();
   }

   public void executeQuery(final String sql, final CallBack<ResultSet, SQLException> callBack) {
      new Thread(new Runnable() {
         @Override
         public void run() {
            ...
            try {
               connection = getHikariDataSource().getConnection();
               ...
            }
         }
      }).start();
   }
}

这将(原子地)交换池,并启动一个线程,在关闭孤立池实例之前等待所有活动连接返回。

这假设您允许HikariCP生成唯一的池名称,即不在属性中设置poolName,并且registerMbeans=true

夹谷沛
2023-03-14

有一个非常非常快速和简短的查看源代码中的HikariDataSource。在其off()中,它调用其内部HikaripoolShutdown()方法,为此它将尝试正确关闭池连接。

如果您甚至想避免强制关闭正在进行的连接,一种方法是使用ReadWriteLock

public class ConnectionPool {
    private HikariDataSource hds;
    private ReentrantReadWriteLock dsLock = ....;
    //....

    public void reloadFile() {
        dsLock.writeLock().lock();
        try {
            if (hds != null) {
                hds.close();
            }

            hds = new HikariDataSource(new HikariConfig(propertyFileName));
        } finally {
            dsLock.writeLock().unlock();
        }
    }

    public void executeQuery(final String sql, final CallBack<ResultSet, SQLException> callBack) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Connection connection = null;
                PreparedStatement preparedStatement = null;
                ResultSet resultSet = null;

                dsLock.readLock().lock();
                try {
                    connection = hds.getConnection();
                    // ....
                } catch (SQLException e) {
                    callBack.call(null, e);
                } finally {
                    // your other cleanups
                    dsLock.readLock().unlock();
                }
            }
        }).start();
    }
    //....
}

这将确保

  1. 多个线程可以访问你的数据源(获得连接等)
  2. 重装数据源需要等待,直到线程使用数据源完成
  3. 没有线程能够使用数据源在重新加载时获得连接。
 类似资料:
  • 我有一个问题与Anylogic7.2数据库对象连接到SQL服务器。我是新来的,但据我所知,我已经正确地设置了它。为了设置SQL服务器的连接,我下载了sqljdbc4-3.0.jar并将其设置为顶层模型中的模型依赖项。在main的导入部分,我调用导入com.microsoft.sqlserver.jdbc.SQLServerDriver; 用户登录具有相关数据库的权限,用户名/密码组合有效。用户可以

  • 当我尝试运行该命令时,总是会收到以下错误消息:Discord。py'NoneType'对象没有属性'send' 错误代码: 文件“C:\Users\NexaHn\AppData\Local\Programs\Python39\lib\site packages\discord\client.py”,第343行,在“运行”事件等待coro(*args,**kwargs)文件“K:\discord B

  • 根据这个答案,https://stackoverflow.com/a/12020435/562222,将一个对象分配给另一个对象只是复制引用,但让我们看看以下代码段: 运行它可以提供:

  • 问题内容: 我今天正在帮助一位同事调试一些代码,但我注意到Google Chrome中有一个奇怪的行为: 看来,如果您: 创建一个嵌套数组(例如[[345,“ test”]]) 使用将该阵列记录到控制台。 修改内部数组值之一,然后将输出后面的值- 而不是 执行时的数组值。 JavaScript : 在Firefox中不会发生此行为。 还要注意,如果我在Chrome调试器中逐行浏览他的代码,则将输出

  • 我正在尝试使用Jackson流API从XML反序列化巨大的对象。其思想是结合流式API和ObjectMapper来按小块解析XML(或JSON)。但是,我看到了一些与XML解析器不一致的行为。使用以下代码段: null 为什么XML1缺少FIELD_NAME标记?为什么第二个XML只有一个START_OBJECT令牌?是否有任何设置可以让我看到外部标记的FIELD_NAME?

  • 遇到了 keyup 和 keydown 区别的问题,于是自己简单在控制台输出了一个小写字母 a。 这是 keydown 的输出打印。 这是 keypress 的打印。 为什么这两个事件对象返回字母 a 的 keyCode 编码竟然不一致?