65.9K
CodeProject 正在变化。 阅读更多。
Home

Peer to Peer ASP.NET State Server

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.99/5 (47投票s)

2009年8月24日

Apache

19分钟阅读

viewsIcon

124995

downloadIcon

3431

ASP.NET 状态服务的分布式实现

引言

ASP.NET Web 开发者有三种内置选项来存储会话状态,即进程内内存、SQL Server 和状态服务器。

进程内内存 提供最快的性能,但不适合在 Web 服务器场中使用,因为会话数据存储在 ASP.NET 工作进程的内存中。

SQL Server 是一种进程外会话状态存储选项,可与 Web 服务器场配合使用。它将会话数据存储在 SQL Server 数据库中。这是最可靠的选项,但性能最差。此选项的一个主要问题是,开发人员经常希望将从数据库检索的数据缓存到会话状态中,以减少数据库查找。SQL Server 会话状态会削弱此目的,因为将从数据库检索的数据缓存到数据库中,性能提升很小。

状态服务器 是一种进程外会话状态存储选项,可与 Web 服务器场配合使用。它将会话数据存储在内存中,并且比 SQL Server 性能更好。这似乎是进程内选项和 SQL Server 选项之间的良好折衷。但是,它存在一些缺点。

首先,多个 Web 服务器通常依赖一个状态服务器来获取会话状态,这引入了一个关键的单点故障。

其次,在负载均衡环境中,负载均衡器可能会将用户的请求重定向到与用户先前请求服务的 Web 服务器不同的 Web 服务器。如果新 Web 服务器与不同的状态服务器通信,则将找不到用户原始的会话状态,并且 Web 应用程序可能无法正常工作。
即使在使用持久性(又称粘性)负载均衡器的情况下,由于错误或服务器故障,也可能会出现此问题。

第三,许多开发人员没有意识到的一点是,Web 服务器和状态服务器以纯文本形式通信。窃听者可以轻松地获取在网络上传输的会话状态数据。如果所有服务器都在内部网络中运行,这可能不是威胁,但当 Web 服务器和状态服务器分布在 Internet 上时,这无疑令人担忧。

本文介绍的 P2P ASP.NET 状态服务器解决了上述问题,同时透明地替换了 Microsoft 提供的状态服务器。

概述

P2P 状态服务器背后的理念很简单——让网络上的状态服务器安全地相互通信并按需传递会话状态数据,如下所示。

此设计通过 Web 服务器可以共享多个状态服务器,从而消除了单点故障,从而提高了可伸缩性。此外,如果负载均衡器错误地或故意将用户重定向到连接到独立状态服务器的其他 Web 服务器,则用户会话状态将从为用户提供服务的状态服务器请求。

安全性也得到了提高,因为可以配置对等方在共享会话状态时加密会话数据。Web 服务器和状态服务器之间的数据传输保持未加密,但可以通过将 Web 服务器和关联的状态服务器保留在受信任的网络中或同一台计算机上来消除窃听攻击。

P2P 状态服务器完全向后兼容 Microsoft 提供的状态服务器,并具有上述所有优点。

安装

编译和安装状态服务器

  1. 下载源文件。
  2. 在 Visual Studio 中打开解决方案。(Visual Studio 2008 会打开一个转换向导。完成向导。)

    状态服务器有两种版本。一种作为控制台应用程序运行,另一种作为 Windows 服务运行。StateService 项目编译为 Windows 服务,可以使用 install_service.batuninstall_service.bat 文件安装和卸载。ConsoleServer 项目将服务作为控制台应用程序运行,这使得测试和调试更加容易。这两个项目共享相同的源,并且功能相同。

  3. 打开要构建的项目属性窗口。
  4. a. 如果使用 Visual Studio 2005,请在“生成”选项卡的“条件编译符号”字段中添加 NET20
    b. 如果使用 Visual Studio 2008,请在“应用程序”选项卡的“目标框架”字段中选择 .NET Framework 3.5
  5. 构建项目。
  6. 如果构建了 StateService 项目,请导航到输出文件夹并运行 install_service.bat 来安装服务。
  7. 如果您的计算机上已运行 Microsoft 状态服务器,请将其停止。
  8. 如果构建并安装了 Windows 服务,您可以在“服务”列表中启动 Peer to Peer State Service。如果构建了控制台服务器,请运行 ConsoleServer.exe 或直接从 Visual Studio 开始调试。
  9. 现在您可以测试并运行您拥有的任何 Web 应用程序,并使用正在运行的状态服务器。

