连接池支持

LDAP 连接池有助于减轻每次 LDAP 交互创建新连接的开销。虽然 Java LDAP 连接池支持 已经存在,但其配置选项和功能有限,例如连接验证和连接池维护。Spring LDAP 支持基于每个 ContextSource 进行详细的连接池配置。

通过在应用上下文配置中的 <ldap:context-source /> 元素中提供一个 <ldap:pooling /> 子元素来提供连接池支持。只读和读写 DirContext 对象会分开进行连接池管理(如果指定了 anonymous-read-only)。Jakarta Commons-Pool 被用来提供底层连接池实现。

DirContext 验证

相较于 JDK 提供的 LDAP 连接池功能,使用自定义连接池库的主要动机是能够对连接池中的连接进行验证。验证允许检查连接池中的 DirContext 连接,以确保它们在从连接池中取出、放回连接池或在连接池中空闲时仍保持正确连接和配置。

如果配置了连接验证,将使用 DefaultDirContextValidator 来验证连接池中的连接。DefaultDirContextValidator 执行一个 DirContext.search(String, String, SearchControls) 操作,使用空名称、过滤器 "objectclass=*",并将 SearchControls 设置为只返回一个结果,仅包含 objectclass 属性,超时时间为 500ms。如果返回的 NamingEnumeration 有结果,则 DirContext 通过验证。如果没有返回结果或抛出异常,则 DirContext 验证失败。默认设置适用于大多数 LDAP 服务器,无需配置更改,并提供了验证 DirContext 的最快方法。如果需要自定义,可以使用 连接池配置 中描述的验证配置属性进行设置。

如果连接抛出被视为非瞬时(non-transient)的异常,它们会自动失效。例如,如果一个 DirContext 实例抛出 javax.naming.CommunicationException,它会被解释为非瞬时错误,并且该实例会自动失效,而无需额外的 testOnReturn 操作开销。哪些异常被解释为非瞬时异常是通过 PoolingContextSourcenonTransientExceptions 属性配置的。

连接池配置

<ldap:pooling /> 元素上提供了以下属性,用于配置 DirContext 连接池

表 1. 连接池配置属性
属性 默认值 描述

max-active

8

同时可以从该连接池中分配的每种类型(只读或读写)的最大活动连接数。可以使用非正数表示无限制。

max-total

-1

同时可以从该连接池中分配的所有类型(只读和读写总计)的最大活动连接总数。可以使用非正数表示无限制。

max-idle

8

每种类型(只读或读写)在连接池中可以保持空闲的最大活动连接数,超过此数量时,额外的连接将被释放。可以使用非正数表示无限制。

min-idle

0

每种类型(只读或读写)在连接池中可以保持空闲的最小活动连接数,低于此数量时,将创建额外的连接。可以使用零(默认值)表示不创建。

max-wait

-1

当没有可用连接时,连接池等待连接返回的最大毫秒数,超过此时间将抛出异常。可以使用非正数表示无限期等待。

when-exhausted

BLOCK

指定当连接池耗尽时的行为。

  • FAIL 选项在连接池耗尽时抛出 NoSuchElementException

  • BLOCK 选项会等待直到有新对象可用。如果 max-wait 为正数,并且在 max-wait 时间过期后没有新对象可用,将抛出 NoSuchElementException

  • GROW 选项会创建一个并返回一个新的对象(这基本上使得 max-active 变得没有意义)。

test-on-borrow

false

从连接池中借用对象之前是否进行验证。如果对象验证失败,它将从连接池中移除,然后尝试借用另一个对象。

test-on-return

false

将对象返回到连接池之前是否进行验证。

test-while-idle

false

对象是否由空闲对象驱逐器(如果存在)进行验证。如果对象验证失败,它将从连接池中移除。

eviction-run-interval-millis

-1

空闲对象驱逐器线程每次运行之间的休眠毫秒数。非正数表示不运行空闲对象驱逐器线程。

tests-per-eviction-run

3

空闲对象驱逐器线程每次运行时(如果存在)检查的对象数量。

min-evictable-time-millis

1000 * 60 * 30 (30 分钟)

对象在连接池中保持空闲的最短时间,超过此时间后,空闲对象驱逐器(如果存在)才有资格将其驱逐。

validation-query-base

LdapUtils.emptyName()

验证连接时使用的搜索基础。仅在指定 test-on-borrow, test-on-returntest-while-idle 时使用。

validation-query-filter

objectclass=*

验证连接时使用的搜索过滤器。仅在指定 test-on-borrow, test-on-returntest-while-idle 时使用。

validation-query-search-controls-ref

null;默认搜索控制设置如上所述。

验证连接时使用的 SearchControls 实例的 ID。仅在指定 test-on-borrow, test-on-returntest-while-idle 时使用。

non-transient-exceptions

javax.naming.CommunicationException

以逗号分隔的 Exception 类列表。列出的异常在立即失效方面被视为非瞬时异常。如果对连接池中的 DirContext 实例的调用抛出列出的任何异常(或其子类),该对象将自动失效,无需额外的 testOnReturn 操作。

Pool2 配置

<ldap:pooling2 /> 元素上提供了以下属性,用于配置 DirContext 连接池

表 2. 连接池配置属性
属性 默认值 描述

max-total

-1

