• ASP.NET版Memcached监控工具(转载)


    在上一篇文章《使用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页面。
    程序代码如下:

     

    1. <%@ Page Language="C#" %> 
    2. <%@ Import Namespace="System" %> 
    3. <%@ Import Namespace="System.IO" %> 
    4. <%@ Import Namespace="System.Net" %> 
    5. <%@ Import Namespace="System.Net.Sockets" %> 
    6. <%@ Import Namespace="System.Collections.Generic" %> 
    7. <%@ Import Namespace="System.Threading" %> 
    8. <%@ Import Namespace="System.Security" %> 
    9. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
    10.  
    11. <script runat="server"> 
    12. /*  
    13.  * 作者:周公  
    14.  * 日期:2011-03-27  
    15.  * 原文出处:http://blog.csdn.net/zhoufoxcn 或http://zhoufoxcn.blog.51cto.com  
    16.  * 版权说明:本文可以在保留原文出处的情况下使用于非商业用途,周公对此不作任何担保或承诺。  
    17.  * */  
    18.       
    19.     /// <summary> 
    20.     /// Memcached服务器监控类  
    21.     /// </summary> 
    22.     public class MemcachedMonitor  
    23.     {  
    24.         /// <summary> 
    25.         /// 连接Memcached的超时时间  
    26.         /// </summary> 
    27.         public TimeSpan ConnectionTimeout { get; set; }  
    28.         /// <summary> 
    29.         /// 接收Memcached返回数据的超时时间  
    30.         /// </summary> 
    31.         public TimeSpan ReceiveTimeout { get; set; }  
    32.         private List<IPEndPoint> serverList;  
    33.         public MemcachedMonitor(ICollection<IPEndPoint> list)  
    34.         {  
    35.             ConnectionTimeout = TimeSpan.FromSeconds(10);  
    36.             ReceiveTimeout = TimeSpan.FromSeconds(20);  
    37.             serverList = new List<IPEndPoint>();  
    38.             serverList.AddRange(list);  
    39.         }  
    40.  
    41.         public List<MemcachedServerStats> GetAllServerStats()  
    42.         {  
    43.             List<MemcachedServerStats> resultList = new List<MemcachedServerStats>();  
    44.             foreach (IPEndPoint endPoint in serverList)  
    45.             {  
    46.                 resultList.Add(GetServerStats(endPoint, ConnectionTimeout, ReceiveTimeout));  
    47.             }  
    48.             return resultList;  
    49.         }  
    50.  
    51.         public static MemcachedServerStats GetServerStats(IPEndPoint ip, TimeSpan connectionTimeout, TimeSpan receiveTimeout)  
    52.         {  
    53.             MemcachedSocket socket = new MemcachedSocket(ip, connectionTimeout, receiveTimeout);  
    54.             MemcachedServerStats stats = socket.GetStats();  
    55.             return stats;  
    56.         }  
    57.  
    58.         public static IPEndPoint Parse(string hostName,int port)  
    59.         {  
    60.             IPHostEntry host=Dns.GetHostEntry(hostName);  
    61.             IPEndPoint endPoint = null;  
    62.             foreach (IPAddress ip in host.AddressList)  
    63.             {  
    64.                 if (ip.AddressFamily == AddressFamily.InterNetwork)  
    65.                 {  
    66.                     endPoint = new IPEndPoint(ip, port);  
    67.                     break;  
    68.                 }  
    69.             }  
    70.             return endPoint;  
    71.         }  
    72.     }  
    73.     /// <summary> 
    74.     /// Memcached服务器运行状态数据类,只有当IsReachable为true时获取的数据才有意义,否则表示不可访问或者Memcached挂了  
    75.     /// </summary> 
    76.     public class MemcachedServerStats  
    77.     {  
    78.         private Dictionary<string, string> results;  
    79.         /// <summary> 
    80.         /// 是否可访问,如果不可访问表示网络故障或者Memcached服务器Down掉了  
    81.         /// </summary> 
    82.         public bool IsReachable { get; set; }  
    83.         /// <summary> 
    84.         /// 服务器运行时间,单位秒(32u)  
    85.         /// </summary> 
    86.         public UInt32 Uptime { get; set; }  
    87.         /// <summary> 
    88.         /// 服务器当前的UNIX时间(32u)  
    89.         /// </summary> 
    90.         public UInt32 Time { get; set; }  
    91.         /// <summary> 
    92.         /// 服务器的版本号(string)  
    93.         /// </summary> 
    94.         public string Version { get; set; }  
    95.         /// <summary> 
    96.         /// 服务器当前存储的内容数量(32u)  
    97.         /// </summary> 
    98.         public UInt32 Curr_Items { get; set; }  
    99.         /// <summary> 
    100.         /// 服务器启动以来存储过的内容总数(32u)  
    101.         /// </summary> 
    102.         public UInt32 Total_Items { get; set; }  
    103.         /// <summary> 
    104.         /// 连接数量(32u)  
    105.         /// </summary> 
    106.         public UInt32 Curr_Connections { get; set; }  
    107.         /// <summary> 
    108.         /// 服务器运行以来接受的连接总数(32u)  
    109.         /// </summary> 
    110.         public UInt32 Total_Connections { get; set; }  
    111.         /// <summary> 
    112.         /// 服务器分配的连接结构的数量(32u)  
    113.         /// </summary> 
    114.         public UInt32 Connection_Structures { get; set; }  
    115.         /// <summary> 
    116.         /// 取回请求总数(32u)  
    117.         /// </summary> 
    118.         public UInt32 Cmd_Get { get; set; }  
    119.         /// <summary> 
    120.         /// 存储请求总数(32u)  
    121.         /// </summary> 
    122.         public UInt32 Cmd_Set { get; set; }  
    123.         /// <summary> 
    124.         /// 请求成功的总次数(32u)  
    125.         /// </summary> 
    126.         public UInt32 Get_Hits { get; set; }  
    127.         /// <summary> 
    128.         /// 请求失败的总次数(32u)  
    129.         /// </summary> 
    130.         public UInt32 Get_Misses { get; set; }  
    131.         /// <summary> 
    132.         /// 服务器当前存储内容所占用的字节数(64u)  
    133.         /// </summary> 
    134.         public UInt64 Bytes { get; set; }  
    135.         /// <summary> 
    136.         /// 服务器从网络读取到的总字节数(64u)  
    137.         /// </summary> 
    138.         public UInt64 Bytes_Read { get; set; }  
    139.         /// <summary> 
    140.         /// 服务器向网络发送的总字节数(64u)  
    141.         /// </summary> 
    142.         public UInt64 Bytes_Written { get; set; }  
    143.         /// <summary> 
    144.         /// 服务器在存储时被允许使用的字节总数(32u)  
    145.         /// </summary> 
    146.         public UInt32 Limit_Maxbytes { get; set; }  
    147.         public IPEndPoint IPEndPoint { get; set; }  
    148.  
    149.         public MemcachedServerStats(IPEndPoint endpoint)  
    150.         {  
    151.             if (endpoint == null)  
    152.             {  
    153.                 throw new ArgumentNullException("endpoint can't be null");  
    154.             }  
    155.             IPEndPoint = endpoint;  
    156.         }  
    157.  
    158.         public MemcachedServerStats(IPEndPoint endpoint, Dictionary<string, string> results)  
    159.         {  
    160.             if (endpoint == null || results == null)  
    161.             {  
    162.                 throw new ArgumentNullException("point and result can't be null");  
    163.             }  
    164.             IPEndPoint = endpoint;  
    165.  
    166.         }  
    167.  
    168.         public void InitializeData(Dictionary<string, string> results)  
    169.         {  
    170.             if (results == null)  
    171.             {  
    172.                 throw new ArgumentNullException("result can't be null");  
    173.             }  
    174.             this.results = results;  
    175.             Uptime = GetUInt32("uptime");  
    176.             Time = GetUInt32("time");  
    177.             Version = GetRaw("version");  
    178.             Curr_Items = GetUInt32("curr_items");  
    179.             Total_Items = GetUInt32("total_items");  
    180.             Curr_Connections = GetUInt32("curr_connections");  
    181.             Total_Connections = GetUInt32("total_connections");  
    182.             Connection_Structures = GetUInt32("connection_structures");  
    183.             Cmd_Get = GetUInt32("cmd_get");  
    184.             Cmd_Set = GetUInt32("cmd_set");  
    185.             Get_Hits = GetUInt32("get_hits");  
    186.             Get_Misses = GetUInt32("get_misses");  
    187.             Bytes = GetUInt64("bytes");  
    188.             Bytes_Read = GetUInt64("bytes_read");  
    189.             Bytes_Written = GetUInt64("bytes_written");  
    190.             Limit_Maxbytes = GetUInt32("limit_maxbytes");  
    191.         }  
    192.  
    193.         private string GetRaw(string key)  
    194.         {  
    195.             string value = string.Empty;  
    196.             results.TryGetValue(key, out value);  
    197.             return value;  
    198.         }  
    199.  
    200.         private UInt32 GetUInt32(string key)  
    201.         {  
    202.             string value = GetRaw(key);  
    203.             UInt32 uptime;  
    204.             UInt32.TryParse(value, out uptime);  
    205.             return uptime;  
    206.         }  
    207.  
    208.         private UInt64 GetUInt64(string key)  
    209.         {  
    210.             string value = GetRaw(key);  
    211.             UInt64 uptime;  
    212.             UInt64.TryParse(value, out uptime);  
    213.             return uptime;  
    214.         }  
    215.     }  
    216.     /// <summary> 
    217.     /// 与Memcached服务器通讯的Socket封装  
    218.     /// </summary> 
    219.     internal class MemcachedSocket : IDisposable  
    220.     {  
    221.         private const string CommandString = "stats ";//发送查询Memcached状态的指令,以" "作为命令的结束  
    222.         private const int ErrorResponseLength = 13;  
    223.         private const string GenericErrorResponse = "ERROR";  
    224.         private const string ClientErrorResponse = "CLIENT_ERROR ";  
    225.         private const string ServerErrorResponse = "SERVER_ERROR ";  
    226.         private Socket socket;  
    227.         private IPEndPoint endpoint;  
    228.         private BufferedStream bufferedStream;  
    229.         private NetworkStream networkStream;  
    230.  
    231.         public MemcachedSocket(IPEndPoint ip, TimeSpan connectionTimeout, TimeSpan receiveTimeout)  
    232.         {  
    233.             if (ip == null)  
    234.             {  
    235.                 throw new ArgumentNullException("ip", "不能为空!");  
    236.             }  
    237.             endpoint = ip;  
    238.  
    239.             socket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);  
    240.  
    241.             socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, connectionTimeout == TimeSpan.MaxValue ? Timeout.Infinite : (int)connectionTimeout.TotalMilliseconds);  
    242.             socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, receiveTimeout == TimeSpan.MaxValue ? Timeout.Infinite : (int)receiveTimeout.TotalMilliseconds);  
    243.  
    244.             // all operations are "atomic", we do not send small chunks of data  
    245.             socket.NoDelay = true;  
    246.         }  
    247.         /// <summary> 
    248.         /// 获取Memcached的运行状态  
    249.         /// </summary> 
    250.         /// <returns></returns> 
    251.         public MemcachedServerStats GetStats()  
    252.         {  
    253.             MemcachedServerStats stats = new MemcachedServerStats(endpoint);  
    254.             try  
    255.             {  
    256.                 socket.Connect(endpoint);  
    257.                 networkStream = new NetworkStream(socket);  
    258.                 bufferedStream = new BufferedStream(networkStream);  
    259.                 byte[] buffer = Encoding.ASCII.GetBytes(CommandString);  
    260.  
    261.                 SocketError socketError;  
    262.                 socket.Send(buffer, 0, buffer.Length, SocketFlags.None, out socketError);  
    263.                 if (socketError != SocketError.Success)  
    264.                 {  
    265.                     stats.IsReachable = false;  
    266.                 }  
    267.                 else  
    268.                 {  
    269.                     stats.IsReachable = true;  
    270.                     string result = ReadLine();  
    271.                     Dictionary<string, string> serverData = new Dictionary<string, string>(StringComparer.Ordinal);  
    272.                     while (!string.IsNullOrEmpty(result))  
    273.                     {  
    274.                         // 返回的数据信息以"END"作为结束标记  
    275.                         if (String.Compare(result, "END", StringComparison.Ordinal) == 0)  
    276.                             break;  
    277.  
    278.                         //期望的响应格式是:"STAT 名称 值"(注意"STAT 名称 值"之间有空格)  
    279.                         if (result.Length < 6 || String.Compare(result, 0, "STAT ", 0, 5, StringComparison.Ordinal) != 0)  
    280.                         {  
    281.                             continue;  
    282.                         }  
    283.  
    284.                         //获取以空格作为分隔符的键值对  
    285.                         string[] parts = result.Remove(0, 5).Split(' ');  
    286.                         if (parts.Length != 2)  
    287.                         {  
    288.                             continue;  
    289.                         }  
    290.                         serverData[parts[0]] = parts[1];  
    291.                         result = ReadLine();  
    292.                     }  
    293.                     stats.InitializeData(serverData);  
    294.                 }  
    295.             }  
    296.             catch (Exception exception)  
    297.             {  
    298.                 stats.IsReachable = false;  
    299.                 //Debug.WriteLine("Exception Message:" + exception.Message);  
    300.             }  
    301.             finally  
    302.             {  
    303.             }  
    304.             return stats;  
    305.  
    306.         }  
    307.         /// <summary> 
    308.         /// 从远程主机的响应流中读取一行数据  
    309.         /// </summary> 
    310.         /// <returns></returns> 
    311.         private string ReadLine()  
    312.         {  
    313.             MemoryStream ms = new MemoryStream(50);  
    314.  
    315.             bool gotR = false;  
    316.             byte[] buffer = new byte[1];  
    317.             int data;  
    318.  
    319.             try  
    320.             {  
    321.                 while (true)  
    322.                 {  
    323.                     data = bufferedStream.ReadByte();  
    324.  
    325.                     if (data == 13)  
    326.                     {  
    327.                         gotR = true;  
    328.                         continue;  
    329.                     }  
    330.  
    331.                     if (gotR)  
    332.                     {  
    333.                         if (data == 10)  
    334.                             break;  
    335.  
    336.                         ms.WriteByte(13);  
    337.  
    338.                         gotR = false;  
    339.                     }  
    340.  
    341.                     ms.WriteByte((byte)data);  
    342.                 }  
    343.             }  
    344.             catch (IOException)  
    345.             {  
    346.  
    347.                 throw;  
    348.             }  
    349.  
    350.             string retureValue = Encoding.ASCII.GetString(ms.GetBuffer(), 0, (int)ms.Length);  
    351.  
    352.  
    353.             if (String.IsNullOrEmpty(retureValue))  
    354.                 throw new Exception("接收到空响应。");  
    355.  
    356.             if (String.Compare(retureValue, GenericErrorResponse, StringComparison.Ordinal) == 0)  
    357.                 throw new NotSupportedException("无效的指令。");  
    358.  
    359.             if (retureValue.Length >= ErrorResponseLength)  
    360.             {  
    361.                 if (String.Compare(retureValue, 0, ClientErrorResponse, 0, ErrorResponseLength, StringComparison.Ordinal) == 0)  
    362.                 {  
    363.                     throw new Exception(retureValue.Remove(0, ErrorResponseLength));  
    364.                 }  
    365.                 else if (String.Compare(retureValue, 0, ServerErrorResponse, 0, ErrorResponseLength, StringComparison.Ordinal) == 0)  
    366.                 {  
    367.                     throw new Exception(retureValue.Remove(0, ErrorResponseLength));  
    368.                 }  
    369.             }  
    370.  
    371.             return retureValue;  
    372.         }  
    373.  
    374.         public void Dispose()  
    375.         {  
    376.             if (socket != null)  
    377.             {  
    378.                 socket.Shutdown(SocketShutdown.Both);  
    379.             }  
    380.             socket = null;  
    381.             networkStream.Dispose();  
    382.             networkStream = null;  
    383.             bufferedStream.Dispose();  
    384.             bufferedStream = null;  
    385.         }  
    386.     }  
    387. </script> 
    388.  
    389. <html xmlns="http://www.w3.org/1999/xhtml"> 
    390. <head> 
    391.     <title>ASP.NET版Memcached监控工具</title> 
    392.     <style> 
    393.         a {  
    394.     color:#000000;  
    395.     text-decoration:none;  
    396. }  
    397. a.current {  
    398.     color:#0000FF;  
    399. }  
    400. a:hover {  
    401.     text-decoration: none;  
    402. }  
    403. body {  
    404.     font-family: verdana, geneva,tahoma, helvetica, arial, sans-serif;  
    405.     font-size: 100%;  
    406.     background-color:#FFFFFF;  
    407.     margin: 0em;  
    408. }  
    409. ul {  
    410.     font-size:80%;  
    411.     color:#666666;  
    412.     line-height: 1.5em;  
    413.     list-style: none;  
    414. }  
    415.     </style> 
    416. </head> 
    417. <body> 
    418. <table border="0"> 
    419. <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> 
    420. <%  
    421.     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>";  
    422.     List<IPEndPoint> list = new List<IPEndPoint> { new IPEndPoint(IPAddress.Parse("127.0.0.1"), 11121), MemcachedMonitor.Parse("localhost",11131) };  
    423.     MemcachedMonitor monitor = new MemcachedMonitor(list);  
    424.     List<MemcachedServerStats> resultList = monitor.GetAllServerStats();  
    425.     string result=string.Empty;  
    426.     foreach (MemcachedServerStats stats in resultList)  
    427.     {  
    428.         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);  
    429.         Response.Write(result);  
    430.     }  
    431. %> 
    432. </table> 
    433. 这些数据所代表的意义如下:  
    434. <ul> 
    435. <li>pid:32u,服务器进程ID。</li>   
    436. <li>uptime:32u, 服务器运行时间,单位秒。</li>   
    437. <li>time :32u, 服务器当前的UNIX时间。</li> 
    438. <li>version :string, 服务器的版本号。 </li> 
    439. <li>curr_items :32u, 服务器当前存储的内容数量</li>   
    440. <li>total_items :32u, 服务器启动以来存储过的内容总数。</li> 
    441. <li>bytes :64u, 服务器当前存储内容所占用的字节数。</li> 
    442. <li>curr_connections :32u, 连接数量。 </li> 
    443. <li>total_connections :32u, 服务器运行以来接受的连接总数。</li> 
    444. <li>connection_structures:32u, 服务器分配的连接结构的数量。</li>   
    445. <li>cmd_get :32u, 取回请求总数。 </li> 
    446. <li>cmd_set :32u, 存储请求总数。 </li> 
    447. <li>get_hits :32u, 请求成功的总次数。</li> 
    448. <li>get_misses :32u, 请求失败的总次数。</li> 
    449. <li>bytes_read :64u, 服务器从网络读取到的总字节数。</li> 
    450. <li>bytes_written :64u, 服务器向网络发送的总字节数。</li> 
    451. <li>limit_maxbytes :32u, 服务器在存储时被允许使用的字节总数。</li> 
    452. </ul> 
    453. 上面的描述中32u和64u表示32位和64位无符号整数,string表示是string类型数据。<br /> 
    454. 作者博客:<a href="http://blog.csdn.net/zhoufoxcn" target="_blank">CSDN博客</a>|<a href="http://zhoufoxcn.blog.51cto.com" target="_blank">51CTO博客</a> 
    455. </body> 
    456. </html> 
     
    说明:周公对CSS不太熟悉,所以没有好好设计页面的显示效果,以能显示各Memcached的运行状态为准。
    总结:Memcached作为一个非常不错的分布式缓存确实能很大程度上提高程序的性能。在上面的例子中有关检测Memcached运行状态数据的代码可 以提取出来应用于WinForm或者Windows Service,一旦检测出Memcached不可访问可以采取更灵活的方式,比如发送邮件到指定的邮箱,关于这一部分的功能相信大家都能轻易实现,所以 在这里就不再赘述了。
  • 相关阅读:
    中国剩余定理(crt)和扩展中国剩余定理(excrt)
    数论集合
    gcd(欧几里得算法)与exgcd(扩展欧几里得算法)
    青蛙的约会
    【杭电多校第七场】A + B = C
    【XDOJ】小W的塔防
    备战省赛组队训练赛第十四场(UPC)
    2019.4.27浙江省赛
    备战省赛组队训练赛第六场(UPC)
    备战省赛组队训练赛第七场(UPC)
  • 原文地址:https://www.cnblogs.com/sandea/p/3294261.html
Copyright © 2020-2023  润新知