在峰值负载下提高 ASP.NET 性能的 4 种方法

 

介绍

ASP.NET 在开发 Web 应用程序方面变得非常流行,其中许多应用程序本质上是高流量并为数百万用户提供服务。 因此,这些应用程序对业务有很大的影响,因此非常重要。

有趣的是,ASP.NET 应用程序体系结构在应用程序层具有很强的可扩展性。 而且,HTTP 协议也是无状态的。 这两者都意味着您可以在负载平衡的 Web 场中运行 ASP.NET 应用程序,其中每个 HTTP 请求都被路由到最合适的 Web 服务器。 随着用户流量的增加,这使您可以轻松地将更多 Web 服务器添加到您的 Web 场。 而且,这使您的 ASP.NET 应用程序层非常可扩展。 但是这里的可扩展性是什么意思?

可扩展性本质上是即使在峰值负载下也能提供高性能的能力。 因此,如果您的 ASP.NET 应用程序页面响应时间在 10 个用户时非常快,并且在 100,000 个用户时保持同样快,那么您的 ASP.NET 应用程序是可伸缩的。 但是,如果您的 ASP.NET 响应时间随着用户数量的增加而变慢,那么您的应用程序就无法扩展。

 

问题:可扩展性瓶颈

尽管在应用程序层具有非常可扩展的体系结构,但今天的 ASP.NET 应用程序面临着主要的可扩展性瓶颈。 这些瓶颈发生在以下四个不同的领域:

  1. 应用数据库
  2. ASP.NET 会话状态存储
  3. ASP.NET View State
  4. 静态输出的 ASP.NET 页面执行

让我在下面更详细地解释每一个。

 

应用数据库

随着事务负载的增加,SQL Server 或 Oracle 等应用程序数据库很快成为可扩展性瓶颈。 发生这种情况是因为尽管您可以通过购买更强大的数据库服务器来扩展数据库层,但您无法通过向数据库层添加更多服务器来进行扩展。 例如,在应用层看到 10 到 20 台服务器是很常见的,但在数据库层不能这样做。

 

ASP.NET 会话状态存储

此外,ASP.NET 会话状态需要存储在某处。 而且,Microsoft 提供的现成选项包括 InProc、StateServer 和 SqlServer。 不幸的是,所有三个选项都存在主要的性能和可伸缩性问题。 InProc 和 StateServer 强制您使用粘性会话并在创建会话的同一服务器上发送所有 HTTP 请求。 如果将 StateServer 配置为独立服务器以避免粘滞会话,那么 StateServer 将成为单点故障,其性能也将成为主要问题。 而且,SqlServer 将 ASP.NET 会话作为 BLOB 存储在 SQL Server 数据库中。 而且,这种方法存在严重的性能和可扩展性问题。

 

ASP.NET View State

ASP.NET View State 是一个编码的隐藏字符串(通常大小为 100 KB),作为 HTTP 响应的一部分发送到用户的浏览器。 浏览器不对其做任何事情,并在 HTTP Post-Back 的情况下将其返回给 Web 服务器。 这会减慢 ASP.NET 页面的响应速度,给 Web 服务器网卡带来更多负担,并且还会消耗大量额外的带宽。 而且,如您所知,带宽并不便宜。

 

静态输出的 ASP.NET 页面执行

最后,ASP.NET framework 对用户请求执行 ASP.NET 页面,即使页面输出与上一个请求相比没有变化。 这在低事务环境中可能没问题。 但是在一个高事务环境中,您已经将所有资源发挥到了极限,这种额外的执行成本可能会变得非常高,并且会成为可扩展性瓶颈。

俗话说“一条链的强弱,取决于它最薄弱的一环”。 因此,只要在 ASP.NET 应用程序环境中的任何地方存在可伸缩性瓶颈,整个应用程序就会变慢甚至停止。

而且,具有讽刺意味的是,当您进行最高级别的业务活动时,这会发生在峰值负载下。 因此,任何减速或停机时间的影响都会对您的业务造成更大的损失。

ASP.NET 面临可扩展性瓶颈
图 1:ASP.NET 面临的可扩展性瓶颈
 

NoSQL Database 不是答案

NoSQL database 移动是由于关系数据库中的可伸缩性问题而开始的。 NoSQL database 将数据分区到多个服务器上,并允许您像应用程序层一样横向扩展。

但 NoSQL database 要求您放弃关系数据库并将数据放入 NoSQL database. 而且,这说起来容易做起来难,原因有很多,实际上在很多情况下是不可能的。

