在上一篇文章《使用Memcached提升.NET應用程序的性能》中周公講述如何在.NET中使用Memcached來提升.NET應用程序的性 能。在實際的使用中有可能出現Memcached由於某些不可預知的緣由掛掉,一旦出現這樣的狀況,就會再次給數據庫增長巨大的壓力,所以須要監控 Memcached的運行狀況。周公在網上找過,在網上有PHP版的Memcached監控工具,打開那個PHP頁面就能夠看到各個Memcached的 運行狀況,一旦不能獲取到這些數據,說明Memcached不可訪問,不可訪問的緣由多是由於網絡故障或者Memcached掛掉了,雖然緣由不一樣,但 是結果是同樣的。參照了Enyim Memcached和PHP版Memcached監控工具的實現,周公實現了一個.NET版的監控工具。
實現思路
上一篇文章《使用Memcached提升.NET應用程序的性能》中周公講述了能夠經過Telnet來獲取Memcached的運行情況,通 過"stats"命令獲得Memcached的數據,若是得不到相應的數據就證實Memcached不可訪問。
其中向Memcached發送"stats"命令獲得的數據的意義以下:
pid:32u,服務器進程ID。
uptime:32u, 服務器運行時間,單位秒。
time :32u, 服務器當前的UNIX時間。
version :string, 服務器的版本號。
curr_items :32u, 服務器當前存儲的內容數量 Current number of items stored by the server
total_items :32u, 服務器啓動以來存儲過的內容總數。
bytes :64u, 服務器當前存儲內容所佔用的字節數。
curr_connections :32u, 鏈接數量。
total_connections :32u, 服務器運行以來接受的鏈接總數。
connection_structures:32u, 服務器分配的鏈接結構的數量。
cmd_get :32u, 取回請求總數。
cmd_set :32u, 存儲請求總數。
get_hits :32u, 請求成功的總次數。
get_misses :32u, 請求失敗的總次數。
bytes_read :64u, 服務器從網絡讀取到的總字節數。
bytes_written :64u, 服務器向網絡發送的總字節數。
limit_maxbytes :32u, 服務器在存儲時被容許使用的字節總數。
上面的描述中32u和64u表示32位和64位無符號整數,string表示是string類型數據。
在本篇中咱們經過Socket而不是Telnet鏈接到Memcached,而後解析返回的數據。
程序代碼
爲了便於管理和維護,在本示例中使用了單頁模式,也就是全部的代碼都在一個ASPX頁面中,沒有對應的aspx.cs頁面。
程序代碼以下:
html
- <%@ Page Language="C#" %>
- <%@ Import Namespace="System" %>
- <%@ Import Namespace="System.IO" %>
- <%@ Import Namespace="System.Net" %>
- <%@ Import Namespace="System.Net.Sockets" %>
- <%@ Import Namespace="System.Collections.Generic" %>
- <%@ Import Namespace="System.Threading" %>
- <%@ Import Namespace="System.Security" %>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
- <script runat="server">
- /*
- * 做者:周公
- * 日期:2011-03-27
- * 原文出處:http://blog.csdn.net/zhoufoxcn 或http://zhoufoxcn.blog.51cto.com
- * 版權說明:本文能夠在保留原文出處的狀況下使用於非商業用途,周公對此不做任何擔保或承諾。
- * */
-
- /// <summary>
- /// Memcached服務器監控類
- /// </summary>
- public class MemcachedMonitor
- {
- /// <summary>
- /// 鏈接Memcached的超時時間
- /// </summary>
- public TimeSpan ConnectionTimeout { get; set; }
- /// <summary>
- /// 接收Memcached返回數據的超時時間
- /// </summary>
- public TimeSpan ReceiveTimeout { get; set; }
- private List<IPEndPoint> serverList;
- public MemcachedMonitor(ICollection<IPEndPoint> list)
- {
- ConnectionTimeout = TimeSpan.FromSeconds(10);
- ReceiveTimeout = TimeSpan.FromSeconds(20);
- serverList = new List<IPEndPoint>();
- serverList.AddRange(list);
- }
-
- public List<MemcachedServerStats> GetAllServerStats()
- {
- List<MemcachedServerStats> resultList = new List<MemcachedServerStats>();
- foreach (IPEndPoint endPoint in serverList)
- {
- resultList.Add(GetServerStats(endPoint, ConnectionTimeout, ReceiveTimeout));
- }
- return resultList;
- }
-
- public static MemcachedServerStats GetServerStats(IPEndPoint ip, TimeSpan connectionTimeout, TimeSpan receiveTimeout)
- {
- MemcachedSocket socket = new MemcachedSocket(ip, connectionTimeout, receiveTimeout);
- MemcachedServerStats stats = socket.GetStats();
- return stats;
- }
-
- public static IPEndPoint Parse(string hostName,int port)
- {
- IPHostEntry host=Dns.GetHostEntry(hostName);
- IPEndPoint endPoint = null;
- foreach (IPAddress ip in host.AddressList)
- {
- if (ip.AddressFamily == AddressFamily.InterNetwork)
- {
- endPoint = new IPEndPoint(ip, port);
- break;
- }
- }
- return endPoint;
- }
- }
- /// <summary>
- /// Memcached服務器運行狀態數據類,只有當IsReachable爲true時獲取的數據纔有意義,不然表示不可訪問或者Memcached掛了
- /// </summary>
- public class MemcachedServerStats
- {
- private Dictionary<string, string> results;
- /// <summary>
- /// 是否可訪問,若是不可訪問表示網絡故障或者Memcached服務器Down掉了
- /// </summary>
- public bool IsReachable { get; set; }
- /// <summary>
- /// 服務器運行時間,單位秒(32u)
- /// </summary>
- public UInt32 Uptime { get; set; }
- /// <summary>
- /// 服務器當前的UNIX時間(32u)
- /// </summary>
- public UInt32 Time { get; set; }
- /// <summary>
- /// 服務器的版本號(string)
- /// </summary>
- public string Version { get; set; }
- /// <summary>
- /// 服務器當前存儲的內容數量(32u)
- /// </summary>
- public UInt32 Curr_Items { get; set; }
- /// <summary>
- /// 服務器啓動以來存儲過的內容總數(32u)
- /// </summary>
- public UInt32 Total_Items { get; set; }
- /// <summary>
- /// 鏈接數量(32u)
- /// </summary>
- public UInt32 Curr_Connections { get; set; }
- /// <summary>
- /// 服務器運行以來接受的鏈接總數(32u)
- /// </summary>
- public UInt32 Total_Connections { get; set; }
- /// <summary>
- /// 服務器分配的鏈接結構的數量(32u)
- /// </summary>
- public UInt32 Connection_Structures { get; set; }
- /// <summary>
- /// 取回請求總數(32u)
- /// </summary>
- public UInt32 Cmd_Get { get; set; }
- /// <summary>
- /// 存儲請求總數(32u)
- /// </summary>
- public UInt32 Cmd_Set { get; set; }
- /// <summary>
- /// 請求成功的總次數(32u)
- /// </summary>
- public UInt32 Get_Hits { get; set; }
- /// <summary>
- /// 請求失敗的總次數(32u)
- /// </summary>
- public UInt32 Get_Misses { get; set; }
- /// <summary>
- /// 服務器當前存儲內容所佔用的字節數(64u)
- /// </summary>
- public UInt64 Bytes { get; set; }
- /// <summary>
- /// 服務器從網絡讀取到的總字節數(64u)
- /// </summary>
- public UInt64 Bytes_Read { get; set; }
- /// <summary>
- /// 服務器向網絡發送的總字節數(64u)
- /// </summary>
- public UInt64 Bytes_Written { get; set; }
- /// <summary>
- /// 服務器在存儲時被容許使用的字節總數(32u)
- /// </summary>
- public UInt32 Limit_Maxbytes { get; set; }
- public IPEndPoint IPEndPoint { get; set; }
-
- public MemcachedServerStats(IPEndPoint endpoint)
- {
- if (endpoint == null)
- {
- throw new ArgumentNullException("endpoint can't be null");
- }
- IPEndPoint = endpoint;
- }
-
- public MemcachedServerStats(IPEndPoint endpoint, Dictionary<string, string> results)
- {
- if (endpoint == null || results == null)
- {
- throw new ArgumentNullException("point and result can't be null");
- }
- IPEndPoint = endpoint;
-
- }
-
- public void InitializeData(Dictionary<string, string> results)
- {
- if (results == null)
- {
- throw new ArgumentNullException("result can't be null");
- }
- this.results = results;
- Uptime = GetUInt32("uptime");
- Time = GetUInt32("time");
- Version = GetRaw("version");
- Curr_Items = GetUInt32("curr_items");
- Total_Items = GetUInt32("total_items");
- Curr_Connections = GetUInt32("curr_connections");
- Total_Connections = GetUInt32("total_connections");
- Connection_Structures = GetUInt32("connection_structures");
- Cmd_Get = GetUInt32("cmd_get");
- Cmd_Set = GetUInt32("cmd_set");
- Get_Hits = GetUInt32("get_hits");
- Get_Misses = GetUInt32("get_misses");
- Bytes = GetUInt64("bytes");
- Bytes_Read = GetUInt64("bytes_read");
- Bytes_Written = GetUInt64("bytes_written");
- Limit_Maxbytes = GetUInt32("limit_maxbytes");
- }
-
- private string GetRaw(string key)
- {
- string value = string.Empty;
- results.TryGetValue(key, out value);
- return value;
- }
-
- private UInt32 GetUInt32(string key)
- {
- string value = GetRaw(key);
- UInt32 uptime;
- UInt32.TryParse(value, out uptime);
- return uptime;
- }
-
- private UInt64 GetUInt64(string key)
- {
- string value = GetRaw(key);
- UInt64 uptime;
- UInt64.TryParse(value, out uptime);
- return uptime;
- }
- }
- /// <summary>
- /// 與Memcached服務器通信的Socket封裝
- /// </summary>
- internal class MemcachedSocket : IDisposable
- {
- private const string CommandString = "stats\r\n";//發送查詢Memcached狀態的指令,以"\r\n"做爲命令的結束
- private const int ErrorResponseLength = 13;
- private const string GenericErrorResponse = "ERROR";
- private const string ClientErrorResponse = "CLIENT_ERROR ";
- private const string ServerErrorResponse = "SERVER_ERROR ";
- private Socket socket;
- private IPEndPoint endpoint;
- private BufferedStream bufferedStream;
- private NetworkStream networkStream;
-
- public MemcachedSocket(IPEndPoint ip, TimeSpan connectionTimeout, TimeSpan receiveTimeout)
- {
- if (ip == null)
- {
- throw new ArgumentNullException("ip", "不能爲空!");
- }
- endpoint = ip;
-
- socket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
-
- socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, connectionTimeout == TimeSpan.MaxValue ? Timeout.Infinite : (int)connectionTimeout.TotalMilliseconds);
- socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, receiveTimeout == TimeSpan.MaxValue ? Timeout.Infinite : (int)receiveTimeout.TotalMilliseconds);
-
- // all operations are "atomic", we do not send small chunks of data
- socket.NoDelay = true;
- }
- /// <summary>
- /// 獲取Memcached的運行狀態
- /// </summary>
- /// <returns></returns>
- public MemcachedServerStats GetStats()
- {
- MemcachedServerStats stats = new MemcachedServerStats(endpoint);
- try
- {
- socket.Connect(endpoint);
- networkStream = new NetworkStream(socket);
- bufferedStream = new BufferedStream(networkStream);
- byte[] buffer = Encoding.ASCII.GetBytes(CommandString);
-
- SocketError socketError;
- socket.Send(buffer, 0, buffer.Length, SocketFlags.None, out socketError);
- if (socketError != SocketError.Success)
- {
- stats.IsReachable = false;
- }
- else
- {
- stats.IsReachable = true;
- string result = ReadLine();
- Dictionary<string, string> serverData = new Dictionary<string, string>(StringComparer.Ordinal);
- while (!string.IsNullOrEmpty(result))
- {
- // 返回的數據信息以"END"做爲結束標記
- if (String.Compare(result, "END", StringComparison.Ordinal) == 0)
- break;
-
- //指望的響應格式是:"STAT 名稱 值"(注意"STAT 名稱 值"之間有空格)
- if (result.Length < 6 || String.Compare(result, 0, "STAT ", 0, 5, StringComparison.Ordinal) != 0)
- {
- continue;
- }
-
- //獲取以空格做爲分隔符的鍵值對
- string[] parts = result.Remove(0, 5).Split(' ');
- if (parts.Length != 2)
- {
- continue;
- }
- serverData[parts[0]] = parts[1];
- result = ReadLine();
- }
- stats.InitializeData(serverData);
- }
- }
- catch (Exception exception)
- {
- stats.IsReachable = false;
- //Debug.WriteLine("Exception Message:" + exception.Message);
- }
- finally
- {
- }
- return stats;
-
- }
- /// <summary>
- /// 從遠程主機的響應流中讀取一行數據
- /// </summary>
- /// <returns></returns>
- private string ReadLine()
- {
- MemoryStream ms = new MemoryStream(50);
-
- bool gotR = false;
- byte[] buffer = new byte[1];
- int data;
-
- try
- {
- while (true)
- {
- data = bufferedStream.ReadByte();
-
- if (data == 13)
- {
- gotR = true;
- continue;
- }
-
- if (gotR)
- {
- if (data == 10)
- break;
-
- ms.WriteByte(13);
-
- gotR = false;
- }
-
- ms.WriteByte((byte)data);
- }
- }
- catch (IOException)
- {
-
- throw;
- }
-
- string retureValue = Encoding.ASCII.GetString(ms.GetBuffer(), 0, (int)ms.Length);
-
-
- if (String.IsNullOrEmpty(retureValue))
- throw new Exception("接收到空響應。");
-
- if (String.Compare(retureValue, GenericErrorResponse, StringComparison.Ordinal) == 0)
- throw new NotSupportedException("無效的指令。");
-
- if (retureValue.Length >= ErrorResponseLength)
- {
- if (String.Compare(retureValue, 0, ClientErrorResponse, 0, ErrorResponseLength, StringComparison.Ordinal) == 0)
- {
- throw new Exception(retureValue.Remove(0, ErrorResponseLength));
- }
- else if (String.Compare(retureValue, 0, ServerErrorResponse, 0, ErrorResponseLength, StringComparison.Ordinal) == 0)
- {
- throw new Exception(retureValue.Remove(0, ErrorResponseLength));
- }
- }
-
- return retureValue;
- }
-
- public void Dispose()
- {
- if (socket != null)
- {
- socket.Shutdown(SocketShutdown.Both);
- }
- socket = null;
- networkStream.Dispose();
- networkStream = null;
- bufferedStream.Dispose();
- bufferedStream = null;
- }
- }
- </script>
-
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <title>ASP.NET版Memcached監控工具</title>
- <style>
- a {
- color:#000000;
- text-decoration:none;
- }
- a.current {
- color:#0000FF;
- }
- a:hover {
- text-decoration: none;
- }
- body {
- font-family: verdana, geneva,tahoma, helvetica, arial, sans-serif;
- font-size: 100%;
- background-color:#FFFFFF;
- margin: 0em;
- }
- ul {
- font-size:80%;
- color:#666666;
- line-height: 1.5em;
- list-style: none;
- }
- </style>
- </head>
- <body>
- <table border="0">
- <tr><th>IP</th><th>Version</th><th>IsReachable</th><th>Bytes</th><th>Bytes_Read</th><th>Bytes_Written</th><th>Cmd_Get</th><th>Cmd_Set</th><th>Curr_Connections</th><th>Curr_Items</th><th>Get_Hits</th><th>Get_Misses</th><th>Limit_Maxbytes</th><th>Total_Items</th></tr>
- <%
- String format = "<tr><td>{0}</td><td>{1}</th><th>{2}</th><th>{3}</th><th>{4}</th><th>{5}</th><th>{6}</th><th>{7}</th><th>{8}</th><th>{9}</th><th>{10}</th><th>{11}</th><th>{12}</th><th>{13}</th></tr>";
- List<IPEndPoint> list = new List<IPEndPoint> { new IPEndPoint(IPAddress.Parse("127.0.0.1"), 11121), MemcachedMonitor.Parse("localhost",11131) };
- MemcachedMonitor monitor = new MemcachedMonitor(list);
- List<MemcachedServerStats> resultList = monitor.GetAllServerStats();
- string result=string.Empty;
- foreach (MemcachedServerStats stats in resultList)
- {
- result = string.Format(format, stats.IPEndPoint, stats.Version,stats.IsReachable, stats.Bytes, stats.Bytes_Read, stats.Bytes_Written, stats.Cmd_Get, stats.Cmd_Set, stats.Curr_Connections, stats.Curr_Items, stats.Get_Hits, stats.Get_Misses, stats.Limit_Maxbytes, stats.Total_Items);
- Response.Write(result);
- }
- %>
- </table>
- 這些數據所表明的意義以下:
- <ul>
- <li>pid:32u,服務器進程ID。</li>
- <li>uptime:32u, 服務器運行時間,單位秒。</li>
- <li>time :32u, 服務器當前的UNIX時間。</li>
- <li>version :string, 服務器的版本號。 </li>
- <li>curr_items :32u, 服務器當前存儲的內容數量</li>
- <li>total_items :32u, 服務器啓動以來存儲過的內容總數。</li>
- <li>bytes :64u, 服務器當前存儲內容所佔用的字節數。</li>
- <li>curr_connections :32u, 鏈接數量。 </li>
- <li>total_connections :32u, 服務器運行以來接受的鏈接總數。</li>
- <li>connection_structures:32u, 服務器分配的鏈接結構的數量。</li>
- <li>cmd_get :32u, 取回請求總數。 </li>
- <li>cmd_set :32u, 存儲請求總數。 </li>
- <li>get_hits :32u, 請求成功的總次數。</li>
- <li>get_misses :32u, 請求失敗的總次數。</li>
- <li>bytes_read :64u, 服務器從網絡讀取到的總字節數。</li>
- <li>bytes_written :64u, 服務器向網絡發送的總字節數。</li>
- <li>limit_maxbytes :32u, 服務器在存儲時被容許使用的字節總數。</li>
- </ul>
- 上面的描述中32u和64u表示32位和64位無符號整數,string表示是string類型數據。<br />
- 做者博客:<a href="http://blog.csdn.net/zhoufoxcn" target="_blank">CSDN博客</a>|<a href="http://zhoufoxcn.blog.51cto.com" target="_blank">51CTO博客</a>
- </body>
- </html>
說明:周公對CSS不太熟悉,因此沒有好好設計頁面的顯示效果,以能顯示各Memcached的運行狀態爲準。 總結:Memcached做爲一個很是不錯的分佈式緩存確實能很大程度上提升程序的性能。在上面的例子中有關檢測Memcached運行狀態數據的代碼可 以提取出來應用於WinForm或者Windows Service,一旦檢測出Memcached不可訪問能夠採起更靈活的方式,好比發送郵件到指定的郵箱,關於這一部分的功能相信你們都能輕易實現,因此 在這裏就再也不贅述了。