You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

371 lines
14 KiB

///-----------------------------------------------------------------------
/// <copyright file="WsSoapExtention.cs" company="Origtek">
/// 程式代號: WsSoapExtention
/// 程式名稱: Web Service 處理狀態監控
/// 程式說明: Web Service 處理狀態監控
/// 起始作者: Allen
/// 起始日期: 2016/06/28
/// </copyright>
///-----------------------------------------------------------------------
#region 程式異動記錄
/// xx.YYYY/MM/DD VER AUTHOR COMMENTS(說明修改的內容)
/// 01.2016/06/28 1.0 Allen CREATE
#endregion
using log4net;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.Services.Protocols;
namespace CounsellorBL.Helper
{
/// <summary>
/// 類別名稱: WsSoapExtention
/// 類別說明: Web Service 處理狀態監控
/// 起始作者: Allen
/// 起始日期: 2016/06/28
/// 最新修改人: Allen
/// 最新修日期: 2016/06/28
/// </summary>
public class WsSoapExtention : SoapExtension
{
#region 常數
/// <summary>
/// Web Service 最大執行時間,超過該時間,記錄Log
/// </summary>
private const int _iMaxRunningMilliseconds = 800;
/// <summary>
/// 記錄最近執行的Web Service的筆數
/// </summary>
private const int LAST_INVOKE_CAPACITY = 20;
/// <summary>
/// 用戶記錄最近執行的Web Service的筆數
/// </summary>
private const int USER_LAST_INVOKE_CAPACITY = 5;
#endregion
#region 私有的成員變數(靜態的用於統計調用的信息;非靜態的用於記錄當前調用的WS的信息)
/// <summary>
/// 記錄當前調用的WS的信息
/// </summary>
private WSInvokeInfo _oInvokeInfo = new WSInvokeInfo();
/// <summary>
/// 寫Log
/// </summary>
private static readonly ILog _log = LogManager.GetLogger(typeof(WsSoapExtention));
/// <summary>
/// 用Timer定時記錄超時執行的Web Service
/// </summary>
private static System.Threading.Timer _tTimerSnap;
/// <summary>
/// 執行中的Web Service
/// </summary>
private static ConcurrentDictionary<Guid, WSInvokeInfo> _diRunningWsMapping = new ConcurrentDictionary<Guid, WSInvokeInfo>();
/// <summary>
/// 記錄最近執行的20筆Web Service
/// </summary>
private static ConcurrentQueue<WSInvokeInfo> _quLastInvokeQueue = new ConcurrentQueue<WSInvokeInfo>();
/// <summary>
/// 按用戶IP統計用戶執行的Web Service
/// </summary>
private static ConcurrentDictionary<string, ConcurrentQueue<WSInvokeInfo>> _diRemoteUserHostInvokeMapping = new ConcurrentDictionary<string, ConcurrentQueue<WSInvokeInfo>>();
#endregion
#region 公開的成員變數
/// <summary>
/// 執行中的Web Service
/// </summary>
public static ConcurrentDictionary<Guid, WSInvokeInfo> RunningWsMapping { get { return _diRunningWsMapping; } }
/// <summary>
/// 記錄最近執行的20筆Web Service
/// </summary>
public static ConcurrentQueue<WSInvokeInfo> LastInvokeQueue { get { return _quLastInvokeQueue; } }
/// <summary>
/// 用戶IP列表
/// </summary>
public static RemoteHostInfo[] RemoteUserHosts { get { return _diRemoteUserHostInvokeMapping.ToList().ConvertAll(d => new RemoteHostInfo() { Host = d.Key, LastInvokes = d.Value.ToArray() }).ToArray(); } }
#endregion
#region 靜態構造方法,用於初始化靜態成員變數(WsSoapExtention)
/// <summary>
/// 函式名稱:WsSoapExtention
/// 函式說明:靜態構造方法,用於初始化靜態成員變數
/// 起始作者:Allen
/// 起始日期:2016/06/29
/// 最新修改人:Allen
/// 最新日期:2016/06/29
/// </summary>
static WsSoapExtention()
{
_tTimerSnap = new System.Threading.Timer(new TimerCallback(e =>
{
//if (RunningWsMapping.Count > 0)
//{
// var running = RunningWsMapping.ToList();
// running.ForEach(d =>
// {
// if (d.Value.UseTime.TotalMilliseconds > _iMaxRunningMilliseconds)
// {
// _log.WriteLog("WsSoapExtention : Web service invoke time is greater than max running time. InvokeInfo " + d.Value.ToString(), null, LoggingLevel.Warning);
// }
// });
//}
}));
_tTimerSnap.Change(1000, 1000);
}
#endregion
#region SoapExtension需重寫的方法
public override System.IO.Stream ChainStream(System.IO.Stream stream) { return stream; }
public override object GetInitializer(Type serviceType) { return null; }
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute) { return null; }
public override void Initialize(object initializer) { }
#region SoapExtension的重寫方法,用於記錄WS的執行信息(ProcessMessage)
/// <summary>
/// 函式名稱:ProcessMessage
/// 函式說明:SoapExtension的重寫方法,用於記錄WS的執行信息
/// 起始作者:Allen
/// 起始日期:2016/06/29
/// 最新修改人:Allen
/// 最新日期:2016/06/29
/// </summary>
public override void ProcessMessage(SoapMessage message)
{
if (message is SoapClientMessage)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
break;
case SoapMessageStage.BeforeDeserialize:
break;
case SoapMessageStage.AfterDeserialize:
break;
default:
throw new Exception("No stage such as this.");
}
}
else if (message is SoapServerMessage)
{
SoapServerMessage ssmMsg = (SoapServerMessage)message;
WSInvokeInfo oRemovedInvokeInfo;
switch (message.Stage)
{
case SoapMessageStage.BeforeDeserialize:
break;
case SoapMessageStage.AfterDeserialize:
//開始執行時間
_oInvokeInfo.BeginInvokeTime = DateTime.Now;
//執行的WS方法名
_oInvokeInfo.MethodName = message.MethodInfo.Name;
//用戶IP
_oInvokeInfo.UserHostAddress = HttpContext.Current.Request.UserHostAddress;
//參數
_oInvokeInfo.Args = message.MethodInfo.InParameters.ToDictionary(o => o.Name, o => message.GetInParameterValue(o.Position));
//將記錄加入執行中的WS映射中
_diRunningWsMapping.TryAdd(this._oInvokeInfo.Id, this._oInvokeInfo);
//將記錄插入最近執行的隊列中
_quLastInvokeQueue.Enqueue(_oInvokeInfo);
if (_quLastInvokeQueue.Count > LAST_INVOKE_CAPACITY)
{
//若隊列超過記錄上限,移除對頭
_quLastInvokeQueue.TryDequeue(out oRemovedInvokeInfo);
}
//將記錄插入用戶最近執行的隊列中
ConcurrentQueue<WSInvokeInfo> quUserLastInvokeQueue = _diRemoteUserHostInvokeMapping.GetOrAdd(_oInvokeInfo.UserHostAddress, new ConcurrentQueue<WSInvokeInfo>());
quUserLastInvokeQueue.Enqueue(_oInvokeInfo);
if (quUserLastInvokeQueue.Count > USER_LAST_INVOKE_CAPACITY)
{
//若隊列超過記錄上限,移除對頭
quUserLastInvokeQueue.TryDequeue(out oRemovedInvokeInfo);
}
break;
case SoapMessageStage.BeforeSerialize:
//結束執行時間
_oInvokeInfo.EndInvokeTime = DateTime.Now;
//將記錄從執行中的WS映射中移除
_diRunningWsMapping.TryRemove(_oInvokeInfo.Id, out oRemovedInvokeInfo);
//若當前WS的執行時間超過最大執行時間,寫Log
//if (_oInvokeInfo.UseTime.TotalMilliseconds > _iMaxRunningMilliseconds)
//{
// _log.WriteLog("WsSoapExtention : Web service invoke time is greater than max running time. InvokeInfo " + _oInvokeInfo.ToString(), null, LoggingLevel.Debug);
//}
break;
case SoapMessageStage.AfterSerialize:
break;
default:
throw new Exception("No stage such as this");
}
}
}
#endregion
#endregion
}
#region Web Service 執行信息記錄(WSInvokeInfo)
/// <summary>
/// 類別名稱: WSInvokeInfo
/// 類別說明: Web Service 執行信息記錄
/// 起始作者: Allen
/// 起始日期: 2016/06/29
/// 最新修改人: Allen
/// 最新修日期: 2016/06/29
/// </summary>
public class WSInvokeInfo
{
/// <summary>
/// ID
/// </summary>
public Guid Id { get; private set; }
/// <summary>
/// 開始執行時間
/// </summary>
public DateTime BeginInvokeTime { get; set; }
/// <summary>
/// 結束執行時間
/// </summary>
public DateTime? EndInvokeTime { get; set; }
/// <summary>
/// 使用時間
/// </summary>
public TimeSpan UseTime { get { return (EndInvokeTime.HasValue ? EndInvokeTime.Value : DateTime.Now) - BeginInvokeTime; } }
/// <summary>
/// Ws方法名
/// </summary>
public string MethodName { get; set; }
/// <summary>
/// 用戶IP
/// </summary>
public string UserHostAddress { get; set; }
/// <summary>
/// 參數
/// </summary>
public Dictionary<string, object> Args { get; set; }
/// <summary>
/// 函式名稱:WSInvokeInfo
/// 函式說明:構造方法
/// 起始作者:Allen
/// 起始日期:2016/06/29
/// 最新修改人:Allen
/// 最新日期:2016/06/29
/// </summary>
public WSInvokeInfo()
{
Id = Guid.NewGuid();
Args = new Dictionary<string, object>();
}
/// <summary>
/// 函式名稱:GetArgsDesc
/// 函式說明:獲取參數描述信息
/// 起始作者:Allen
/// 起始日期:2016/06/29
/// 最新修改人:Allen
/// 最新日期:2016/06/29
/// </summary>
/// <returns>參數描述信息</returns>
public string GetArgsDesc()
{
List<string> lsArgsDesc = Args.ToList().ConvertAll(d => string.Format("{0}:={1}", d.Key, d.Value == null ? "null" : (d.Value is ICollection ? GetString((ICollection)d.Value) : d.Value.ToString())));
string sAllArgsDesck = string.Join(", ", lsArgsDesc);
return sAllArgsDesck;
}
/// <summary>
/// 顯示對象信息
/// </summary>
/// <returns></returns>
public override string ToString()
{
string sAllArgsDesck = GetArgsDesc();
return string.Format("Host: {0}, BeginInvokeTime: {1}, EndInvokeTime: {2}, UseTime: {3}, MethodName: {4}, Arg: {5}", UserHostAddress ?? "none", BeginInvokeTime, EndInvokeTime, UseTime.TotalMilliseconds, MethodName ?? "unkown", sAllArgsDesck);
}
/// <summary>
/// 函式名稱:GetString
/// 函式說明:顯示集合的信息
/// 起始作者:Allen
/// 起始日期:2016/06/29
/// 最新修改人:Allen
/// 最新日期:2016/06/29
/// </summary>
/// <param name="i_cCollection">集合</param>
/// <returns>集合的描述信息</returns>
private static string GetString(ICollection i_cCollection)
{
List<string> lsResult = new List<string>();
IEnumerator iterValues = i_cCollection.GetEnumerator();
while (iterValues.MoveNext())
{
if (iterValues == null)
{
lsResult.Add("null");
}
else if (iterValues.Current is ICollection)
{
lsResult.Add(GetString((ICollection)iterValues.Current));
}
else
{
lsResult.Add(iterValues.Current.ToString());
}
}
return string.Concat("[", string.Join(", ", lsResult.ToArray()), "]");
}
}
#endregion
#region 調用WS的用戶信息(RemoteHostInfo)
/// <summary>
/// 類別名稱: RemoteHostInfo
/// 類別說明: 調用WS的用戶信息
/// 起始作者: Allen
/// 起始日期: 2016/06/29
/// 最新修改人: Allen
/// 最新修日期: 2016/06/29
/// </summary>
public class RemoteHostInfo
{
/// <summary>
/// IP
/// </summary>
public string Host { get; set; }
/// <summary>
/// 記錄最後調用的WS
/// </summary>
public WSInvokeInfo[] LastInvokes { get; set; }
}
#endregion
}