同时可以从该连接池中分配的所有类型(只读和读写总计)的最大活动连接总数。可以使用非正数表示无限制。

max-total-per-key

8

连接池按键(key)分配的对象实例数量限制(已借出或空闲)。达到限制时,子池耗尽。负值表示无限制。

max-idle-per-key

8

每种类型(只读或读写)在连接池中可以保持空闲的最大活动连接数,超过此数量时,额外的连接将被释放。负值表示无限制。

min-idle-per-key

0

每种类型(只读或读写)在连接池中可以保持空闲的最小活动连接数,低于此数量时,将创建额外的连接。可以使用零(默认值)表示不创建。

max-wait

-1

当没有可用连接时,连接池等待连接返回的最大毫秒数,超过此时间将抛出异常。可以使用非正数表示无限期等待。

block-when-exhausted

true

是否等待直到有新对象可用。如果 max-wait 为正数,并且在 maxWait 时间过期后没有新对象可用,将抛出 NoSuchElementException

test-on-create

false

在借用对象之前是否进行验证。如果对象验证失败,则借用失败。

test-on-borrow

false

从连接池中借用对象之前是否进行验证的指示器。如果对象验证失败,它将从连接池中移除,然后尝试借用另一个对象。

test-on-return

false

将对象返回到连接池之前是否进行验证的指示器。

test-while-idle

false

对象是否由空闲对象驱逐器(如果存在)进行验证的指示器。如果对象验证失败,它将从连接池中移除。

eviction-run-interval-millis

-1

空闲对象驱逐器线程每次运行之间的休眠毫秒数。非正数表示不运行空闲对象驱逐器线程。

tests-per-eviction-run

3

空闲对象驱逐器线程每次运行时(如果存在)检查的对象数量。

min-evictable-time-millis

1000 * 60 * 30 (30 分钟)

对象在连接池中保持空闲的最短时间,超过此时间后,空闲对象驱逐器(如果存在)才有资格将其驱逐。

soft-min-evictable-time-millis

-1

对象在连接池中保持空闲的最短时间,超过此时间后,空闲对象驱逐器才有资格将其驱逐,但附加条件是每个键(key)至少保留最小数量的对象实例。如果 min-evictable-time-millis 设置为正值,则此设置将被覆盖。

eviction-policy-class

org.apache.commons.pool2.impl.DefaultEvictionPolicy

此连接池使用的驱逐策略实现。连接池尝试使用线程上下文类加载器加载该类。如果失败,连接池将尝试使用加载此类的类加载器加载该类。

fairness

false

连接池公平地服务等待借用连接的线程。true 意味着等待线程按照 FIFO(先进先出)队列的方式服务。

jmx-enable

true

为连接池启用了平台 MBean 服务器的 JMX。

jmx-name-base

null

用作 JMX 启用连接池名称一部分的 JMX 名称基础。

jmx-name-prefix

pool

用作 JMX 启用连接池名称一部分的 JMX 名称前缀。

lifo

true

指示连接池对空闲对象是否具有 LIFO(后进先出)行为或作为 FIFO(先进先出)队列。LIFO 总是返回连接池中最近使用的对象,而 FIFO 总是返回空闲对象池中最旧的对象。

validation-query-base

LdapUtils.emptyPath()

用于验证搜索的基础 DN。

validation-query-filter

objectclass=*

用于验证查询的过滤器。

validation-query-search-controls-ref

null;默认搜索控制设置如上所述。

验证连接时使用的 SearchControls 实例的 ID。仅在指定 test-on-borrow, test-on-returntest-while-idle 时使用

non-transient-exceptions

javax.naming.CommunicationException

以逗号分隔的 Exception 类列表。列出的异常在立即失效方面被视为非瞬时异常。如果对连接池中的 DirContext 实例的调用抛出列出的任何异常(或其子类),该对象将自动失效,无需额外的 testOnReturn 操作。

配置

配置连接池需要在 <ldap:context-source> 元素中嵌套添加一个 <ldap:pooling> 元素,如下所示

<beans>
   ...
    <ldap:context-source
        password="secret" url="ldap://localhost:389" username="cn=Manager">
        <ldap:pooling />
    </ldap:context-source>
   ...
</beans>

在实际情况中,你可能会配置连接池选项并启用连接验证。上面的示例展示了基本思路。

验证配置

以下示例在将每个 DirContext 传递给客户端应用之前对其进行测试,并测试在连接池中处于空闲状态的 DirContext 对象

<beans>
   ...
    <ldap:context-source
        username="cn=Manager" password="secret" url="ldap://localhost:389" >
        <ldap:pooling
            test-on-borrow="true"
            test-while-idle="true" />
    </ldap:context-source>
   ...
</beans>

已知问题

本节描述了人们在使用 Spring LDAP 时有时会遇到的问题。目前,它涵盖了以下问题

自定义认证

PoolingContextSource 假定从 ContextSource.getReadOnlyContext() 获取的所有 DirContext 对象都具有相同的环境,同样地,从 ContextSource.getReadWriteContext() 获取的所有 DirContext 对象也具有相同的环境。这意味着将配置了 AuthenticationSourceLdapContextSource 包装到 PoolingContextSource 中时,其行为不会按预期进行。连接池将使用第一个用户的凭据填充,并且除非需要新的连接,否则后续的上下文请求将不会为请求线程指定的 AuthenticationSource 用户填充。