
class TestCh02 { public int LIMIT = 10000000; public bool QUIT = false; CSRedisClient _conn; public TestCh02() { _conn = new CSRedis.CSRedisClient("127.0.0.1:6379,defaultDatabase=14,poolsize=500,ssl=false,writeBuffer=10240"); } public string check_token(CSRedisClient conn, string token) { return conn.HGet("login:", token); } public void update_token(CSRedisClient conn, string token, string user, string item = null) { var timestamp = (DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds; conn.HSet("login:", token, user); conn.ZAdd("recent:", ((decimal)timestamp, token)); if (!string.IsNullOrEmpty(item)) { conn.ZAdd("viewed:" + token, ((decimal)timestamp, item)); conn.ZRemRangeByRank("viewed:" + token, 0, -26); conn.ZIncrBy("viewed:", item, -1); } } public void clean_sessions(object obj) { CSRedisClient conn = obj as CSRedisClient; while (!QUIT) { var size = conn.ZCard("recent:"); if (size <= LIMIT) { Thread.Sleep(1000); continue; } var end_index = Math.Min(size - LIMIT, 100); var tokens = conn.ZRange("recent:", 0, end_index - 1); List<string> session_keys = new List<string>(); foreach (var token in tokens) { session_keys.Add("viewed:" + token); } conn.Del(session_keys.ToArray()); conn.HDel("login:", tokens); conn.ZRem("recent:", tokens); } } public void add_to_cart(CSRedisClient conn, string session, string item, int count) { if (count <= 0) conn.HDel("cart:" + session, item); else conn.HSet("cart:" + session, item, count); } public void clean_full_sessions(object obj) { CSRedisClient conn = obj as CSRedisClient; while (!QUIT) { var size = conn.ZCard("recent:"); if (size <= LIMIT) { Thread.Sleep(1000); continue; } var end_index = Math.Min(size - LIMIT, 100); var sessions = conn.ZRange("recent:", 0, end_index - 1); List<string> session_keys = new List<string>(); foreach (var sess in sessions) { session_keys.Add("viewed:" + sess); session_keys.Add("cart:" + sess); } conn.Del(session_keys.ToArray()); conn.HDel("login:", sessions); conn.ZRem("recent:", sessions); } } public string cache_request(CSRedisClient conn, string request, Func<string, string> callback) { if (!can_cache(conn, request)) { return callback(request); ; } var page_key = "cache:" + hash_request(request); var content = conn.Get(page_key); if (string.IsNullOrEmpty(content)) { content = callback(request); conn.Set(page_key, content, 300); } return content; } public void schedule_row_cache(CSRedisClient conn, string row_id, int delay) { conn.ZAdd("delay:", (delay, row_id)); conn.ZAdd("schedule:", ((decimal)(DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds, row_id)); } public void cache_rows(object obj) { CSRedisClient conn = obj as CSRedisClient; while (!QUIT) { var next = conn.ZRangeWithScores("schedule:", 0, 0); var now = (DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds; if (next != null && next[0].score > (decimal)now) { Thread.Sleep(50); continue; } var row_id = next[0].member; var delay = conn.ZScore("delay:", row_id) ?? 0; if (delay <= 0) { conn.ZRem("delay:", row_id); conn.ZRem("schedule:", row_id); conn.Del("inv:" + row_id); } var row = new Inventory(row_id); conn.ZAdd("schedule:", ((decimal)now + delay, row_id)); conn.Set("inv:" + row_id, Newtonsoft.Json.JsonConvert.SerializeObject(row.to_dict())); } } public void rescale_viewed(CSRedisClient conn) { while (!QUIT) { conn.ZRemRangeByRank("viewed:", 20000, -1); conn.ZInterStore("viewed:", new decimal[] { (decimal)0.5 }, RedisAggregate.Max, "viewed:"); Thread.Sleep(300000); } } public bool can_cache(CSRedisClient conn, string request) { var item_id = extract_item_id(request); if (string.IsNullOrEmpty(item_id) || is_dynamic(request)) return false; var rank = conn.ZRank("viewed:", item_id); return rank.HasValue && rank.Value < 10000; } public string extract_item_id(string request) { NameValueCollection nameValueCollection; string url; UrlHelper.ParseUrl(request, out url, out nameValueCollection); return nameValueCollection.Get("item"); } public bool is_dynamic(string request) { NameValueCollection nameValueCollection; string url; UrlHelper.ParseUrl(request, out url, out nameValueCollection); return !string.IsNullOrEmpty(nameValueCollection.Get("_")); } public string hash_request(string request) { return request.GetHashCode().ToString(); } public void tearDown() { var conn = _conn; var to_del = conn.Keys("login:*").Union(conn.Keys("recent:*")).Union(conn.Keys("viewed:*")).Union(conn.Keys("cart:*")).Union(conn.Keys("cache:*")).Union(conn.Keys("delay:*")).Union(conn.Keys("schedule:*")).Union(conn.Keys("inv:*")).ToArray(); conn.Del(to_del); QUIT = false; LIMIT = 10000000; } public void test_login_cookies() { var conn = _conn; var token = Guid.NewGuid().ToString(); update_token(conn, token, "username", "itemX"); Console.WriteLine("We just logged-in/updated token:" + token); Console.WriteLine("For user:" + "username"); Console.WriteLine(); Console.WriteLine("What username do we get when we look-up that token?"); var r = check_token(conn, token); Console.WriteLine(r); Console.WriteLine(); Console.WriteLine("Let's drop the maximum number of cookies to 0 to clean them out"); Console.WriteLine("We will start a thread to do the cleaning, while we stop it later"); LIMIT = 0; Thread thread = new Thread(new ParameterizedThreadStart(clean_sessions));//创建线程 thread.Start(conn); //启动线程 Thread.Sleep(1000); QUIT = true; Thread.Sleep(2000); var s = conn.HLen("login:"); Console.WriteLine("The current number of sessions still available is:" + s); } public void test_shopping_cart_cookies() { var conn = _conn; var token = Guid.NewGuid().ToString(); Console.WriteLine("We'll refresh our session..."); update_token(conn, token, "username", "itemX"); Console.WriteLine("And add an item to the shopping cart"); add_to_cart(conn, token, "itemY", 3); var r = conn.HGetAll("cart:" + token); Console.WriteLine("Our shopping cart currently has:" + PrintHelper.Dictionary2String(r)); Console.WriteLine(); Console.WriteLine("Let's clean out our sessions and carts"); LIMIT = 0; Thread thread = new Thread(new ParameterizedThreadStart(clean_full_sessions));//创建线程 thread.Start(conn); //启动线程 Thread.Sleep(1000); QUIT = true; Thread.Sleep(2000); r = conn.HGetAll("cart:" + token); Console.WriteLine("Our shopping cart now contains:" + PrintHelper.Dictionary2String(r)); } public void test_cache_request() { var conn = _conn; var token = Guid.NewGuid().ToString(); Func<string, string> callback = (request) => { return "content for " + request; }; update_token(conn, token, "username", "itemX"); var url = "http://test.com/?item=itemX"; Console.WriteLine("We are going to cache a simple request against" + url); var result = cache_request(conn, url, callback); Console.WriteLine("We got initial content:" + result); Console.WriteLine(); Console.WriteLine("To test that we've cached the request, we'll pass a bad callback"); var result2 = cache_request(conn, url, null); Console.WriteLine("We ended up getting the same response!" + result2); } public void test_cache_rows() { var conn = _conn; Console.WriteLine("First, let's schedule caching of itemX every 5 seconds"); schedule_row_cache(conn, "itemX", 5); Console.WriteLine("Our schedule looks like:"); var s = conn.ZRangeWithScores("schedule:", 0, -1); Console.WriteLine(PrintHelper.ValueTuple2String(s)); Console.WriteLine("We'll start a caching thread that will cache the data..."); Thread thread = new Thread(new ParameterizedThreadStart(cache_rows)); thread.Start(conn); Thread.Sleep(1000); Console.WriteLine("Our cached data looks like:"); var r = conn.Get("inv:itemX"); Console.WriteLine(r); Console.WriteLine(); Console.WriteLine("We'll check again in 5 seconds..."); Thread.Sleep(5000); Console.WriteLine("Notice that the data has changed..."); var r2 = conn.Get("inv:itemX"); Console.WriteLine(r2); Console.WriteLine(); Console.WriteLine("Let's force un-caching"); schedule_row_cache(conn, "itemX", -1); Thread.Sleep(1000); r = conn.Get("inv:itemX"); Console.WriteLine("The cache was cleared?" + r); Console.WriteLine(); QUIT = true; Thread.Sleep(2000); } } public class Inventory { public string id { get; set; } public string data { get; set; } public decimal cached { get; set; } public Inventory(string id) { this.id = id; } public Inventory get(string id) { return new Inventory(id); } public Dictionary<string, object> to_dict() { return new Dictionary<string, object>() { { "id", this.id }, { "data", "data to cache..."}, { "cached", (decimal)(DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds }, }; } } public class UrlHelper { /// <summary> /// 分析 url 字符串中的参数信息 /// </summary> /// <param name="url">输入的 URL</param> /// <param name="baseUrl">输出 URL 的基础部分</param> /// <param name="nvc">输出分析后得到的 (参数名,参数值) 的集合</param> public static void ParseUrl(string url, out string baseUrl, out NameValueCollection nvc) { if (url == null) throw new ArgumentNullException("url"); nvc = new NameValueCollection(); baseUrl = ""; if (url == "") return; int questionMarkIndex = url.IndexOf('?'); if (questionMarkIndex == -1) { baseUrl = url; return; } baseUrl = url.Substring(0, questionMarkIndex); if (questionMarkIndex == url.Length - 1) return; string ps = url.Substring(questionMarkIndex + 1); // 开始分析参数对 Regex re = new Regex(@"(^|&)?(w+)=([^&]+)(&|$)?", RegexOptions.Compiled); MatchCollection mc = re.Matches(ps); foreach (Match m in mc) { nvc.Add(m.Result("$2").ToLower(), m.Result("$3")); } } }