Autore: Iqbal Khan
Con l'esplosione di app Web con transazioni estremamente elevate, SOA, grid computing e altre applicazioni server, l'archiviazione dei dati non è in grado di tenere il passo. Il motivo è che l'archiviazione dei dati non può continuare ad aggiungere più server per la scalabilità orizzontale, a differenza delle architetture applicative estremamente scalabili.
In queste situazioni, la cache distribuita in memoria offre un'ottima soluzione ai colli di bottiglia dell'archiviazione dei dati. Si estende su più server (chiamati cluster) per raggruppare la loro memoria e mantenere tutta la cache sincronizzata tra i server. E può continuare a far crescere questo cluster di cache all'infinito, proprio come i server delle applicazioni. Ciò riduce la pressione sull'archiviazione dei dati in modo che non rappresenti più un collo di bottiglia per la scalabilità.
Esistono due modi principali in cui le persone utilizzano una cache distribuita:
Cache-aside è una tecnica molto potente e consente di eseguire query di database complesse che coinvolgono join e query nidificate e di manipolare i dati nel modo desiderato. Nonostante che, Lettura / Scrivere attraverso presenta vari vantaggi rispetto alla cache a parte, come indicato di seguito:
Read-through/Write-through non deve essere utilizzato per tutti gli accessi ai dati nell'applicazione. È più adatto per situazioni in cui si leggono singole righe dal database o si leggono dati che possono essere mappati direttamente a un singolo elemento della cache. È ideale anche per i dati di riferimento che devono essere conservati nella cache per letture frequenti anche se questi dati cambiano periodicamente.
Un gestore read-through viene registrato con il server della cache e consente alla cache di leggere direttamente i dati dal database. Il NCache server fornisce un'interfaccia del gestore Read-through che è necessario implementare. Ciò consente NCache per chiamare il tuo gestore Read-through.
public class SqlReadThruProvider : IReadThruProvider
{
private SqlConnection _connection;
// Called upon startup to initialize connection
public void Init(IDictionary parameters, string cacheId)
{
_connection = new SqlConnection(parameters["connstring"]);
_connection.Open();
}
// Called at the end to close connection
public void Dispose()
{
_connection.Close();
}
// Responsible for loading object from external data source
public ProviderCacheItem LoadFromSource(string key)
{
string sql = "SELECT * FROM Customers WHERE ";
sql += "CustomerID = @ID";
SqlCommand cmd = new SqlCommand(sql, _connection);
cmd.Parameters.Add("@ID", System.Data.SqlDbType.VarChar);
// Let's extract actual customerID from "key"
int keyFormatLen = "Customers:CustomerID:".Length;
string custId = key.Substring(keyFormatLen,
key.Length - keyFormatLen);
cmd.Parameters["@ID"].Value = custId;
// fetch the row in the table
SqlDataReader reader = cmd.ExecuteReader();
Customers customer = new Customers();
// copy data from "reader" to "cust" object
FillCustomers(reader, customer);
ProviderCacheItem cacheItem = new ProviderCacheItem(customer);
// specify a SqlCacheDependency for this object
CacheDependency dep = new SqlCacheDependency(_connection.ToString(), cmd.ToString());
cacheItem.Dependency = dep;
return cacheItem;
}
Init()
esegue alcune attività di allocazione delle risorse come stabilire connessioni all'origine dati principale, mentre Dispose()
ha lo scopo di ripristinare tutte queste allocazioni. LoadFromSource()
è ciò che la cache chiama per leggere gli oggetti.
Viene richiamato il gestore di scrittura quando la cache deve scrivere nel database mentre la cache viene aggiornata. Normalmente, l'applicazione emette un aggiornamento alla cache tramite aggiunta, inserimento o rimozione.
public class SqlWriteThruProvider : IWriteThruProvider
{
private SqlConnection _connection;
// Called upon startup to initialize connection
public void Init(IDictionary parameters, string cacheId)
{
_connection = new SqlConnection((string)parameters["connstring"]);
_connection.Open();
}
// Called at the end to close connection
public void Dispose()
{
_connection.Close();
}
public OperationResult WriteToDataSource(WriteOperation operation)
{
int rowsChanged = 0;
OperationResult result = new OperationResult(operation, OperationResult.Status.Failure);
ProviderCacheItem cacheItem = operation.ProviderItem;
Customers cust = cacheItem.GetValue<Customers>();
string[] customer = {cust.Id,cust.ContactName,cust.CompanyName,
cust.Address,cust.City, cust.Country,cust.PostalCode,
cust.Phone,cust.Fax};
SqlCommand cmd = _connection.CreateCommand();
cmd.CommandText = String.Format(CultureInfo.InvariantCulture,
"Update dbo.Customers " + "Set CustomerID='{0}'," +
"ContactName='{1}',CompanyName='{2}'," +
"Address='{3}',City='{4}'," +
"Country='{5}',PostalCode='{6}'," +
"Phone='{7}',Fax='{8}'" +
"Where CustomerID = '{0}'", customer);
rowsChanged = cmd.ExecuteNonQuery();
if (rowsChanged > 0)
{
result.OperationStatus = OperationResult.Status.Success;
return result;
}
return result;
}
}
Init()
esegue attività di allocazione delle risorse come stabilire connessioni all'origine dati, mentre Dispose()
ha lo scopo di ripristinare tutte queste allocazioni. Save è il metodo che la cache chiama per gli oggetti write-through.
Il codice di esempio seguente mostra l'uso delle funzionalità di lettura/scrittura della cache da una semplice applicazione Windows
using Alachisoft.NCache.Client;
...
internal class MainForm : System.Windows.Forms.Form
{
/// Fetches record from the cache, which internally accesses the
/// datasource using read-thru provider
private void OnClickFind(object sender, System.EventArgs e)
{
Customer customer;
Cache cache = NCache.Caches[CacheName];
string key = cboCustomerID.Text.Trim();
string providerName = cboReadThruProvider.Text;
customer = (Customer) cache.Get(key,
providerName,
DSReadOption.ReadThru);
...
}
/// Updates the record using the cache, which internally accesses
/// the datasource using write-thru provider
private void OnClickUpdate(object sender, System.EventArgs e)
{
Cache cache = NCache.Caches[CacheName];
Customer customer = new Customer();
...
string key = customer.CustomerID;
string providerName = cboWriteThruProvider.Text;
cache.Insert(key, new CacheItem(customer), DSWriteOption.WriteThru, providerName, null);
...
}
}