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 视图状态是一个经过编码的隐藏字符串(通常大小达数百 KB),它作为 HTTP 响应的一部分发送到用户的浏览器。浏览器不会对其进行任何处理,而是在 HTTP 回发的情况下将其返回给 Web 服务器。这会降低 ASP.NET 页面的响应速度,增加 Web 服务器网卡的负担,并且还会消耗大量额外的带宽。众所周知,带宽成本并不低廉。
最后,ASP.NET 框架会根据用户请求执行 ASP.NET 页面,即使页面输出与上一个请求没有变化。这在低事务环境中可能没问题。但在高事务环境中,您已经将所有资源扩展到极限,这种额外的执行可能会变得非常昂贵,并成为可扩展性的瓶颈。
俗话说“一条链的强弱,取决于它最薄弱的一环”。 因此,只要在 ASP.NET 应用程序环境中的任何地方存在可伸缩性瓶颈,整个应用程序就会变慢甚至停止。
而且,具有讽刺意味的是,当您进行最高级别的业务活动时,这会发生在峰值负载下。 因此,任何减速或停机时间的影响都会对您的业务造成更大的损失。
NoSQL数据库的兴起源于关系型数据库的可扩展性问题。NoSQL数据库将数据分区到多个服务器上,并允许像应用层一样进行横向扩展。
但是,NoSQL 数据库要求你放弃关系型数据库,并将数据迁移到 NoSQL 数据库中。这说起来容易做起来难,原因有很多,实际上在很多情况下根本无法实现。
NoSQL 数据库不具备与关系型数据库相同的数据管理和搜索能力,并且要求您以与关系型数据库完全不同的方式存储数据。此外,对于大多数企业而言,关系型数据库的生态系统过于强大,难以放弃。
因此,NoSQL 数据库仅适用于处理非结构化数据。而大多数 ASP.NET 应用程序处理的业务数据主要是结构化的,更适合关系型数据库。因此,这些数据无法轻易迁移到 NoSQL 数据库中。
而且,即使最终使用 NoSQL 数据库的用户,也只是将其用于一小部分非结构化数据。此外,他们还会将 NoSQL 数据库与现有的关系型数据库结合使用。
因此,大多数时候您需要使用关系数据库并为您的可伸缩性问题找到另一种解决方案。 幸运的是,有一个非常可行的解决方案,我将在下面讨论。
上述所有问题的解决方案是在您的应用程序部署中使用内存中分布式缓存,例如 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 视图状态是一个编码字符串,由 Web 服务器发送到用户的浏览器,然后浏览器在 HTTP 回发的情况下将其返回给 Web 服务器。但是,借助内存分布式缓存,例如 NCache您可以将此 ASP.NET 视图状态缓存在服务器上,并仅发送一个小的唯一 ID 来代替它。
NCache 实施了一项 ASP.NET 视图状态缓存模块 通过自定义 ASP.NET Page Adapter 和 ASP.NET PageStatePersister。 这边走, NCache 拦截 HTTP 请求和响应。 在响应时间, NCache 删除编码字符串的“值”部分并将其缓存,而是在此“值”中放置一个唯一标识符(缓存键)。
然后,当下一个 HTTP 请求到来时,它会再次拦截它,并用它之前放入缓存中的实际编码字符串替换唯一标识符。 这样,ASP.NET 页面就不会注意到任何不同之处,而是像以前一样使用包含视图状态的编码字符串。
下面的示例显示了未缓存的 ASP.NET 视图状态编码字符串,以及加入缓存后发生的情况。
//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 视图状态编码字符串(带缓存或不带缓存)
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 在以下链接