NoSQL databases 不具备与关系数据库相同的数据管理和搜索能力,并且希望您以与关系数据库完全不同的方式存储数据。 此外,围绕关系数据库的生态系统对于大多数企业来说太强大而无法放弃。

其结果是, NoSQL databases 只有在处理非结构化数据时才有用。 而且,大多数 ASP.NET 应用程序都在处理主要是结构化并适合关系数据库的业务数据。 因此,这些数据无法移动到 NoSQL database 容易。

而且,即使是那些最终使用 NoSQL database 这样做是为了他们总数据的一小部分可以被认为是非结构化的。 而且,他们使用 NoSQL database 以及他们现有的关系数据库。

因此,大多数时候您需要使用关系数据库并为您的可伸缩性问题找到另一种解决方案。 幸运的是,有一个非常可行的解决方案,我将在下面讨论。

 

解决方案:内存中分布式缓存

上述所有问题的解决方案是在您的应用程序部署中使用内存中分布式缓存,例如 NCache. NCache 是一个 开源分布式缓存 .NET 非常快速且可线性扩展。 将其视为也是分布式的内存对象存储。 在内存中使其速度非常快,而分布式使其具有线性可扩展性。

内存中分布式缓存的好处 NCache 是它不会要求您停止使用现有的关系数据库。 您可以在关系数据库之上使用缓存,因为缓存消除了所有关系数据库的可伸缩性瓶颈。

NCache 提供线性可扩展性
图2: NCache 提供线性可扩展性

那么内存中的分布式缓存如何 NCache 比关系数据库更具可扩展性? 好, NCache 形成一个缓存服务器集群,并将所有这些服务器的 CPU、内存和其他资源汇集在一起​​。

而且, NCache 允许您在运行时添加缓存服务器,而无需停止缓存或应用程序。 而且,这使您能够线性扩展您的应用程序并处理极端事务负载。 这是关系数据库无法做到的。

内存中分布式缓存,如 NCache 通过允许您在运行时将缓存服务器添加到缓存集群(缓存层)来线性扩展。 但是,您应该从解决方案中获得什么类型的性能数据,例如 NCache.

以下是 NCache 性能数字。 你可以看到完整的 NCache 绩效基准 点击此处。

性能数字 NCache
图 3:性能数据 NCache

如您所见,内存中的分布式缓存 NCache 为读取和写入提供亚毫秒级的性能,并允许您通过简单地添加更多缓存服务器来线性扩展事务容量。

现在让我们看看内存中的分布式缓存是怎样的 NCache 解决了上面提到的各种可扩展性瓶颈。

 

应用程序数据缓存

应用程序数据缓存使您能够消除数据库瓶颈。 内存中分布式缓存,如 NCache 允许您缓存应用程序数据并减少那些昂贵的数据库访问。 您可以预期将 70-90% 的数据库流量转移到内存中分布式缓存。 这减少了数据库的压力,并允许它更快地执行并处理更大的事务负载而不会减慢速度。

Customer Load(string customerId)
{
 // Key format: Customer:PK:1000
 string key = "Customers:CustomerID:" + customerId;
 Customer cust = (Customer) _cache[key];
 if (cust == null)
 { // Item not in cache so load from db
LoadCustomerFromDb(cust);
// Add item to cache for future reference
_cache.Insert(key, cust);
 }
 return cust;
}

图 4:使用内存中分布式缓存进行应用数据缓存

应用程序数据缓存意味着您缓存从关系数据库获得的任何应用程序数据。 这通常采用域对象(也称为实体)的形式。 这是一个关于如何使用分布式缓存的示例 NCache 用于应用程序数据缓存。

 

ASP.NET 会话状态缓存

内存中分布式缓存,如 NCache 也是存储 ASP.NET 会话状态的好地方。 它比上面提到的所有三个选项(InProc、StateServer 和 SqlServer)都快得多且可扩展性更高。 NCache 更快,因为它在内存中并提供键值接口,其值是 ASP.NET 会话状态所在的“对象”。 而且,它是可扩展的,因为它是一个分布式缓存。

而且, NCache 还通过其丰富的缓存拓扑智能地复制会话,因此即使缓存服务器出现故障,也不会丢失会话数据。 这种复制是必要的,因为 NCache 提供内存存储,而内存是违反存储的。

NCache 还加快了 ASP.NET 会话状态对象的序列化,这是在进程外存储之前所需的。 NCache 通过使用比常规 .NET 序列化快 10 倍的动态压缩序列化功能来实现这一点。 您无需更改任何代码即可使用此功能。

