2015-09-05 10 views
9

मैं एक परिदृश्य में फंस गया हूं।ऑब्जेक्ट कैश के साथ कैश ऑब्जेक्ट .NET समाप्ति समय

अद्यतन:: मेरे कोड के नीचे की तरह है इसके बारे में नहीं कैसे डेटा कैश का उपयोग करने के, मैं पहले से ही यह काम कर रहे हैं और उसके उपयोग कर रहा हूँ उसके फैलते यह इतना विधि समाप्ति के समय के बीच फोन करना नहीं है के बारे में और बाहरी स्रोत से नए डेटा प्राप्त करने

object = (string)this.GetDataFromCache(cache, cacheKey); 

if(String.IsNullOrEmpty(object)) 
{ 
    // get the data. It takes 100ms 
    SetDataIntoCache(cache, cacheKey, object, DateTime.Now.AddMilliseconds(500)); 
} 

तो उपयोगकर्ता कैश मारा और यदि आइटम समाप्त हो यह कॉल करता है और सेवा से डेटा प्राप्त और मामले में यह बचाने के यह से डेटा प्राप्त, टी वह समस्या है, जब कभी एक लंबित अनुरोध (अनुरोध जारी है) सेवा एक और अनुरोध भेजती है क्योंकि ऑब्जेक्ट समाप्त हो गया है। फाइनल में अधिकतम 2-3 कॉल/सेकेंड होना चाहिए और बाह्य सेवा में प्रति सेकंड 10-20 कॉल हैं।

क्या ऐसा करने का कोई इष्टतम तरीका है ताकि अन्य समय के अनुरोधों के बीच कोई संघर्ष न हो और फिर अपने कस्टम क्लास को सरणी और समय टिकटें आदि के साथ बनाया जा सके?

btw कैश है-

private void SetDataIntoCache(ObjectCache cacheStore, string cacheKey, object target, DateTime slidingExpirationDuration) 
{ 
    CacheItemPolicy cacheItemPolicy = new CacheItemPolicy(); 

    cacheItemPolicy.AbsoluteExpiration = slidingExpirationDuration; 
    cacheStore.Add(cacheKey, target, cacheItemPolicy); 
} 
+0

इसे जांचें। मुझे लगता है कि यह आपकी समस्या का समाधान करेगा। http://stackoverflow.com/questions/26581065/memory-cache-in-web-api –

+0

नमस्ते, कम से कम जो मैं समझता हूं, मैं पहले से ही कक्षा का उपयोग कर रहा हूं और इसके कामकाजी जुर्माना, इसका उपयोग कैसे नहीं किया जा रहा है, यह भी नहीं है मेरे प्रश्न – kawafan

+0

@ कवाफन को अपडेट किया गया, कारी द्वारा सुझाए गए उत्तर सही हैं, आलसी सिंगलटन निष्पादन का समर्थन करता है और यह लॉकिंग का प्रबंधन करता है। यह कक्षा या गणना करने के बारे में नहीं है, यह आलसी का उपयोग करने के बारे में है। –

उत्तर

1

मैं MvcSiteMapProvider के लिए System.Runtime.Caching.ObjectCache साथ प्रयोग के लिए Micro Caching in .NET से समाधान ढाल लिया है। पूर्ण कार्यान्वयन में ICacheProvider इंटरफ़ेस है जो System.Runtime.Caching और System.Web.Caching के बीच स्वैपिंग की अनुमति देता है, लेकिन यह एक कट ऑफ संस्करण है जो आपकी आवश्यकताओं को पूरा करना चाहिए।

इस पैटर्न की सबसे आकर्षक विशेषता यह है कि यह आलसी लॉक के हल्के संस्करण का उपयोग करता है ताकि यह सुनिश्चित किया जा सके कि डेटा को डेटा स्रोत से लोड किया गया हो, कैश समाप्त हो जाने के बाद केवल 1 बार कितने समवर्ती थ्रेड हैं डेटा लोड करें।

using System; 
using System.Runtime.Caching; 
using System.Threading; 

public interface IMicroCache<T> 
{ 
    bool Contains(string key); 
    T GetOrAdd(string key, Func<T> loadFunction, Func<CacheItemPolicy> getCacheItemPolicyFunction); 
    void Remove(string key); 
} 

