WebService接口攔截方案


一、背景

       目前考慮到產品基於歷史原因,采用的接口webservice+webapi混合,webservice接口涉及產品winform端的業務,尤為重要,經常性出現winform端的一些性能問題,極為苦惱,想在接口端做一些性能監控以輔助分析。

 

二、思路

      產品為單體架構,非平台級,目前能想到的方案就是結合webservice提供的擴展點來做請求攔截處理,分析記錄當前調用接口在調用時入參,調用時間點,耗時,調用來自的winform端等信息,記錄到客戶本地庫,再定時將統計信息上傳到公司服務器,過程中需考慮到寫本地庫對客戶本地庫造成的影響,已經上傳公司服務器帶來的並發。

      1.  對於寫入本地庫,默認產品不寫入監控數據到本地庫即不啟用性能監控,只有當需要進行監控時才通過公司平台進行啟用,當然這里涉及到的前提是要收集到客戶的產品外網地址,結合產品提供的接口來進行啟用。

      2.  考慮到接口數量目前6百多,可能要對接口涉及的業務類型進行划分,讓自己可以單獨啟用關注的業務模塊接口,或者啟動監控所有接口。

      3.  數據寫入考慮到數據量對本地數據庫的沖擊,第一是寫入性能數據給接口帶來的損耗 第二 不能 影響接口的調用即考慮監控功能的異常處理 第三 數據量的增長,可考慮定時清理,保留1周的數據

      4.  統計數據的上傳到公司服務器,考慮當天上傳前一天的性能統計數據以減小數據量,考慮到客戶體量,上傳時間點盡量分散且控制的客戶業務較閑時段,服務器接收考慮峰值,可采用MQ來削峰以保證服務器平穩。

 

三、 實現

      webservice 提供的擴展點SoapExtension ,有兩種攔截方式,1種是擴展后將擴展點寫入web.config文件,但這樣的自由度不強,他會攔截所有接口,不法達到只攔截部分業務接口的目標。要達到目標就需要采用第2種方式 結合SoapExtensionAttribute 來對接口打上標簽並同時對接口分類,不廢話,上代碼:

 1 public class TraceAttribute : SoapExtensionAttribute
 2 {
 3     TraceLevel _level = TraceLevel.NONE;
 4     public TraceLevel TraceLevel// 業務類別
 5     {
 6         get { return _level; }
 7         set { _level = value; }
 8     }
 9 
10 
11     int _priority = 1;
12 
13     public override int Priority
14     {
15         get
16         {
17             return _priority;
18         }
19         set
20         {
21             _priority = value;
22         }
23     }
24 
25     public override Type ExtensionType
26     {
27         get { return typeof(WSMonitorExtension); }//定義擴展soap的類型
28     }
29 }

      針對TraceLevel 這里的一個小技巧就是,將其枚舉值按2的指數級賦值,這樣在接口上打標簽時可以多個業務標簽同時打,比如: TraceLevel.A | TraceLevel.B

      假設 A業務類型枚舉值 2的1次方  即 00000010

             B業務類型枚舉值 2的2次方  即  00000100

     則 A | B = 00000010

                      00000100

      即 00000110

      此時在SoapExtension 的擴展中判斷是否需要記錄監控數據時, 就可以根據設置的監控業務類型來進行 "與" 運算來判斷是否需要記錄,比如: 設置了監控A 級別業務接口即

       00000110 & 00000010 == 00000010 可以看出需要記錄監控數據,此時就把數據異步寫入本地庫中。

       還要說一個的就是對參數的解析,目前采用解析成json進行存儲,這里就涉及到參數對象的轉換,盡量考慮全一點,比如 datatable,dataset,枚舉,空等情況,借用網上代碼,稍作修改,奉上:

       

 1 public static object ConvertToObject(object obj, Type type)
 2     {
 3         if (type == null) return obj;
 4         if (obj == null) return type.IsValueType ? Activator.CreateInstance(type) : null;
 5 
 6         Type underlyingType = Nullable.GetUnderlyingType(type);
 7         if (type.IsAssignableFrom(obj.GetType()))
 8         {
 9             return obj;
10         } else if ((underlyingType ?? type).IsEnum) // 如果待轉換的對象的基類型為枚舉
11         {
12             if (underlyingType != null && string.IsNullOrEmpty(obj.ToString())) // 如果目標類型為可空枚舉,並且待轉換對象為null 則直接返回null值
13             {
14                 return null;
15             }
16             else
17             {
18                 return Enum.Parse(underlyingType ?? type, obj.ToString());
19             }
20         }else if (typeof(IConvertible).IsAssignableFrom(underlyingType ?? type)) // 如果目標類型的基類型實現了IConvertible,則直接轉換
21         {
22             try
23             {
24                 return Convert.ChangeType(obj, underlyingType ?? type, null);
25             }
26             catch
27             {
28                 return underlyingType == null ? Activator.CreateInstance(type) : null;
29             }
30         }
31         elseelse if (typeof(IConvertible).IsAssignableFrom(underlyingType ?? type)) // 如果目標類型的基類型實現了IConvertible,則直接轉換
32         {
33             try
34             {
35                 return Convert.ChangeType(obj, underlyingType ?? type, null);
36             }
37             catch
38             {
39                 return underlyingType == null ? Activator.CreateInstance(type) : null;
40             }
41         }
42         else {
43             TypeConverter converter = TypeDescriptor.GetConverter(type);
44             if (converter.CanConvertFrom(obj.GetType()))
45             {
46                 return converter.ConvertFrom(obj);
47             }
48             ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
49             if (constructor != null)
50             {object o = constructor.Invoke(null);
51                 PropertyInfo[] propertys = type.GetProperties();
52                 Type oldType = obj.GetType();
53                 foreach (PropertyInfo property in propertys)
54                 {
55                     PropertyInfo p = oldType.GetProperty(property.Name);
56                     if (property.CanWrite && p != null && p.CanRead)
57                     {
58                         property.SetValue(o, ConvertToObject(p.GetValue(obj, null), property.PropertyType), null);
59                     }
60                 }
61                 return o;
62             }
63         }
64         return obj;
65     }

 

      對於 統計數據寫入公司平台服務器,第一控制 客戶端上傳的時間點盡量分散以減小並發帶來的壓力,第二 采用rabbitmq 接受數據,並寫入服務器,服務端這邊采用站點形式供查詢統計數據,接口監控開啟設置等,涉及的技術棧: .net core+webapi+elementui+vuejs+rabbitmq 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM