日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

NET插件系统之四——提升系统搜索插件和启动速度的思考

發布時間:2025/5/22 windows 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NET插件系统之四——提升系统搜索插件和启动速度的思考 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

一. 面臨的問題

  開發插件系統的主要優勢是擴展性,我們不需要為系統模塊的集成再多費腦筋,但這也帶來了額外的問題。通常,系統需要在每次啟動時搜索固定目錄下的符合要求的插件。但是,當系統變得越來越龐大,所引用的dll文件越來越多時,就會出現很嚴重的問題:開啟時間慢,性能差,用戶體驗降低,尤其是在調試程序時,會浪費大量寶貴的時間。

  我確確實實的面臨了這樣的問題,有興趣的讀者可以看看我的插件系列文章的前幾篇,這兩天痛定思痛,決心提升系統搜索插件的性能。

  我們先看一段普通的搜索插件的代碼:

?  

1 public void GetAllPluginInPath(string Path, string InterFaceName) 2 { 3 var DllFileName = from file in Directory.GetFileSystemEntries(Path) 4 where file.Contains(".dll") 5 select file; 6 7 8 9 //string[] DllFileName = Directory.GetFileSystemEntries(Path); 10 Type[] types; 11 foreach (string file in DllFileName) 12 { 13 14 if (System.IO.Path.GetExtension(file) == ".dll") 15 { 16 Assembly assembly; 17 18 try 19 { 20 assembly = Assembly.LoadFrom(file); 21 } 22 catch 23 { 24 continue; 25 } 26 27 try 28 { 29 types = assembly.GetTypes(); 30 } 31 catch (Exception ex) 32 { 33 continue; 34 } 35 36 foreach (Type type in types) 37 { 38 if (type.GetInterface(InterFaceName) != null && !type.IsAbstract) 39 { 40 object thisObject = Activator.CreateInstance(type); 41 42 43 IXPlugin rc1 = thisObject as IXPlugin; 44 45 46 //如果要在啟動時被加載 47 if (rc1 != null && rc1.isStartLoaded) 48 { 49 AddPlugin(rc1); 50 } 51 } 52 } 53 } 54 } 55 }

?

  造成啟動慢的主要原因有:

  1. 目錄下包含大量dll文件(這是因為項目引用了大量第三方庫),它們并不包含我們開發的組件,卻白白浪費大量搜索時間。有些dll文件不是托管dll,在獲取程序集時還會拋出異常,直接捕獲后,也會造成時間損失。

  2. 上述代碼僅搜索了滿足一種接口規范的插件, (見函數的參數InterFaceName)。如果不止一種插件類型,那么可能要做很多次這樣的查找,對性能的影響更大。

????? 3. 為了獲取插件的一些信息(比如是否要在啟動時加載),不得不實例化其對象獲取字段,這種性能損失也是不能承受的。

二. 解決途徑

???? 找到問題,我們對癥下藥:

  1.成熟的軟件系統采用了插件樹的機制,將插件存儲為樹結構,包含父子關系,這樣能盡可能的提升搜索和加載性能,同時方便管理,比如Ecilpse。 但是,這種復雜的插件管理機制可能不適用于我們開發的輕量級系統,因此我們僅僅考慮扁平化的插件結構。

  2. 雖然插件的數量是經常變化的,但通常加載的dll文件種類很少變化。我們可以考慮把實際包含所需插件的dll文件名列表存儲起來,從而在搜索時僅搜索這些固定的dll文件,提升性能。

  3. 插件的種類可能多種多樣,所以我們希望能一次性獲得全部類型的插件。

  4. 采用.NET4.0的并行庫機制實現插件的并行搜索。

三.?插件結構表述

  該部分已經在我的插件系列文章的.NET插件系統之二——不實例化獲取插件信息和可視化方法?中進行了描述,主要是標記接口名稱的InterfaceAttribute 和 標記實現接口的插件的XFrmWorkAttribute,你需要在插件接口和插件實現的類上添加這兩類標識,此處不再贅述。

  我們定義兩個數據結構存儲插件名稱和插件信息:

????

/// <summary>/// 為了便于序列化而簡化的插件接口數據類型,是簡化的InterfaceAttribute/// </summary> [Serializable]public class PluginNameLite{public string myName { get; set; }public SearchStrategy mySearchStrategy { get; set; }public string detailInfo { get; set; }public PluginNameLite(){}public PluginNameLite(InterfaceAttribute attr){myName = attr.myName;mySearchStrategy = attr.mySearchStrategy;detailInfo = attr.DetailInfo;}}/// <summary>/// 插件集合/// </summary>public class PluginCollection : ObservableCollection<XFrmWorkAttribute>{public PluginCollection(): base(){}/// <summary>/// 可以被序列化的簡化插件字典,僅包含插件接口名稱和搜索策略/// </summary>static List<PluginNameLite> myPluginNameList = new List<PluginNameLite>();/// <summary>/// 插件字典/// </summary>static Dictionary<Type, PluginCollection> mPluginDictionary = new Dictionary<Type, PluginCollection>();

?

四. 插件搜索的方法

  我們將插件搜索的步驟分為兩步:

  1. 搜索所有接口契約(即搜索所有的接口)

  

/// <summary>/// 獲取所有的插件接口契約名稱/// </summary>/// <param name="Path"></param>/// <param name="InterFaceName"></param>public static void GetAllPluginName(string folderLocation, bool isRecursiveDirectory){List<PluginNameLite> mPluginNameList = new List<PluginNameLite>(); //緩存所有插件名稱if (!isRecursiveDirectory) {try //如果不執行遞歸搜索,則查看在目錄下是否有保存了插件名稱的文件,若有,直接反序列化之,不執行插件名稱搜索 { mPluginNameList = CustomSerializer.Deserialize<List<PluginNameLite>>(folderLocation + "\\PluginLog.xml"); myPluginNameList.AddRange(mPluginNameList);return;}catch (Exception ex){}}var DllFile = from file in Directory.GetFileSystemEntries(folderLocation) //若無緩存文件,獲取目錄下全部的dll文件執行搜索where file.Contains(".dll")select file;Parallel.ForEach(DllFile, //并行化處理file =>{Type[] types;Assembly assembly;try{assembly = Assembly.LoadFrom(file);}catch{return;}try{types = assembly.GetTypes();}catch{return;}foreach (Type type in types){if (type.IsInterface == false)continue;// Iterate through all the Attributes for each method.foreach (Attribute attr intype.GetCustomAttributes(typeof(InterfaceAttribute), false)){mPluginNameList.Add(new PluginNameLite(attr as InterfaceAttribute));}}});if (isRecursiveDirectory) ////執行遞歸搜索 {foreach (var dir in Directory.GetDirectories(folderLocation)){GetAllPluginName(dir, isRecursiveDirectory);}}else //保存當前目錄下的插件名稱 {CustomSerializer.Serialize(mPluginNameList, folderLocation + "\\PluginLog.xml");myPluginNameList.AddRange(mPluginNameList);}}

??? 流程圖如下:

?

  2. 搜索所有實現接口契約的插件

? ?直接用代碼說話

/// <summary>/// 獲取所有插件/// </summary>/// <param name="folderLocation"></param>/// <param name="isRecursiveDirectory">是否進行目錄遞歸搜索</param>public static void GetAllPlugins(string folderLocation, bool isRecursiveDirectory){bool isSaved = false; //是否已經保存了包含插件的dll文件列表List<string> mPluginFileList = new List<string>(); //包含插件的dll文件列表List<string> allDllFileList = new List<string>(); //所有dll文件列表if (!isRecursiveDirectory){try{mPluginFileList = CustomSerializer.Deserialize<List<string>>(folderLocation + "\\PluginFileLog.xml");isSaved = true;}catch (Exception ex){allDllFileList = (from file in Directory.GetFileSystemEntries(folderLocation)where file.Contains(".dll")select file).ToList();}}else{allDllFileList = (from file in Directory.GetFileSystemEntries(folderLocation)where file.Contains(".dll")select file).ToList(); ;}Type[] types;IEnumerable<string> dllPluginFils; //最終要進行處理的的dll文件if (mPluginFileList.Count == 0) //如果不存在插件文件目錄,則獲取該目錄下所有dll文件dllPluginFils = allDllFileList;elsedllPluginFils = from file in mPluginFileListselect folderLocation + file; //否則將插件文件名稱拼接為完整的文件路徑 Parallel.ForEach(dllPluginFils,file =>{Assembly assembly;try{assembly = Assembly.LoadFrom(file);}catch{return;}try{types = assembly.GetTypes();}catch{return;}foreach (Type type in types){if (type.IsInterface == true)continue;Type interfaceType = null;string interfaceName = null;foreach (var interfacename in myPluginNameList) //對該Type,依次查看是否實現了插件名稱列表中的接口 {interfaceType = type.GetInterface(interfacename.myName);if (interfaceType != null){interfaceName = interfacename.myName;// Iterate through all the Attributes for each method.foreach (Attribute attr intype.GetCustomAttributes(typeof(XFrmWorkAttribute), false)) //獲取該插件的XFrmWorkAttribute標識 {XFrmWorkAttribute attr2 = attr as XFrmWorkAttribute;attr2.myType = type; //將其類型賦值給XFrmWorkAttributeif (attr2.MainKind != interfaceName){continue;}PluginCollection pluginInfo = null; //保存到插件字典當中if (mPluginDictionary.TryGetValue(interfaceType, out pluginInfo)){pluginInfo.Add(attr2); //若插件字典中已包含了該interfaceType的鍵,則直接添加 }else{var collection = new PluginCollection();collection.Add(attr2);mPluginDictionary.Add(interfaceType, collection); //否則新建一項并添加之 }file = file.Replace(folderLocation, ""); //獲取文件在該文件夾下的真實名稱if (!mPluginFileList.Contains(file)) //若插件文件列表中不包含此文件則添加到文件目錄中 mPluginFileList.Add(file);goto FINISH;}}}FINISH:;}});if (isRecursiveDirectory) //執行遞歸搜索 {foreach (var dir in Directory.GetDirectories(folderLocation)){GetAllPlugins(dir, isRecursiveDirectory);}}else{if (!isSaved) //若沒有保存插件文件目錄,則反序列化保存之。CustomSerializer.Serialize(mPluginFileList, folderLocation + "\\PluginFileLog.xml");}}

??? 由于篇幅有限,搜索插件的流程與搜索插件名稱的流程基本相同,因此省略流程圖。
  3. 并行化優化

  讀者可以看到,在搜索不同dll文件的插件時 ,使用了?Parallel.ForEach ,網上介紹該方法的文章很多,此處不再贅述。 同時,系統直接將所有插件一次性的搜索完成。大幅度的提升了搜索速度。

????

五. 總結和問題

  總結:

  1. 系統盡可能的減少了對插件本身的限制,通過添加attribute的方式即可標記插件,減少了對原生代碼的修改。

  2. 實測表明,文件目錄下存在60個左右的dll文件,其中只有6個是作者完成的包含插件的文件,?在I7 2600K的電腦上:(Debug版本)

    原始版本的插件搜索和實例化需要將近5s的啟動時間  

????????? 通過緩存文件目錄和插件目錄,時間減少2.7s

?  ?? 通過并行化搜索dll文件下的插件,時間進一步減少1s

?????? ?最終,啟動時間僅僅為1.3s左右,同時還避免了多次搜索插件

???? 存在的問題:

  1. 插件系統可以自動檢測出同一dll文件中插件的變化,但在緩存了dll文件名之后,是無法自動檢測出dll文件的變化的。這種情況下,需要首先刪除記錄插件名稱和文件的緩存xml文件才能檢測出來。

  2. 依然有一定的性能提升的余地。   

  以下是我的插件搜索器的完整代碼,歡迎有問題隨時交流:

???

完整的插件搜索器代碼 using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks;namespace XFrmWork.Data {/// <summary>/// 插件集合/// </summary>public class PluginCollection : ObservableCollection<XFrmWorkAttribute>{public PluginCollection(): base(){}}/// <summary>/// 執行搜索策略/// </summary>public enum SearchStrategy{/// <summary>/// 目錄內搜索/// </summary> FolderSearch,/// <summary>/// 目錄內遞歸搜索/// </summary> RecursiveFolderSearch,}/// <summary>/// 該類定義了插件系統的接口契約記錄/// </summary>public class InterfaceAttribute : Attribute{/// <summary>/// 該插件接口的名稱/// </summary>public string myName { get; set; }/// <summary>/// 搜索策略/// </summary>public SearchStrategy mySearchStrategy { get; set; }/// <summary>/// 相關信息/// </summary>public string DetailInfo { get; set; }public InterfaceAttribute(string thisName, string thisDetailInfo, SearchStrategy thisSearchStrategy)// 定位參數 {this.myName = thisName;this.DetailInfo = thisDetailInfo;this.mySearchStrategy = thisSearchStrategy;}}/// <summary>/// 為了便于序列化而簡化的插件接口數據類型,是簡化的InterfaceAttribute/// </summary> [Serializable]public class PluginNameLite{public string myName { get; set; }public SearchStrategy mySearchStrategy { get; set; }public string detailInfo { get; set; }public PluginNameLite(){}public PluginNameLite(InterfaceAttribute attr){myName = attr.myName;mySearchStrategy = attr.mySearchStrategy;detailInfo = attr.DetailInfo;}}/// <summary>/// 單例模式提供的插件搜索器/// </summary>public class PluginProvider{PluginProvider(){}/// <summary>/// 可以被序列化的簡化插件字典,僅包含插件接口名稱和搜索策略/// </summary>static List<PluginNameLite> myPluginNameList = new List<PluginNameLite>();/// <summary>/// 插件字典/// </summary>static Dictionary<Type, PluginCollection> mPluginDictionary = new Dictionary<Type, PluginCollection>();/// <summary>/// 獲取某插件在插件目錄中的索引號/// </summary>/// <param name="interfaceName">接口名稱</param>/// <param name="className">類名</param>/// <returns></returns>public static int GetObjectIndex(Type interfaceName, Type className){foreach (var rc in GetPluginCollection(interfaceName)){if (rc.myType == className)return GetPluginCollection(interfaceName).IndexOf(rc);}return 100;}/// <summary>/// 獲取所有的插件接口契約名稱/// </summary>/// <param name="Path"></param>/// <param name="InterFaceName"></param>public static void GetAllPluginName(string folderLocation, bool isRecursiveDirectory){List<PluginNameLite> mPluginNameList = new List<PluginNameLite>(); //緩存所有插件名稱if (!isRecursiveDirectory){try //如果不執行遞歸搜索,則查看在目錄下是否有保存了插件名稱的文件,若有,直接反序列化之,不執行插件名稱搜索 {mPluginNameList = CustomSerializer.Deserialize<List<PluginNameLite>>(folderLocation + "\\PluginLog.xml");myPluginNameList.AddRange(mPluginNameList);return;}catch (Exception ex){}}var DllFile = from file in Directory.GetFileSystemEntries(folderLocation) //若無緩存文件,獲取目錄下全部的dll文件執行搜索where file.Contains(".dll")select file;Parallel.ForEach(DllFile, //并行化處理file =>{Type[] types;Assembly assembly;try{assembly = Assembly.LoadFrom(file);}catch{return;}try{types = assembly.GetTypes();}catch{return;}foreach (Type type in types){if (type.IsInterface == false)continue;// Iterate through all the Attributes for each method.foreach (Attribute attr intype.GetCustomAttributes(typeof(InterfaceAttribute), false)){mPluginNameList.Add(new PluginNameLite(attr as InterfaceAttribute));}}});if (isRecursiveDirectory) ////執行遞歸搜索 {foreach (var dir in Directory.GetDirectories(folderLocation)){GetAllPluginName(dir, isRecursiveDirectory);}}else //保存當前目錄下的插件名稱 {CustomSerializer.Serialize(mPluginNameList, folderLocation + "\\PluginLog.xml");myPluginNameList.AddRange(mPluginNameList);}}/// <summary>/// 獲取所有插件/// </summary>/// <param name="folderLocation"></param>/// <param name="isRecursiveDirectory">是否進行目錄遞歸搜索</param>public static void GetAllPlugins(string folderLocation, bool isRecursiveDirectory){bool isSaved = false; //是否已經保存了包含插件的dll文件列表List<string> mPluginFileList = new List<string>(); //包含插件的dll文件列表List<string> allDllFileList = new List<string>(); //所有dll文件列表if (!isRecursiveDirectory){try{mPluginFileList = CustomSerializer.Deserialize<List<string>>(folderLocation + "\\PluginFileLog.xml");isSaved = true;}catch (Exception ex){allDllFileList = (from file in Directory.GetFileSystemEntries(folderLocation)where file.Contains(".dll")select file).ToList();}}else{allDllFileList = (from file in Directory.GetFileSystemEntries(folderLocation)where file.Contains(".dll")select file).ToList(); ;}Type[] types;IEnumerable<string> dllPluginFils; //最終要進行處理的的dll文件if (mPluginFileList.Count == 0) //如果不存在插件文件目錄,則獲取該目錄下所有dll文件dllPluginFils = allDllFileList;elsedllPluginFils = from file in mPluginFileListselect folderLocation + file; //否則將插件文件名稱拼接為完整的文件路徑 Parallel.ForEach(dllPluginFils,file =>{Assembly assembly;try{assembly = Assembly.LoadFrom(file);}catch{return;}try{types = assembly.GetTypes();}catch{return;}foreach (Type type in types){if (type.IsInterface == true)continue;Type interfaceType = null;string interfaceName = null;foreach (var interfacename in myPluginNameList) //對該Type,依次查看是否實現了插件名稱列表中的接口 {interfaceType = type.GetInterface(interfacename.myName);if (interfaceType != null){interfaceName = interfacename.myName;// Iterate through all the Attributes for each method.foreach (Attribute attr intype.GetCustomAttributes(typeof(XFrmWorkAttribute), false)) //獲取該插件的XFrmWorkAttribute標識 {XFrmWorkAttribute attr2 = attr as XFrmWorkAttribute;attr2.myType = type; //將其類型賦值給XFrmWorkAttributeif (attr2.MainKind != interfaceName){continue;}PluginCollection pluginInfo = null; //保存到插件字典當中if (mPluginDictionary.TryGetValue(interfaceType, out pluginInfo)){pluginInfo.Add(attr2); //若插件字典中已包含了該interfaceType的鍵,則直接添加 }else{var collection = new PluginCollection();collection.Add(attr2);mPluginDictionary.Add(interfaceType, collection); //否則新建一項并添加之 }file = file.Replace(folderLocation, ""); //獲取文件在該文件夾下的真實名稱if (!mPluginFileList.Contains(file)) //若插件文件列表中不包含此文件則添加到文件目錄中 mPluginFileList.Add(file);goto FINISH;}}}FINISH:;}});if (isRecursiveDirectory) //執行遞歸搜索 {foreach (var dir in Directory.GetDirectories(folderLocation)){GetAllPlugins(dir, isRecursiveDirectory);}}else{if (!isSaved) //若沒有保存插件文件目錄,則反序列化保存之。CustomSerializer.Serialize(mPluginFileList, folderLocation + "\\PluginFileLog.xml");}}/// <summary>/// 獲取接口中固定索引類型的實例/// </summary>/// <param name="interfaceName">接口名稱</param>/// <param name="index">索引號</param>/// <returns>實例化的引用</returns>public static Object GetObjectInstance(Type interfaceName, int index){return Activator.CreateInstance(GetPluginCollection(interfaceName)[index].myType);}/// <summary>/// 獲取某程序集的接口列表/// </summary>/// <param name="interfaceName"></param>/// <param name="myAssembly"></param>/// <returns></returns>public static ObservableCollection<XFrmWorkAttribute> GetPluginCollection(Type interfaceName, Assembly myAssembly){return GetPluginCollection(interfaceName, myAssembly, false);}public static ObservableCollection<XFrmWorkAttribute> GetPluginCollection(Type interfaceName, Assembly myAssembly, bool isAbstractRequired){if (mPluginDictionary.ContainsKey(interfaceName))return mPluginDictionary[interfaceName];else //若發現插件不存在,則再執行搜索(這種可能性很低) {PluginCollection tc = new PluginCollection();Type[] types = myAssembly.GetTypes();foreach (Type type in types){if (type.GetInterface(interfaceName.ToString()) != null){if (!isAbstractRequired && type.IsAbstract == true){continue;}// Iterate through all the Attributes for each method.foreach (Attribute attr intype.GetCustomAttributes(typeof(XFrmWorkAttribute), false)){XFrmWorkAttribute attr2 = attr as XFrmWorkAttribute;tc.Add(attr2);}}}mPluginDictionary.Add(interfaceName, tc);return tc;}}public static ObservableCollection<XFrmWorkAttribute> GetPluginCollection(Type interfaceName){if (mPluginDictionary.ContainsKey(interfaceName))return mPluginDictionary[interfaceName];else{PluginCollection tc = new PluginCollection();Assembly assembly = Assembly.GetAssembly(interfaceName);Type[] types = assembly.GetTypes();foreach (Type type in types){if (type.GetInterface(interfaceName.ToString()) != null && !type.IsAbstract){// Iterate through all the Attributes for each method.foreach (Attribute attr intype.GetCustomAttributes(typeof(XFrmWorkAttribute), false)){XFrmWorkAttribute attr2 = attr as XFrmWorkAttribute;tc.Add(attr2);}}}mPluginDictionary.Add(interfaceName, tc);return tc;}}}}

?

  

  

  

轉載于:https://www.cnblogs.com/buptzym/archive/2012/06/05/2534703.html

總結

以上是生活随笔為你收集整理的NET插件系统之四——提升系统搜索插件和启动速度的思考的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产色婷婷一区二区三区竹菊影视 | 福利免费在线观看 | 亚洲一区二区在线 | 麻豆视频成人 | 欧美影视一区二区三区 | 91精品国产乱码久久久张津瑜 | 天堂在线观看视频 | 91精品啪在线观看国产 | 国产乱码精品一区二区三区五月婷 | 91久久久久久久久久 | 欧美日韩国产成人 | 又大又粗欧美黑人aaaaa片 | 亚洲AV无码国产精品国产剧情 | 国产无遮挡18禁无码网站不卡 | 精品日本一区二区三区在线观看 | 欧美巨大荫蒂茸毛毛人妖 | 欧美色乱 | 992tv在线影院 | 国产123| 中文av一区二区三区 | 欧美三级在线播放 | 久久综合狠狠综合久久综合88 | av免费久久 | 正在播放木下凛凛88av | av免费看片| a黄色一级片 | 嫩草视频一区二区三区 | 日韩欧美一区二区三区四区五区 | 手机av网 | 先锋影音在线 | 日本伦理中文字幕 | 一级黄色片在线观看 | 欧美日韩午夜激情 | 欧美丰满美乳xxx高潮www | 国产成人精品免费视频 | 国产精品一区二区三区在线免费观看 | 日韩中文字幕电影 | 色播视频在线播放 | 不卡av免费在线观看 | 色男人天堂av | 少妇太爽了在线观看 | 成年男女免费视频网站 | 加勒比视频在线观看 | 日韩激情一区二区三区 | 2024av视频| 欧美大肚乱孕交hd孕妇 | 激情精品 | 亚洲天堂不卡 | 亚洲精品国产片 | 视频这里只有精品 | 国产一级片免费 | 欧美亚洲综合一区 | 亚洲视频精选 | 国产特级淫片免费看 | 在线观看视频福利 | 免费一区二区三区视频在线 | 久久r这里只有精品 | 九九久久综合 | 久久精品国产精品亚洲 | 猎艳山村丰满少妇 | 天天想你在线观看完整版电影免费 | 日皮视频免费看 | 日产精品久久久 | 国产成人精品免高潮费视频 | 久久久91精品国产一区二区三区 | 久久久成人精品一区二区三区 | 久久图库 | 久久77777 | 黄色成人在线 | 国产尤物av | 亚洲欧美国产精品久久久久久久 | 乳揉みま痴汉4在线播放 | 有码在线播放 | 视频黄页在线观看 | 国产精品av在线播放 | 国产在线v | 久久成人综合网 | 日本亚洲免费 | 麻豆亚洲精品 | 不卡免费视频 | 国产视频一级 | 樱空桃在线 | 一级网站在线观看 | 18黄暴禁片在线观看 | 久热网站 | 欧美交换国产一区内射 | 四虎影视黄色 | 中文字幕一区二区三区四区免费看 | 日韩精品一区在线观看 | 嫩草国产精品 | 激情黄色小说网站 | 国产色综合天天综合网 | 狠狠艹狠狠干 | 卡通动漫亚洲综合 | 欧美做受视频 | 欧美囗交做爰视频 | 日韩亚州 | 国产欧美视频在线播放 | 国产在成人精品线拍偷自揄拍 |