public class MicroCache<T> : IMicroCache<T> 
{ 
    public MicroCache(ObjectCache objectCache) 
    { 
     if (objectCache == null) 
      throw new ArgumentNullException("objectCache"); 

     this.cache = objectCache; 
    } 
    private readonly ObjectCache cache; 
    private ReaderWriterLockSlim synclock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); 

    public bool Contains(string key) 
    { 
     synclock.EnterReadLock(); 
     try 
     { 
      return this.cache.Contains(key); 
     } 
     finally 
     { 
      synclock.ExitReadLock(); 
     } 
    } 

    public T GetOrAdd(string key, Func<T> loadFunction, Func<CacheItemPolicy> getCacheItemPolicyFunction) 
    { 
     LazyLock<T> lazy; 
     bool success; 

     synclock.EnterReadLock(); 
     try 
     { 
      success = this.TryGetValue(key, out lazy); 
     } 
     finally 
     { 
      synclock.ExitReadLock(); 
     } 

     if (!success) 
     { 
      synclock.EnterWriteLock(); 
      try 
      { 
       if (!this.TryGetValue(key, out lazy)) 
       { 
        lazy = new LazyLock<T>(); 
        var policy = getCacheItemPolicyFunction(); 
        this.cache.Add(key, lazy, policy); 
       } 
      } 
      finally 
      { 
       synclock.ExitWriteLock(); 
      } 
     } 

     return lazy.Get(loadFunction); 
    } 

    public void Remove(string key) 
    { 
     synclock.EnterWriteLock(); 
     try 
     { 
      this.cache.Remove(key); 
     } 
     finally 
     { 
      synclock.ExitWriteLock(); 
     } 
    } 


    private bool TryGetValue(string key, out LazyLock<T> value) 
    { 
     value = (LazyLock<T>)this.cache.Get(key); 
     if (value != null) 
     { 
      return true; 
     } 
     return false; 
    } 

    private sealed class LazyLock<T> 
    { 
     private volatile bool got; 
     private T value; 

     public T Get(Func<T> activator) 
     { 
      if (!got) 
      { 
       if (activator == null) 
       { 
        return default(T); 
       } 

       lock (this) 
       { 
        if (!got) 
        { 
         value = activator(); 

         got = true; 
        } 
       } 
      } 

      return value; 
     } 
    } 
} 

प्रयोग

// Load the cache as a static singleton so all of the threads 
// use the same instance. 
private static IMicroCache<string> stringCache = 
    new MicroCache<string>(System.Runtime.Caching.MemoryCache.Default); 

public string GetData(string key) 
{ 
    return stringCache.GetOrAdd(
     key, 
     () => LoadData(key), 
     () => LoadCacheItemPolicy(key)); 
} 

private string LoadData(string key) 
{ 
    // Load data from persistent source here 

    return "some loaded string"; 
} 

private CacheItemPolicy LoadCacheItemPolicy(string key) 
{ 
    var policy = new CacheItemPolicy(); 

    // This ensures the cache will survive application 
    // pool restarts in ASP.NET/MVC 
    policy.Priority = CacheItemPriority.NotRemovable; 

    policy.AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(1); 

    // Load Dependencies 
    // policy.ChangeMonitors.Add(new HostFileChangeMonitor(new string[] { fileName })); 

    return policy; 
} 

नोट: जैसा कि पहले उल्लेख किया गया था, तो आप शायद कि 100ms लेता है केवल 500ms के लिए पुनः प्राप्त करने के एक मूल्य कैशिंग द्वारा कुछ भी प्राप्त कर रहा नहीं कर रहे हैं। कैश में आइटम रखने के लिए आपको अधिक समय की अवधि चुननी चाहिए। क्या आइटम वास्तव में डेटा स्रोत में अस्थिर हैं कि वे इसे जल्दी से बदल सकते हैं? यदि ऐसा है, तो हो सकता है कि आपको किसी भी स्टेल डेटा को अमान्य करने के लिए ChangeMonitor का उपयोग करना चाहिए ताकि आप कैश लोड करने वाले CPU समय का इतना अधिक खर्च न करें। फिर आप मिलीसेकंड के बजाय कैश समय को मिनटों में बदल सकते हैं।

+0

नमस्ते, हां, असल में ये एक्सचेंज कीमतें हैं, और इसके परिवर्तन 200ms की तरह हैं, और मेरे पास प्रति सेकंड आने वाले लगभग 500 अनुरोध हैं। सीपीयू उपयोग कोई समस्या नहीं है मेरे पास इस प्रोजेक्ट पर 64 कोर सर्वर चल रहा है और मैं चरम घंटों पर अधिकतम 7-8% भार स्पर्श करता हूं – kawafan

0

लिए बचत कोड तुम्हें यकीन अनुरोध बनाने के लिए ताला लगा भेज नहीं है जब कैश समाप्त हो गई है और एक अन्य धागा, यह दूरस्थ/धीमी गति से सेवा से यह हो रही है का उपयोग करना होगा कुछ इस तरह दिखाई (वहाँ बेहतर कार्यान्वयन कि उपयोग करने के लिए आसान कर रहे हैं, लेकिन वे अलग वर्ग की आवश्यकता होती है):

private static readonly object _Lock = new object(); 

... 

object = (string)this.GetDataFromCache(cache, cacheKey); 

if(object == null) 
{ 
    lock(_Lock) 
    { 
     object = (string)this.GetDataFromCache(cache, cacheKey); 
     if(String.IsNullOrEmpty(object)) 
     { 
      get the data // take 100ms 
      SetDataIntoCache(cache, cacheKey, object, DateTime.Now.AddMilliseconds(500)); 
     } 
    } 
} 

return object; 

