国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Memcached 滑動過期實現與二分法優化

2019-11-17 02:27:46
字體:
來源:轉載
供稿:網友

Memcached 滑動過期實現與二分法優化

Jusfr 原創,轉載請注明來自博客園。

注意:方案有漏洞見文章末尾,思路可以看看仍然發出來了。代碼見github。

第一部分 前言

HttPRuntime.Cache.Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration) 方法的 slidingExpiration 參數能保證緩存被訪問后,有效時間延長;而 Memcached 并沒有實現該能力,形如第3方類庫 EnyimCaching 提供的 MemcachedClient.Store() 方法提供的重載仍然是按絕對時間過期,和前者并非同一語義,本文目的是處理這個問題,并給出更優化的方案。

    public bool Store(StoreMode mode, string key, object value);    public bool Store(StoreMode mode, string key, object value, DateTime expiresAt);    public bool Store(StoreMode mode, string key, object value, TimeSpan validFor);

EnyimCaching 接口封裝的很好,需要注意的是像配置錯誤等都不會導致異常拋出,所以使用時最好來個測試用例進行確認。

    [TestMethod]    public void Online() {        using (MemcachedClient client = new MemcachedClient("enyim.com/memcached")) {            String key = Guid.NewGuid().ToString("n");            Object value = client.Get(key);            Assert.IsNull(value);            var exist = client.TryGet(key, out value);            Assert.IsFalse(exist);            Assert.IsNull(value);            value = Guid.NewGuid();            client.Store(StoreMode.Set, key, value);            exist = client.TryGet(key, out value);            Assert.IsTrue(exist);            Assert.IsNotNull(value);        }    }
View Code

同樣需要注意的是客戶端時間與 Memcached 所在服務器時間不一致時將導致過期時間和預期不太一致,考慮到是 Windows 下開發,像 Ubuntu 服務器可以使用以下命令同步時間并重新啟動 Memcached 進程

    sudo ntpdate time.windows.com    sudo hwclock -w        sudo service memcached restart

