編者按:Michelle Ufford(Netflix大數(shù)據(jù)工具負(fù)責(zé)人)、M Pacer(Netflix工程師、Jupyter核心開發(fā)者)、Matthew Seal(Netflix工程師)、Kyle Kelley(Netflix工程師)介紹了Netflix如何拓展Jupyter notebook使用場(chǎng)景,以及為支持新使用場(chǎng)景進(jìn)行的基礎(chǔ)設(shè)施建設(shè)。
Jupyter notebook在數(shù)據(jù)科學(xué)家當(dāng)中快速流行開來,成為編寫快速原型和進(jìn)行探索性分析的事實(shí)標(biāo)準(zhǔn)。在Netflix,我們進(jìn)一步開疆拓土,重新設(shè)想了notebook的形態(tài)、適用人群、用途,并且投入很多資源以實(shí)現(xiàn)我們的愿景。
本文將分享我們的動(dòng)機(jī),我們?yōu)槭裁从X得Jupyter notebook這么有競爭力。本文也將介紹我們的notebook基礎(chǔ)設(shè)施的組成部分,同時(shí)探索我們?cè)贜etflix的一些創(chuàng)新的使用notebook的方式。
如果你比較匆忙,我們建議你直接從使用案例一節(jié)開始閱讀。
動(dòng)機(jī)
數(shù)據(jù)賦能Netflix。數(shù)據(jù)滲入我們的想法,為我們的決策提供信息,并挑戰(zhàn)我們的假設(shè)。數(shù)據(jù)為在前所未有的尺度上的試驗(yàn)和創(chuàng)新提供燃料。數(shù)據(jù)幫助我們發(fā)現(xiàn)美妙的內(nèi)容并向全世界一億三千萬會(huì)員提供個(gè)性化的體驗(yàn)。
實(shí)現(xiàn)這一切可不是小事一樁;它需要全面的工程支持和基礎(chǔ)設(shè)施支撐。每天有超過一萬億事件寫入攝取信息流的過程,經(jīng)過處理后,再寫入100PB的數(shù)據(jù)倉庫云。同時(shí)每天運(yùn)行150000項(xiàng)數(shù)據(jù)任務(wù),范圍無所不包,從報(bào)告分析到機(jī)器學(xué)習(xí),到推薦算法。為了支撐如此巨大的尺度上的使用案例,我們創(chuàng)建了一個(gè)業(yè)界領(lǐng)先的靈活、強(qiáng)大、復(fù)雜(必要的復(fù)雜性)的數(shù)據(jù)平臺(tái)。我們同時(shí)創(chuàng)建了補(bǔ)充工具及服務(wù)的豐富生態(tài)系統(tǒng),比如Genie(聯(lián)合任務(wù)執(zhí)行服務(wù))和Metacat(聯(lián)合元存儲(chǔ))。這些工具簡化了復(fù)雜度,使其可以支持全公司范圍廣泛的使用需求。
Netflix的數(shù)據(jù)角色
譯者注:上圖羅列了Netflix的數(shù)據(jù)角色,包括商業(yè)分析師、數(shù)據(jù)分析師、量化分析師、算法工程師、分析工程師、數(shù)據(jù)工程師、數(shù)據(jù)科學(xué)家、機(jī)器學(xué)習(xí)科學(xué)家、研究科學(xué)家。
多樣性的數(shù)據(jù)使用者很令人興奮,但它不是沒有代價(jià)的:數(shù)據(jù)平臺(tái)——以及配套的工具、服務(wù)生態(tài)系統(tǒng)——必須支持更多使用案例、語言、訪問模式,等等。為了更好地理解這一問題,考慮3種常見角色:分析工程師、數(shù)據(jù)科學(xué)家、數(shù)據(jù)工程師。
不同角色(分析工程師、數(shù)據(jù)科學(xué)家、數(shù)據(jù)工程師)可能選擇不同的工具和語言
一般來說,每種角色依賴不同的工具和語言組合。例如,一個(gè)數(shù)據(jù)工程師可能在IntelliJ中使用Scala創(chuàng)建一個(gè)包含數(shù)萬億音視頻流事件的新的聚合數(shù)據(jù)集。一個(gè)分析工程師可能使用SQL和Tableau基于這一聚合創(chuàng)建關(guān)于全球音視頻流質(zhì)量的報(bào)告。這份報(bào)告可能導(dǎo)致一個(gè)數(shù)據(jù)科學(xué)家在RStudio下用R編寫一個(gè)新的音視頻流壓縮模型。表面上看,這些都是分散的、不存在互補(bǔ)性的工作流程。但是,如果我們深挖一些,我們會(huì)發(fā)現(xiàn)每個(gè)工作流程中都有一些相通的任務(wù):
數(shù)據(jù)探索—— 發(fā)生在項(xiàng)目早期;可能包括查看樣本數(shù)據(jù),運(yùn)行查詢請(qǐng)求以進(jìn)行統(tǒng)計(jì)分析和探索性分析,以及可視化數(shù)據(jù)。
數(shù)據(jù)準(zhǔn)備—— 迭代任務(wù);可能包括清理、標(biāo)準(zhǔn)化、轉(zhuǎn)換、逆歸一化、聚合數(shù)據(jù);通常是整個(gè)項(xiàng)目最花時(shí)間的任務(wù)。
數(shù)據(jù)校驗(yàn)—— 重復(fù)任務(wù);可能包括查看樣本數(shù)據(jù),運(yùn)行查詢請(qǐng)求以進(jìn)行統(tǒng)計(jì)分析、聚合分析,以及可視化數(shù)據(jù);通常作為數(shù)據(jù)探索、數(shù)據(jù)準(zhǔn)備、開發(fā)、部署前、部署后等階段的一部分。
產(chǎn)品化—— 發(fā)生在項(xiàng)目后期;可能包括部署代碼至生產(chǎn)環(huán)境,裝填數(shù)據(jù)集,訓(xùn)練模型,校驗(yàn)數(shù)據(jù),規(guī)劃工作流程。
為了幫助拓展使用者范圍,我們想要讓這些任務(wù)盡可能地省力。為了幫助拓展我們的平臺(tái),我們想要最小化需要支持的工具數(shù)量。但是怎么才能做到呢?沒有一個(gè)工具可以完成所有這些任務(wù);不僅如此,單個(gè)任務(wù)經(jīng)常需要多種工具。然而,當(dāng)我們?cè)偌由弦粚映橄蟮臅r(shí)候,在這些工具和語言之上涌現(xiàn)出了一種共同模式:運(yùn)行代碼,探索數(shù)據(jù),呈現(xiàn)結(jié)果。
碰巧有一個(gè)開源項(xiàng)目正是為此設(shè)計(jì)的:Jupyter Notebook
Jupyter Notebook
nteract下的Jupyter notebook,其中使用了Vega和Altair可視化
始于2014年的Jupyter項(xiàng)目的目標(biāo)是創(chuàng)建一組一致的工具,用于科研、可重現(xiàn)工作流程、計(jì)算敘述、數(shù)據(jù)分析。這些工具遷移到業(yè)界的效果很不錯(cuò),今天Jupyter notebook已經(jīng)成為數(shù)據(jù)科學(xué)家工具箱的必備之物。Jupyter曾被授予2017年度ACM軟件系統(tǒng)獎(jiǎng),該獎(jiǎng)授予對(duì)技術(shù)概念和商業(yè)接受度方面產(chǎn)生了持久影響的軟件系統(tǒng),歷史上Java、Unix、Web曾獲此獎(jiǎng)。
在我們看來,Jupyter notebook極具競爭力,它提供了這些核心功能:
語言無關(guān)的內(nèi)省和執(zhí)行代碼的消息傳遞協(xié)議
描述代碼、代碼輸出、markdown筆記的可編輯文件格式
基于web的用戶界面,以供編寫、運(yùn)行代碼,以及可視化輸出
Jupyter使用核作為計(jì)算引擎,Jupyter協(xié)議提供了與核通訊的標(biāo)準(zhǔn)消息傳遞API。這一協(xié)議使分離內(nèi)容編寫(用戶界面)和代碼執(zhí)行(核)的可組合架構(gòu)成為可能。通過將運(yùn)行時(shí)從界面中隔離出去,notebook可以在保持配置執(zhí)行環(huán)境的靈活性的同時(shí),跨多語言。如果存在知道如何基于Jupyter協(xié)議通訊的語言核,notebook就可以通過與核收發(fā)消息來運(yùn)行代碼。
支撐這一切的是將代碼和結(jié)果保存在一起的文件格式。這意味著,無需重新運(yùn)行代碼,就可以在之后訪問結(jié)果。此外,notebook保存了給出上下文的豐富文本。這使notebook成為溝通業(yè)務(wù)上下文,文檔化假設(shè),注釋代碼,描述結(jié)論等的理想格式。
使用案例
在眾多使用案例中,我們現(xiàn)在最常用的用途有三種:數(shù)據(jù)訪問、notebook模板、計(jì)劃notebook。
數(shù)據(jù)訪問
Netflix最早引入notebook是為了支持?jǐn)?shù)據(jù)科學(xué)工作流程。隨著越來越多的數(shù)據(jù)科學(xué)家開始使用notebook,我們看到了擴(kuò)張其工具效應(yīng)的機(jī)會(huì)。我們意識(shí)到,我們可以利用Jupyter notebook的多功能和架構(gòu),拓展其使用范圍為通用的數(shù)據(jù)訪問。我們從2017年第三季度開始認(rèn)真對(duì)待這一想法,將notebook從小眾工具提升為數(shù)據(jù)平臺(tái)的一等公民。
從使用者的角度來說,notebook提供了一個(gè)交互式地運(yùn)行代碼、探索輸出、可視化數(shù)據(jù)的易用界面——全都可以通過云端開發(fā)環(huán)境達(dá)成。我們同時(shí)維護(hù)了一個(gè)Python庫,加強(qiáng)了對(duì)平臺(tái)API的訪問。這意味著使用者基本上可以在notebook中編程訪問整個(gè)平臺(tái)。由于notebook的用途廣泛、功能強(qiáng)大、使用方便,我們發(fā)現(xiàn)notebook在整個(gè)數(shù)據(jù)平臺(tái)的各種使用者中間自然而然地快速流行開來。
現(xiàn)在,notebook是Netflix內(nèi)處理數(shù)據(jù)最流行的工具。
notebook模板
隨著我們?yōu)閚otebook擴(kuò)展平臺(tái)支持,我們開始引入新功能以滿足新使用案例的需求。由此涌現(xiàn)出了參數(shù)化notebook。顧名思義,參數(shù)化notebook讓你可以指定代碼中的參數(shù),并在運(yùn)行時(shí)輸入數(shù)值。這提供了出色的機(jī)制,讓使用者可以將notebook定義為可重用的模板。
使用者為這些模板找到的用途出乎意料地多。其中一部分最常見的用途是:
數(shù)據(jù)科學(xué)家:以不同的系數(shù)運(yùn)行試驗(yàn),并總結(jié)結(jié)果
數(shù)據(jù)工程師:作為部署流程的一部分,執(zhí)行一組數(shù)據(jù)質(zhì)量審計(jì)
數(shù)據(jù)分析師:分享預(yù)備好的查詢和可視化,讓股東以比Tableau更深入的方式探索數(shù)據(jù)
軟件工程師:每次遇到錯(cuò)誤,發(fā)送排錯(cuò)腳本的結(jié)果到郵箱
計(jì)劃notebook
我們利用notebook比較新穎的一種方式是將其作為計(jì)劃工作流程的統(tǒng)一層。
由于每個(gè)notebook可以運(yùn)行任意核,我們可以支持使用者定義的任何執(zhí)行環(huán)境。同時(shí)因?yàn)閚otebook描述的是分割為單元的線性執(zhí)行流,我們可以將錯(cuò)誤映射到具體的單元。這讓使用者可以簡要地描述執(zhí)行和可視化,以后運(yùn)行時(shí)可以精確地報(bào)告結(jié)果。
這一范式意味著我們可以用notebook處理交互式工作,之后平滑地遷移到計(jì)劃重復(fù)運(yùn)行的工作。這對(duì)使用者來說非常方便。許多使用者在單本notebook中構(gòu)造整個(gè)工作流程,當(dāng)他們準(zhǔn)備就緒,可以部署的時(shí)候,只需復(fù)制/粘貼進(jìn)一些單獨(dú)文件以便計(jì)劃執(zhí)行。將notebook視作邏輯工作流程,讓我們很容易就可以像其他工作流程一樣做計(jì)劃。
我們也可以計(jì)劃其他種類的工作。執(zhí)行一項(xiàng)Spark或Presto工作時(shí),插入源代碼至新創(chuàng)建的notebook,然后執(zhí)行。那本notebook便成為不可更改的歷史紀(jì)錄,包含所有東西——源代碼、參數(shù)、運(yùn)行時(shí)配置、執(zhí)行日志、錯(cuò)誤信息,等等。調(diào)錯(cuò)時(shí),這提供了一個(gè)探查的快捷入口,因?yàn)樗邢嚓P(guān)的信息都在一處,同時(shí)notebook可供運(yùn)行,以便進(jìn)行交互式調(diào)試。
Notebook基礎(chǔ)設(shè)施
在Netflix的規(guī)模上支持這些使用案例需要全面的基礎(chǔ)設(shè)施。我們將簡要介紹其中一些項(xiàng)目。
nteract是基于React的Jupyter notebook用戶界面。它提供了一個(gè)簡潔直觀的界面,以及一些改進(jìn),例如單元內(nèi)的工具欄,拖放單元,內(nèi)建的數(shù)據(jù)探索工具。
Papermill是用來支持我們之前提到的參數(shù)化notebook的工具。Papermill讓我們可以并行執(zhí)行使用不同參數(shù)組合的多本notebook。Papermill還可以收集、總結(jié)一組notebook中的指標(biāo)。
Commuter是一個(gè)輕量級(jí)的查看、分享notebook的服務(wù)。它提供了一個(gè)兼容Jupyter的內(nèi)容API,使得讀取本地或遠(yuǎn)程(Amazon S3)的notebook一樣方便。它同時(shí)提供了一個(gè)目錄瀏覽器,以供查找和分享notebook。
Titus是一個(gè)容器管理平臺(tái),支持可伸縮、可靠的容器執(zhí)行,同時(shí)集成了Amazon AWS服務(wù)。Titus是Netflix內(nèi)部創(chuàng)建的工具,并用于生產(chǎn)環(huán)境(串流、推薦、內(nèi)容系統(tǒng))。
我們將在后續(xù)的文章中更深入地探索這些基礎(chǔ)設(shè)施。現(xiàn)在,讓我們重點(diǎn)關(guān)注三個(gè)基礎(chǔ)組件:存儲(chǔ)、計(jì)算、界面。
Netflix的notebook基礎(chǔ)設(shè)施
存儲(chǔ)
Netflix數(shù)據(jù)平臺(tái)使用Amazon S3和EFS作為云存儲(chǔ),notebook視為虛擬文件系統(tǒng)。這意味著每個(gè)使用者在EFS上有一個(gè)家目錄,其中包含了notebook工作區(qū)。這個(gè)工作區(qū)中存放了所有用戶創(chuàng)建、上傳的notebook。當(dāng)使用者交互式地運(yùn)行notebook時(shí),所有的讀寫活動(dòng)發(fā)生在工作區(qū)中。[workspace + filename]的組合構(gòu)成notebook的命名空間,例如/efs/users/kylek/notebooks/MySparkJob.ipynb。這一命名空間用于查看、分享、計(jì)劃notebook。這一慣例可以防止名稱沖突,同時(shí)從中了解使用者是誰以及notebook在EFS卷中的位置也很容易。
工作區(qū)路徑可以為使用者抽象掉云存儲(chǔ)的復(fù)雜性。例如,在列出目錄的時(shí)候,只顯示notebook的文件名,如MySparkJob.ipynb。在終端下則可以通過~/notebooks/MySparkJob.ipynb訪問這一文件。
notebook存儲(chǔ)和訪問
當(dāng)使用者計(jì)劃一本notebook時(shí),調(diào)度程序會(huì)從EFS復(fù)制使用者的notebook到S3的共享目錄。S3上的notebook成為調(diào)度程序信任的源notebook。調(diào)度程序每次運(yùn)行notebook時(shí),基于源notebook初始化一本新notebook。新notebook是實(shí)際執(zhí)行的notebook,并成為這次執(zhí)行的不可更改的記錄,包括代碼、輸出、日志。我們稱之為輸出notebook。
Netflix的工作以協(xié)作為基礎(chǔ)。因此使用者開始分享notebook的URL一點(diǎn)也不讓人吃驚。隨著這一做法的流行,我們經(jīng)常碰到因?yàn)槎嗳送瑫r(shí)訪問同一notebook而導(dǎo)致的意外覆蓋。使用者希望能以只讀的方式分享活躍的notebook。這導(dǎo)致我們創(chuàng)建了commuter。commuter在幕后提供了Jupyter兼容的/files和/api/contensAPI,以列出目錄內(nèi)容,查看文件內(nèi)容,訪問文件元數(shù)據(jù)。這意味著使用者可以安全地查看notebook,無需擔(dān)心影響生產(chǎn)環(huán)境的工作或正在運(yùn)行的notebook。
計(jì)算
管理計(jì)算資源是處理數(shù)據(jù)最有挑戰(zhàn)性的部分之一。在Netflix尤其如此,因?yàn)槲覀冊(cè)贏WS上部署了一個(gè)高伸縮性的容器化架構(gòu)。數(shù)據(jù)平臺(tái)的所有工作運(yùn)行在容器中——包括查詢、數(shù)據(jù)流、notebook。因此我們很自然地想要抽象掉盡可能多的復(fù)雜性。
使用者運(yùn)行notebook服務(wù)時(shí)會(huì)配備一個(gè)容器。我們默認(rèn)分配的容器資源可以滿足大約87.3%的執(zhí)行模式。資源不夠用的時(shí)候,使用者可以通過簡單的界面請(qǐng)求更多資源。
我們同時(shí)也通過預(yù)先準(zhǔn)備好的容器鏡像提供統(tǒng)一的執(zhí)行環(huán)境。鏡像預(yù)裝了常用庫和一組默認(rèn)核。并不是鏡像中的一切都是靜態(tài)的——我們的核會(huì)拉取最新版本的Spack以及最新的平臺(tái)集群配置。預(yù)先準(zhǔn)備好的鏡像減少了新建notebook所需的配置時(shí)間和麻煩,并且在一般情況下保持了單一的執(zhí)行環(huán)境。
這一切背后的功臣是我們的Docker容器服務(wù)Titus。我們進(jìn)一步在服務(wù)中封裝了用戶特定的服務(wù)配置和鏡像。鏡像種同時(shí)包括用戶的安全組和角色,以及所包含的庫常用的環(huán)境變量。這意味著使用者可以在基礎(chǔ)設(shè)施上花更少的時(shí)間,在數(shù)據(jù)上花更多的時(shí)間。
界面
之前提到了我們的愿景,讓notebook成為處理數(shù)據(jù)的標(biāo)準(zhǔn)工具。但這帶來了一項(xiàng)有意思的挑戰(zhàn):單一界面如何支持所有使用者?我們?nèi)匀徊煌耆宄@個(gè)問題的答案,但已經(jīng)有了一些想法。
我們需要一個(gè)直觀的用戶界面,極簡主義風(fēng)格的美學(xué),也需要精心斟酌的用戶體驗(yàn),使困難的事情容易做到。nteract遵循這一理念,將簡單性和組合性作為核心設(shè)計(jì)原則。這使得nteract成為我們想要做的工作的理想構(gòu)件。
我們最常從使用者那里聽到的抱怨之一是缺乏跨語言的原生數(shù)據(jù)可視化,使用Python之外的語言的人特別愛抱怨這一點(diǎn)。nteract的數(shù)據(jù)探索工具提供了語言無關(guān)的迅速探索數(shù)據(jù)的方式。之前我們說過,要讓困難的事情容易做到,這是一個(gè)很好的例子。
你可以在MyBinder的樣例notebook上直接體驗(yàn)數(shù)據(jù)探索工具的效果:(注意:加載可能需要花一分鐘)
https://mybinder.org/v2/gh/nteract/examples/master?urlpath=%2Fnteract%2Fedit%2Fpython%2Fhappiness.ipynb
使用nteract的數(shù)據(jù)探索工具可視化世界幸福感報(bào)告數(shù)據(jù)集
我們也引入了參數(shù)表示的原生支持,這使得計(jì)劃notebook和創(chuàng)建可重用模板更加容易了。
nteract原生支持參數(shù)化notebook
盡管notebook已經(jīng)在Netflix提供了大量價(jià)值,其實(shí)一切才剛剛開始。我們需要在前端和后端投入更多以提升notebook總的體驗(yàn)。我們接下來12個(gè)月的工作將聚焦在提升可靠性、可見性和協(xié)作性上。上下文環(huán)境對(duì)使用者來說是第一位的,因此我們正致力于提升可見性,包括集群狀態(tài)、核狀態(tài)、工作歷史,等等。我們同時(shí)致力于自動(dòng)版本控制,原生應(yīng)用內(nèi)計(jì)劃,更好地支持可視化Spark的DataFrame,更穩(wěn)定的Scala核。我們將在之后的博客文章中討論這些工作的細(xì)節(jié)。
開源項(xiàng)目
Netflix一貫倡導(dǎo)開放源代碼。我們高度評(píng)價(jià)開源協(xié)作種涌現(xiàn)的活力、開放標(biāo)準(zhǔn)、想法交流。許多我們?yōu)镹etflix數(shù)據(jù)平臺(tái)開發(fā)的應(yīng)用已經(jīng)開源。同時(shí),我們不打算創(chuàng)建一次性的解決方案,或屈從于“非我所創(chuàng)”的心態(tài)。只要有可能,我們利用現(xiàn)有的開源項(xiàng)目,并向其貢獻(xiàn)代碼,例如Spark、Jupyter、pandas。
我們之前描述的基礎(chǔ)設(shè)施重度依賴Jupyter項(xiàng)目的生態(tài)系統(tǒng),但也有一些分歧之處。最主要的是我們選擇了nteract作為Netflix的notebook用戶界面。我們做出這一決定有很多原因,包括對(duì)齊我們的技術(shù)棧和設(shè)計(jì)哲學(xué)。隨著我們突破notebook可以做什么的限制,我們很可能會(huì)創(chuàng)建新工具、庫、服務(wù)。這些作為nteract生態(tài)系統(tǒng)組成部分的項(xiàng)目也將開源。
我們認(rèn)識(shí)到對(duì)Netflix有意義的東西不一定對(duì)所有人有意義。因此我們?cè)谠O(shè)計(jì)這些項(xiàng)目的時(shí)候考慮了模塊性。這樣,你可以只選用對(duì)你的環(huán)境有意義的組件,例如Papermill,而不用投入整個(gè)Netflix的notebook生態(tài)系統(tǒng)。
后續(xù)
作為一個(gè)平臺(tái)團(tuán)隊(duì),我們的責(zé)任是讓Netflix人可以在數(shù)據(jù)上做出令人驚嘆的東西。notebook在Netflix已經(jīng)有了驚人的影響力。隨著我們?cè)谶@一領(lǐng)域的更多投入,我們很興奮,能看到這一影響的擴(kuò)大。如果你希望成為我們的一員,可以看看我們的招聘頁面。
呀!感謝你耐心讀完這篇長文。不過本文只是浮光掠影地介紹了我們?cè)趎otebook上做的工作。在后續(xù)的博客文章中,我們將更深入地探索notebook計(jì)劃背后的架構(gòu)。在這篇文章發(fā)布之前,你可以通過以下途徑了解更多Netflix在數(shù)據(jù)上做什么,怎么做:
Twitter上的NetflixData賬號(hào)
YouTube上的Netflix Data頻道
Netflix Research網(wǎng)站:research.netflix.com
同時(shí)我們也樂不可支地贊助今年的JupyterCon。我們的工程師將在JupyterCon上做5場(chǎng)演講:
8/22 1:30 PM, How to Build on top of Jupyter’s Protocols, Kyle Kelley
8/23 1:50 PM, Scheduled Notebooks: Manageable and traceable code execution, Matthew Seal
8/23 2:40 PM, Notebooks @ Netflix: From Analytics to Engineering, Michelle Ufford, Kyle Kelley
8/23 5:00 PM, Making beautiful objects with Jupyter, M Pacer
8/24 2:40 PM, Jupyter’s configuration system, M Pacer等
8/25 9AM?—?5PM JupyterCon Community Sprint Day
-
機(jī)器學(xué)習(xí)
+關(guān)注
關(guān)注
66文章
8497瀏覽量
134226 -
Netflix
+關(guān)注
關(guān)注
0文章
90瀏覽量
11525
原文標(biāo)題:交互之外:Netflix在Jupyter Notebook上的創(chuàng)新工作
文章出處:【微信號(hào):jqr_AI,微信公眾號(hào):論智】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
評(píng)論