2009-03-31 7 views
1

Je suis personnellement engagé dans les solutions de cache distribuées .net, mais je pense que cette question est intéressante sur toutes les plateformes.Références d'objet "réel" dans le cache distribué?

Existe-t-il une solution de mise en cache distribuée (ou stratégie générique) qui permet à la fois de stocker des objets dans le cache tout en conservant l'intégrité des références entre eux?

Pour donner un exemple - Supposons que j'ai un objet Foo foo qui fait référence à un objet Bar bar et aussi et l'objet Foo foo2 qui fait référence à ce même Bar bar. Si je charge foo dans le cache, une copie de bar est stockée avec elle. Si je charge également foo2 dans le cache, une copie distincte de bar est stockée avec cela. Si je change foo.bar dans le cache, le changement n'a pas d'impact foo2.bar :(

Y at-il une solution de cache distribuée existante qui me permettra de charger foo, foo2 et bar dans le cache tout en maintenant les foo.barfoo2.bar références?

Répondre

3

d'abord et avant tout

Je ne connais pas de système distribué, et je ne prétends pas construire un. ce message explique comment vous pouvez simuler ce comportement aveC# .NET et C en utilisant le IObjectReference inte rface avec des objets sérialisables.

Maintenant, laisse aller avec le spectacle

Je ne sais pas d'un tel système distribué, mais vous pouvez facilement achive un peu cela avec .NET en utilisant l'interface IObjectReference. Votre implémentation de ISerializable.GetObjectData devra appeler SerializationInfo.SetType pour indiquer une classe de proxy qui implémente IObjectReference et pourra (avec l'aide des données fournies par votre méthode GetObjectData) obtenir une référence à l'objet réel qui doit être utilisé.

code Exemple:

[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); 
    } 
} 

Ce code serait appelé à SerializationProxy.PrepareSerialization (info, myKey, myKey => LoadedInstances.GetById (myKey)) à partir de votre méthode GetObjectData et votre LoadedInstances.GetById devrait revenir l'instance d'un dictionnaire < TKey, WeakReference > ou le charger du cache/base de données s'il n'est pas déjà chargé.

EDIT:

J'ai écrit quelques exemples de code pour montrer ce que je veux dire.

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

Simon, merci pour la réponse élaborée. J'ai peur que ça me dépasse un peu. Pourriez-vous expliquer comment le proxy de sérialisation se rapporte au cache distribué où j'ai l'intention de stocker les objets? – urig

Questions connexes