2009-03-31 2 views
1

Я лично привержен распределенным кэширующим решениям .net, но я думаю, что этот вопрос интересен на всех платформах.Реальные ссылки на объекты в распределенном кэше?

Есть ли распределенное кэширующее решение (или общая стратегия), которое позволяет одновременно хранить объекты в кеше, сохраняя целостность ссылок между ними?

В качестве примера - Предположим, у меня есть объект Foo foo, который ссылается на объект Bar bar, а также и объект Foo foo2, который ссылается на что же Bar bar. Если я загружаю foo в кеш, вместе с ним сохраняется копия bar. Если я также загружаю foo2 в кеш, вместе с ним сохраняется отдельная копия bar. Если изменить foo.bar в кэше, то изменение не влияет foo2.bar :(

Есть ли существующий распределенное решение кэша, который позволит мне загрузить foo, foo2 и bar в кэш при сохранении foo.barfoo2.bar ссылки?

ответ

3

Прежде всего

Я не знаю ни одной распределенной системы, и я не претендую, чтобы построить один. Это сообщение объясняет, как можно смоделировать такое поведение с .NET и C# с помощью IObjectReference Inte rface с сериализуемыми объектами.

Теперь, давайте идти с шоу

Я не знаю такой распределенной системы, но вы можете несколько легко achive это с .NET, используя интерфейс IObjectReference. Для вашей реализации ISerializable.GetObjectData необходимо вызвать SerializationInfo.SetType, чтобы указать прокси-класс, который реализует IObjectReference, и сможет (с помощью данных, предоставленных вашим методом GetObjectData) получить ссылку на реальный объект, который должен использоваться.

Пример кода:

[Serializable] 
internal sealed class SerializationProxy<TOwner, TKey> : ISerializable, IObjectReference { 
    private const string KeyName = "Key"; 
    private const string InstantiatorName = "Instantiator"; 
    private static readonly Type thisType = typeof(SerializationProxy<TOwner, TKey>); 
    private static readonly Type keyType = typeof(TKey); 

    private static readonly Type instantiatorType = typeof(Func<TKey, TOwner>); 
    private readonly Func<TKey, TOwner> _instantiator; 
    private readonly TKey _key; 

    private SerializationProxy() { 
    } 

    private SerializationProxy(SerializationInfo info, StreamingContext context) { 
     if (info == null) throw new ArgumentNullException("info"); 

     _key = (TKey)info.GetValue(KeyName, keyType); 
     _instantiator = (Func<TKey, TOwner>)info.GetValue(InstantiatorName, instantiatorType); 
    } 

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { 
     throw new NotSupportedException("This type should never be serialized."); 
    } 

    object IObjectReference.GetRealObject(StreamingContext context) { 
     return _instantiator(_key); 
    } 

    internal static void PrepareSerialization(SerializationInfo info, TKey key, Func<TKey, TOwner> instantiator) { 
     if (info == null) throw new ArgumentNullException("info"); 
     if (instantiator == null) throw new ArgumentNullException("instantiator"); 

     info.SetType(thisType); 
     info.AddValue(KeyName, key, keyType); 
     info.AddValue(InstantiatorName, instantiator, instantiatorType); 
    } 
} 

Этот код будет вызываться с SerializationProxy.PrepareSerialization (информация, MYKEY, MYKEY => LoadedInstances.GetById (MYKEY)) от вашего метода GetObjectData, и ваш LoadedInstances.GetById должен вернуться экземпляр из словаря < TKey, WeakReference > или загрузить его из кеша/базы данных, если он еще не загружен.

EDIT:

Я написал некоторый пример кода, чтобы показать, что я имею в виду.

public static class Program { 
    public static void Main() { 
     // Create an item and serialize it. 
     // Pretend that the bytes are stored in some magical 
     // domain where everyone lives happily ever after. 
     var item = new Item { Name = "Bleh" }; 
     var bytes = Serialize(item); 

     { 
      // Deserialize those bytes back into the cruel world. 
      var loadedItem1 = Deserialize<Item>(bytes); 
      var loadedItem2 = Deserialize<Item>(bytes); 

      // This should work since we've deserialized identical 
      // data twice. 
      Debug.Assert(loadedItem1.Id == loadedItem2.Id); 
      Debug.Assert(loadedItem1.Name == loadedItem2.Name); 

      // Notice that both variables refer to the same object. 
      Debug.Assert(ReferenceEquals(loadedItem1, loadedItem2)); 

      loadedItem1.Name = "Bluh"; 
      Debug.Assert(loadedItem1.Name == loadedItem2.Name); 
     } 

     { 
      // Deserialize those bytes back into the cruel world. (Once again.) 
      var loadedItem1 = Deserialize<Item>(bytes); 

      // Notice that we got the same item that we messed 
      // around with earlier. 
      Debug.Assert(loadedItem1.Name == "Bluh"); 

      // Once again, force the peaceful object to hide its 
      // identity, and take on a fake name. 
      loadedItem1.Name = "Blargh"; 

      var loadedItem2 = Deserialize<Item>(bytes); 
      Debug.Assert(loadedItem1.Name == loadedItem2.Name); 
     } 
    } 

    #region Serialization helpers 
    private static readonly IFormatter _formatter 
     = new BinaryFormatter(); 

    public static byte[] Serialize(ISerializable item) { 
     using (var stream = new MemoryStream()) { 
      _formatter.Serialize(stream, item); 
      return stream.ToArray(); 
     } 
    } 

    public static T Deserialize<T>(Byte[] bytes) { 
     using (var stream = new MemoryStream(bytes)) { 
      return (T)_formatter.Deserialize(stream); 
     } 
    } 
    #endregion 
} 

// Supercalifragilisticexpialidocious interface. 
public interface IDomainObject { 
    Guid Id { get; } 
} 

// Holds all loaded instances using weak references, allowing 
// the almighty garbage collector to grab our stuff at any time. 
// I have no real data to lend on here, but I _presume_ that this 
// wont be to overly evil since we use weak references. 
public static class LoadedInstances<T> 
    where T : class, IDomainObject { 

    private static readonly Dictionary<Guid, WeakReference> _items 
     = new Dictionary<Guid, WeakReference>(); 

    public static void Set(T item) { 
     var itemId = item.Id; 
     if (_items.ContainsKey(itemId)) 
      _items.Remove(itemId); 

     _items.Add(itemId, new WeakReference(item)); 
    } 

    public static T Get(Guid id) { 
     if (_items.ContainsKey(id)) { 
      var itemRef = _items[id]; 
      return (T)itemRef.Target; 
     } 

     return null; 
    } 
} 

[DebuggerDisplay("{Id} {Name}")] 
[Serializable] 
public class Item : IDomainObject, ISerializable { 
    public Guid Id { get; private set; } 
    public String Name { get; set; } 

    // This constructor can be avoided if you have a 
    // static Create method that creates and saves new items. 
    public Item() { 
     Id = Guid.NewGuid(); 
     LoadedInstances<Item>.Set(this); 
    } 

    #region ISerializable Members 
    public void GetObjectData(SerializationInfo info, StreamingContext context) { 
     // We're calling SerializationProxy to call GetById(this.Id) 
     // when we should be deserialized. Notice that we have no 
     // deserialization constructor. Fxcop will hate us for that. 
     SerializationProxy<Item, Guid>.PrepareSerialization(info, Id, GetById); 
    } 
    #endregion 

    public static Item GetById(Guid id) { 
     var alreadyLoaded = LoadedInstances<Item>.Get(id); 
     if (alreadyLoaded != null) 
      return alreadyLoaded; 

     // TODO: Load from storage container (database, cache). 
     // TODO: The item we load should be passed to LoadedInstances<Item>.Set 
     return null; 
    } 
} 
+0

Саймон, благодарю вас за подробный ответ. Боюсь, что это немного пошло мне на голову. Не могли бы вы объяснить, как прокси-сервер сериализации относится к распределенному кешу, где я намерен хранить объекты? – urig

Смежные вопросы