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 应用程序面临着主要的可扩展性瓶颈。 这些瓶颈发生在以下四个不同的领域:
让我在下面更详细地解释每一个。
随着事务负载的增加,SQL Server 或 Oracle 等应用程序数据库很快成为可扩展性瓶颈。 发生这种情况是因为尽管您可以通过购买更强大的数据库服务器来扩展数据库层,但您无法通过向数据库层添加更多服务器来进行扩展。 例如,在应用层看到 10 到 20 台服务器是很常见的,但在数据库层不能这样做。
此外,ASP.NET 会话状态需要存储在某处。 而且,Microsoft 提供的现成选项包括 InProc、StateServer 和 SqlServer。 不幸的是,所有三个选项都存在主要的性能和可伸缩性问题。 InProc 和 StateServer 强制您使用粘性会话并在创建会话的同一服务器上发送所有 HTTP 请求。 如果将 StateServer 配置为独立服务器以避免粘滞会话,那么 StateServer 将成为单点故障,其性能也将成为主要问题。 而且,SqlServer 将 ASP.NET 会话作为 BLOB 存储在 SQL Server 数据库中。 而且,这种方法存在严重的性能和可扩展性问题。
ASP.NET View State 是一个编码的隐藏字符串(通常大小为 100 KB),作为 HTTP 响应的一部分发送到用户的浏览器。 浏览器不对其做任何事情,并在 HTTP Post-Back 的情况下将其返回给 Web 服务器。 这会减慢 ASP.NET 页面的响应速度,给 Web 服务器网卡带来更多负担,并且还会消耗大量额外的带宽。 而且,如您所知,带宽并不便宜。
最后,ASP.NET framework 对用户请求执行 ASP.NET 页面,即使页面输出与上一个请求相比没有变化。 这在低事务环境中可能没问题。 但是在一个高事务环境中,您已经将所有资源发挥到了极限,这种额外的执行成本可能会变得非常高,并且会成为可扩展性瓶颈。
俗话说“一条链的强弱,取决于它最薄弱的一环”。 因此,只要在 ASP.NET 应用程序环境中的任何地方存在可伸缩性瓶颈,整个应用程序就会变慢甚至停止。
而且,具有讽刺意味的是,当您进行最高级别的业务活动时,这会发生在峰值负载下。 因此,任何减速或停机时间的影响都会对您的业务造成更大的损失。
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 比关系数据库更具可扩展性? 好, NCache 形成一个缓存服务器集群,并将所有这些服务器的 CPU、内存和其他资源汇集在一起。
而且, NCache 允许您在运行时添加缓存服务器,而无需停止缓存或应用程序。 而且,这使您能够线性扩展您的应用程序并处理极端事务负载。 这是关系数据库无法做到的。
内存中分布式缓存,如 NCache 通过允许您在运行时将缓存服务器添加到缓存集群(缓存层)来线性扩展。 但是,您应该从解决方案中获得什么类型的性能数据,例如 NCache.
以下是 NCache 性能数字。 你可以看到完整的 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 用于应用程序数据缓存。
内存中分布式缓存,如 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 是由 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 输出缓存框架来解决页面执行过多的问题,即使页面输出没有改变。 此框架允许您缓存整个页面或页面某些部分的输出,以便下次调用此页面时,将不会执行它,而是显示其缓存的输出。 显示已经缓存的输出比再次执行整个页面要快得多。
<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 提供。 他们是:
让我解释下面的每个区域。
最重要的架构目标之一 NCache 就是实现高可用和缓存弹性。 而且,它通过以下架构功能做到这一点:
由于内存中的分布式缓存像 NCache 使用内存作为存储,它必须提供数据复制以保证可靠性。 但是,与此同时,它不能在线性可扩展性上妥协,因为这是使用分布式缓存的最重要原因 NCache.
这里有一些 NCache 缓存拓扑 这有助于实现这两个目标。
如您所见,Partitioned-Replica Cache 在每台缓存服务器上放置一个分区和一个副本。 并且,出于可靠性目的,它确保副本始终位于不同的缓存服务器上。
我试图强调当今 ASP.NET 应用程序面临的最常见的性能和可伸缩性瓶颈,并向您展示如何通过使用内存中分布式缓存来克服这些瓶颈,例如 NCache. NCache 是用于 .NET 和 Java 应用程序的开源分布式缓存。 因此,您可以不受任何限制地使用它。 您可以了解更多关于 NCache 在以下链接