• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • mod_unique_id

    描述:为每个请求提供具有唯一标识符的环境变量
    状态:延期
    模块标识符:unique_id_module
    源文件:mod_unique_id.c

    摘要

    该模块为每个请求提供了一个魔术令牌,保证在非常特定条件下的“所有”请求中是唯一的。在正确配置的 cluster 计算机中,唯一标识符在多台计算机上甚至是唯一的。环境变量UNIQUE_ID设置为每个请求的标识符。唯一标识符可用于各种原因,这些原因超出了本文档的范围。

    理论

    首先简要回顾一下 Apache 服务器如何在 Unix 机器上运行。 Windows NT 目前不支持此 feature。在 Unix 机器上,Apache 创建了几个子节点,子节点 process 在 time 时请求一个子节点。每个孩子可以在其一生中提供多个请求。出于本讨论的目的,孩子们不会彼此共享任何数据。我们将这些子节点称为 httpd 进程。

    您的网站有一台或多台受您管理控制的机器,我们将它们称为机器集群。每台机器都可能运行 Apache 的多个实例。所有这些都被视为“宇宙”,并且通过某些假设,我们将展示在此 Universe 中,我们可以为每个请求生成唯一标识符,而无需在 cluster 中的计算机之间进行广泛的通信。

    cluster 中的机器应满足这些要求。(即使您只有一台机器,也应该将其时钟与 NTP.)同步

    • 机器的时间通过 NTP 或其他网络 time 协议同步。
    • 机器的主机名都不同,因此模块可以在主机名上执行主机名查找,并为 cluster 中的每台机器接收不同的 IP 地址。

    就操作系统假设而言,我们假设 pids(process ids)适合 32-bits。如果操作系统对 pid 使用的值超过 32-bits,则修复程序很简单,但必须在 code 中执行。

    鉴于这些假设,在 time 中的单个点,我们可以从所有其他 httpd 进程中识别 cluster 中任何机器上的任何 httpd process。机器的 IP 地址和 httpd process 的 pid 足以完成此操作。如果使用 multi-threaded MPM,httpd process 可以同时处理多个请求。在 order 中识别线程,我们使用一个线程索引 Apache httpd 在内部使用。因此,为了生成请求的唯一标识符,我们只需要区分 time 中的不同点。

    为区分 time,我们将使用 Unix 时间戳(1970 年 1 月 1 日以来的秒数)和 16-bit 计数器。时间戳只有一个秒的粒度,因此计数器用于在一秒钟内表示最多 65536 个值。四元组(ip_addr,pid,time_stamp,计数器)足以枚举每个 httpd process 每秒 65536 个请求。然而,在 time 期间有 pid 重用的问题,并且计数器用于缓解此问题。

    当创建 httpd 子节点时,计数器初始化为(当前微秒除以 10)模 65536(选择此公式是为了消除某些系统上微秒定时器的低 order 位的一些方差问题)。生成唯一标识符时,使用的 time 标记是请求到达 web 服务器的 time。每生成一个标识符(并允许翻转),计数器就会递增。

    内核为每个 process 生成一个 pid,因为它会分叉 process,并允许 pid 翻转(它们在许多 Unix 上都是 16-bits,但是新系统已经扩展到 32-bits)。所以在 time 期间,相同的 pid 将被重用。但是,除非它在同一秒内重复使用,否则它不会破坏我们四重奏的唯一性。也就是说,我们假设系统不会在一秒间隔内产生 65536 个进程(在某些 Unix 上甚至可能是 32768 个进程,但即使这样也不太可能发生)。

    假设 time 因某种原因重复出现。也就是说,假设系统的时钟被搞砸了并且它重新访问了过去 time(或者它太远了,正确地重置,然后重新访问未来 time)。在这种情况下,我们可以轻松地显示我们可以获得 pid 和 time 标记重用。计数器的初始化程序的选择旨在帮助打败这一点。请注意,我们确实需要一个随机数来初始化计数器,但在大多数系统上都没有任何现成的 numbers(i.e.,你不能使用 rand(),因为你需要为 generator 播种,并且不能使用它来播种它 time 因为 time,至少在一秒钟的分辨率下,已经重复了一次)。这不是一个完美的防守。

    防守有多好?假设你的一台机器每秒最多服务 500 个请求(这是写作时非常合理的上限,因为系统通常不仅仅是挖出静态 files)。要做到这一点,它将需要许多孩子,这取决于你有多少并发客户端。但我们会感到悲观,并假设一个孩子每秒能够提供 500 个请求。有 1000 个可能的起始计数器值,使得 500 个请求的两个 sequences 重叠。因此,如果 time(在一秒钟的分辨率下)重复自己,这个孩子将重复一个计数器值,并且唯一性将被打破,有 1.5%的可能性。这是一个非常悲观的例子,有了现实世界的价值,它甚至不太可能发生。如果您的系统仍然可能发生,那么也许您应该将计数器设为 32 位(通过编辑 code)。

    您可能会担心在夏令时夏令时“退回”时钟。然而,这不是问题,因为这里使用的时间是 UTC,“总是”前进。请注意,基于 x86 的 Unix 可能需要正确的 configuration 以使其成为 true -它们应配置为假设主板时钟处于 UTC 并进行适当补偿。但即使如此,如果您正在运行 NTP,那么您的 UTC time 在重启后很快就会正确。

    UNIQUE_ID环境变量是通过以类似于 MIME base64 编码的方式使用字母[A-Za-z0-9@-]编码 144-bit(32-bit IP 地址,32 位 pid,32 位 time 标记,16 位计数器,32 位线程索引)四倍来构造的,生成 24 个字符。 MIME base64 字母实际上是[A-Za-z0-9+/]但是+/需要在 URL 中进行特殊编码,这使得它们不太理想。所有值都以网络字节 ordering 编码,因此编码在不同字节 ordering 的体系结构中是可比较的。编码的实际 ordering 是:time 戳,IP 地址,pid,计数器。这个 ordering 有一个目的,但应该强调应用程序不应该剖析编码。 Applications 应该将整个编码的UNIQUE_ID视为一个不透明的标记,只能与其他UNIQUE_ID进行比较。

    选择了 ordering,以便将来可以更改编码,而不必担心与现有的UNIQUE_ID数据库发生冲突。新编码还应将 time 标记保留为第一个元素,否则可以使用相同的字母和位长。由于 time 标记本质上是一个递增的序列,因此 flag 秒就足以使 cluster 中的所有计算机停止服务于任何请求,并停止使用旧的编码格式。之后他们可以恢复请求并开始发布新的编码。

    我们认为这是解决此问题的相对便携的解决方案。生成的标识符基本上具有无限 life-time,因为未来的标识符可以根据需要更长。 cluster 中的机器之间基本上不需要通信(只需要 NTP 同步,这是低开销),并且不需要 httpd 进程之间的通信(通信隐含在内核分配的 pid value 中)。在非常特定的情况下,可以缩短标识符,但需要假设更多信息(例如,对于任何站点,32-bit IP 地址都是过度杀伤,但是没有便携式的更短替换它)。

    上篇:mod_suexec

    下篇:mod_unixd