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

  1. ///-----------------------------------------------------------------------
  2. /// <copyright file="WsSoapExtention.cs" company="Origtek">
  3. /// 程式代號: WsSoapExtention
  4. /// 程式名稱: Web Service 處理狀態監控
  5. /// 程式說明: Web Service 處理狀態監控
  6. /// 起始作者: Allen
  7. /// 起始日期: 2016/06/28
  8. /// </copyright>
  9. ///-----------------------------------------------------------------------
  10. #region 程式異動記錄
  11. /// xx.YYYY/MM/DD VER AUTHOR COMMENTS(說明修改的內容)
  12. /// 01.2016/06/28 1.0 Allen CREATE
  13. #endregion
  14. using log4net;
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Concurrent;
  18. using System.Collections.Generic;
  19. using System.Configuration;
  20. using System.Linq;
  21. using System.Threading;
  22. using System.Web;
  23. using System.Web.Services.Protocols;
  24. namespace CounsellorBL.Helper
  25. {
  26. /// <summary>
  27. /// 類別名稱: WsSoapExtention
  28. /// 類別說明: Web Service 處理狀態監控
  29. /// 起始作者: Allen
  30. /// 起始日期: 2016/06/28
  31. /// 最新修改人: Allen
  32. /// 最新修日期: 2016/06/28
  33. /// </summary>
  34. public class WsSoapExtention : SoapExtension
  35. {
  36. #region 常數
  37. /// <summary>
  38. /// Web Service 最大執行時間,超過該時間,記錄Log
  39. /// </summary>
  40. private const int _iMaxRunningMilliseconds = 800;
  41. /// <summary>
  42. /// 記錄最近執行的Web Service的筆數
  43. /// </summary>
  44. private const int LAST_INVOKE_CAPACITY = 20;
  45. /// <summary>
  46. /// 用戶記錄最近執行的Web Service的筆數
  47. /// </summary>
  48. private const int USER_LAST_INVOKE_CAPACITY = 5;
  49. #endregion
  50. #region 私有的成員變數(靜態的用於統計調用的信息;非靜態的用於記錄當前調用的WS的信息)
  51. /// <summary>
  52. /// 記錄當前調用的WS的信息
  53. /// </summary>
  54. private WSInvokeInfo _oInvokeInfo = new WSInvokeInfo();
  55. /// <summary>
  56. /// 寫Log
  57. /// </summary>
  58. private static readonly ILog _log = LogManager.GetLogger(typeof(WsSoapExtention));
  59. /// <summary>
  60. /// 用Timer定時記錄超時執行的Web Service
  61. /// </summary>
  62. private static System.Threading.Timer _tTimerSnap;
  63. /// <summary>
  64. /// 執行中的Web Service
  65. /// </summary>
  66. private static ConcurrentDictionary<Guid, WSInvokeInfo> _diRunningWsMapping = new ConcurrentDictionary<Guid, WSInvokeInfo>();
  67. /// <summary>
  68. /// 記錄最近執行的20筆Web Service
  69. /// </summary>
  70. private static ConcurrentQueue<WSInvokeInfo> _quLastInvokeQueue = new ConcurrentQueue<WSInvokeInfo>();
  71. /// <summary>
  72. /// 按用戶IP統計用戶執行的Web Service
  73. /// </summary>
  74. private static ConcurrentDictionary<string, ConcurrentQueue<WSInvokeInfo>> _diRemoteUserHostInvokeMapping = new ConcurrentDictionary<string, ConcurrentQueue<WSInvokeInfo>>();
  75. #endregion
  76. #region 公開的成員變數
  77. /// <summary>
  78. /// 執行中的Web Service
  79. /// </summary>
  80. public static ConcurrentDictionary<Guid, WSInvokeInfo> RunningWsMapping { get { return _diRunningWsMapping; } }
  81. /// <summary>
  82. /// 記錄最近執行的20筆Web Service
  83. /// </summary>
  84. public static ConcurrentQueue<WSInvokeInfo> LastInvokeQueue { get { return _quLastInvokeQueue; } }
  85. /// <summary>
  86. /// 用戶IP列表
  87. /// </summary>
  88. public static RemoteHostInfo[] RemoteUserHosts { get { return _diRemoteUserHostInvokeMapping.ToList().ConvertAll(d => new RemoteHostInfo() { Host = d.Key, LastInvokes = d.Value.ToArray() }).ToArray(); } }
  89. #endregion
  90. #region 靜態構造方法,用於初始化靜態成員變數(WsSoapExtention)
  91. /// <summary>
  92. /// 函式名稱:WsSoapExtention
  93. /// 函式說明:靜態構造方法,用於初始化靜態成員變數
  94. /// 起始作者:Allen
  95. /// 起始日期:2016/06/29
  96. /// 最新修改人:Allen
  97. /// 最新日期:2016/06/29
  98. /// </summary>
  99. static WsSoapExtention()
  100. {
  101. _tTimerSnap = new System.Threading.Timer(new TimerCallback(e =>
  102. {
  103. //if (RunningWsMapping.Count > 0)
  104. //{
  105. // var running = RunningWsMapping.ToList();
  106. // running.ForEach(d =>
  107. // {
  108. // if (d.Value.UseTime.TotalMilliseconds > _iMaxRunningMilliseconds)
  109. // {
  110. // _log.WriteLog("WsSoapExtention : Web service invoke time is greater than max running time. InvokeInfo " + d.Value.ToString(), null, LoggingLevel.Warning);
  111. // }
  112. // });
  113. //}
  114. }));
  115. _tTimerSnap.Change(1000, 1000);
  116. }
  117. #endregion
  118. #region SoapExtension需重寫的方法
  119. public override System.IO.Stream ChainStream(System.IO.Stream stream) { return stream; }
  120. public override object GetInitializer(Type serviceType) { return null; }
  121. public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute) { return null; }
  122. public override void Initialize(object initializer) { }
  123. #region SoapExtension的重寫方法,用於記錄WS的執行信息(ProcessMessage)
  124. /// <summary>
  125. /// 函式名稱:ProcessMessage
  126. /// 函式說明:SoapExtension的重寫方法,用於記錄WS的執行信息
  127. /// 起始作者:Allen
  128. /// 起始日期:2016/06/29
  129. /// 最新修改人:Allen
  130. /// 最新日期:2016/06/29
  131. /// </summary>
  132. public override void ProcessMessage(SoapMessage message)
  133. {
  134. if (message is SoapClientMessage)
  135. {
  136. switch (message.Stage)
  137. {
  138. case SoapMessageStage.BeforeSerialize:
  139. break;
  140. case SoapMessageStage.AfterSerialize:
  141. break;
  142. case SoapMessageStage.BeforeDeserialize:
  143. break;
  144. case SoapMessageStage.AfterDeserialize:
  145. break;
  146. default:
  147. throw new Exception("No stage such as this.");
  148. }
  149. }
  150. else if (message is SoapServerMessage)
  151. {
  152. SoapServerMessage ssmMsg = (SoapServerMessage)message;
  153. WSInvokeInfo oRemovedInvokeInfo;
  154. switch (message.Stage)
  155. {
  156. case SoapMessageStage.BeforeDeserialize:
  157. break;
  158. case SoapMessageStage.AfterDeserialize:
  159. //開始執行時間
  160. _oInvokeInfo.BeginInvokeTime = DateTime.Now;
  161. //執行的WS方法名
  162. _oInvokeInfo.MethodName = message.MethodInfo.Name;
  163. //用戶IP
  164. _oInvokeInfo.UserHostAddress = HttpContext.Current.Request.UserHostAddress;
  165. //參數
  166. _oInvokeInfo.Args = message.MethodInfo.InParameters.ToDictionary(o => o.Name, o => message.GetInParameterValue(o.Position));
  167. //將記錄加入執行中的WS映射中
  168. _diRunningWsMapping.TryAdd(this._oInvokeInfo.Id, this._oInvokeInfo);
  169. //將記錄插入最近執行的隊列中
  170. _quLastInvokeQueue.Enqueue(_oInvokeInfo);
  171. if (_quLastInvokeQueue.Count > LAST_INVOKE_CAPACITY)
  172. {
  173. //若隊列超過記錄上限,移除對頭
  174. _quLastInvokeQueue.TryDequeue(out oRemovedInvokeInfo);
  175. }
  176. //將記錄插入用戶最近執行的隊列中
  177. ConcurrentQueue<WSInvokeInfo> quUserLastInvokeQueue = _diRemoteUserHostInvokeMapping.GetOrAdd(_oInvokeInfo.UserHostAddress, new ConcurrentQueue<WSInvokeInfo>());
  178. quUserLastInvokeQueue.Enqueue(_oInvokeInfo);
  179. if (quUserLastInvokeQueue.Count > USER_LAST_INVOKE_CAPACITY)
  180. {
  181. //若隊列超過記錄上限,移除對頭
  182. quUserLastInvokeQueue.TryDequeue(out oRemovedInvokeInfo);
  183. }
  184. break;
  185. case SoapMessageStage.BeforeSerialize:
  186. //結束執行時間
  187. _oInvokeInfo.EndInvokeTime = DateTime.Now;
  188. //將記錄從執行中的WS映射中移除
  189. _diRunningWsMapping.TryRemove(_oInvokeInfo.Id, out oRemovedInvokeInfo);
  190. //若當前WS的執行時間超過最大執行時間,寫Log
  191. //if (_oInvokeInfo.UseTime.TotalMilliseconds > _iMaxRunningMilliseconds)
  192. //{
  193. // _log.WriteLog("WsSoapExtention : Web service invoke time is greater than max running time. InvokeInfo " + _oInvokeInfo.ToString(), null, LoggingLevel.Debug);
  194. //}
  195. break;
  196. case SoapMessageStage.AfterSerialize:
  197. break;
  198. default:
  199. throw new Exception("No stage such as this");
  200. }
  201. }
  202. }
  203. #endregion
  204. #endregion
  205. }
  206. #region Web Service 執行信息記錄(WSInvokeInfo)
  207. /// <summary>
  208. /// 類別名稱: WSInvokeInfo
  209. /// 類別說明: Web Service 執行信息記錄
  210. /// 起始作者: Allen
  211. /// 起始日期: 2016/06/29
  212. /// 最新修改人: Allen
  213. /// 最新修日期: 2016/06/29
  214. /// </summary>
  215. public class WSInvokeInfo
  216. {
  217. /// <summary>
  218. /// ID
  219. /// </summary>
  220. public Guid Id { get; private set; }
  221. /// <summary>
  222. /// 開始執行時間
  223. /// </summary>
  224. public DateTime BeginInvokeTime { get; set; }
  225. /// <summary>
  226. /// 結束執行時間
  227. /// </summary>
  228. public DateTime? EndInvokeTime { get; set; }
  229. /// <summary>
  230. /// 使用時間
  231. /// </summary>
  232. public TimeSpan UseTime { get { return (EndInvokeTime.HasValue ? EndInvokeTime.Value : DateTime.Now) - BeginInvokeTime; } }
  233. /// <summary>
  234. /// Ws方法名
  235. /// </summary>
  236. public string MethodName { get; set; }
  237. /// <summary>
  238. /// 用戶IP
  239. /// </summary>
  240. public string UserHostAddress { get; set; }
  241. /// <summary>
  242. /// 參數
  243. /// </summary>
  244. public Dictionary<string, object> Args { get; set; }
  245. /// <summary>
  246. /// 函式名稱:WSInvokeInfo
  247. /// 函式說明:構造方法
  248. /// 起始作者:Allen
  249. /// 起始日期:2016/06/29
  250. /// 最新修改人:Allen
  251. /// 最新日期:2016/06/29
  252. /// </summary>
  253. public WSInvokeInfo()
  254. {
  255. Id = Guid.NewGuid();
  256. Args = new Dictionary<string, object>();
  257. }
  258. /// <summary>
  259. /// 函式名稱:GetArgsDesc
  260. /// 函式說明:獲取參數描述信息
  261. /// 起始作者:Allen
  262. /// 起始日期:2016/06/29
  263. /// 最新修改人:Allen
  264. /// 最新日期:2016/06/29
  265. /// </summary>
  266. /// <returns>參數描述信息</returns>
  267. public string GetArgsDesc()
  268. {
  269. 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())));
  270. string sAllArgsDesck = string.Join(", ", lsArgsDesc);
  271. return sAllArgsDesck;
  272. }
  273. /// <summary>
  274. /// 顯示對象信息
  275. /// </summary>
  276. /// <returns></returns>
  277. public override string ToString()
  278. {
  279. string sAllArgsDesck = GetArgsDesc();
  280. return string.Format("Host: {0}, BeginInvokeTime: {1}, EndInvokeTime: {2}, UseTime: {3}, MethodName: {4}, Arg: {5}", UserHostAddress ?? "none", BeginInvokeTime, EndInvokeTime, UseTime.TotalMilliseconds, MethodName ?? "unkown", sAllArgsDesck);
  281. }
  282. /// <summary>
  283. /// 函式名稱:GetString
  284. /// 函式說明:顯示集合的信息
  285. /// 起始作者:Allen
  286. /// 起始日期:2016/06/29
  287. /// 最新修改人:Allen
  288. /// 最新日期:2016/06/29
  289. /// </summary>
  290. /// <param name="i_cCollection">集合</param>
  291. /// <returns>集合的描述信息</returns>
  292. private static string GetString(ICollection i_cCollection)
  293. {
  294. List<string> lsResult = new List<string>();
  295. IEnumerator iterValues = i_cCollection.GetEnumerator();
  296. while (iterValues.MoveNext())
  297. {
  298. if (iterValues == null)
  299. {
  300. lsResult.Add("null");
  301. }
  302. else if (iterValues.Current is ICollection)
  303. {
  304. lsResult.Add(GetString((ICollection)iterValues.Current));
  305. }
  306. else
  307. {
  308. lsResult.Add(iterValues.Current.ToString());
  309. }
  310. }
  311. return string.Concat("[", string.Join(", ", lsResult.ToArray()), "]");
  312. }
  313. }
  314. #endregion
  315. #region 調用WS的用戶信息(RemoteHostInfo)
  316. /// <summary>
  317. /// 類別名稱: RemoteHostInfo
  318. /// 類別說明: 調用WS的用戶信息
  319. /// 起始作者: Allen
  320. /// 起始日期: 2016/06/29
  321. /// 最新修改人: Allen
  322. /// 最新修日期: 2016/06/29
  323. /// </summary>
  324. public class RemoteHostInfo
  325. {
  326. /// <summary>
  327. /// IP
  328. /// </summary>
  329. public string Host { get; set; }
  330. /// <summary>
  331. /// 記錄最後調用的WS
  332. /// </summary>
  333. public WSInvokeInfo[] LastInvokes { get; set; }
  334. }
  335. #endregion
  336. }