///----------------------------------------------------------------------- /// /// 程式代號: WsSoapExtention /// 程式名稱: Web Service 處理狀態監控 /// 程式說明: Web Service 處理狀態監控 /// 起始作者: Allen /// 起始日期: 2016/06/28 /// ///----------------------------------------------------------------------- #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 { /// /// 類別名稱: WsSoapExtention /// 類別說明: Web Service 處理狀態監控 /// 起始作者: Allen /// 起始日期: 2016/06/28 /// 最新修改人: Allen /// 最新修日期: 2016/06/28 /// public class WsSoapExtention : SoapExtension { #region 常數 /// /// Web Service 最大執行時間,超過該時間,記錄Log /// private const int _iMaxRunningMilliseconds = 800; /// /// 記錄最近執行的Web Service的筆數 /// private const int LAST_INVOKE_CAPACITY = 20; /// /// 用戶記錄最近執行的Web Service的筆數 /// private const int USER_LAST_INVOKE_CAPACITY = 5; #endregion #region 私有的成員變數(靜態的用於統計調用的信息;非靜態的用於記錄當前調用的WS的信息) /// /// 記錄當前調用的WS的信息 /// private WSInvokeInfo _oInvokeInfo = new WSInvokeInfo(); /// /// 寫Log /// private static readonly ILog _log = LogManager.GetLogger(typeof(WsSoapExtention)); /// /// 用Timer定時記錄超時執行的Web Service /// private static System.Threading.Timer _tTimerSnap; /// /// 執行中的Web Service /// private static ConcurrentDictionary _diRunningWsMapping = new ConcurrentDictionary(); /// /// 記錄最近執行的20筆Web Service /// private static ConcurrentQueue _quLastInvokeQueue = new ConcurrentQueue(); /// /// 按用戶IP統計用戶執行的Web Service /// private static ConcurrentDictionary> _diRemoteUserHostInvokeMapping = new ConcurrentDictionary>(); #endregion #region 公開的成員變數 /// /// 執行中的Web Service /// public static ConcurrentDictionary RunningWsMapping { get { return _diRunningWsMapping; } } /// /// 記錄最近執行的20筆Web Service /// public static ConcurrentQueue LastInvokeQueue { get { return _quLastInvokeQueue; } } /// /// 用戶IP列表 /// public static RemoteHostInfo[] RemoteUserHosts { get { return _diRemoteUserHostInvokeMapping.ToList().ConvertAll(d => new RemoteHostInfo() { Host = d.Key, LastInvokes = d.Value.ToArray() }).ToArray(); } } #endregion #region 靜態構造方法,用於初始化靜態成員變數(WsSoapExtention) /// /// 函式名稱:WsSoapExtention /// 函式說明:靜態構造方法,用於初始化靜態成員變數 /// 起始作者:Allen /// 起始日期:2016/06/29 /// 最新修改人:Allen /// 最新日期:2016/06/29 /// 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) /// /// 函式名稱:ProcessMessage /// 函式說明:SoapExtension的重寫方法,用於記錄WS的執行信息 /// 起始作者:Allen /// 起始日期:2016/06/29 /// 最新修改人:Allen /// 最新日期:2016/06/29 /// 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 quUserLastInvokeQueue = _diRemoteUserHostInvokeMapping.GetOrAdd(_oInvokeInfo.UserHostAddress, new ConcurrentQueue()); 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) /// /// 類別名稱: WSInvokeInfo /// 類別說明: Web Service 執行信息記錄 /// 起始作者: Allen /// 起始日期: 2016/06/29 /// 最新修改人: Allen /// 最新修日期: 2016/06/29 /// public class WSInvokeInfo { /// /// ID /// public Guid Id { get; private set; } /// /// 開始執行時間 /// public DateTime BeginInvokeTime { get; set; } /// /// 結束執行時間 /// public DateTime? EndInvokeTime { get; set; } /// /// 使用時間 /// public TimeSpan UseTime { get { return (EndInvokeTime.HasValue ? EndInvokeTime.Value : DateTime.Now) - BeginInvokeTime; } } /// /// Ws方法名 /// public string MethodName { get; set; } /// /// 用戶IP /// public string UserHostAddress { get; set; } /// /// 參數 /// public Dictionary Args { get; set; } /// /// 函式名稱:WSInvokeInfo /// 函式說明:構造方法 /// 起始作者:Allen /// 起始日期:2016/06/29 /// 最新修改人:Allen /// 最新日期:2016/06/29 /// public WSInvokeInfo() { Id = Guid.NewGuid(); Args = new Dictionary(); } /// /// 函式名稱:GetArgsDesc /// 函式說明:獲取參數描述信息 /// 起始作者:Allen /// 起始日期:2016/06/29 /// 最新修改人:Allen /// 最新日期:2016/06/29 /// /// 參數描述信息 public string GetArgsDesc() { List 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; } /// /// 顯示對象信息 /// /// 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); } /// /// 函式名稱:GetString /// 函式說明:顯示集合的信息 /// 起始作者:Allen /// 起始日期:2016/06/29 /// 最新修改人:Allen /// 最新日期:2016/06/29 /// /// 集合 /// 集合的描述信息 private static string GetString(ICollection i_cCollection) { List lsResult = new List(); 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) /// /// 類別名稱: RemoteHostInfo /// 類別說明: 調用WS的用戶信息 /// 起始作者: Allen /// 起始日期: 2016/06/29 /// 最新修改人: Allen /// 最新修日期: 2016/06/29 /// public class RemoteHostInfo { /// /// IP /// public string Host { get; set; } /// /// 記錄最後調用的WS /// public WSInvokeInfo[] LastInvokes { get; set; } } #endregion }