你可以插 NCache 会话状态提供者 (SSP) 模块添加到您的 ASP.NET 应用程序,方法是在您的 web.config 文件中进行一些更改,如下所示。

<system.web>
 ...
 <assemblies>
<add assembly="Alachisoft.NCache.SessionStoreProvider, Version=4.3.0.0,
 Culture=neutral, PublicKeyToken=CFF5926ED6A53769" />
 </assemblies>
 <sessionState cookieless="false" regenerateExpiredSessionId="true"
 mode="Custom" customProvider="NCacheSessionProvider"
timeout="20">
<providers>
 <add name="NCacheSessionProvider"
 type="Alachisoft.NCache.Web.SessionState.NSessionStoreProvider"
 useInProc="false" cacheName="myDistributedCache"
 enableLogs="false“ writeExceptionsToEventLog="false” />
</providers>
 </sessionState>
 ...
</system.web>

图 5:插件 NCache 作为 Web.Config 中的 ASP.NET 会话状态提供程序 (SSP)

 

ASP.NET View State 高速缓存

我已经描述了如何 ASP.NET View State 是由 Web 服务器发送到用户浏览器的编码字符串,然后在 HTTP Post Back 的情况下将其返回给 Web 服务器。 但是,在 InMemory 分布式缓存的帮助下 NCache, 你可以缓存这个 ASP.NET View State 在服务器上,并且只发送一个小的唯一 ID 来代替它。

NCache 实施了一项 ASP.NET View State 缓存模块 通过自定义 ASP.NET Page Adapter 和 ASP.NET PageStatePersister。 这边走, NCache 拦截 HTTP 请求和响应。 在响应时间, NCache 删除编码字符串的“值”部分并将其缓存,而是在此“值”中放置一个唯一标识符(缓存键)。

然后,当下一个 HTTP 请求到来时,它会再次拦截它,并用它之前放入缓存中的实际编码字符串替换唯一标识符。 这样,ASP.NET 页面就不会注意到任何不同之处,而是像以前一样使用包含视图状态的编码字符串。

下面的示例显示了一个 ASP.NET View State 没有缓存的编码字符串,以及合并缓存时会发生什么。

//ASP.NET View State without Caching
<input id="__VIEWSTATE"
 	type="hidden"
 	name="__VIEWSTATE"
 	value="/wEPDwUJNzg0MDMxMDA1D2QWAmYPZBYCZg9kFgQCAQ9kFgICBQ9kFgJmD2QWAgIBD
		xYCHhNQcm2aW91c0NvbnRyb2xNb2RlCymIAU1pY3Jvc29mdC5TaGFyZVBvaW50Lld
		lYkNvbnRyb2xzLlNQQ29udHJbE1vZDA1XzRlMjJfODM3Y19kOWQ1ZTc2YmY1M2IPD
		xYCHhNQcm2aW91c0NvbnRyb2xNb2RlCymIAU1pY3Jvc29mdC5TaGFyZVBvaW50Lld
		lYkNvbnRyb2xzLlNQQ29udHJbE1vZDA1XzRlMjJfODM3Y19kOWQ1ZTc2YmY1M2IPD
		... ==" />
			
//ASP.NET View State with Caching
<input id="__VIEWSTATE"
	type="hidden"
	name="__VIEWSTATE"
	value="vs:cf8c8d3927ad4c1a84da7f891bb89185" />

图6: ASP.NET View State 带或不带缓存的编码字符串

 

用于静态页面输出的 ASP.NET 输出缓存

ASP.NET 提供了一个 ASP.NET 输出缓存框架来解决页面执行过多的问题,即使页面输出没有改变。 此框架允许您缓存整个页面或页面某些部分的输出,以便下次调用此页面时,将不会执行它,而是显示其缓存的输出。 显示已经缓存的输出比再次执行整个页面要快得多。

<caching>
   <outputCache defaultProvider ="NOutputCacheProvider">
     <providers>
       <add name="NOutputCacheProvider"
         type="Alachisoft.NCache.OutputCacheProvider.NOutputCacheProvider,
               Alachisoft.NCache.OutputCacheProvider, Version=x.x.x.x,
               Culture=neutral, PublicKeyToken=1448e8d1123e9096"
				  
               cacheName="myDistributedCache" exceptionsEnabled="false"
               writeExceptionsToEventLog="false" enableLogs="true” />"
     </providers>
   </outputCache>
</caching>

图 7:设置 ASP.NET 输出缓存提供程序 NCache 在 Web.Config 中

