使用 USNChanged 轮询更改
DirSync 控件功能强大且高效,但也存在两个明显的局限性:
- 仅适用于高特权应用程序:要使用 DirSync 控件,应用程序必须在域控制器上具有 SE_SYNC_AGENT_NAME 权限的帐户下运行。 很少有帐户拥有如此高的特权,因此普通用户无法运行使用 DirSync 控件的应用程序。
- 无子树范围:DirSync 控件会返回命名上下文中发生的所有更改。 如果应用程序只对命名上下文的一个小型子树中发生的更改感兴趣,就必须浏览许多无关的更改,这对应用程序和域控制器而言都是低效的。
还可以通过查询 uSNChanged 属性来获取 Active Directory 中的更改,从而避免 DirSync 控件的限制。 此替代方法在各方面都不如 DirSync 控件,因为它需要在任何属性发生变化时传输所有属性,而且需要应用程序开发人员做更多工作才能正确处理某些故障情况。 目前,它是编写某些更改跟踪应用程序的最佳方式。
当域控制器修改对象时,它会将该对象的 uSNChanged 属性设置为一个比该对象 uSNChanged 属性的上一个值更大的值,并且设置的值也大于该域控制器上持有的所有其他对象 uSNChanged 属性的当前值。 因此,应用程序可以通过查找具有最大 uSNChanged 值的对象来查找域控制器上最近更改的对象。 域控制器上最近更改次数第二多的对象将具有第二大的 uSNChanged 值,依此类推。
uSNChanged 属性不会被复制,因此在两个不同的域控制器上读取一个对象的 uSNChanged 属性通常都会得到不同的值。
例如,uSNChanged 属性可用于跟踪子树 S 中的更改。首先,对子树 S 执行一次“完全同步”。假设 S 中任何对象的最大 uSNChanged 值为 U。定期查询子树 S 中 uSNChanged 值大于 U 的所有对象。将 U 设置为这些已更改对象中最大的 uSNChanged 值,然后就可以再次轮询了。
实现 uSNChanged 同步应用程序的微妙之处包括:
使用 rootDSE 的 highestCommittedUSN 属性绑定 uSNChanged 筛选器。 也就是说,在开始完全同步之前,要先读取附属域控制器的 highestCommittedUSN。 然后,执行一次完整的同步查询(使用分页结果)来初始化数据库。 完成此操作后,存储完全同步查询前读取的 highestCommittedUSN 值;作为下一次同步时 uSNChanged 属性的下限。 之后,要执行渐进式同步,请重新读取 highestCommittedUSN rootDSE 属性。 然后,使用分页结果查询相关对象,这些对象的 uSNChanged 大于上次同步保存的 uSNChanged 属性值的下限。 使用这些信息来更新数据库。 完成后,根据渐进式同步查询前读取的 highestCommittedUSN 值来更新 uSNChanged 属性的下限。 始终将 uSNChanged 属性值的下限存储在应用程序与域控制器内容同步的同一存储区中。
遵循此过程,而不是基于检索对象上的 uSNChanged 值的程序,可避免服务器重新检索不属于适用于应用程序的集合的更新对象。
由于 uSNChanged 是一个不可复制的属性,因此应用程序每次运行时都必须绑定到同一个域控制器。 如果无法与该域控制器绑定,则它必须等待,直到可以绑定为止,或者与某个新的域控制器绑定,并与该域控制器执行完全同步。 当应用程序隶属于域控制器时,它会在稳定存储区中记录该域控制器的 DNS 名称,也就是与域控制器内容保持一致的同一个存储区。 然后,它会使用存储的 DNS 名称绑定到同一域控制器,以进行后续同步。
应用程序必须检测当前隶属的域控制器何时已从备份中恢复,因为这可能会导致不一致。 当应用程序隶属于域控制器时,它会将该域控制器的“调用 ID”缓存到稳定存储区,即与域控制器内容保持一致的存储区。 域控制器的“调用 ID”是一个 GUID,它存储在域控制器服务对象的 invocationID 属性中。 要获取域控制器服务对象的可分辨名称,请读取 rootDSE 的 dsServiceName 属性。
请注意,当从备份恢复应用程序的稳定存储区时,不会出现一致性问题,这是因为域控制器名称、调用 ID 和 uSNChanged 属性值的下限与域控制器内容会同步存储。
在查询服务器(包括完全同步和渐进式同步)时使用分页,以避免同时检索大型结果集的可能性。 有关详细信息,请参阅指定其他搜索选项。
执行基于索引的查询,以避免在使用分页结果时强制服务器存储大量中间结果。 有关详细信息,请参阅索引的属性。
通常,不要使用服务器端对搜索结果进行排序,否则会强制服务器对大量的中间结果进行存储和排序。 这同时适用于完全同步和渐进式同步。 有关详细信息,请参阅指定其他搜索选项。
正常处理任何父条件。 应用程序可能会在识别对象的父对象之前识别该对象。 根据不同的应用程序,这可能会成为一个问题。 应用程序可以随时从目录中读取父进程的当前状态。
要处理移动或删除的对象,请存储每个跟踪对象的 objectGUID 属性。 无论对象在整个林中移动到哪个位置,其 objectGUID 属性都不会变。
要处理已移动的对象,要么执行定期的完全同步,要么增大搜索范围并在客户端筛选掉不感兴趣的更改。
要处理已删除的对象,要么定期执行完全同步,要么在执行渐进式同步时单独搜索已删除的对象。 在查询已删除的对象时,请检索已删除对象的 objectGUID 以确定要从数据库中删除的对象。 有关详细信息,请参阅检索已删除的对象。
请注意,搜索结果只包括调用方有权限读取的对象和属性(基于各种对象上的安全描述符和 DACL)。 有关详细信息,请参阅安全性对查询的影响。
有关详细信息,以及展示 USNChanged 同步应用程序基础的代码示例,请参阅使用 USNChanged 检索更改的代码示例。