Обработка сеанса в основных приложениях .net требует, чтобы вы сначала преобразовали его в байтовый массив перед его извлечением.По сравнению с хранилищем .net Framework накладные расходы на эти операции сериализации и десериализации влекут за собой значительные накладные расходы.Операции, которые занимают 5 мс в наших приложениях .Net Framework, занимают более 850 мс в ядре .Net.Мне нужна возможность хранить и извлекать довольно большие объемы данных из кэша сервера высокопроизводительным способом, аналогичным тому, как мы можем использовать сессионное хранилище в .Net Framework.
Наши приложения используют много довольно больших массивов данных ADO.NET.Они нередко содержат тысячи строк и десятки столбцов.В прошлом мы использовали .Net Framework с хранилищем сеансов для быстрого извлечения объектов ADO.NET с данными и из сеансов.
DataTable dt = new DataTable();
HttpContext.Current.Session["data"] = dt; // store in session
dt = (DataTable)HttpContext.Current.Session["data"]; // retrieve from session
У нас также есть собственные пользовательские классы, в которых в качестве членов могут использоваться таблицы данных.Эти классы манипулируют данными по-разному.Мы также храним и извлекаем их из сеанса.
[Serializable]
MyClass {
public DataTable dt;
}
В наших приложениях .NET Framework типичные запросы для фильтрации и подкачки данных занимают около 5 мс и обратно.Доступ к сеансу очень быстрый, а потеря производительности для каждой операции получения и установки незначительна.
Мы пытались перейти на .NET Core, который управляет сессией немного по-другому.Вместо того, чтобы сохранять и извлекать любой объект в сеансе, я сначала должен преобразовать его в байтовый массив.Я использую двоичный форматер с небольшой логикой для обработки операций get и set.Приведенный ниже код не так эффективен, как мог бы быть, но, безусловно, самым большим узким местом является операция десериализации в методе retrieveData.
Public class SessionManager : ISessionManager
{
private readonly IHttpContextAccessor _contextAccessor;
public SessionManager(IHttpContextAccessor contextAccessor) {
_contextAccessor = contextAccessor;
}
public T get<T>(string key)
{
object data = retrieveData(key);
return data == null ? default(T) : (T)data;
}
public void set(string key, object data)
{
serializeBinary(key, data);
}
public void remove(string key) {
_contextAccessor.HttpContext.Session.Remove(key);
}
private void serializeBinary(string key, object data) {
BinaryFormatter bf = new BinaryFormatter();
using (var ms = new MemoryStream())
{
bf.Serialize(ms, data);
var bytes = ms.ToArray();
_contextAccessor.HttpContext.Session.Set(key, bytes);
}
}
private object retrieveData(string key) {
byte[] data = null;
_contextAccessor.HttpContext.Session.TryGetValue(key, out data);
if (data == null) return null;
using (MemoryStream ms = new MemoryStream(data))
{
IFormatter br = new BinaryFormatter();
return br.Deserialize(ms);
}
}
}
}
Использование:
MyClass c;
c.dt = _myRepo.getLotsOfData();
_SessionManager.set("data", c);
c = _SessionManager.get<MyClass>("data");
Те же самые операции подкачки и фильтрации, которые мы выполняем для DataTables с использованием тех же классов, занимают от 850 мс до 950 мс по сравнению с нашими приложениями .Net Framework.которые занимают 5 мс.Профилируя производительность в визуальной студии, операция десериализации заняла львиную долю того времени, в 600 мс только для этой операции.
Мне известны другие библиотеки (например, protobuf), которые работают намного быстрее двоичного форматера, и, вероятно, я пойду дальше.Однако, если я уменьшу время десерилизации до 300 мс или даже 200 мс, я все равно потеряю большую производительность по сравнению с .Net Framework.
Мне кажется, что требуется другой тип стратегии кэширования.Есть ли способ хранить и извлекать данные в основных приложениях .Net, который не требует дополнительных затрат на сериализацию и десериализацию данных в первую очередь?