इसके अलावा, आप के रूप में यह है कि मान लेंगे आपकी सेवा वापसी अशक्त नहीं करता सुनिश्चित करना चाहते हैं कोई कैश मौजूद नहीं है और ev पर डेटा प्राप्त करने का प्रयास करेगा ery अनुरोध। यही कारण है कि अधिक उन्नत कार्यान्वयन आम तौर पर कैशऑब्जेक्ट जैसे कुछ का उपयोग करते हैं, जो शून्य मान भंडारण का समर्थन करता है।

5

उपयोग Double-checked locking पैटर्न:

var cachedItem = (string)this.GetDataFromCache(cache, cacheKey); 
if (String.IsNullOrEmpty(object)) { // if no cache yet, or is expired 
    lock (_lock) { // we lock only in this case 
     // you have to make one more check, another thread might have put item in cache already 
     cachedItem = (string)this.GetDataFromCache(cache, cacheKey); 
     if (String.IsNullOrEmpty(object)) { 
      //get the data. take 100ms 
      SetDataIntoCache(cache, cacheKey, cachedItem, DateTime.Now.AddMilliseconds(500)); 
     } 
    } 
} 

इस तरह, अपने कैश में किसी आइटम है, जबकि (हां, अभी तक नहीं समाप्त हो गई है), सभी अनुरोधों को ताला लगा के बिना पूरा हो जाएगा। लेकिन अगर अभी तक कोई कैश प्रविष्टि नहीं है, या यह समाप्त हो गया है - केवल एक थ्रेड डेटा प्राप्त करेगा और उसे कैश में डाल देगा। सुनिश्चित करें कि आप उस पैटर्न को समझते हैं, क्योंकि .NET में इसे लागू करते समय कुछ चेतावनी हैं।

टिप्पणियों में उल्लेख किया गया है, प्रत्येक एकल कैश पहुंच की सुरक्षा के लिए एक "वैश्विक" लॉक ऑब्जेक्ट का उपयोग करना आवश्यक नहीं है। मान लीजिए कि आपके कोड में दो विधियां हैं, और उनमें से प्रत्येक विधि कैश की अपनी कैश कुंजी का उपयोग कर ऑब्जेक्ट कैश करती है (लेकिन फिर भी उसी कैश का उपयोग कर रही है)। फिर आपको दो अलग लॉक ऑब्जेक्ट्स का उपयोग करना होगा, क्योंकि यदि आप एक "ग्लोबल" लॉक ऑब्जेक्ट का उपयोग करेंगे, तो एक विधि को कॉल करने के लिए अन्य विधि को कॉल के लिए अनावश्यक इंतजार होगा, जबकि वे कभी भी उसी कैश कुंजी के साथ काम नहीं करेंगे।

+1

यहां जोर देना महत्वपूर्ण है कि '_lock' समरूपता के लिए ग्रैन्युलरिटी को परिभाषित करता है; अक्सर, एक ही कैश ऑब्जेक्ट में असंबद्ध और गैर-प्रतिस्पर्धी डेटा की एक श्रृंखला होती है, इसलिए यह सुनिश्चित करना आवश्यक हो सकता है कि लॉक ग्रैन्युलरिटी डेटा को प्रतिबिंबित करे। –

0

वैसे, 500 मिलीसेकंड कैश करने के लिए बहुत छोटा समय है, तो आप कैश जोड़ने/निकालने के लिए बहुत से CPU चक्र को समाप्त कर देंगे जो अंततः किसी भी अन्य अनुरोध को कैश का लाभ प्राप्त करने से पहले कैश को हटा देगा। यह देखने के लिए कि क्या यह वास्तव में लाभान्वित है, आपको अपना कोड प्रोफाइल करना चाहिए।

याद रखें, लॉकिंग, हैशिंग और कई अन्य डेटा के चारों ओर चलने के मामले में कैश के पास बहुत सी कोड है, जो सीपीयू चक्रों की अच्छी मात्रा खर्च करता है और याद रखता है, हालांकि सीपीयू चक्र छोटे होते हैं, लेकिन बहु थ्रेडेड, बहु कनेक्शन सर्वर में, सीपीयू में कई अन्य चीजें हैं।

मूल उत्तर https://stackoverflow.com/a/16446943/85597

private string GetDataFromCache(
      ObjectCache cache, 
      string key, 
      Func<string> valueFactory) 
{ 
    var newValue = new Lazy<string>(valueFactory);    

    //The line below returns existing item or adds 
    // the new value if it doesn't exist 
    var value = cache.AddOrGetExisting(key, newValue, DateTimeOffset.Now.AddMilliseconds(500)) as Lazy<string>; 
    // Lazy<T> handles the locking itself 
    return (value ?? newValue).Value; 
} 


// usage... 


object = this.GetDataFromCache(cache, cacheKey,() => { 

     // get the data... 

     // this method will be called only once.. 

     // Lazy will automatically do necessary locking 
     return data; 
}); 
संबंधित मुद्दे