添加对等服务器

  1. 将编译后的可执行文件和应用程序配置文件复制到网络上的另一台计算机。
  2. 打开配置文件,然后在 <Peers> 部分添加一个新对等服务器。例如,要将状态服务器配置为连接到运行在名为 SV3 的计算机上的另一个状态服务器,并使用对等端口号 42425,您需要将 <add key="MyPeer" value="pc2:42425" /> 添加到 <Peers> 部分。
  3. 您可以在计算机上启动状态服务器,它将与网络上的其他状态服务器连接。
  4. 您可以按照自己喜欢的任何拓扑设置网络。例如,考虑一个包含三个状态服务器的网络,如下所示,每个机器上的状态服务器都将具有如下配置

您可以在同一台计算机上运行多个控制台服务器对等点,但每个控制台服务器都必须具有唯一的 Web 服务器端口和对等端口设置。

工作原理

Microsoft 提供的状态服务器工作流程如下

P2P 状态服务器的工作方式与上面完全相同,只是当状态服务器没有请求的会话状态时,它会在响应之前从网络请求会话状态,如下所示。

如果在设定的时间段内未传输请求的会话状态,则状态服务器假定该会话状态在网络上不存在,然后继续处理 Web 服务器的请求而没有会话状态。GetTransferMessage 类代表节点请求会话时在网络上广播的消息。对等方主要通过保持彼此之间的连接来转发此消息。会话状态传输发生在对等网络带外进行。

实现说明

各种编程技术用于实现状态服务器的不同方面。其中一些值得注意的方面如下。

平台

状态服务器是用 C# 2.0 编写的,但以 NET 3.5 框架为目标,以便利用 ReaderWriterLockSlim 类。如果定义了 NET20 符号,则服务器将使用较慢的 ReaderWriterLock 类,并且能够以 .NET 2.0 框架为目标。

协议

为了创建能够透明替换状态服务器的状态服务器,我需要获取并理解 Web 服务器与 Microsoft 提供的状态服务器之间通信协议的完整规范。有关梳理协议的步骤记录在 http://ahuwanya.net/blog/category/Peer-to-Peer-Session-State-Service.aspx 的逆向时序中。

消息传递

该服务器很大程度上是消息驱动的。消息子系统如下所示。

当服务器从套接字接收数据时,数据会累积在当前分配给该套接字的 HTTPPartialData 类的实例中。HTTPPartialData 实例验证数据,确定累积数据是否为完整的 HTTP 消息,并检查累积数据中的错误。如果存在数据错误(例如,如果数据不符合 HTTP),则将丢弃所有累积数据并关闭套接字。如果数据有效但尚未完成,则套接字会等待更多数据到达。
如果累积的数据是完整的 HTTP 消息,则会将数据发送到 MessageFactory 对象。MessageFactory 对象检查数据以确定要创建的适当 ServiceMessage 子类的实例。ServiceMessage 子类被实例化,并调用其 Process 方法的实现来处理消息。

并发处理

在访问会话字典中的会话状态时,采用了悲观并发机制,该机制由 SessionDictionary 类定义。一次只能由一个线程读取或修改一块会话状态。线程通过将 IsInUse 属性设置为 true 来声明对一块会话状态的独占访问。这是通过调用原子比较交换 CompareExchangeInUse 方法(.NET Interlocked.CompareExchange 方法的一个包装器,它操作 IsInUse 属性)来实现的。将此属性设置为 true 会让其他线程知道另一个线程正在使用该会话状态。

如果另一个线程想要访问相同的会话状态并尝试声明独占访问,则该尝试将失败,因为另一个线程已经拥有独占访问权。该线程将继续尝试获取独占访问权,并在其他线程释放访问权后最终获取它。这效果很好,因为大多数时候,只有一个线程需要访问会话状态,而且大多数对会话状态的操作都很快完成。导出(传输)操作需要更长的时间,它通过一种略有不同的机制处理,并在下面的争用管理部分中讨论。

计时器——或者缺乏计时器

该代码中有许多对象会过期或超时,并且必须在过期时执行某些操作——例如,过期的单个会话状态字典条目或超时的异步消息。不是为跟踪这些对象分配计时器或等待句柄——它们存储在一个特殊集合类的实例中,称为 DateSortedDictionary。此字典中的对象按其分配的时间戳就地排序。专门指定的线程会轮询这些日期排序字典中的过期项,并在项过期时执行相关操作。此设计显着减少了跟踪过期项所需的线程数。

诊断

Diags 类用于跟踪消息、记录服务器活动和检测死锁。Diags 类上的方法是条件性的,不会编译到发布配置代码中。
可以定义 VERBOSE 符号来查看或记录服务器上发生的所有活动。这对于控制台服务器特别有用,它将此信息输出到控制台窗口。如果未定义 VERBOSE 符号,则只显示关键信息或意外错误。

安全

