亚洲有码Av一区二区三区_国产高清啪啪免费视频_69色视频国产_国产成人人人爆出白浆_国产精品自在线拍国_一本久久伊人热热精品无码_午夜性刺激在线看免费带字幕_助力高品质欧美狂喷水_亚洲精品日韩无码_精品无码一区二区三区蜜臀_麻豆高清国产AV_熟妇人素无码中文字幕_亚洲a级片在线观看_国产欧美日韩三区_99国产成人高清在线观看

ARTICLE DETAIL

資訊詳情

深耕商務(wù)建站與企業(yè)官網(wǎng)運(yùn)營的一線實(shí)戰(zhàn)洞察。

插件系統(tǒng)原理與故障排查:從接口約定到插件開發(fā)實(shí)踐

插件系統(tǒng)原理與故障排查:從接口約定到插件開發(fā)實(shí)踐 1. 從一次加載失敗說起插件是怎么工作的我先說一個(gè)最近的經(jīng)歷。有次在配置一個(gè)持續(xù)交付流水線啟動(dòng)服務(wù)后給我甩了一個(gè)failed to load plugins web boot: 2 entries did not activate的報(bào)錯(cuò)附帶兩個(gè)插件名稱。當(dāng)時(shí)第一反應(yīng)是插件沒生效但深入排查后才發(fā)現(xiàn)這背后牽出了插件系統(tǒng)整套機(jī)制的不少坑。正好后臺(tái)也有朋友在問plugins 到底是干什么的索性把這塊內(nèi)容系統(tǒng)整理一下從插件的基本原理講到加載失敗的排查思路再講講怎么自己寫一個(gè)能用的插件。插件plugins這個(gè)詞說白了就是把一個(gè)主程序的某些能力拆出來做成可獨(dú)立開發(fā)、獨(dú)立部署、獨(dú)立啟停的模塊。主程序定義好接口插件按接口實(shí)現(xiàn)功能然后在運(yùn)行期被主程序動(dòng)態(tài)加載。它的好處也很直觀主程序不需要為了新功能反復(fù)重發(fā)大版本第三方也能在不改主程序源碼的前提下擴(kuò)展能力用戶也可以按需啟用或關(guān)閉功能。像是瀏覽器里的廣告攔截器、編輯器里的代碼格式化工具、CI/CD 平臺(tái)里的通知插件本質(zhì)上都是同一套思路。那為什么會(huì)出現(xiàn)加載失敗這就要說到插件系統(tǒng)的幾個(gè)關(guān)鍵環(huán)節(jié)了。一個(gè)插件要想被正確加載至少要滿足三件事第一插件代碼本身沒問題能跑起來第二插件和主程序之間的接口匹配簽名對得上第三主程序在啟動(dòng)時(shí)能掃描到插件的位置并完成注冊。任何一個(gè)環(huán)節(jié)出錯(cuò)都會(huì)導(dǎo)致插件無法激活。我遇到的這個(gè)報(bào)錯(cuò)就是典型的插件注冊階段出了問題——系統(tǒng)找到了插件但插件在初始化時(shí)沒有完成激活。1.1 插件背后的接口約定思想要理解插件就必須理解接口約定這四個(gè)字。主程序不會(huì)關(guān)心插件內(nèi)部怎么實(shí)現(xiàn)它只關(guān)心插件有沒有按照約定提供某些能力。舉個(gè)例子一個(gè)音樂播放器主程序約定插件必須導(dǎo)出search和getPlayUrl這兩個(gè)函數(shù)。那么任何 JavaScript 模塊只要提供這兩個(gè)函數(shù)就可以被當(dāng)成音源插件使用。主程序不會(huì)去審閱插件的每一行代碼它只檢查這兩函數(shù)存在不存在、返回值是否符合預(yù)期。這種解耦設(shè)計(jì)讓主程序和插件的開發(fā)可以完全并行也讓生態(tài)有了自由生長的空間。接口約定的價(jià)值在于它讓擴(kuò)展變成一種可預(yù)期的事情。如果沒有約定管理插件的成本會(huì)高得驚人——主程序需要知道所有插件的內(nèi)部細(xì)節(jié)這等于把插件退化成了主程序的一部分。反過來有了穩(wěn)定的約定任何人寫插件都像在填一份本就設(shè)計(jì)好的答卷主程序也能用統(tǒng)一的代碼去加載、調(diào)用、銷毀所有插件。1.2 為什么我們需要插件而不是直接修改主程序直接改主程序當(dāng)然也能加功能但在很多場景下代價(jià)太高。比如一個(gè)已經(jīng)上線運(yùn)行的商業(yè)化軟件主程序每改一個(gè)功能點(diǎn)都要經(jīng)過完整回歸測試如果這個(gè)功能點(diǎn)只是某個(gè)用戶特有的需求那為一個(gè)人改代碼顯然不劃算。插件讓主程序保持相對穩(wěn)定把變化隔離在獨(dú)立模塊中可以單獨(dú)發(fā)布、單獨(dú)回滾甚至由用戶自己決定是否安裝。這正是插件系統(tǒng)在現(xiàn)代軟件中如此普遍的核心原因——它降低了運(yùn)維和交付的成本也提高了系統(tǒng)的靈活性。2. 插件加載失敗的核心原因與排查思路很多朋友看到failed to load plugins這種報(bào)錯(cuò)就發(fā)懵其實(shí)這行信息已經(jīng)告訴你很多了。2 entries did not activate說明掃描到兩個(gè)插件條目但都沒激活成功。后面跟著的linxin666/dsh-p這類名稱是插件的標(biāo)識(shí)符通常對應(yīng)著某個(gè)包名或文件路徑。拿到這個(gè)信息第一步不是去問為什么沒激活而是先確認(rèn)這兩個(gè)插件到底存不存在、路徑對不對。如果插件是存放在某個(gè)專用目錄下的比如plugins/或.config/plugin/那就先看目錄里有沒有對應(yīng)的文件。很多時(shí)候是安裝步驟沒做完插件包沒解壓到位或者配置文件里寫了插件名但文件根本沒放進(jìn)去系統(tǒng)自然就找不到。我排查時(shí)習(xí)慣先用一條命令列出目錄內(nèi)容確認(rèn)硬件層面文件是否存在再做代碼層面的分析。2.1 錯(cuò)誤信息里的關(guān)鍵信息怎么讀failed to load plugins web boot這種報(bào)錯(cuò)看起來像是一整句話但拆開看每個(gè)詞都有意義。web boot表示這是在 Web 啟動(dòng)階段發(fā)生的加載失敗說明主程序的入口框架在啟動(dòng)時(shí)就已經(jīng)掃描插件了。2 entries did not activate明確告訴你掃描到的條目數(shù)以及有多少個(gè)沒激活成功。如果后面還跟著具體的插件名那就直接鎖定了排查目標(biāo)。比如我遇到的那條報(bào)錯(cuò)里linxin666/dsh-p是一個(gè)完整的包名。看到這種帶scope/name格式的標(biāo)識(shí)符就要想到它可能來自 npm 或類似包管理器管理的目錄。這時(shí)候先檢查node_modules或plugins目錄下是否有對應(yīng)的文件夾。很多人在這一步會(huì)忽略權(quán)限問題——文件在但主程序進(jìn)程沒有讀取權(quán)限同樣會(huì)報(bào)找不到或無法加載。2.2 版本沖突和依賴缺失是重災(zāi)區(qū)排除了文件缺失下一個(gè)高頻原因是依賴問題。插件不是脫離主程序獨(dú)立運(yùn)行的它通常會(huì)依賴主程序提供的一系列 API甚至依賴第三方庫。如果主程序升級后改了接口簽名舊插件可能就調(diào)不通了反過來插件依賴的某個(gè)庫版本和主程序自帶的庫版本沖突也可能導(dǎo)致初始化失敗。這類問題報(bào)錯(cuò)不一定直接告訴你版本沖突可能只是拋出一個(gè)NoSuchMethodError或ClassNotFoundException需要看完整日志才能定位。我那次遇到的情況實(shí)際是插件 A 依賴的某個(gè)公共庫是 1.2 版而主程序內(nèi)置的是 1.0 版插件 B 又依賴了 1.3 版。三個(gè)版本湊在一起插件 A 先加載時(shí)用了 1.0 的類等插件 B 要用新特性時(shí)類已經(jīng)被加載了直接就把整個(gè) web boot 的激活流程卡住。處理辦法也很簡單統(tǒng)一依賴版本或者把公共庫抽出來由主程序統(tǒng)一管理插件只聲明需要什么能力不直接綁定具體實(shí)現(xiàn)。2.3 注冊表和清單文件插件激活的合同插件的激活信息通常在清單文件里比如manifest.json或plugin.yaml。這個(gè)文件就像一份合同寫著插件的主類、版本、最低主程序版本、暴露的擴(kuò)展點(diǎn)等。如果清單文件里寫錯(cuò)了名字或者格式有問題主程序掃描時(shí)就會(huì)跳過甚至報(bào)錯(cuò)。特別是手工編輯過清單文件之后很容易出現(xiàn) JSON 語法錯(cuò)誤、字段名大小寫不匹配這類低級問題。我見過一個(gè)真實(shí)案例有人在manifest.json里把main字段的值寫成了dist/index.js但實(shí)際編譯后的文件在build/index.js結(jié)果插件加載時(shí)找不到入口文件直接did not activate。所以排查插件加載問題務(wù)必要逐項(xiàng)比對清單文件里的路徑、類名、版本號(hào)并且確認(rèn)這些路徑是從哪個(gè)基準(zhǔn)目錄開始計(jì)算的。有的加載器以插件目錄為基準(zhǔn)有的以主程序根目錄為基準(zhǔn)搞混了就必然出錯(cuò)。2.4 完整的排查步驟清單基于上面的經(jīng)驗(yàn)我整理了一套套路化的排查步驟遇到插件加載失敗可以按順序走確認(rèn)插件文件確實(shí)存在于期望的目錄檢查權(quán)限是否可讀。查看完整日志找到具體是哪個(gè)插件、哪個(gè)類、哪個(gè)方法報(bào)錯(cuò)。核對插件清單文件里的主入口、版本、依賴聲明是否和實(shí)際環(huán)境匹配。確認(rèn)主程序和插件之間的接口版本是否兼容必要時(shí)查看主程序的更新日志或 API 文檔。嘗試單獨(dú)加載一個(gè)插件排除多個(gè)插件之間的互相干擾。如果插件是從網(wǎng)絡(luò)下載的第三方資源確認(rèn)其來源可靠、完整性校驗(yàn)通過。這套流程看起來簡單但每一步都可能藏著坑。比如第 4 步主程序版本和插件的最低版本要求很多人在升級主程序后忘了檢查兼容性結(jié)果插件全部失效。又比如第 6 步下載的插件包如果被截?cái)嘟鈮簳r(shí)不會(huì)報(bào)錯(cuò)但運(yùn)行時(shí)就會(huì)莫名其妙加載不出來這時(shí)用校驗(yàn)工具比對一下文件的哈希值往往能很快發(fā)現(xiàn)問題。下面是一個(gè)常見錯(cuò)誤信息的速查表也是我這幾年反復(fù)踩過的坑報(bào)錯(cuò)關(guān)鍵詞常見原因優(yōu)先排查方向did not activate插件初始化邏輯拋異常插件入口代碼、依賴初始化failed to load plugins文件缺失、路徑錯(cuò)誤、加載失敗文件系統(tǒng)、權(quán)限、路徑配置web boot啟動(dòng)階段加載插件確認(rèn)啟動(dòng)環(huán)境、插件掃描目錄no such method/class not found接口版本不匹配或依賴丟失檢查主程序 API 版本、依賴樹manifest not found清單文件缺失或格式錯(cuò)誤檢查插件目錄結(jié)構(gòu)和文件完整性minHostVersion not satisfied主程序版本低于插件要求升級主程序或更換插件版本2.5 工具鏈層面的排查技巧如果插件運(yùn)行在 Node.js 環(huán)境npm ls是排查依賴沖突的好幫手。它會(huì)打印出完整的依賴樹你能直觀看到某個(gè)包被哪些插件引用了幾次分別是什么版本。我習(xí)慣在排查一開始就運(yùn)行一次往往能大大縮小問題范圍。如果是 Java 環(huán)境那就用dependency:tree插件查看依賴沖突。本質(zhì)都是看依賴之前是否存在降級或重復(fù)問題。另外日志級別也很關(guān)鍵。很多時(shí)候默認(rèn)日志只顯示ERROR級別的信息但插件激活過程里會(huì)有很多WARN級別甚至DEBUG級別的線索。把日志級別調(diào)到DEBUG重新啟動(dòng)一次也許能看到插件加載到哪一步為止、哪個(gè)屬性缺失了。這比對著最終報(bào)錯(cuò)死磕要高效得多。3. 不同場景下的插件生態(tài)實(shí)例插件不是某一種技術(shù)特有的概念在不同工具鏈里它的表現(xiàn)形式差別很大但核心邏輯是一樣的。我挑了三個(gè)比較有代表性的場景來展開說一下分別是嵌入式開發(fā)工具、音樂播放器擴(kuò)展、持續(xù)交付平臺(tái)。3.1 嵌入式開發(fā)工具里的插件以 IAR 為例IAR 是嵌入式開發(fā)中很經(jīng)典的集成開發(fā)環(huán)境它支持插件擴(kuò)展主要用于自定義編譯器行為、代碼檢查、燒錄輔助等。很多團(tuán)隊(duì)會(huì)基于 IAR 的插件機(jī)制寫自己的靜態(tài)檢查規(guī)則把公司內(nèi)部的編碼規(guī)范固化成工具。比如規(guī)定某些寄存器不允許直接操作必須通過專用 APIIAR 插件就可以對代碼樹做檢查發(fā)現(xiàn)違規(guī)時(shí)直接高亮報(bào)錯(cuò)。這類插件的特點(diǎn)是和 IDE 的耦合度極高通常要用特定語言比如 C/C 或 C#調(diào)用 IDE 暴露的 API。寫起來沒那么輕松但好在 IAR 官方有比較詳細(xì)的插件開發(fā)文檔社區(qū)里也有一些開源示例可以參考。如果你要在 IAR 里做插件我的建議是先別急著寫業(yè)務(wù)邏輯先在你的本地環(huán)境把官方的 sample 插件跑通確認(rèn)工具鏈沒問題再改造它。這樣做至少能避開一半的環(huán)境配置坑。開發(fā) IAR 插件時(shí)還會(huì)遇到一個(gè)典型問題IDE 版本升級后插件 API 的名字和參數(shù)可能變了。有些老插件在舊版本上運(yùn)行正常換了新版本 IDE 就加載不了。遇到這種情況先去看 IDE 的版本遷移說明很多第三方插件作者也會(huì)更新兼容版本。這本質(zhì)上也是接口版本管理的問題和前面說的清單文件校驗(yàn)是一個(gè)道理。3.2 播放器擴(kuò)展里的插件以 MusicFree 為例MusicFree 是一個(gè)開源的音樂播放器它最吸引人的一個(gè)特性就是插件化。用戶可以安裝不同的音源插件播放器本身不提供內(nèi)嵌內(nèi)容服務(wù)而是通過插件動(dòng)態(tài)獲取音樂資源。這種設(shè)計(jì)讓主程序保持輕量同時(shí)內(nèi)容來源由用戶自己決定也規(guī)避了很多版權(quán)方面的爭議。MusicFree 的插件通常是一個(gè) JS 文件或一個(gè)包含 JS 的目錄里面導(dǎo)出了幾個(gè)固定的函數(shù)比如search、getMusicUrl、getLyric等。主程序在運(yùn)行時(shí)按約定調(diào)用這些函數(shù)把用戶搜索、播放、顯示歌詞的能力交給插件去實(shí)現(xiàn)。這種模式對開發(fā)者相當(dāng)友好只要懂一點(diǎn) JavaScript就能在本地快速寫一個(gè)插件把某個(gè)網(wǎng)站的搜索接口包裝成符合規(guī)范的函數(shù)即可。我試過寫一個(gè)簡單的 MusicFree 音源插件總體感受是接口設(shè)計(jì)得比較清晰難點(diǎn)反而在于處理不同網(wǎng)站返回的數(shù)據(jù)格式。比如有的網(wǎng)站返回的播放地址是加密的有的需要帶特殊請求頭這些都需要在插件里做額外處理。如果你打算做這類插件建議先仔細(xì)讀一下官方的插件接口文檔再看幾個(gè)現(xiàn)成插件的源碼理解數(shù)據(jù)流的走向比自己去猜要高效得多。這個(gè)例子也說明插件系統(tǒng)不一定要龐大復(fù)雜。幾個(gè)導(dǎo)出函數(shù)、一份約定文檔就能形成一套可用的生態(tài)。很多時(shí)候我們不需要追求大而全的插件框架小步快跑地定義好接口讓第三方能接入就已經(jīng)贏了一半。3.3 持續(xù)交付平臺(tái)里的插件以 Harness 為例回到文章開頭提到的 Harness它是一個(gè)持續(xù)集成/持續(xù)交付平臺(tái)支持通過插件擴(kuò)展流水線的能力。比如你可以寫一個(gè)插件在部署前自動(dòng)執(zhí)行某些驗(yàn)證腳本或者把構(gòu)建產(chǎn)物上傳到特定的存儲(chǔ)服務(wù)。Harness 的插件加載機(jī)制和前面說的類似也是在啟動(dòng)時(shí)掃描插件目錄讀取清單然后按清單初始化。如果我遇到的failed to load plugins web boot發(fā)生在這種平臺(tái)上大概率還是上述的依賴沖突或入口路徑問題。因?yàn)榱魉€平臺(tái)往往運(yùn)行在容器里插件依賴的本地文件、環(huán)境變量、系統(tǒng)庫可能都和開發(fā)環(huán)境不一致。有時(shí)候本地跑得好好的插件丟到容器里就加載失敗多半就是缺系統(tǒng)庫或環(huán)境變量沒設(shè)置。所以我有個(gè)習(xí)慣在本地寫 Harness 插件時(shí)盡量用一個(gè)和線上環(huán)境相同的容器鏡像做測試保證運(yùn)行環(huán)境一致。這一步看起來麻煩但能省下無數(shù)個(gè)為什么本地可以線上不行的排查夜。容器鏡像的差異非常隱蔽比如某些插件需要系統(tǒng)時(shí)區(qū)數(shù)據(jù)而精簡鏡像里沒有這份數(shù)據(jù)就會(huì)導(dǎo)致初始化失敗。另外在這種平臺(tái)里插件還會(huì)面臨權(quán)限模型的問題。插件可能需要調(diào)用平臺(tái)提供的 API 獲取部署信息而這些 API 往往需要特定的令牌或角色。如果插件的身份沒有配好它會(huì)報(bào)授權(quán)錯(cuò)誤而不是加載錯(cuò)誤??吹竭@類報(bào)錯(cuò)時(shí)不要把注意力全部放在加載機(jī)制上也要檢查認(rèn)證和授權(quán)配置。3.4 插件生態(tài)給我們的啟示從 IAR 到 MusicFree 再到 Harness你會(huì)發(fā)現(xiàn)插件系統(tǒng)的本質(zhì)就是一份接口約定。誰都可以基于約定去實(shí)現(xiàn)功能主程序不需要關(guān)心插件的內(nèi)部實(shí)現(xiàn)。這也是為什么插件系統(tǒng)的設(shè)計(jì)文檔往往比實(shí)現(xiàn)代碼更重要——接口定得好生態(tài)就繁榮接口定得隨意后期維護(hù)就是災(zāi)難。一個(gè)反例是某些老舊系統(tǒng)把一堆內(nèi)部數(shù)據(jù)結(jié)構(gòu)直接暴露給插件第三方插件確實(shí)能調(diào)用所有功能但主程序一旦重構(gòu)內(nèi)部代碼所有插件全部報(bào)錯(cuò)沒人敢升級。反觀那些設(shè)計(jì)良好的系統(tǒng)接口長期保持穩(wěn)定內(nèi)部怎么折騰都不影響插件。這種穩(wěn)定性的價(jià)值往往要積累多年插件數(shù)量后才體現(xiàn)得出來。4. 自己動(dòng)手開發(fā)一個(gè)最小插件實(shí)例光說不練假把式我們實(shí)際操作一下。我寫一個(gè)極簡的插件用來給一個(gè)模擬的主程序增加打招呼功能。因?yàn)椴煌脚_(tái)的插件開發(fā)方式差異很大這里我選一個(gè)最通用的場景主程序用 Node.js 編寫插件也是一個(gè) Node.js 模塊通過約定好的接口導(dǎo)出功能。4.1 定義主程序和插件之間的接口先看主程序怎么加載插件。假設(shè)我們的主程序會(huì)讀取plugins/目錄下的所有模塊并調(diào)用每個(gè)模塊暴露的activate函數(shù)。activate接收一個(gè)上下文對象插件可以通過這個(gè)對象向主程序注冊能力。接口約定如下插件必須導(dǎo)出一個(gè)activate函數(shù)。activate接收一個(gè)context對象其中包含register方法。register方法用于注冊插件提供的具體功能例如register(greet, fn)。這就是一個(gè)極簡的擴(kuò)展點(diǎn)設(shè)計(jì)。它包含了幾個(gè)關(guān)鍵信息插件的入口形式導(dǎo)出activate、能力的注冊方式調(diào)用register、以及能力的具體標(biāo)識(shí)這里是greet。看起來簡單但可以作為很多真實(shí)插件系統(tǒng)的原型。4.2 編寫插件代碼在plugins/hello-plugin/目錄下創(chuàng)建一個(gè)index.js。module.exports { activate(context) { context.register(greet, (name) { return Hello, ${name}! From hello-plugin.; }); } };這段代碼沒什么復(fù)雜邏輯但它完整地演示了按約定導(dǎo)出和注冊能力兩個(gè)核心動(dòng)作。主程序只要按約定加載它就能在運(yùn)行時(shí)動(dòng)態(tài)獲得greet這個(gè)新功能。注意這里的context.register方法雖然是傳入的但我們在插件里直接調(diào)用它而不需要知道主程序內(nèi)部是如何存儲(chǔ)這些函數(shù)的。這正是接口抽象的體現(xiàn)插件只依賴你給了我一個(gè)能注冊能力的方法這個(gè)事實(shí)不關(guān)心背后是內(nèi)存數(shù)組、SQLite 還是消息隊(duì)列。4.3 主程序加載插件的實(shí)現(xiàn)主程序main.js這樣寫const fs require(fs); const path require(path); function loadPlugins() { const pluginsDir path.join(__dirname, plugins); const plugins []; fs.readdirSync(pluginsDir).forEach((dir) { const pluginPath path.join(pluginsDir, dir); if (fs.existsSync(path.join(pluginPath, index.js))) { const plugin require(pluginPath); if (typeof plugin.activate function) { const context { handlers: {}, register(key, fn) { this.handlers[key] fn; } }; plugin.activate(context); plugins.push(context.handlers); } } }); return plugins; } const handlersList loadPlugins(); const allHandlers Object.assign({}, ...handlersList); if (allHandlers.greet) { console.log(allHandlers.greet(world)); }運(yùn)行node main.js你會(huì)在控制臺(tái)看到Hello, world! From hello-plugin.。這樣一個(gè)最小插件系統(tǒng)就閉環(huán)了主程序掃描目錄、加載模塊、調(diào)用activate、注冊功能、對外使用。所有插件都遵循同一套接口你可以隨意增加新插件而不需要改動(dòng)主程序代碼。這里有個(gè)細(xì)節(jié)加載器用require同步加載插件這意味著插件代碼在加載階段會(huì)和主程序共享同一個(gè)進(jìn)程上下文。如果某個(gè)插件的activate拋出異常整個(gè)主程序也會(huì)崩潰。所以實(shí)際生產(chǎn)環(huán)境里加載器一定會(huì)給每個(gè)插件的activate調(diào)用包裹try...catch并把錯(cuò)誤信息記錄下來而不是讓異常直接冒泡。我在下一步會(huì)補(bǔ)上這個(gè)容錯(cuò)邏輯。4.4 給插件加一個(gè)清單文件在實(shí)際生產(chǎn)環(huán)境里我們不會(huì)只靠目錄里有沒有 index.js來判斷插件是否合法通常還會(huì)要求提供manifest.json聲明插件名稱、版本、入口文件、依賴信息等。這樣做的好處是可以提前校驗(yàn)而不是加載到一半才發(fā)現(xiàn)格式不對。我給上面的插件補(bǔ)一個(gè)manifest.json{ name: hello-plugin, version: 1.0.0, main: index.js, minHostVersion: 1.0.0 }主程序加載時(shí)可以先讀取這個(gè)清單檢查當(dāng)前主程序版本是否滿足minHostVersion檢查入口文件是否存在然后再require。這樣能顯著減少加載期錯(cuò)誤也讓插件管理更加規(guī)范。很多大型插件的加載器本質(zhì)上就是把這個(gè)校驗(yàn)過程做得更復(fù)雜、更健壯。4.5 用容錯(cuò)和日志讓插件系統(tǒng)可用上面的最小系統(tǒng)沒有處理異常我們來增強(qiáng)一下loadPlugins函數(shù)function loadPlugins() { const pluginsDir path.join(__dirname, plugins); const plugins []; if (!fs.existsSync(pluginsDir)) { console.warn([plugger] plugins directory not found: ${pluginsDir}); return plugins; } fs.readdirSync(pluginsDir).forEach((dir) { const manifestPath path.join(pluginsDir, dir, manifest.json); if (!fs.existsSync(manifestPath)) { console.warn([plugger] manifest not found in ${dir}, skip); return; } try { const manifest JSON.parse(fs.readFileSync(manifestPath, utf8)); const entry path.join(pluginsDir, dir, manifest.main || index.js); if (!fs.existsSync(entry)) { console.error([plugger] entry not found: ${entry}); return; } const plugin require(entry); if (typeof plugin.activate ! function) { console.error([plugger] plugin ${dir} does not export activate); return; } const context { handlers: {}, register(key, fn) { this.handlers[key] fn; } }; plugin.activate(context); plugins.push(context.handlers); console.log([plugger] plugin ${dir} activated); } catch (err) { console.error([plugger] error while loading ${dir}:, err); } }); return plugins; }這樣每個(gè)插件的錯(cuò)誤都被捕獲單獨(dú)一個(gè)插件有問題不會(huì)拖垮主程序。同時(shí)每次加載成功或失敗都有日志輸出排查問題一目了然。這就是在插件系統(tǒng)里很重要的一點(diǎn)加載過程要具備可觀測性而不是默默地失敗。5. 插件系統(tǒng)設(shè)計(jì)的幾個(gè)關(guān)鍵權(quán)衡如果只是用插件了解前面這些就夠了。但如果你要自己設(shè)計(jì)一套插件系統(tǒng)有幾個(gè)點(diǎn)必須提前想清楚不然以后維護(hù)的時(shí)候會(huì)想罵人。5.1 接口的穩(wěn)定性是生死線插件系統(tǒng)的接口一旦發(fā)布就相當(dāng)于對外的承諾。你要是三天兩頭改接口簽名所有第三方插件都會(huì)跟著遭殃。常見的做法是給接口版本號(hào)比如context.version 1.0并在加載時(shí)做兼容性檢查。主程序可以支持多個(gè)版本接口并存插件聲明自己需要哪個(gè)版本主程序按需提供。我在實(shí)際做項(xiàng)目時(shí)寧可初期多花時(shí)間把接口設(shè)計(jì)得抽象一些也不愿意為了圖省事把主程序內(nèi)部對象直接暴露給插件。因?yàn)橐坏┍┞读藘?nèi)部結(jié)構(gòu)插件就會(huì)依賴這些細(xì)節(jié)主程序內(nèi)部重構(gòu)就會(huì)導(dǎo)致所有插件失效。接口盡量只暴露能力不暴露實(shí)現(xiàn)。比如一個(gè)媒體播放器的主程序不要直接把音頻設(shè)備數(shù)組這個(gè)內(nèi)部狀態(tài)交給插件而應(yīng)該提供一個(gè)類似context.getAudioDevices()的方法。插件用這個(gè)方法拿到的是一份拷貝無論它怎么改都不會(huì)污染主程序內(nèi)部狀態(tài)。這種隔離思想要貫徹到每一個(gè)接口設(shè)計(jì)里。5.2 依賴管理全局統(tǒng)一還是各自打包前面提到依賴沖突這是插件系統(tǒng)最煩的問題之一。有兩種主流方案一是插件聲明依賴由主程序統(tǒng)一安裝和管理二是每個(gè)插件把自己依賴打包進(jìn)自己的目錄與主程序和其他插件隔離。第一種方案對用戶友好但容易產(chǎn)生版本沖突第二種方案隔離性好但會(huì)增大體積且公共依賴會(huì)重復(fù)加載?,F(xiàn)在很多桌面應(yīng)用和 IDE 采用第二種思路把插件跑在獨(dú)立的子進(jìn)程或沙箱里和主程序完全隔離。這樣哪怕插件崩潰也不會(huì)影響主程序依賴也不會(huì)互相污染。代價(jià)是性能和通信復(fù)雜度上升。我見過一個(gè)折中做法主程序只提供基礎(chǔ)的 API 和常用的工具庫并把這些工具庫的版本凍結(jié)插件如果需要額外的依賴必須打包進(jìn)自己的目錄。這樣公共依賴版本一致專用依賴互相隔離。這個(gè)方法雖然不是百分百避免沖突但能把沖突面壓縮到很小。5.3 安全沙箱不能因?yàn)椴寮妥屨麄€(gè)系統(tǒng)裸奔插件本質(zhì)上是一段第三方代碼等于你允許別人在主程序的內(nèi)存里跑任意邏輯。如果這個(gè)主程序有用戶數(shù)據(jù)那插件就有機(jī)會(huì)訪問這些數(shù)據(jù)。所以設(shè)計(jì)插件系統(tǒng)時(shí)安全邊界一定要清晰。最簡單的做法是不做任何隔離但要求插件必須來源于可信渠道比如官方應(yīng)用市場。進(jìn)階做法是加載到獨(dú)立進(jìn)程或使用 VM 沙箱限制插件能訪問的 API。像很多瀏覽器擴(kuò)展就是采用后一種思路把插件的能力限制在特定 API 上。做任何涉及敏感數(shù)據(jù)的插件系統(tǒng)都建議至少做一層權(quán)限聲明機(jī)制讓用戶知道插件要干什么再由用戶決定是否授權(quán)。權(quán)限聲明聽起來復(fù)雜其實(shí)實(shí)現(xiàn)起來并不難??梢栽谇鍐挝募锛右粋€(gè)permissions字段比如{ name: hello-plugin, permissions: [network, storage, clipboard] }主程序在加載前先彈出提示告訴用戶這個(gè)插件需要訪問網(wǎng)絡(luò)和存儲(chǔ)是否允許 用戶同意后才加載。這樣至少給了用戶一個(gè)知情和選擇的機(jī)會(huì)也是很多成熟插件系統(tǒng)的基本做法。5.4 調(diào)試和可觀測性插件系統(tǒng)最怕的就是黑盒化。插件加載失敗、運(yùn)行異常如果日志里只有一行plugin failed那開發(fā)者和用戶都會(huì)很崩潰。所以設(shè)計(jì)插件系統(tǒng)時(shí)最好提供詳細(xì)的加載日志、運(yùn)行狀態(tài)上報(bào)、甚至一個(gè)插件管理界面列出所有插件的激活狀態(tài)、版本號(hào)、最近錯(cuò)誤。我在寫前面的最小插件系統(tǒng)時(shí)就特意把a(bǔ)ctivate調(diào)用包在一個(gè)try...catch里把錯(cuò)誤信息記錄到日志文件?,F(xiàn)實(shí)中的插件加載器都應(yīng)該這樣做并且要把錯(cuò)誤信息分級警告級別比如某個(gè)插件列表里不存在、錯(cuò)誤級別比如插件入口無法加載、致命級別比如主程序因插件崩潰。這樣排查問題時(shí)第一眼就能定位到是哪個(gè)插件、哪個(gè)環(huán)節(jié)出了問題。還有一個(gè)容易被忽略的點(diǎn)插件之間的依賴關(guān)系。有些插件會(huì)依賴另一個(gè)插件提供的功能。這種情況下加載順序很關(guān)鍵。我的建議是插件系統(tǒng)提供顯式的依賴聲明加載器能根據(jù)依賴關(guān)系自動(dòng)計(jì)算加載順序或者在發(fā)現(xiàn)依賴缺失時(shí)給出清晰報(bào)錯(cuò)而不是讓插件運(yùn)行到一半才崩潰。6. 案例分析一個(gè)真實(shí)的插件加載失敗問題復(fù)盤說了這么多回到開頭那個(gè)報(bào)錯(cuò)。我直接把那次排查過程整理成一個(gè)案例方便大家對照。6.1 現(xiàn)象與初步定位服務(wù)啟動(dòng)日志里出現(xiàn)failed to load plugins web boot: 2 entries did not activate linxin666/dsh-p, another/plugin日志還提示有一個(gè)harness failed to load plugins web boot: 1 entry did not activate huayu-yuan的記錄。因?yàn)?Harness 本身也是一個(gè)插件化平臺(tái)所以這兩個(gè)報(bào)錯(cuò)可能來自不同層級。我先確認(rèn)了這些插件對應(yīng)的目錄發(fā)現(xiàn)linxin666/dsh-p這個(gè)包名對應(yīng)的文件在node_modules里存在而huayu-yuan對應(yīng)的文件也齊全。說明不是文件缺失問題。6.2 依賴沖突排查接著我查看完整堆棧發(fā)現(xiàn)錯(cuò)誤發(fā)生在初始化一個(gè)工具類的時(shí)候。這個(gè)工具類被多個(gè)插件引用但不同插件引用了不同版本。通過npm ls檢查依賴樹確認(rèn)有一個(gè)傳遞依賴被提升到了頂層導(dǎo)致其中一個(gè)插件優(yōu)先加載了舊版本。處理方法是修改主程序的依賴解析配置把公共工具類固定成統(tǒng)一版本同時(shí)刪除了node_modules目錄和鎖文件重新安裝。重啟后兩個(gè)插件都成功激活。這種情況在 Node.js 生態(tài)里特別常見因?yàn)橐蕾囂嵘龣C(jī)制會(huì)把不同版本的包都放到頂層一旦插件 A 先加載了包 X 的 v1插件 B 再用時(shí)需要 v2 的類就會(huì)得到意外的結(jié)果。這里我想多說一句npm ls輸出的信息量很大但很多人不會(huì)去看。它會(huì)把樹展開當(dāng)某個(gè)包出現(xiàn)deduped或invalid標(biāo)記時(shí)通常就是問題的信號(hào)。排查依賴沖突時(shí)不妨把npm ls的輸出保存到文件再搜索自己的公共依賴名看看它被多少個(gè)版本實(shí)例化。6.3 環(huán)境差異問題至于huayu-yuan那個(gè)插件在本地一切正常但到測試環(huán)境的容器里就加載失敗。最終發(fā)現(xiàn)是容器鏡像缺少插件運(yùn)行需要的某個(gè)系統(tǒng)庫而開發(fā)環(huán)境自帶了這個(gè)庫。加上這個(gè)系統(tǒng)庫后插件恢復(fù)正常。這個(gè)問題提醒我插件系統(tǒng)跨環(huán)境運(yùn)行的時(shí)候能跑和能穩(wěn)定跑之間隔著一條執(zhí)行環(huán)境差異的河。最好的辦法是讓插件盡可能少依賴系統(tǒng)層面的東西或者提供容器鏡像時(shí)做一個(gè)完整的依賴清單。比如把運(yùn)行插件所需的動(dòng)態(tài)庫、字體、時(shí)區(qū)數(shù)據(jù)全部列在一個(gè)runtime-dependencies.txt里每次構(gòu)建鏡像時(shí)按清單安裝。6.4 給插件加載問題的解決順序結(jié)合這次復(fù)盤我再調(diào)整一下排查順序的優(yōu)先級先看日志中的插件標(biāo)識(shí)符確認(rèn)是哪些插件。確認(rèn)這些插件在文件系統(tǒng)里真實(shí)存在且路徑正確。檢查插件清單和入口文件的有效性。對比主程序版本和插件要求的版本是否匹配。檢查依賴樹重點(diǎn)看公共依賴的版本沖突。復(fù)現(xiàn)環(huán)境差異用和生產(chǎn)環(huán)境相同的容器或系統(tǒng)跑一次??紤]插件之間的相互影響嘗試逐個(gè)插件加載定位。這個(gè)順序我已經(jīng)用了很久基本能覆蓋大部分加載失敗和未激活的問題。當(dāng)然插件系統(tǒng)五花八門有些特殊場景可能需要額外的檢查手段但整體思路是通用的從靜態(tài)環(huán)境到動(dòng)態(tài)運(yùn)行時(shí)從單個(gè)插件到插件組合逐步縮小范圍。我還習(xí)慣在排查過程中保留“變更對照”的習(xí)慣每次修改一個(gè)變量后比如替換依賴版本、修改清單字段、切換加載順序都記錄下來并重啟驗(yàn)證。插件加載問題往往是多個(gè)因素疊加導(dǎo)致的一次性改太多東西反而不知道是哪個(gè)改動(dòng)起到了作用。堅(jiān)持做單一變量驗(yàn)證能省下大量反復(fù)猜測的時(shí)間。7. 我在插件使用和開發(fā)上的幾點(diǎn)體會(huì)最后聊一些比較主觀的經(jīng)驗(yàn)不一定適合所有場景但對普通開發(fā)者和運(yùn)維人員應(yīng)該有點(diǎn)參考價(jià)值。首先遇到插件報(bào)錯(cuò)不要慌更不要第一時(shí)間想著把插件刪了。插件的價(jià)值在于擴(kuò)展功能刪掉它確實(shí)能消除報(bào)錯(cuò)但也可能讓主程序失去某些重要能力。先嘗試?yán)斫鈭?bào)錯(cuò)信息很多時(shí)候問題出在配置和環(huán)境而不是插件本身。我剛?cè)胄袝r(shí)也傻過一看到插件加載失敗就禁用所有插件結(jié)果功能缺失更嚴(yán)重后來才慢慢學(xué)會(huì)定位。其次安裝第三方插件之前盡量先看它的清單文件和依賴說明。如果可能用鎖定版本的依賴管理方式不要隨便用latest。因?yàn)椴寮到y(tǒng)的主程序和插件是長期共存的版本漂移是很多詭異問題的根源。我以前圖省事直接寫*作為依賴版本號(hào)結(jié)果某天一個(gè)依賴小版本更新直接讓兩個(gè)插件因?yàn)樾袨樽兓ハ鄾_突折騰了一整天。再次如果你要開發(fā)插件一定要讓你的插件做好降級準(zhǔn)備。也就是當(dāng)某個(gè)依賴接口不存在時(shí)插件要能給出清晰的錯(cuò)誤提示而不是拋出一堆內(nèi)部異常。好的插件甚至可以在缺少某個(gè)能力時(shí)自動(dòng)禁用自身而不影響主程序運(yùn)行。這需要插件在activate階段就持有對上下文能力的判斷而不是假設(shè)一定能拿到所有資源。我曾在自研插件框架里遇到過這樣的插件它假設(shè)主程序一定提供context.getConfig()但某個(gè)舊版本主程序并沒有這個(gè)方法。插件在激活時(shí)直接拋 TypeError導(dǎo)致整個(gè)加載流程中止。后來我改了插件先判斷typeof context.getConfig function如果不是就打印一行警告并繼續(xù)執(zhí)行其他邏輯。這樣即使在新舊版本混合的環(huán)境里插件也能優(yōu)雅降級。最后插件不是萬能的。有些功能用插件做可能比直接改主程序更復(fù)雜。判斷什么時(shí)候該引入插件標(biāo)準(zhǔn)很簡單這個(gè)功能是否會(huì)被多個(gè)使用者或第三方復(fù)用是否有明確的擴(kuò)展點(diǎn)如果只是內(nèi)部一個(gè)很小的定制需求直接做主程序的可配置開關(guān)可能是更簡單的選擇沒必要為了插件化而插件化。我自己踩過的最大的坑就是在項(xiàng)目早期過度設(shè)計(jì)插件架構(gòu)定義了一堆看似靈活的接口結(jié)果最后只有一個(gè)內(nèi)部模塊在用反而給主程序增加了很多不必要的抽象層級。所以如果你也是剛開始搭建系統(tǒng)我建議從最小可用的插件接口做起等真的有外部擴(kuò)展需求再逐步演進(jìn)。插件系統(tǒng)的價(jià)值是在長期演進(jìn)中體現(xiàn)的而不是在第一天就堆出來的。
返回列表
PREV
查看更多資訊
NEXT
返回資訊列表
资源在线观一 二| 婷婷在线视频| 9久久久久| 精品人妻一区二区三区四区| 视频在线观看青青99国产| 国产亚洲日本| 我爱搞逼综合网| 国产又大又粗又色生活片亚洲国产精品成人久久久综合免费 | 伊人午夜福利视频| 欧美爱爱97| 国产主播福利| 欧美性五月| 亚洲动态色图| 97色伦97色伦国产欧美| 午夜性刺激视频免费观看| 99久久99久久免费精品蜜臀| 日本免费人成视频播放120秒| 亚洲图片日本AⅤ欧美在线| 可以免费观看的日韩av毛片| 丁香五月影院| 是还免费视频1727我| 国产女主播视频在线观看| 中文字幕精品三级久久久| 爱爱动态60秒| 天天干人妇| 欧美日韩成人在线| 91精品人妻一区二区-全集完整版免费正片国语-B02AV | 日韩激情视频| 欧美日韩中文字幕不卡| 黄色无码高清黄色无码网站| 手机在线免费看的av| 亚洲免费成人在线高清无码视频| 久久激情亚洲精品无码?V | 韩国女主播青草福利视频| 欧美天堂日韩三级国产传媒| 黑人与人妻| 欧美日韩不卡传媒| 黑人免费福利视频| 亚欧成人中文字幕一区| 伊人久久亚洲中文字幕不卡| 国内外内射高清视频| 青青操狠狠撩| 亚洲 图片 综合91| 91精品国久久久久久无码| 青娱乐黄色录像| 大香蕉视频一二三区| 91精品丝袜久久久久久| 一区麻豆 高清中文字幕| 猛猛干| 8x福利精品第一福利视频导航| 久久免费精彩视频| 色哟哟av| 乱伦一区二区三区‘| 日韩素人无码一区二区三区三州| 综合欧美色图| 性久久| 色婷婷久久| 蜜臀AV成人精品蜜臀| 精品午夜福利| 九九综合久久| 熟妇人妻一二三区免费| 国产综合操逼高清| 国精综合一二三区影视| 亚洲精品国产熟女| 色网1| 黄页视频网站野外| 亚洲天堂久| 国产精品视频一区二区三区八戒| 亚洲国产亚洲天堂| 亚洲熟女乱色一区二区三区| 色综合色综合网| 黄站在线免费观看| 欧美在线视频99| 九九这里只有精品| 亚洲色图欧洲| 中文字幕91综合| 亚洲加勒比久久日本道| 无码人妻一区二区一牛影视| 精品无人区麻豆乱码1区2区图片| 国产强奸超碰AV| 精品一区二区三区四区外站| 免费一级特黄特色大片在线观看看| 婷婷四五区| 蜜桃久久精品一区二区三区| A级毛片在线看免费| 又大又白奶子| 欧美色宗合| 97精品国产97久久久久久户外免费| 亚欧成人一级片在线播放| 熟女精品一区二区三区| 密臀在线视频| 97九色人妻| 日韩三级在线观看网站| 青木玲在线不卡| 一区二区三区高清天码| 91超碰在线播放| 欧美日韩国产高清在线一二三区| 久久久久婷婷精品av电影| 久久99午夜精品一区人妻| 夜夜做夜夜爽精品视频| 五月婷婷色色| 国产一级内射高清视频| 97爱亚洲综合色| 丰满人妻一区二区三区色-百度| 天天干人妇| 大香蕉中文网| 另类专区加勒比| 五月丁香综合啪啪| 国产精品ww久久| 国产精品suv一区| 91成人在线免费视频| 好看的久久不射无码影视影院| av最新免费中文字幕| 天天操狠狠日夜夜干超大胆开放com大香蕉视频在线观看 | 精品人妻视频一区二区三区蜜桃视频| 日韩视频中文字幕| 四虎在线播放| 亚 欧 美 综合| 97色碰| 五月开心久久AV官网| 大香蕉一线视频| 日韩精品午夜操呦呦不卡影院 | 激情四射婷婷六月天| 婷婷五月在线视频| 国产偷拍网站| 久久久久久中文版| 激情色色| 国产三级中文有码在线视频| 97在线看| 国产午夜视频| 九九内射在线| 日韩欧美aⅴ综合网站发布| 亚洲黄色电影| 亚洲男人的天堂AV| 狠狠干婷婷| 肉动漫无遮挡h在线观看| 久久久久久久久久久久久久久久9| A 天堂在线观看视频| 手机看片91人妻| 韩国嫰模上门援交视频| 97亚洲综合电影| 久久九九国产精品| 日韩av性爱在线播放| 亚洲啪啪啪啪视香蕉| 天综合中文| 日韩一区二区三区四区五区| 91久久久亚洲| 丰满人妻一区二区三区免费| 日韩啪啪啪视频| 亚洲色五月| 综合久久久久久久综合网| 精品人妻一区二区免费看| 99热导航| 97九色人妻| 另类专区在线观看| 天天操人人操狠狠插| 男人的天堂1024| 日韩中文字幕2020| 亚洲影院小综合| 99视频只有精品| 性色av网站| 操操逼视频| 91熟女视频| 久久久月天| 男人的天堂久久狠| 91伊人大香蕉| 欧美精品91| 亚欧精品久久久久久久久久久| 乱伦图一区| 91欧洲国产成人久久精品网站| 亚洲情色 无码专区| 97在线欧洲| 欧美日韩国产传媒在线精品| 欧美一级色| 人人操人人大香蕉| 思思热久久成人| a片在线播放| 人妻夜夜爽天天爽三区麻豆AV网站| 超碰偷拍| 91劲爆| 日本欧美m v精品网站加| 国产精品久久9| 国产精品久久久久久久久AV大片| 亚洲成人福利电影免费| 377p欧洲日本亚洲大胆| 手机在线播放国产福利| 自拍偷拍草一草| 女人天堂av在线播放| 天堂综合网| 久夜操| 久草毛片| 老熟女91视频| 97日韩欧美| 96麻豆精品一区二区三区| 中文字幕成人乱码熟女精品国50 | 亚欧美天堂在线| 操逼网站地址| 日本三级中国三级99人妇网站| 精品一二三区久久AAA片| 蜜乳视频网站| 天天躁日日躁成人字幕aⅴ| 乱操乱伦AV| 五月丁香六月综合缴清无码| 国产婷婷综合在线观看| 人妻9117c| 日本操逼aaaaa| 9色国产精品一区粉嫩| 日韩无限资源| 日本乱人伦片中文三区| 强奸乱伦 亚洲一区| 久久久久久久强迫| 欧美黄色片在线播放| 久久久久久亚洲Av无码| 性在久久久久久| 色婷婷九月天天综合| 亚洲黄网在哪免费看| 日本精品一区二区中文字幕| 久久久97| 日本青青草在线| 国产熟女少妇一区| 91久久久视| 精品无码一二三四区| 校园春色亚洲无码| 日韩欧美~中文字| 国产97av| 午夜无遮挡男女啪啪视频| 无码日韩人妻av一| 永久免费发布性爱网| 干婷婷综合网| 韩国久久97| 18一区二区三区| 超碰中文字幕人妻草一区| 99re6国产精品99re| 男女啊啊啊啊啊| 日韩三级伊人| 日韩免费大片一级播放| 色五月婷婷在线| 四虎永久在线精品免费网址| 日日碰视频网| 97超碰欧美中文字幕| 国产一级舔足在线观看| 日韩欧美午夜视频在线| 亚洲欧美精品一区天堂久久| 少妇人妻精品| 亚洲黄色电影| 国产精品麻豆成人av| 日本免费一级AAA大片器 | 亚洲永久AV无码精品秋霞| 天天超级碰碰碰| 精品人妻一区二区三区四区| a片在线播放| Blackedraw视频一区二区| 久久九九99| 婷婷五月天久久精品视频一区二区三区| 伊人亚洲综合| 欧美在线播放| 欧美顶级黄色大片免费| 人人色97| 日韩亚洲97| 亚洲欧美不卡线| 一类无码操逼视频| 91N欧美| 欧亚成人在线视频| 97国产精品一区二区传媒公司| 欧美色www亚洲国产阿娇要播| 顶级丝袜熟女一区二区三区| 精品妇女一区二区三区| 怡红院亚洲怡春院av| 男人天堂电影院| 国产麻豆福利av在线播放| 亚洲天堂男人在线| 亚洲怡春院| 无码精品久久久久久亚洲| 太久视频| 少妇69中文| 日韩精品一区二区人人人| 奇米四色影视777久久久| 日本大片日本一区二区免费高清| 粉嫩AV输入| 色欲av国内精品久久久久久| 免费综合亚洲中文| 欧美猛交黑寡妇中文字幕| 国产9l 大屁股| 国产日韩区| 毛片中心9视频99| 婷婷性网| 国产精品一区二区 尿失禁| 亚洲欧美国产中文字幕| 黄骗免费网站| 大香蕉久久| 9999九九九久久久| 美女熟妇色| 97超碰碰碰| 久久久久幕乱码| 天天艹天天日| 亚洲熟女综合| 91久久| 无遮挡男女激烈动态图| 夜夜国自区| 精品天堂| 人人考人人摸人人干| 青青草男人天堂| 人妻少妇久久中文字幕一区二区 麻豆| 另类小色呦| 亚洲性猛交| 性爱网站一区二区| 不卡九肏| 18禁看网站一区| 欧美制服另类丝袜| 人妻三级在线中文字幕| 亚洲欧美精品一区天堂久久| AV天堂因数| 激情久久av一区av二区av| 加勒比人妻综合| 日韩乱伦AⅤ| 日本97久久久精品| 欧美日韩国产三级黄色| 亚洲污污网站| 青青操综合网| 自拍第一页| 蜜乳性色无码专日粉嫩骚逼AV| 欧美国产一区二区三区麻豆传媒| 久久精品国产亚洲妲己影视| 亚洲美欧999| 三上悠亚在线毛片91| 日韩欧美视频青青| 久久久精品网站| 精品久久久九九九孕妇| 成人国产视频在线观看| 日日橹狠狠爱欧美超碰| 日韩欧美成人午夜福利| 性爱AV天堂| 亚洲午夜福利在线影院| 极品一区二区三区免费| 最新日韩黄片| 高颜值美女口爆高潮浪叫| 丁香五月影院| 国产400孕妇孕交群| 欧美日不卡| 高潮精品| 九X超碰| 亚洲无吗在线视频| 蜜臀va69| 大香交伊人网| 日韩有码 一区二区三区| 精品美女少妇一区二区三区| 极品尤物自安慰| 91熟女视频网| 啊啊啊啊啊在线观看网址| 国产精品久久久吖| ai欧美亚洲小说| 在线观看黄色电话| 天天影视亚洲| 秋霞男人网| 日本性爰一道本| 懂色影视久久| 又黄又爽在线观看视频| 377p欧洲日本亚洲大胆| 91老妇女| 亚洲欧美另类少妇精品| 91操人| 极品肉射| 9精品在线| 懂色天天爱天天日天天射天天澡| 97资源亚洲| 日韩紧密久久| 色呦呦呦在线观看视频| 97这里有精品| 久久久久久久久久久久黄色 | 一本色道久久天天射天天干| 国产人人干| 亚州国产精品乱| 浓厚中出中文字幕在线| 91精品91久久久久77777| 25国产精品免费观看| 99e久久国产精品| 90后后入| 色噜噜人妻丝袜AV资源| 操逼日韩无码 | 亚洲精品国产专区在线观看| 99夜夜操| 国产精品在线网站| 少妇二级| 一级久久性爱视频| 亚洲欧美国产va在线播放频| 精品二区三四区五电影 | 国产精品香蕉热久久新品| 免费视频一二三区| 97丝袜亚洲在线播放| 欧美精品第3页| 久久亚洲欧美中文字幕国语| 揉揉日日日日| 亚洲熟女一区二区| 久久蜜色情在线视频xxx免费观看| 亚洲乱码尤物193YW| 国产精品人妻无码久久久老鸭窝| 91精品人妻五十路| 97视频在线观看高清资源| 色色婷婷五月| 国产91久久九九免费精品无码| 美女裸体无遮挡永久免费观看网站| 中文字幕无码不卡啪啪| 91在线色| 久久婷婷色综合一区二区三区| 啪一啪免费视频| 国产乱伦视频污| 四虎影视欧美| 99热99re6国产在线播放| 天天综合网91入口| 一区二区三区 丝袜 高跟 美腿| 日韩精品视频在线观看一卡二卡| 人人操人人操人人人操| 久操91视频| 精品对白久久不卡| 青女偷拍网| 97伦乱| 日本黄色裸日本黄色裸体| 后入 亚洲 美女 射| 亚洲黄色影视| 人人色人人操在线| 99re超碰| 香蕉精品二区二区| 国产精品97超碰| 日韩精品9区| 亚洲精品骚逼| 玖玖爱综合| 国产精品久久久亚洲一区| 9999免费精彩视频| 91人妻尻屄视频| av在线一区二区三区| 欧美区亚洲区偷拍区 | 国产成人精品日本视频| 黄色av片三级三级三级免费看| 午夜一级免费毛片| 宅男影院久久久,99| 日产操逼| 午夜男女爽爽大片免费观看| 秋霞午夜成人福利片片| 97碰| 国产主播福利| 亚洲日本激情| 人人妻人人爽 97人人看碰人免费公开视频| 亚洲男人天堂网| 中文字幕乱亚洲美女精品一区| 国产又粗又长又大的视频| 夜夜嗨一区| 骚鸭AV| 日日爽熟女| 97欧美日韩综合| 欧美碰碰综合色| 水滴偷拍| 蜜臀久久99精品久久久久| 四虎在线观看视频| 日本一区二区亚洲综合| 国产主播福利| 九九精品99| 午夜超爽| 亚洲色图殴美色图激情乱伦| 国产又色又爽又舒服的三级视频| www久久99| 青青草操逼逼视频| 美女啊啊啊啊pc| 国产精品久久久久绯色| surenchaopeng| 人妻熟女一区二区| 97神马久久| 中文字幕欧美丝袜07资源| 精品一区二区三区国产| 91香蕉视频在线观看免费| 亚洲一区二区三区播放在线| 人人摸人人干人人拍97| 人妻少妇一区二区| 亚洲宗合电影| 天美传媒av一区二区| 国产精品久久久午夜夜伦鲁鲁| 97久久久网站| 国产婷婷综合在线观看| japan日本高清乱xxxx| 在线视频日韩欧美国产| 久久色人体| 日本啊啊啊啊啊视频| 亚洲人综合| 91 综合网| 小说区 图片区色 综合区| 国产av强奸美女| 成人性爱电影一区二区| 欧美黑人猛交春色影视大全| 国产精品视频| 一本久久久精品| 女性喷水高潮在线观看| 综合久久六月久久婷婷| 日本欧美韩国日产片片在线看免| 人人妻人人色| 欧美热图99| 色色色日本| 91精品无码人妻系列| 激情自拍 校园春色| 激情欧美日韩女同久久| 日日躁夜夜躁狠狠躁超爽| 国产亚洲精品A在线观看下载| 亚洲精品第一| 嗯嗯啊啊操死我| 屁股久久久久久久久| 91一区二匹| 亚洲制服aⅴ中文字幕| 99操碰| 欧美日韩一干二干| 中文字幕视频在线观看| 91精品国产日韩欧美综合| 丁香六月综合激情| 超碰1997| 麻豆美女丝袜人妻中文| 国语对白在线播放视频| 天天摸夜夜摸| 亚洲精品黄码久久久久| 亚洲精品毛片在线观看| 偷拍欧美激情| 天天色综合影视网| 精品人妻一区二区视频| 青青11操操操操操操操操| 久久成人网站| 国产精品蜜臀久久久久无码AV| 97在线视频观看| 精品国产久久乱码| 中文AV制服乱伦| www.色五月| 狠狠做深爱婷婷久久二区| 国产精品com| 亚洲高清在线| 青青草久久在线| 最近2018中文字幕在线高清第一页| 99操逼| 91无摭挡| 亚洲精品尤物yw在线影院| 东京太热久久久| 欧美激情亚洲| 国产精品成人蜜臀AV在线| 91红杏| 久久精品店| 欧美性爱在线无码| 香蕉色网| 99久久久无码精品国产人| 久久久久精| 亚洲天堂男| 亚洲福利中文字幕在线| 超碰久久网| 国产精品一区av在线| 中文字幕一区二区三区蜜臀| 日本在线伊人啪啪| 丁香色五月 97干| 加勒比色综合| 思思热在线cao| 婷婷另类小说| 国产视频一区二区三区在线免费观看| 九九玖玖精品| 1769一区| 欧美色图天堂网m| 一级一性爱免费视频| 亚洲天堂综合AV| 东京太热男人的天堂久久久| 天天干天天狼在线视频| 亚洲91射| 欧美十八禁在线看| 蜜桃传媒视频第一区入口在线看| 粉嫩绯色AV一区二区在线| 国产不卡的视频| 99久久久er直播网址| 98超碰日本| 逼操网站| 亚洲精品97中文字幕| 91成人久久| 欧美在线综合| 国产精品久久久久综合| 色网亚洲人| 日本人妻A片成人免费看片| 可乐操亚洲蜜911| 久热免费视频| 91美女网站| 蜜桃中文字日产乱幕4区| 国产综合日韩伦理| 麻豆AV短剧| 欧美αv.com| 中文字幕乱偷人妻久久艾草网| 亚洲黄色网址视频| 噜噜瑟| 日本一本一区二区三区四区五区欧美日韩中文字幕 | av草草在线电影| 欧美性生活综合| 不卡二三区人妻少妇| 日韩免费人妻色情网站| 一起草三级AV电影在线观看 | 久久伊人大香蕉| 久 久无码人妻AV| 久9久| 精品欧美А∨无码黑人大荫蒂| 人妻激情在线视频| 国内毛片无码一级毛片| 9118禁| 精品国产99| 欧美日韩国产高清在线一二三区| 激情另类激情| 国产精品青青草| 黄色操人| 亚洲伊人青青草| 美中韩AV综合网| 操逼日批| 亚洲天天天| 人妻天天操天天爽视频免费| 国产精品网址| 97超级久久| 97高清啪啪| 91精品人妻电影| 丰满人妻一区二区三区| 色九九九综合| 99自拍视频在线| 亚洲码在线中文在线观看| 任你干在线视频| 欧美躁死她一区二区| 亚洲成人黄色在线观看| 国产人人干| 无码91| 92午夜免费福利视频| 亚洲色图图片| 国产第二页| 97天天摸天天碰| 日韩人妻一二三区视频| 国产视频第二页| 操死我了嗯嗯嗯| 久久人妻精品| 日韩免费大片一级播放| 欧美综合网在线| 国产妇女精品视频青青草| 一区二区 韩日AV| 日韩人妻一二三区视频| 啊啊啊啊好多水| 国产一区二区三区,在线观看观看| 激情五月激情综合网| 岛国片在线播放| 日韩有码中文字幕女同性恋| 国产精品久久久久久久久久久久久久久久 | 亚洲综合影院| 久久国产精品视频| 精品国产72| 五月天偷拍| 国产精品蜜乳AV| 欧美一级黄色免费专区| 青青草色AV| 老司机免费视频在线91| 亚洲欧洲视频小说在线观看| 青青草丝袜在线视频| 欧美人人曰人人操人人射射| 九热久| 亚洲综人| 丝袜美女诱惑 91 视频| 淫色网综合| 狠狠操狠狠| 国产小视频91| 天天操天天射天天日| 大象AV在线| 午夜啊啊| 久久9免费视频| 欧美 日韩 婷婷 五月| 中文一区二区婷婷视频| 中文字幕 码 自拍 视频 区| 嫖老熟女A片一二三区| 老熟女搡BBBB搡BBBB视频| 玖草在线视频| 偷拍视频青青草在线视频| 日韩高清一二三| 97bbn| 91欧美美女日韩国产婷婷| 97视频网站在线观看| 丁香九月婷婷| 韩国黄色片精品久久久 | 九九久精品| 国产AV人人夜夜澡人人爽麻豆| 九九九久久久久| 欧美性暴力猛交XXXX| 国产高清1234区| 亚洲欧洲偷拍一区| 9色在线| 国产精品国产拍高清AV| 女人双腿搬开让男人桶| 日韩人妻中文视频| 精品一区二区成人| 日本视频一区二区三区| 天天色,天天干,天天干| 国产精品久久久久久久电影渣男| 国产91福利小视频在线观看| 热99这里有精品综合久久| 国产精品扒开腿做爽爽爽视频| 久久伊人东京热| 老熟女综合网| 国产91美女视频| 天天天肏屄肏屄肏屄欧美欧美| 免看60秒涩涩视频| 男人的天堂三级| 婷婷五月天色网| 亚洲色欧| 夜夜高潮夜夜爽夜夜爱爱一区| 高颜值美女口爆高潮浪叫| 亚洲精品九九九九九九| 嗯嗯啊啊啊好爽| 九九热三级片| 久久大黄片| 成年人网站在线免费观看| 豆花视频操逼网址| 91午夜无码| 国产高清在线观看欧美| 极品尤物女神在线观看| 欧美成人一区二区三区在线播放 | 9超碰免费| 欧美国产有色电影| 久久成人午夜狠狠| 伊人网在线点播| 农村妇女精品一二区| 国产精品熟女一区二区三区| 国产精品人妻免费精品| 国产精品成人久久一区二区三区| 人人 操人人 操人人| 中文字幕交换人妻| 精品人人插人人操| 密桃99999| 日韩一区二区熟女| 吉川爱美亚洲二区在线| 精久久久| 久久久精品网| 强奸乱伦AV一天堂网| 强奸乱伦大香蕉| 亚洲国产欧美中日韩成人综合视频| 67194国产| 免费看污网站| 欧美在线中M| 91欧| 四虎午夜影院| 日韩一区二区三区四区五区| 天天干天天拍| 男人的天堂色偷偷青青草视频婷婷网| 激情四射五月天| 日比av无码| 天天做天天爱夜夜爽毛片试看| 91在线视频免费播放| 亚洲乱熟女一区二区三区大香蕉| 大胆91| 婷婷香网站| 麻豆天美在线| 日本成人A片网站| 色婷亚洲五月在线观看| 国产精品乱人伊人网| 丝袜美女诱惑 91 视频| 91欧美网| 日韩激情小说一区二区| 久久婷婷五月天| 色av中文字幕| 99热在线只有精品| a片在线播放| 99热只有这里有精品| 久久久久久久 九九九九九九九| 久久香蕉超碰97国产精品| 在线观看啊啊啊啊啊| 人人干人人操人人爱| 欧美一级做a爰片免费视频| 91 丝袜在线| 91久久久久| 日韩激情无码影院| 4399成人黄A片| 中文幕97| 校园春色 欧美| 3028国产精品| 日韩紧密久久| 91N综合网| 天天干干天天干干| 久久手机好看网站| 成人夜夜| 中文精品一区二去| 日韩亚洲美女一区久久| 舔人妻中文免费视频| 人人看人人插| 精品一区二区啪啪啪| 久久亚洲天堂| 一本色道久久天天射天天干| 中文字幕黑人大片| 亚州五月| 啪啪资源网| 超碰 另类 欧美| 亚洲性高潮| 亚洲精品亚洲人成在线麻豆| 99综合自拍| 啊啊啊免费视频| 99久久综合网| 日韩精品一区,二区 九九...老司机| 操国产逼| 日韩美一区| 偷偷人人精品女女久久| 天天做日日做天天欢。| 99re只有精品| 秋霞午夜成人福利片片| 九九在线视频| 天美传媒精品久久视频| 99在线无码精品秘 入口黑人| 99啪啪视频| 97在线视频免费| 久久婷五月| 日韩电影中文字幕| 亚洲av淫乱| 亚洲色悠悠久久88| 91丨九色丨国产丨人妻在线| GVH-003 母子姦 青木玲-麻豆视频,麻豆视传媒短视频网站入口,麻豆视传媒官网直 | 日本激情免费大片| 青青草白白色| 黄色片A级一区二区三区| 久悠悠av| 黄骗免费网站| 欧美色三级片91| 天天爱天天操| 久久精彩免费视频| 色97欧美| 丁香婷婷啪啪| 欧美色交| 九久9精品| 99AV| 婷婷五月激情综合| 夜夜夜爽www精品视频| 人人色人人操在线| 色欧美在线| 色情五月丁香| 91制服丝袜| 狠狠干狠狠色| 深夜视频| 天天综合~91入口| 999综合网| 性色avv| 久久性爱视频免费看| 91 丝袜在线| 国产精品不卡一区二区电影| 中文字幕av久久爽Av| 日本性交操一区二区不卡系列| 激情图片伦理国产一区二区日韩| 亚洲中文字幕熟女| 久久超碰、| 岛国福利在线精品播放| 97超级欧美| 男女激情黄色网址| 国产三级中文字幕粉嫩| 99久久综合网| 密臀视频三区免费网站| 校园春色之综合网| 亚洲影院成人| 91网站视频在线观看| 老熟妇一区二区三区…| 久久久久久裸体| 99久久婷婷| 亚洲啪啪综合?v一区综合精品区| 麻豆久久久久久久久丝袜 | 两女互慰AV高潮喷水在线观看| 欧美成人色| 亚洲 国产 精品一区| 97在线观看视频| 久久久久久亚洲精品不卡人乳| 日本欧美中文字幕| 黄呦呦在线| 8050无码八戒| 91九色丨风韵犹存| 欧美一区二区日韩传媒搭讪精品| 超碰无码加勒比| 综合色久欲| 美女黄色一级A视频| 97无码视频在线播放| 大香蕉欧美国产日韩高潮| 色乱二区| 综合第一页| 婷婷情色五月天| 日韩不卡a级视频专区| 欧美的精品的视频| 欧美日日夜夜| 久久久久亚洲Aⅴ无码| 伊人九九九| 男人天堂久久日韩| 亚洲免费人妻在| 好爽视频在线观看视频 | 99性爱在线观看| 精品人妻一区二区三区免费视频| 九一亚洲国产免费| 国产三级中文有码在线视频| 欧美色另类| 91九九九逼| 日本久久天堂| 豆花视频操逼网址| 四虎影视国产精品| 天天摸夜夜摸| 强免费黄色网址| 家庭乱伦麻豆| 九草九九九| 国产有码一区| 亚洲欧洲网站免费观看| 人人搞人人插人人操| 啊啊啊啊好疼视频| 人人喜人人妻| 热99这里只有精品| 久久综合九色综合欧洲98| 欧亚日韩综合精品国产| 97精品国产97久久久久久| 91在线超高颜值国产| 香蕉99秘 一区精品蜜桃臀| 欧日韩在线观看| 嗯啊不要啊在线| 综合操逼| 久久亚洲中文字幕视频| 成人草草视频| 四季AV一区二区凹凸精品小说| 五月丁香婷婷综合| 色天使亚洲综合在线观看| 96久久久久| 久久精品99| 9超碰免费| 丁香激情网| 唐山老熟妇露脸啪啪叫| 曰韩人妻中文字幕在线| 疯操AV| 日欧毛片久久| 国产极品999| 狠狠色婷婷7777久| 在线综合色| 日韩av免费一级电影| 欧美懂色综合网| 久久久一二三四区| 日欧毛片久久| 爱欲AV| 亚洲 图片 综合91| 丁香九月激情| 五月天丁香婷婷综合网站| 亚洲**2021在线观看| 蜜臀久久99精品久久久久久-DVD| 午夜福利一区二区三区四区五区色婷婷| 超碰97资源大奶| 欧美性特| 激情五月综合网| 全球成人中文在线| 久久riav中文精品| 精品人妻视频入口| 久久大| 97狠狠| 亚洲交性| 色亚洲欧美| 蜜桃臀久久| 人妻激情在线视频| 超碰98综合网| 男人久久精品| 国产传媒美日韩av| 97 亚洲 日韩 欧美 在线| 尹人大香蕉视频在线| 亚洲AV无码乱码在线观看性色| 国产理论视频在线播放| 高潮的A片激情扒开一区| 日本黄大片在线观看视频| 激情综合网激情综合| 亚州男人天堂| 老妇女91| 精品对白久久不卡| 亚洲欧美清纯| 极品色综合| 91久久婷婷| 亚洲成人ab| 久久久久白虎| 人人澡人人干| 国产一区在线看| 伊人91| 天堂男人网| 97超碰色色| 999久久久| 一区二区三区国产在线播放 | 亚洲欧美精品福利在线| 能直接看AV的网站| 久久婷婷综合国际产色怕| 色综合网1| 九九99精品| 色婷婷成人| 欧美日韩丝袜| 国产精品小视频一区二区三区| 人妻日日夜夜精品| 欧美一区二区三区大综合| 97超碰天天爱天天爱| 欧美日韩国产电影| 人妻丝袜肏逼| 热久久无毒不卡| 天干天干天干天天做| 九九九国产| 野狼福利社区| 韩国免费播放一级毛片| 亚洲无码太久| 国产高清在线观看欧美| WWW黄片COM| 97人人干| 色一射色一射| 黄色不卡视频| 中文字幕免费看| 亚洲图片小说欧洲| 亚州综合| 午夜超爽| 色噜噜人妻丝袜a∨先锋影 | 中文字幕视频在线观看一区二区| 手机不卡视频不卡在线一二三区| 欧美熟妇操操视频| 久久精品99久久久久久| www.人人摸在线视频| 五十路三区在线| 精品国产av一区二区三区四区入口| 无码精品久久| 亚洲欧美国产成人综合不卡| 亚洲免费看片| 久久99国产综合精品女同| 色五月第四色| 91九色丰满高潮| 久 久无码人妻AV| 欧美的性爱网站免费| 国产一区二区三区导航| 激情五月天婷婷| 色综合潮| 人妻无码一区二区三区久久99| 亚洲人妻爽爽爽| 日韩精品一区二区高清| 成年人一级黄色毛片大全在线观看| 99热精品在线| 91色爽欧美| 天天热精品| 91狠狠综| 18禁网站在线播放| 少妇超碰在线| 亚洲诱惑| www.91视频网| 国产日韩色综合| 久99视频| 欧美一区二区传媒| 2020天天色综合| 亚洲一区在线观看欧洲| 国产av美女被艹的乱叫| 99re6久热只有精品6在线直播 | 伦理片秋霞免费影院| 热天堂一区二区| 一区二区亚州激情久婷婷欧美| 久久激情婷婷| 女人 A一级| 精品人人| 久久华人网| 精品国产av一区二区三区四区入口| 亚洲色图日韩精品| 啊啊啊 在线观看| 色五月AV在线| 内射夫妻三片| 丁香五月婷婷啪啪| 91美女色视频亚洲| 日日摸天天爽夜夜欢| 综合免费无码中文| 人妻日日夜夜精品| 97网站在线观看| 久久天天艹| 精品国产乱码久久久久久久久1| 国产一区二区三区,在线观看观看| 熟妇熟女一区二区三区| 国产亚洲日本精品在线| 丝袜喷水在线| 精品国产乱码久久久A| 日本操大逼| 不卡九肏| 狠狠爱夜夜| 日韩欧美亚洲一区二区三区影院 | 亚洲三级。日韩三级| 国产高清MV操逼视频| 九九热免费在线国产视频伊人五月| 啊啊啊操死我了| 国产成人无码啪| 久久久久久久久久久久久9999| 狠久久| 欧美激情中文字幕另类小说| 鸥美精品一区二区久久婷婷| 男人的天堂va| 欧美一区二区亚洲天堂| 色www精品视频在线观看| 日本影视久久免费| 五月丁香影院| 国产丝袜一区二区三区| 国产欧美精选激情视频| 性欧美体内射精| 97天堂| 婷婷99狠狠| 后入式999| 操逼操逼逼操操逼91 | 国产日韩精品一区二区三区| 日韩精品 欧美激情| 国产成人bd在线观看| ai欧美亚洲小说| 国产精品99精品视频网站| 亚洲,欧美,春色,另类| 91天天综合在线| www.国产高潮精品| 夜夜青青无码影院| 无码天天操| 亚热日本熟女| 男人天堂黄片| 亚洲免费97免费| av网站免费线看| 99啪| 国产在线播放成人免费| 七久久久| 色爱亚洲| 骚逼高潮久久精品| 国产999精品久久久| www.久久久久| 就去色综合| 欧美色五月| 欧美碰碰综合色| 99这里有精品| 久久久久久久9999| 色色色网站| 美女啊啊啊啊啊啊| 小少妇| 亚洲色婷婷久久久综合日本| 国产精品久久久亚洲一区| AV和黑人在线播放| 国产精品无码av在线 | 国内精品a| 久久97资源 网| 国产精品成人久久一区二区三区 | 日韩精品一区二区人人人| 啪啪啪大香蕉| 久久超碰com| 天天欧美欧美亚洲网| 久久男人的天堂| GVH-003 母子姦 青木玲-麻豆视频,麻豆视传媒短视频网站入口,麻豆视传媒官网直 | 78精品在线| 中文字幕五区| 欧美黄页| 欧美内射少妇| 日韩熟女乱伦中出| 玖玖草久草99蜜月一区二区三区| 美中日韩无码| 婷婷久久久精品| 天天爽天天| yiqicaoav| 亚洲91网。| 国产精品视频麻豆入口| 欧美中字不卡| 手机看片1025| 九久9精品| 操死我干死我| 欧美天天干| 一本色道久久综合狠狠操| 思思热在线视频精品| 亚洲一区二区在线观看91| 久久久久久久久九九久孕交| 国产91乱伦| 在线啊啊啊| 日本人体九九九九九九| 亚洲欧美日韩制服另类| 欧美 亚洲 另类 综合| 在线v中文字幕一区二区三区| 久操频道免费在线呗看| 韩国一区二区精品亚洲| 日本一区二区三区免费观看| 色偷综合|