然后是原始滑動過期的測試使用

    [TestMethod]    public void Sliding() {        using (MemcachedClient client = new MemcachedClient("enyim.com/memcached")) {            String key = Guid.NewGuid().ToString("n");            Object value = Guid.NewGuid();            client.Store(StoreMode.Set, key, value, TimeSpan.FromSeconds(15D));            Thread.Sleep(TimeSpan.FromSeconds(10D));            var exist = client.TryGet(key, out value);            Assert.IsTrue(exist);             Assert.IsNotNull(value);            Thread.Sleep(TimeSpan.FromSeconds(10D));            exist = client.TryGet(key, out value);            Assert.IsTrue(exist); //failed            Assert.IsNotNull(value);        }    }

測試用例未通過,這里使用的過期參數為15秒,10秒后檢查緩存得知存在,再過10秒后緩存已經過期。

第二部分 實現

你可以已經看出來了,如果希望第2個10秒時,緩存仍然可以訪問,那么第1個10秒時,將已經取出的緩存再存一次即可。確實如此,障礙是緩存取出時沒有攜帶時間信息,我翻看了API,好像沒有這辦法(如果謬誤,還請指正),所以我們可以拿一個 泛型Wraper 將時間和值一起存到緩存時,取出時進行對比。

定義 SlidingCacheWraper<TCache> 如下,注意 [Serializable] 在 EnyimCaching 的使用中是需要的:

    [Serializable]    public class SlidingCacheWraper<TCache> {        public TCache Cache { get; private set; }        public DateTime CachingTime { get; private set; }        public TimeSpan SlidingExpiration { get; private set; }        public SlidingCacheWraper(TCache cache, TimeSpan slidingExpiration) {            Cache = cache;            SlidingExpiration = slidingExpiration;            CachingTime = DateTime.Now;        }    }

大致邏輯是這樣:當需要存儲一個滑動過期項時,我們先使用 SlidingCacheWraper 包裹起這個緩存項,記錄緩存時間及滑動過期參數,送入 Memcached;當從 Memcached 取出這項時,先對比當前時間和緩存時間間隔,如果沒有超出滑動過期參數, 將其按照該參數重新存入;

    [TestMethod]    public void SlidingWithResotre() {        using (MemcachedClient client = new MemcachedClient("enyim.com/memcached")) {            String key = Guid.NewGuid().ToString("n");            Object value = Guid.NewGuid();            {                var slidingExpiration = TimeSpan.FromSeconds(15D);                Object wraper = new SlidingCacheWraper<Object>(value, slidingExpiration);                client.Store(StoreMode.Set, key, wraper, TimeSpan.FromSeconds(12D));            }            Thread.Sleep(TimeSpan.FromSeconds(10D));            {                Object wraperObj;                var exist = client.TryGet(key, out wraperObj);                Assert.IsTrue(exist);                Assert.IsNotNull(wraperObj);                Assert.IsTrue(wraperObj is SlidingCacheWraper<Object>);                var wraper = (SlidingCacheWraper<Object>)wraperObj;                Assert.IsNotNull(wraper.Cache);                Assert.AreEqual(value, wraper.Cache);                client.Store(StoreMode.Set, key, wraper, wraper.SlidingExpiration);            }            Thread.Sleep(TimeSpan.FromSeconds(10D));            {                Object wraperObj;                var exist = client.TryGet(key, out wraperObj);                Assert.IsTrue(exist);                Assert.IsNotNull(wraperObj);                Assert.IsTrue(wraperObj is SlidingCacheWraper<Object>);                var wraper = (SlidingCacheWraper<Object>)wraperObj;                Assert.IsNotNull(wraper.Cache);                Assert.AreEqual(value, wraper.Cache);                client.Store(StoreMode.Set, key, wraper, wraper.SlidingExpiration);            }            Thread.Sleep(TimeSpan.FromSeconds(20D));            {                Object wraperObj;                var exist = client.TryGet(key, out wraperObj);                Assert.IsFalse(exist);                Assert.IsNull(wraperObj);            }        }    }

先存儲滑過過期參數為15秒的緩存項,然后兩次間隔10秒訪問并期望緩存未過期,再進行間隔20秒訪問并期望緩存已經過期,相似代碼塊括起來了,測試通過。

第三部分 優化

誠然前面的邏輯已經實現了滑動過期的效果,但你應該注意到一個問題,就是為了這個需求,每次緩存讀取,我們都得重新寫入一次,即便 Memcached 性能很棒,反復命中緩存時的大量寫入也有 IO 及序列化壓力,這是可以優化的。

滑動過期就意味著緩存項的存活時間延長,又不能通過寫入解決,我的直觀思路就是:給緩存項加上“偽過期時間”,從起始算起在多出來的緩存時間時命中則不更新緩存。

    [TestMethod]    public void SlidingWithResotre() {        using (MemcachedClient client = new MemcachedClient("enyim.com/memcached")) {            String key = Guid.NewGuid().ToString("n");            Object value = Guid.NewGuid();            Action handler = () => {                Object wraperObj;                var exist = client.TryGet(key, out wraperObj);                if (exist) {                    Assert.IsNotNull(wraperObj);                    Assert.IsTrue(wraperObj is SlidingCacheWraper<Object>);                    var wraper = (SlidingCacheWraper<Object>)wraperObj;                    Assert.IsNotNull(wraper.Cache);                    Assert.AreEqual(value, wraper.Cache);                    TimeSpan diffSpan = DateTime.Now - wraper.CachingTime;                    Console.WriteLine("Get cache, diff {0:F0} sec., sliding {1:f0} sec.",                        diffSpan.TotalSeconds, wraper.SlidingExpiration.TotalSeconds);                    //當前時間-設置時間 > 滑動時間, 已經過期                    if (diffSpan > wraper.SlidingExpiration) {                        Console.WriteLine("Remove cache");                        client.Remove(key);                        return;                    }                    //當前時間-設置時間 > 滑動時間/2, 更新緩存                    if (diffSpan.Add(diffSpan) > wraper.SlidingExpiration) {                        Console.WriteLine("Restore cahce");                        client.Store(StoreMode.Set, key, wraper, wraper.SlidingExpiration);                    }                }                else {                    Console.WriteLine("Overdue");                }            };            var slidingExpiration = TimeSpan.FromSeconds(15D);            Object extendedWraper = new SlidingCacheWraper<Object>(value, TimeSpan.FromSeconds(slidingExpiration.TotalSeconds * 1.5));            client.Store(StoreMode.Set, key, extendedWraper, slidingExpiration);            var random = new Random();            for (int i = 0; i < 6; i++) {                Thread.Sleep(TimeSpan.FromSeconds(15 * random.NextDouble()));                handler();            }        }    }<
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 电白县| 丹阳市| 南宁市| 自治县| 陇川县| 安康市| 沙坪坝区| 盘山县| 胶南市| 庐江县| 阿坝县| 白银市| 电白县| 勐海县| 盐山县| 福建省| 湖南省| 波密县| 沛县| 常山县| 盐城市| 连山| 安顺市| 从江县| 博野县| 湘阴县| 岳普湖县| 庄河市| 焦作市| 浦北县| 康保县| 宁晋县| 澜沧| 皮山县| 天全县| 义马市| 达州市| 阿图什市| 酒泉市| 酒泉市| 西充县|