Microsoft 提供的状态服务器向 Web 服务器传输和接收未加密的数据。这很可能是出于性能原因。为了与 Microsoft 提供的状态服务器兼容,P2P 状态服务器向 Web 服务器传输未加密的数据。然而,P2P 状态服务器可以配置为在对等方之间传输加密数据。当 Web 服务器和关联的状态服务器安装在同一台计算机或受信任的网络上时,这可以有效地阻止网络窃听攻击。

例如,以 Web 服务器 – Microsoft 状态服务器配置为例,如下所示。

两个 Web 服务器通过公共 Internet 连接以访问状态服务器。

使用 P2P 状态服务器,可以通过让 Web 服务器拥有自己的本地状态服务器,该状态服务器代表它们安全地连接到远程状态服务器,从而保护网络,如下所示。

   

本地状态服务器可以安装在与 Web 服务器相同的机器上,以获得最大的安全性和最低的延迟。

此方法有助于保护地理上分布的 Web 服务器和状态服务器。
对等状态服务器在连接时还会相互进行身份验证,以确保另一方是授权的对等方。

网络拓扑

对等方之间的连接形成逻辑网络,这些逻辑网络可以根据常见的网络拓扑进行设计。

上面所示的网络 A 是 P2P 状态服务器的环形网络,每个服务器单独连接到 Web 服务器;而网络 B 是计算机的环形网络,其中同时连接并运行状态服务器和 Web 服务器。通过用 P2P 状态服务器替换 Microsoft 状态服务器并按网络 A 所示进行连接,可以将现有的独立 Microsoft 状态服务器网络升级为更大的 P2P 网络。网络 B 受益于前面提到的安全措施,并且在某种程度上更具可伸缩性,因为网络上的任何节点都是 Web 服务器 P2P 状态服务器。

与总线网络不同,这两个网络即使有一个节点发生故障仍能正常运行,但是,随着网络中节点的增加,消息在网络上传输所需的时间也越长。

 

网络 C 是星形网络。星形网络的优点在于,无论在环形网络中添加多少新节点,消息到达网络上的任何节点只需要两次跃迁。

网络 D 是三个星形网络组成的更大星形网络。此网络也需要较少的跃迁次数才能使消息在网络上传输。这两个网络都存在一个缺点,即如果中心节点发生故障,整个网络都会失败。

通过连接网络 D 的叶节点,形成网络 E,一个部分网状网络。网络 E 是环形网络和星形网络的巧妙组合。如果中心节点发生故障,网络仍将运行,并且与在环形网络中相比,消息在网络上传输所需的跃迁次数也更少。

正如所演示的,P2P 状态服务器网络的拓扑仅受网络设计者想象力的限制。

有趣的场景

状态服务器中发生了许多场景,这些场景使用传统的 P2P 进程来处理,例如“生存时间”报头,用于防止消息在网络上永久循环,以及对等方用于识别先前已看到的邮件的消息标识符。但是,此对等网络中存在两个不太常见的特定场景。

关机传输

为确保在服务器关机期间不会丢失会话数据,当启动服务器关机时,状态服务器会以循环方式将其所有会话状态数据传输到已连接的对等方。

重播

网络上的会话请求可能会在恰好要传输给它的节点时错过它,如下所示。

如上所示,节点 1 正在从网络上查找会话 A,而节点 4 此时正要将该会话传输到节点 2。

当节点 1 的消息到达节点 2 时,节点 2 将消息转发给节点 3,因为它没有该会话。

当消息到达节点 3 时,节点 4 和节点 2 之间的会话传输开始,并且当消息到达节点 4 时,传输已完成,节点 4 不再拥有该会话,并将其消息转发给节点 5。

因此,消息在网络上传播,但没有到达拥有该会话的任何节点,即使该会话存在于网络中。

状态服务器通过让最近传输了会话的节点重播消息来解决此问题,如下所示。

在此,节点 4 重播消息,使其也沿着来时的路径传播,并最终到达拥有该会话的节点 2。

重播的消息是原始消息的副本,只是它们具有不同的 Broadcast ID 报头,对等方使用该报头来识别这是一个不同的广播。

争用管理

如前所述,状态服务器在访问会话字典中的会话状态条目时使用悲观并发模型。这效果很好,因为大多数请求处理时间都很短。然而,一个特定的请求可能需要更长的时间来处理,并可能导致资源耗尽和性能下降。

当对等方需要处理它没有的会话状态时,会发起一个 GetTransferMessage 消息广播。当广播到达拥有请求会话状态的对等方时,会将该会话状态传输给请求的对等方。

与对会话状态的其他操作不同,传输可能需要很长时间,因为对等方必须连接到另一个对等方,可能进行身份验证,并传输(可能很大的)数据。需要注意的是,Web 服务器的任何请求都可以启动 GetTransferMessage 广播。

