通过编程修改JNDI连接池

6

我在项目中使用了Apache Tomcat JDBC连接池库,并相应地配置了context.xml文件。我的应用实例需要在多个位置运行,但应用负载会有所不同,因此我想根据特定实例的客户大小在运行时修改maxActive大小和其他一些属性。

  <Context path="/abc"
             docBase="abc"
             debug="5"
             reloadable="false"
             crossContext="true">
       <Resource name="jdbc/abc"
          auth="Container"
          type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
          driverClassName="xxxxx"
          url="xxxxxxx"
          username="xxxxx" password="xxxxxx"
          maxActive="20"
          initialSize="0"
          ...
          />
     </Context>

你确定你正在使用Tomcat JDBC吗?如果没有使用“factory”属性,Tomcat的默认设置是使用重新打包的DBCP2版本。 - Piotr P. Karwasz
感谢您。在打字时错过了工厂。 - Avyaan
多个位置 -- 你是说客户端在多台机器上,但 MySQL 是在单个服务器上吗?有多少台机器? - Rick James
@RickJames,同一服务器上也没有MySQL。每个应用程序实例都有自己的数据库。 - Avyaan
3个回答

4
你可以尝试使用标准的JMX来实现这个目的。
正如你在文档中看到的那样,Tomcat可以将连接池暴露为MBean对象,你可以使用像JConsole这样的工具进行交互。

MBean实现基本上委托给org.apache.tomcat.jdbc.pool.ConnectionPool实际执行可以通过MBean接口执行的不同操作,而且据我所知,ConnectionPool根据当前配置动态分配连接


基于配置而非运行时行为的原始答案

请考虑查看这个相关的SO问题我提供的答案, 我认为它可能会有帮助。

Tomcat在其配置文件中替换系统提供的环境变量:

Tomcat配置文件格式为无模式XML; 元素和属性区分大小写。支持Apache Ant样式的变量替换; 可以使用${propname}语法在配置文件中使用名为propname的系统属性。所有系统属性都可用,包括使用-D语法设置的属性,JVM自动提供的属性以及在$CATALINA_BASE/conf/catalina.properties文件中配置的属性。

如上所述,您可以通过使用-D选项并可能在JAVA_OPTS环境变量中传递它们作为系统属性的方式来动态地替换Tomcat需要的属性。

如前面提到的问题所示,并且在catalina.sh中也建议这样做:

# Environment Variable Prerequisites
#
#   Do not set the variables in this script. Instead put them into a script
#   setenv.sh in CATALINA_BASE/bin to keep your customizations separate.
#

例如,您可以在位于$CATALINA_BASE/bin目录中的setenv.sh文件中定义它们。

例如:

#! /bin/sh

export MAX_ACTIVE_CONNECTIONS=20

export JAVA_OPTS="$JAVA_OPTS -DmaxActiveConnections=$MAX_ACTIVE_CONNECTIONS"

在您的XML配置文件中使用这些属性:

<Context path="/abc"
         docBase="abc"
         debug="5"
         reloadable="false"
         crossContext="true">
    <Resource name="jdbc/abc"
              auth="Container"
              type="javax.sql.DataSource"
              factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
              driverClassName="xxxxx"
              url="xxxxxxx"
              username="xxxxx" password="xxxxxx"
              maxActive="${maxActiveConnections}"
              initialSize="0"
              ...
    />
</Context>

1
这不像是OP要求的编程修改。 - eis
非常感谢您指出这一点,@eis。抱歉,我没有注意到问题的那部分。 - jccampanero
非常感谢 @eis。我使用基于运行时的解决方案更新了答案。很抱歉第一次忽略了运行时要求。 - jccampanero
1
我支持JMX变体。从注册表中获取MBean以进行编程修改没有问题。另一方面,我会让连接池实现处理连接数。 - jausen brett

4

通过JNDI创建的数据源是没有什么特殊之处的:如果您知道它的类(在您的情况下是org.apache.tomcat.jdbc.pool.DataSource),则可以将其转换为该类并使用可用的设置器对其进行配置:

    private void customizeDataSource(final DataSource ds) {
        if (ds instanceof PoolConfiguration) {
            final PoolConfiguration poolConfig = (PoolConfiguration) ds;
            poolConfig.setMaxActive(10);
        }
    }

请参阅PoolConfiguration的定义。实现了javax.sql.DataSource的对象还实现了一个非常有用的接口Wrapper,如果您的代码将Tomcat JDBC数据源包装在其他内容中,则可能会很方便。
    private void customizeDataSource(final DataSource ds) throws SQLException {
        if (ds.isWrapperFor(PoolConfiguration.class)) {
            final PoolConfiguration poolConfig = ds.unwrap(PoolConfiguration.class);
            poolConfig.setMaxActive(10);
        }
    }

然而,以上的编程方法可能会出现一些问题:

  • 如果您将tomcat-jdbc.jar与您的应用程序捆绑在一起,那么只有在您的context.xml中配置的JNDI资源才会被代码识别。而在GlobalNamingResources中配置的资源将使用Tomcat捆绑的org.apache.tomcat.jdbc.pool.DataSource副本,并且不会匹配instanceof条件。
  • 另一方面,如果您没有将tomcat-jdbc.jar包含在WAR文件中,您必须确保所设置的参数受到应用程序运行的所有Tomcat版本的支持。

1
全局命名资源可以通过Java反射访问。只有少数不同的数据源实现。可以迭代多个可能的方法名称,直到它起作用... - 30thh

1

MySQL连接速度快,因此连接池的使用受到限制。

通常情况下,如果存在性能问题,则最好使用其他技术来处理——如组合索引、重新构建查询、解决MySQL优化限制等。

你是否愿意退回一步,我们一起分析瓶颈所在?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接