NCache 已为 .NET 4.0 或更高版本实现了 ASP.NET 输出缓存提供程序。 这允许您插入 NCache 无缝且无需任何编程工作。 的情况下 NCache,此提供程序适用于跨多个服务器的内存中分布式缓存。 因此,如果您的 ASP.NET 应用程序在负载平衡的 Web 场中运行,则从服务器 1 缓存的页面输出将立即可供 Web 场中的所有其他服务器使用。 下面是如何插入 NCache 作为 ASP.NET 输出缓存提供程序。

 

分布式缓存架构

高流量 ASP.NET 应用程序无法承受停机,尤其是在高峰时段。 对于这些类型的应用程序,良好的内存中分布式缓存具有三个重要的架构目标 NCache 提供。 他们是:

  1. 高可用性
  2. 线性可扩展性
  3. 数据复制和可靠性

让我解释下面的每个区域。

 

高可用性

最重要的架构目标之一 NCache 就是实现高可用和缓存弹性。 而且,它通过以下架构功能做到这一点:

  1. 自愈对等缓存集群: NCache 通过 TCP/IP 构建缓存服务器集群。 这个集群有一个 对等网络 架构,这意味着没有主/从节点,也没有多数规则集群。 相反,每个节点都是平等的对等点。 这使 NCache 处理任何节点可能出现故障的情况,并且集群会自动调整并继续运行,并且您的应用程序不会中断。
  2. 动态配置: 这意味着您不必在配置文件中进行硬编码。 这是因为 NCache 在运行时将大量配置信息传播到缓存客户端(即您的应用程序)。 因此,当您在运行时添加缓存服务器时,集群成员资格会自动更新,并且集群会通知所有缓存客户端有关此更改的信息。 还有许多其他配置更改以相同的方式处理。
  3. 连接故障转移支持: 这是一种能力,当缓存服务器出现故障时,缓存集群和缓存客户端能够继续工作而不会出现任何中断。 在缓存集群的情况下,我已经讨论了解决这种情况的自我修复质量。 在缓存客户端的情况下,这意味着缓存客户端通过与集群中的其他缓存服务器交互来继续工作。
 

具有线性可扩展性的数据复制

由于内存中的分布式缓存像 NCache 使用内存作为存储,它必须提供数据复制以保证可靠性。 但是,与此同时,它不能在线性可扩展性上妥协,因为这是使用分布式缓存的最重要原因 NCache.

这里有一些 NCache 缓存拓扑 这有助于实现这两个目标。

  1. 分区缓存: NCache 根据缓存服务器的数量对缓存进行分区,并为每个缓存服务器分配一个分区。 当您在运行时添加或删除缓存服务器时,它还会调整分区数。 分区是确保线性可扩展性的主要方式,因为当您添加更多服务器时,这种缓存拓扑会增加整体存储大小和 CPU 处理能力。
  2. 分区副本缓存: 除了分区, NCache 还为每个分区提供副本。 这些副本驻留在与分区本身不同的缓存服务器上,以确保如果缓存服务器与其分区一起出现故障,则副本立即变得可用。 这样,提供了数据可靠性。 通过在另一个缓存服务器上只复制每个分区一次, NCache 在不影响线性可扩展性的情况下实现数据可靠性。
  3. 客户端缓存(近缓存): 另一个非常重要的能力 NCache 是客户端缓存。 这是一个本地缓存,位于缓存客户端计算机(即您的 Web 或应用服务器)上,甚至可以是 InProc(意味着它驻留在您的应用程序进程中)。 这本质上是缓存之上的缓存,并提供了极大的性能提升以及增加的可扩展性 NCache 本身,因为即使到缓存层的流量也会下降。
分区副本缓存拓扑 NCache
图 8:分区副本缓存拓扑 NCache

如您所见,Partitioned-Replica Cache 在每台缓存服务器上放置一个分区和一个副本。 并且,出于可靠性目的,它确保副本始终位于不同的缓存服务器上。

 

结论

我试图强调当今 ASP.NET 应用程序面临的最常见的性能和可伸缩性瓶颈,并向您展示如何通过使用内存中分布式缓存来克服这些瓶颈,例如 NCache. NCache 是用于 .NET 和 Java 应用程序的开源分布式缓存。 因此,您可以不受任何限制地使用它。 您可以了解更多关于 NCache 在以下链接

接下来做什么?

联系我们

联系电话
©版权所有 Alachisoft 2002 - 版权所有。 NCache 是 Diyatech Corp. 的注册商标。