.NET Core e ASP.NET Core estão ganhando popularidade devido à sua simplicidade de design, sendo leve, de código aberto e capaz de rodar em Windows e Linux. Como resultado, muitos aplicativos existentes também estão migrando para .NET Core do .NET Framework. Quase todas as novas aplicações estão sendo desenvolvidas em .NET Core.
Muitos desses .NET Core os aplicativos são de alto tráfego por natureza, atendendo a milhões de usuários e transações. Como resultado, esses aplicativos têm um enorme impacto em seus negócios e, portanto, são muito importantes.
A .NET Core aplicativos que geralmente precisam de escalabilidade são aplicativos de servidor que devem processar muitas transações muito rapidamente com tempos de resposta muito rápidos. Muitos desses aplicativos são voltados para o cliente, o que significa que estão processando solicitações do cliente. Se eles não realizarem as solicitações dos clientes rapidamente, o custo para a empresa será alto em termos de perda de receita e perda de clientes satisfeitos.
Agora sobre o .NET Core aplicativos requer escalabilidade:
Curiosamente, todos os aplicativos mencionados acima têm arquiteturas de nível de aplicativo muito escaláveis. Cada um deles permite dimensionar linearmente à medida que a carga da transação aumenta, adicionando mais servidores, VMs ou instâncias de contêiner junto com um balanceador de carga.
Mas, apesar de uma arquitetura muito escalável na camada de aplicação, .NET Core os aplicativos de servidor hoje estão enfrentando grandes gargalos de escalabilidade. Esses gargalos estão ocorrendo em diferentes áreas, como:
O maior gargalo para todo o tráfego intenso .NET Core aplicativos é seu banco de dados de aplicativos. A maioria dos aplicativos hoje ainda usa um banco de dados relacional como SQL Server ou Oracle. Esses bancos de dados rapidamente se tornam gargalos de escalabilidade à medida que você aumenta as cargas de transação nesses aplicativos. Isso é verdade se você estiver usando o SQL Server em uma VM ou no Banco de Dados SQL do Azure.
Isso acontece porque um banco de dados relacional não pode ser particionado logicamente como um banco de dados relacional NoSQL database e, em vez disso, permanece em um local físico; mesmo alguns particionamentos em nível de coluna não são nada parecidos com um verdadeiro NoSQL partição de estilo. Portanto, você não pode aumentar a capacidade de transação da camada de banco de dados adicionando mais servidores de banco de dados como faria com um NoSQL database.
Por exemplo, enquanto sua camada de aplicativo pode facilmente ter 10, 20, 30 ou mais servidores de aplicativos à medida que sua carga de transações aumenta, sua camada de banco de dados não pode crescer da mesma forma.
Por tudo isso, seu banco de dados relacional se torna um gargalo de desempenho para quaisquer dados armazenados nele (dados do aplicativo ou outros dados).
O SQL Server introduziu otimizações na memória para aumentar o número de transações por segundo. A Oracle também forneceu sua própria versão de tabelas In-Memory.
Embora as otimizações na memória tragam melhorias de desempenho, elas não abordam a questão central da escalabilidade linear. As tabelas na memória geralmente são usadas para dados somente leitura e, para dimensionar uma capacidade de transação somente leitura, você precisa adicionar mais instâncias do SQL Server em máquinas mais avançadas.
As tabelas na memória também têm limitações quanto ao tamanho dos dados; você não pode colocar tabelas grandes na memória, pois a tabela inteira deve ser colocada na memória. E sua replicação para outras instâncias do SQL Server só pode ser feita para outras tabelas In-Memory e não para um banco de dados adequado.
Em resumo, essas Otimizações In-Memory em bancos de dados SQL Server e Oracle não são capazes de resolver totalmente suas .NET Core necessidades de escalabilidade do aplicativo.
Uma das razões NoSQL databases se tornaram populares porque fornecem particionamento adequado de dados com base em algoritmos baseados em Hash e outros. Isso resolve muitos dos problemas de escalabilidade para capacidade de transação que bancos de dados relacionais como SQL Server e Oracle enfrentam.
Mas, há razões NoSQL databases não são a solução ideal para esses gargalos de banco de dados.
A solução para todos os problemas mencionados acima é usar um Cache Distribuído In-Memory como NCache na sua .NET Core implantação do aplicativo. NCache é um cache distribuído de código aberto para .NET e .NET Core que é extremamente rápido e linearmente escalável. Pense nisso como um armazenamento de dados na memória que também é distribuído. Estar na memória o torna extremamente rápido e ser distribuído o torna linearmente escalável.
NCache é linearmente escalável porque cria um cluster TCP de servidores de cache de baixo custo (mesma configuração dos servidores de aplicativos Web, mas com mais memória) e agrupa os recursos de memória e CPU de todos esses servidores em uma capacidade lógica. NCache em seguida, permite adicionar servidores de cache a esse cluster em tempo de execução à medida que a carga da transação aumenta. E desde NCache é tudo na memória, é super rápido e oferece tempos de resposta abaixo de milissegundos que você não pode esperar de seus bancos de dados relacionais ou mesmo NoSQL databases.
Além de fornecer escalabilidade linear, um cache distribuído como NCache replica os dados de forma inteligente para que seu desempenho não seja comprometido e, ao mesmo tempo, alcance a confiabilidade dos dados caso algum servidor de cache fique inativo.
NCache permite dimensionar sua .NET Core aplicações através do seguinte:
O gargalo mais importante enfrentado .NET Core aplicativos é o "Banco de Dados de Aplicativos". A coisa legal sobre NCache é que ao contrário NoSQL databases, NCache não pede que você pare de usar seu banco de dados relacional existente. Você pode continuar usando SQL Server, Banco de Dados SQL do Azure, Oracle, etc. como seu banco de dados e ainda obter escalabilidade linear usando NCache no topo de seu banco de dados relacional. Isto é porque NCache remove todos os gargalos de escalabilidade do banco de dados relacional porque, diferentemente do seu banco de dados, NCache é realmente linearmente escalável.
O Application Data Caching permite que você remova os gargalos do banco de dados. NCache permite armazenar em cache os dados do aplicativo e reduzir essas viagens caras ao banco de dados. Você pode esperar desviar 80-90% do tráfego de banco de dados para NCache. Isso reduz a pressão em seu banco de dados e permite que ele tenha um desempenho mais rápido e lide com cargas de transações maiores sem diminuir a velocidade.
O cache de dados de aplicativos significa que você armazena em cache quaisquer dados de aplicativos obtidos de seu banco de dados relacional. Isso geralmente está na forma de objetos de domínio (também chamados de entidades). Aqui está um exemplo de como usar um cache distribuído como NCache para cache de dados do aplicativo.
Customer Load(string custId)
{
ICache cache = CacheManager.GetCache("myCache");
string key = "Customer:CustomerID:" + custId;
Customer cust = cache.Get<Customer>(key);
if (cust == null) {
// Item not in cache so load from db
LoadCustomerFromDb(cust);
// Add item to cache for future reference
cache.Add(key, cust);
}
return cust;
}
Figura 3: como usar o cache distribuído na memória para o cache de dados do aplicativo
Outro possível gargalo é se você armazenar seu ASP.NET Core Sessões no SQL Server ou MemoryCache autônomo. Ambas as opções têm grandes limitações sobre desempenho e escalabilidade. O armazenamento do SQL Server não é bom para ASP.NET Core sessões e rapidamente se torna um gargalo, assim como para dados de aplicativos.
NCache é um ótimo lugar para armazenar seu ASP.NET Core Sessions porque é muito mais rápido e escalável do que outras opções de armazenamento. NCache é mais rápido porque está na memória e fornece uma interface de valor-chave com o valor sendo um "objeto" que um ASP.NET Core Sessão é. E é escalável porque é um cache distribuído.
E, NCache também replica de forma inteligente ASP.NET Core sessões por meio de suas ricas topologias de cache, portanto, mesmo que um servidor de cache fique inativo, não haverá perda de dados de sessão. Essa replicação é necessária porque NCache fornece um armazenamento na memória e a memória viola o armazenamento.
NCache também acelera sua serialização do ASP.NET Core Sessão que é necessária antes que possa ser armazenada fora do processo. NCache faz isso usando seu recurso Dynamic Compact Serialization que é 10 vezes mais rápido que o .NET normal e .NET Core serialização. Você pode usar esse recurso sem fazer nenhuma alteração no código.
Você pode usar NCache como seu ASP.NET Core Armazenamento de sessão de duas maneiras.
Abaixo está um exemplo de como você pode configurar seu ASP.NET Core aplicativo para usar NCache Provedor de sessão:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Specify NCache as the session provider
services.AddNCacheSession(Configuration.GetSection("NCacheSettings"));
...
}
public void Configure(IApplicationBuilder app, ...)
{
// select NCache session provider for ASP.NET Core
app.UseNCacheSession();
...
}
}
Figura 4: Plug-in NCache como ASP.NET Core Sessões
ASP.NET Core aplicativos, que de outra forma têm um conteúdo bastante dinâmico, enfrentam situações em que, para algumas de suas páginas, o conteúdo ou a resposta não mudam em várias solicitações. Mas essas páginas ainda precisam ser executadas toda vez que a solicitação chegar. E isso sobrecarrega desnecessariamente os recursos do servidor web e também todas as camadas desse aplicativo. Como resultado, isso também aumenta os gargalos de desempenho e limita a escalabilidade do aplicativo.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Turn on ASP.NET Core Response Cache with IDistributedCache
services.AddResponseCaching();
// Select NCache as IDistributedCache provider
services.AddNCacheDistributedCache(Configuration.GetSection("NCacheSettings"));
...
}
}
Figura 5: Plug-in NCache como ASP.NET Core Middleware de cache de resposta
Para resolver a sobrecarga da execução de página repetitiva em que a resposta da página não muda, ASP.NET Core forneceu um mecanismo de cache de resposta de página chamado ASP.NET Response Cache Middleware. E, NCache implementou a interface IDistributedCache em ASP.NET Core devido ao qual você pode facilmente plug-in NCache como seu ASP.NET Core Middleware de Cache de Resposta.
Então você pode usar NCache armazenar em cache ASP.NET Core respostas de página por um determinado período de tempo para que, na próxima vez que a mesma página for chamada com os mesmos parâmetros, essa resposta em cache possa ser reajustada em vez de executar a página inteira novamente. Abaixo está o exemplo de código de como configurar NCache como seu ASP.NET Core Middleware de Cache de Resposta.
Se o seu ASP.NET Core aplicativo é um aplicativo da Web em tempo real, então é mais provável que use ASP.NET Core SignalR por fornecer esse comportamento em tempo real. Os aplicativos da Web em tempo real fornecem atualizações de alta frequência do servidor para o cliente. Os exemplos de tais aplicativos incluem jogos, leilão, votação, redes sociais, etc.
Se o seu ASP.NET Core aplicativo está sendo executado em um ambiente multi-servidor com balanceamento de carga, então ele tem que usar um ASP.NET Core SignalR Backplane provedor para compartilhar eventos em vários servidores da web. E, este Backplane tem que ser escalável. Caso contrário, seu ASP.NET Core O aplicativo SignalR começa a enfrentar gargalos de desempenho.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Specify NCache as the ASP.NET Core SignalR Backplane
services.AddSignalR().AddNCache(ncacheOptions =>
{ ncacheOptions.CacheName = "myPartitionedCache"; });
...
}
public void Configure(IApplicationBuilder app, ...)
{
// Use SignalR in ASP.NET Core
app.UseSignalR(config => { config.MapHub<MessageHub>("/messages"); });
...
}
}
Figura 6: Plug-in NCache como ASP.NET Core SignalR Backplane provedor do cliente
NCache implementou um ASP.NET Core SignalR Backplane provedor. NCacheASP do.NET Core SignalR Backplane provedor usa recursos do Pub/Sub Messaging de NCache que são super-rápidos por serem totalmente in-memory. Isso permite que seu ASP.NET Core Aplicação SignalR para acelerar a propagação de eventos SignalR entre todos os servidores web e como resultado para os clientes.
E isso torna seu aplicativo da Web em tempo real mais responsivo ao fornecer essas atualizações frequentes aos clientes. E você pode continuar aumentando o número de clientes e também adicionar mais servidores da Web sem temer gargalos de desempenho.
Se seu .NET Core Se o aplicativo precisar usar o Pub/Sub Messaging ou Events, provavelmente está usando uma plataforma de mensagens Pub/Sub que não é totalmente in-memory e, em vez disso, armazena todas as mensagens no disco. Como resultado, isso pode facilmente se tornar um gargalo de desempenho se seu aplicativo for realmente de alta transação.
NCache também fornece um Pub/Sub Messaging que é super-rápido porque é totalmente in-memory. E, ele replica todas as mensagens para outro NCache servidor para garantir que não haja perda de dados no caso de qualquer servidor ficar inativo.
Portanto, se o seu .NET Core usos do aplicativo NCache como sua plataforma Pub/Sub Messaging, ele terá desempenho super-rápido e escalabilidade linear porque NCache em si é linearmente escalável.
Veja abaixo um exemplo de como você pode usar o Pub/Sub Messaging fornecido por NCache na sua .NET Core aplicação.
private void PublishMessage (string topicName)
{
ITopic topic = _cache.MessagingService.GetTopic(topicName);
Order order = Order.GenerateOrder<Order>();
// Publish message containing "order" with expiry
Message message = new Message(order, new TimeSpan(0, 0, 15));
topic.Publish(message, DeliveryOption.All, true);
}
private ITopicSubscription SubscribeMessage (string topicName)
{
ITopic topic = _cache.MessagingService.GetTopic(topicName);
// Subscribes to the topic. Message delivered to MessageReceivedCallback
return topic.CreateSubscription(MessageReceivedCallback);
}
static void MessageReceivedCallback(object sender, MessageEventArgs args) { ... }
Figura 7: como usar as mensagens do Pub/Sub em .NET Core Apps
O maior gargalo de escalabilidade que seu .NET Core aplicativo deve remover é do banco de dados do aplicativo. Nessa área, seus aplicativos podem alcançar alto desempenho e escalabilidade linear por meio do armazenamento em cache de dados de aplicativos. A razão para isso é simples. A maioria .NET Core os aplicativos lidam com muitos dados de ida e volta do banco de dados.
Quando se trata de cache de dados de aplicativos, o maior medo que as pessoas têm é que o cache fique obsoleto, o que significa que contém uma versão mais antiga dos dados que já foram alterados no banco de dados por outro usuário ou outro aplicativo.
Esse medo de um cache se tornar obsoleto é tão forte que a maioria das pessoas armazena apenas dados somente leitura ou estáticos (dados de referência). Mas, esses dados somente leitura são apenas 20% do total de dados na forma de tabelas de pesquisa e outros dados de referência. A maior parte dos dados no banco de dados é transacional, incluindo clientes, contas, atividades, etc. E, se você não armazenar em cache esses dados transacionais, não se beneficiará totalmente do armazenamento em cache.
Portanto, o benefício real do armazenamento em cache vem se você puder armazenar em cache todos os tipos de dados sem o medo de que o armazenamento em cache fique obsoleto. NCache fornece uma série de recursos para resolver essa preocupação.
A maneira mais eficaz de manter seu cache atualizado é mantê-lo sempre sincronizado com seu banco de dados. NCache permite fazer isso com uma variedade de bancos de dados da seguinte forma:
Ao sincronizar seu cache com o SQL Server, você pergunta NCache para se registrar como cliente do SQL Server e, em seguida, emitir uma chamada SqlDependency junto com um conjunto de dados baseado em consulta SQL. Então, quando o SQL Server vê alguma alteração neste conjunto de dados, ele notifica NCache sobre isso
private static void CreateSqlDependency (Product product)
{
string connectionString = "Data Source=localhost;Database=northwind;...";
// SQL stmt on which the SQL Dependency is created in SQL Server
string sqlStmt = "SELECT ProductID, ProductName, QuantityPerUnit, UnitPrice " +
"FROM dbo.PRODUCTS WHERE ProductID = " + product.Id;
CacheDependency sqlDependency = new SqlCacheDependency(connectionString, sqlStmt);
CacheItem cacheItem = new CacheItem(product) { Dependency = sqlDependency };
string key = "Product:ProductId:" + product.Id; ;
cache.Add(key, cacheItem);
}
Figura 8: Usando SqlDependency para sincronizar o cache com o SQL Server
Em seguida, NCache remove este item do cache para que na próxima vez que o aplicativo precisar dele, ele tenha que buscar a cópia mais recente do banco de dados. Se você estiver usando o manipulador de leitura (veja abaixo), então NCache também pode recarregar automaticamente a última cópia do banco de dados para você. Abaixo está um exemplo de como você pode usar SqlDependency para sincronizar seu cache com o SQL Server.
Read-through Cache é um cache capaz de ler dados do seu banco de dados chamando um Readthrough Handler que você desenvolveu e forneceu ao cache. Da mesma forma, um cache de gravação pode gravar alterações de dados em seu banco de dados chamando um manipulador de gravação que você desenvolveu e forneceu ao cache. Cache Write-behind é o mesmo que Write-through, exceto que as atualizações do banco de dados são feitas de forma assíncrona.
Read-through, Write-through e Write-behind fornecem muitos benefícios para o seu .NET Core aplicações incluindo:
Abaixo está um exemplo de como você pode usar Read-through com NCache.
// Read through handler for SQL Server
public class SqlReadThruProvider : Runtime.DatasourceProviders.IReadThruProvider
{
public void Init(IDictionary parameters, string cacheId) {}
public void Dispose() {}
// Get object from the database/data-source based on the key
public ProviderCacheItem LoadFromSource(string key) {}
// Bulk-Get objects from the database/data-source based on the keys
public IDictionary<string, ProviderCacheItem> LoadFromSource(ICollection<string> keys)
{}
}
Figura 9: Usando o manipulador de leitura com NCache
Quando estiver confortável com o armazenamento em cache de todos os dados, você poderá começar a colocar muitos dados em um cache distribuído. Aqui você pode começar a enfrentar outro problema peculiar de como encontrar seus dados de maneira rápida e fácil. Como a maioria dos caches distribuídos são armazenamentos de valores-chave, fica muito difícil acompanhar todos os seus dados apenas por meio de chaves.
Aqui é onde NCache fornece várias maneiras de encontrar rapidamente dados do seu cache. Exemplos incluem:
Abaixo está um exemplo de como você pode usar consultas baseadas em LINQ com NCache.
// Search the cache based on object attributes by using LINQ
IQueryable>Product< products = new NCacheQuery<Product>(_cache);
var result = from product in products
where product.Id > 10
select product;
if (result != null)
{
foreach (Product p in result1)
{
// Process each “product” fetched from the database
Console.WriteLine("ProductID : " + p.Id);
}
}
Figura 10: Usando consultas LINQ com NCache
Alto tráfego .NET Core os aplicativos não podem se dar ao luxo de cair, especialmente durante os horários de pico. Para esses tipos de aplicativos, existem três objetivos arquiteturais realmente importantes que um bom Cache Distribuído InMemory como NCache cumpre.
Deixe-me explicar cada um abaixo.
NCache fornece um Client Cache que é um cache local muito próximo ao seu aplicativo. Pode ser InProc (o que significa que reside dentro do seu processo de inscrição) ou OutProc local. De qualquer forma, ele fornece acesso muito rápido a um subconjunto dos dados armazenados em cache que seu aplicativo neste servidor de aplicativos precisa no momento. O Cache do Cliente ao mesmo tempo permanece sincronizado com a camada de armazenamento em cache para que quaisquer dados alterados por outros usuários ou aplicativos na camada de armazenamento em cache sejam imediatamente propagados para o Cache do Cliente. O cliente permite que você tenha velocidade InProc enquanto ainda faz parte de uma camada de cache muito escalável.
Um dos objetivos arquitetônicos mais importantes da NCache é alcançar escalabilidade linear com confiabilidade de dados por meio de suas topologias de armazenamento em cache. Aqui estão alguns NCache Topologias de cache que ajudam a alcançar esses dois objetivos.
Um dos objetivos arquitetônicos mais importantes da NCache é alcançar alta disponibilidade e elasticidade de cache. Ele faz isso por meio dos seguintes recursos de arquitetura: