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