在传输过程中,会话被标记为“使用中”,并且对该会话的其他请求将像往常一样等待。然而,由于这需要更长的时间,等待传输完成的线程会消耗大量系统资源。它们也可能超时,如果传输时间过长,或者如果由于消息泛滥而导致会话在网络上来回传输。一个糟糕的情况如下所示。

在上图中,一个用户正在向 Web 应用程序发送大量请求,这反过来又导致将会话请求传输到状态服务器。

由于所有请求都来自一个用户,因此所有会话请求都引用相同的会话 ID。负载均衡器或状态分区器将这些请求分发到三个状态服务器。

需要注意的是,尽管负载均衡器或状态分区器不太可能将对会话的请求分发到不同的状态服务器,但用户可以通过简单地按住浏览器刷新键来生成上述场景,该场景适用于实现不佳的状态分区器或故障的负载均衡器的 Web 应用程序。
此外,一组有组织的恶意用户(或僵尸网络)可以在状态分区器和负载均衡器正常运行的情况下生成此场景。

每个状态服务器都有等待处理的请求。如果高度需求的会话位于状态服务器 3 上,则该状态服务器上的请求将一个接一个地快速处理。

状态服务器 1 和 2 发出广播请求会话传输。消息最终到达状态服务器 3,并且会话被传输到状态服务器 2。状态服务器 3 上未处理的请求将等待传输完成。

会话传输到服务器 2 完成后,将处理服务器 2 上的请求,而服务器 3 上的请求将发出广播请求会话。

来自状态服务器 1 的广播消息到达状态服务器 2,并将会话传输到状态服务器 1。这种情况会持续下去,服务器之间不断地传输会话,而大多数请求都在等待,因为即使会话被传输,状态服务器也只能处理少量请求,然后就会被传输到另一个状态服务器。

更糟糕的是,如果一个状态服务器在最近传输了会话后收到 GetTransferMessage 消息,它会重播该消息(如前所述),这会导致网络上出现更多的 GetTransferMessage 广播,更多的来回传输以及持续的资源耗尽。

传输过程相对缓慢,并且由于所有请求都必须等待每个状态服务器逐个处理,因此请求开始超时,Web 服务器开始丢弃请求。状态服务器不知道 Web 服务器已丢弃这些请求,仍然继续处理它们。

这些冗余的请求在等待其处理时间时,会消耗宝贵的服务器处理器周期并降低服务质量。

如果出现大量此类请求,它们将迅速耗尽所有处理器资源,服务器将停止运行。

虽然不可能阻止任何用户群向状态服务器发送大量请求,但状态服务器通过以下原则来防止争用会话:由于争用会话而导致的任何服务降级主要应影响该会话的用户,并通过以下机制实现此目标:

  1. 当需要处理请求并且服务器发现会话正在传输时,请求将停止处理并排队,等待传输完成后再处理。这可以防止请求在等待时消耗处理器周期,并释放资源,以便可以处理来自其他用户的其他请求。如果等待会话传输的队列中的请求数量过多,则会丢弃所有这些消息,因为这意味着该会话是争用的,服务器不应 bother 处理它们。
  2. 传输完成后,当一个排队的请求准备好重新处理,并且服务器注意到同一会话正在被另一个请求再次传输时,该请求将被丢弃并且不会被处理,因为这意味着该会话是高度争用的。
  3. 在请求尝试查询网络(通过广播)以获取会话之前,它会检查是否正在等待之前对该会话的查询的回复,如果是,则将请求排入待处理请求列表,并在收到查询时进行处理。这减少了将在网络上生成的 GetTransferMessage 消息的数量,从而减少了不必要的重播和查找。如果等待会话到达的队列中的请求数量过多,则会丢弃所有这些请求,因为这意味着该会话是争用的。
  4. 最后,所有传入的请求都排入其会话 ID 特定的队列,消息处理器以循环方式轮询传入的请求队列并逐个处理它们,如下所示。

    这意味着所有会话请求都得到公平对待,没有单个用户可以显著干扰来自其他用户的消息的处理速率。此外,如果特定会话 ID 的队列太长,该队列将被丢弃,因为这意味着该会话是争用的。

由状态服务器采用的所有这些技术只会对违规用户的 Web 应用程序产生不利影响。

结论

P2P 状态服务器完全向后兼容 Microsoft 提供的状态服务器,并且可以透明地替换它。对等状态服务器可以相互传输会话,从而提高了依赖会话状态的 Web 应用程序的可靠性。对等状态服务器还可以作为安全层,保护网络上的会话数据。

这个项目最初只是一个简单的想法,但很快就发展成了一项复杂的任务。希望这个实现和本文提出的其他想法将对有兴趣分布式系统的开发人员有所帮助。由于复杂性水平,将会有 bug 和需要解决的问题。我们欢迎贡献和 bug 报告。

历史

  • 2009 年 8 月 24 日:首次发布
© . All rights reserved.