<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    敏捷、分布式、ALM過(guò)程自動(dòng)化、企業(yè)應(yīng)用架構(gòu)
    posts - 14, comments - 0, trackbacks - 0, articles - 1
      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

    2012年4月1日

    Hadoop實(shí)施已經(jīng)有快一個(gè)月了,對(duì)Hadoop的概念理解、使用,Linux與shell腳本,甚至mysql都有了更多的理解。


    項(xiàng)目背景:用于互聯(lián)網(wǎng)信息收集后的關(guān)鍵詞匹配與內(nèi)容提取。

    主要系統(tǒng)架構(gòu)分為互聯(lián)網(wǎng)爬蟲(chóng)、分析、業(yè)務(wù)應(yīng)用三塊:

    簡(jiǎn)單架構(gòu)描述

    由于我在當(dāng)中的角色主要負(fù)責(zé)分析架構(gòu)的搭建,所以其他兩塊都畫(huà)得簡(jiǎn)單,下面也不會(huì)過(guò)多的描述。


    Hadoop理解:提到Hadoop都想到的是云、分布式計(jì)算,在一段時(shí)間的實(shí)施之后有了一些具體的理解。

    Hadoop的優(yōu)勢(shì):

    針對(duì)性能指標(biāo),當(dāng)業(yè)務(wù)數(shù)據(jù)量總量或增速上升到一定級(jí)別,依靠關(guān)系型數(shù)據(jù)庫(kù)一定無(wú)法支持。對(duì)于非關(guān)系型數(shù)據(jù)庫(kù),包括Nosql和Solr一類存儲(chǔ)方式,稍顯復(fù)雜,對(duì)于機(jī)器集群性能要求偏高(相對(duì)于文件系統(tǒng))。從數(shù)據(jù)使用模式上來(lái)講,目前海量數(shù)據(jù)的常常是不包含復(fù)雜邏輯的簡(jiǎn)單統(tǒng)計(jì)整理(比如上述系統(tǒng)中的關(guān)鍵詞匹配)。這時(shí)候文件系統(tǒng)的優(yōu)勢(shì)反而比較明顯(結(jié)構(gòu)簡(jiǎn)單,邏輯簡(jiǎn)單)。

    如上述系統(tǒng)的應(yīng)用場(chǎng)景是怎么樣的呢,在一個(gè)強(qiáng)大的爬蟲(chóng)系統(tǒng)之下,每個(gè)小時(shí)的數(shù)據(jù)增量在G到10G的級(jí)別,需要搜索所有的文件,獲取關(guān)鍵字的匹配,并且對(duì)匹配內(nèi)容進(jìn)行摘要。很類似我們windows里面的搜索功能,需要解決的就是如何在這樣增幅的文件系統(tǒng)之下,如何滿足業(yè)務(wù)系統(tǒng)的需求。

    對(duì)分析系統(tǒng)有什么要求呢?

    能夠建立集群,分布式的保存數(shù)據(jù)文件內(nèi)容(統(tǒng)一控制,可配置)。

    有一定的保護(hù)機(jī)制,保證數(shù)據(jù)或節(jié)點(diǎn)丟失不會(huì)影響系統(tǒng)使用。

    如果有一個(gè)任務(wù)腳本執(zhí)行框架機(jī)制就好了(用于并行計(jì)算)。

    能夠進(jìn)行節(jié)點(diǎn)間的數(shù)據(jù)均衡。

    能夠簡(jiǎn)單的查看所有的狀態(tài)與日志(web客戶端)

    可能主要是這些了。若自己實(shí)現(xiàn),確實(shí)是個(gè)復(fù)雜而龐大的工程,現(xiàn)在我們有了Hadoop。


    系統(tǒng)物理架構(gòu):

    我們使用了一臺(tái)服務(wù)器,利用虛擬化,安裝了7套64x位的CentOS。一個(gè)Namenode,6個(gè)Datanode,復(fù)制數(shù)設(shè)置為3。每個(gè)系統(tǒng)分配到一個(gè)cpu,2G內(nèi)存,Datanode掛載了500G的存儲(chǔ)空間。

    理想的Hadoop的搭建環(huán)境,參照《Best Practices for Selecting Apache Hadoop Hardware》(http://hortonworks.com/blog/best-practices-for-selecting-apache-hadoop-hardware/)一文,以及一些其他的文章。

    CPU:最好是雙CPU,8核左右。不用太高了。

    內(nèi)存:推薦48G,但是4G應(yīng)該就可以運(yùn)行Hadoop了。

    硬盤(pán):7200轉(zhuǎn)的SATA硬盤(pán)即可,Hadoop很占空間,所以盡量加。

    網(wǎng)絡(luò):內(nèi)部的數(shù)據(jù)交換要求非常高,內(nèi)網(wǎng)最好是千兆網(wǎng)卡,帶寬為1GB。

    理想與現(xiàn)實(shí),有錢(qián)與沒(méi)錢(qián),呵呵。


    系統(tǒng)軟件架構(gòu):

    Hadoop:版本使用的是1.0.3,再下來(lái)就是2了,為了盡量簡(jiǎn)化應(yīng)用,所以不考慮2的新特性。對(duì)Hadoop沒(méi)有做太多的設(shè)置,基本基于默認(rèn)。70為Namenode,71-76為Datanode。

    JDK:1.6.0_33 (64x)


    系統(tǒng)實(shí)施過(guò)程:

    HDFS部分:

    爬蟲(chóng)抓取數(shù)據(jù),整理后存放在50文件服務(wù)器,70以外部掛載的形式讀取。網(wǎng)頁(yè)文件比較小,假如直接寫(xiě)入Hadoop對(duì)Namenode負(fù)載過(guò)大,所以入庫(kù)前合并,將每小時(shí)網(wǎng)頁(yè)整合成為一個(gè)文件寫(xiě)入HDFS,由于區(qū)分類別,所以每小時(shí)基本寫(xiě)入10個(gè)文件左右,總量在5-8G,耗時(shí)在40-50分鐘。(這個(gè)過(guò)程中,由于爬蟲(chóng)的IO過(guò)于頻繁,導(dǎo)致文件讀取困難,所以做了定時(shí)任務(wù),每小時(shí)啟動(dòng)一次,將需要處理的文件先拷貝到臨時(shí)區(qū)域,合并入庫(kù)之后再刪除。此處應(yīng)該是受到單核cpu的限制,所有操作均是串行,包括拷貝(cp)和合并入庫(kù)(java),所以Namenode嚴(yán)重建議配置稍高。)

    此處沒(méi)有太多問(wèn)題。

    MapReduce部分:

    寫(xiě)入完成后,進(jìn)行分析工作,MapReduce。此處的工作過(guò)程為:數(shù)據(jù)庫(kù)定時(shí)生成關(guān)鍵詞列表文件。Job執(zhí)行時(shí)會(huì)讀取列表文件,匹配指定范圍內(nèi)的HDFS文件(過(guò)去一小時(shí)),匹配出對(duì)應(yīng)的表達(dá)式與HTML,Map過(guò)程結(jié)束。在Reduce階段,會(huì)將Map的所有數(shù)據(jù)入數(shù)據(jù)庫(kù)(Mysql)。

    此處出現(xiàn)過(guò)一些問(wèn)題,記錄下來(lái)。

    1. Reduce階段需要加載Mysql的第三方驅(qū)動(dòng)包。我在三個(gè)環(huán)境測(cè)試過(guò)(公司、家里、發(fā)布環(huán)境),使用 -libjars 一定可以,有的地方不需要也可以。不明確,懷疑與HADOOP_HOME環(huán)境變量有關(guān)。

    2. MR過(guò)程中使用log4j打印日志,在Hadoop臨時(shí)目錄(如果你沒(méi)有配置dfs.name.dir,dfs.data.dir,mapred.local.dir.mapred.system.dir等目錄,這些都會(huì)在hadoop.tmp.dir當(dāng)中,我就偷懶都沒(méi)配置)mapred文件夾中查看一下。

    整個(gè)過(guò)程實(shí)際上還是比較簡(jiǎn)單的,基本編碼量就在Job的部分,但是一個(gè)Java文件就夠了。在目前初級(jí)階段應(yīng)該還是比較好用的。現(xiàn)在還沒(méi)有測(cè)試Job的執(zhí)行效率。完成后會(huì)繼續(xù)記錄下來(lái)。有什么問(wèn)題可以提出。我想到什么也會(huì)在本文繼續(xù)更新。

    posted @ 2012-08-08 20:21 一酌散千憂 閱讀(588) | 評(píng)論 (0)編輯 收藏

    硬件資源:

    三臺(tái)CentOS5.6虛擬機(jī)(Vmware

    本機(jī) windows7 64x

     

    基本資源配置:

    三臺(tái)虛擬機(jī)均是克隆自同一個(gè)鏡像

    已經(jīng)安裝了Java環(huán)境(jdk1.6.0_25

    Hadoop路徑在/usr/hadoop/hadoop-0.20.205.0

     

    操作步驟:

    1、機(jī)器名稱規(guī)范

    ip分別為128、129、130,將128設(shè)置為master,其他設(shè)置為slave

    修改

    /etc/sysconfig/network

    /etc/hosts

    兩處配置,名稱分別為hadoop-master\hadoop-slave01\hadoop-slave02

    注意:此處名稱最好不用使用下劃線,有可能引發(fā)namenode的啟動(dòng)異常。

     

    2、修改Hadoop配置 

    master節(jié)點(diǎn)的conf中修改masterslave文件,分別為機(jī)器的ip地址

     

    修改master節(jié)點(diǎn)的conf中:

    core-site.xml

    <property>

    <name>fs.default.name</name>

    <value>hdfs://ip-master:9000</value>

    </property>

     

    mapred-site.xml

    <property>

    <name>mapred.job.tracker</name>                                   

    <value>master:9001</value>                                

    </property>

     

    hdfs-site.xm

    <property>

    <name>dfs.replication</name>

    <value>2</value>

    </property>

    注意此處的端口號(hào)均為默認(rèn)。

     

     

    3、建立m-s之間的ssh連接

    首先masterslave機(jī)器都需要進(jìn)行ssh信任文件生成,執(zhí)行如下命令:

    $ ssh-keygen -t rsa

    中間需要輸入的地方直接回車,接受缺省值即可

     

    由于使用root用戶登錄,所以密鑰文件生成在 /root/.ssh/文件夾下,存有一對(duì)密鑰id_dsaid_dsa.pub

    此處id_dsa(私鑰)必須為其他用戶不可讀,所以文件屬性應(yīng)當(dāng)是600

     

    master機(jī)器執(zhí)行:

    id_dsa.pub(公鑰)復(fù)制為 authorized_keys

    $ cp id_dsa.pub authorized_keys

    如果是多臺(tái)機(jī)器需要,無(wú)密碼登陸,則各自機(jī)器產(chǎn)生公鑰追加到authorized_keys即可.

     

    使用scp協(xié)議覆蓋slave端的密鑰文件夾,使得slave機(jī)器信任來(lái)自master的連接:

    $ scp /root/.ssh/* ip-slave:/root/.ssh

     

     

    4、啟動(dòng)服務(wù) 

    建議將$HADOOP_HOME/bin下的所有文件給與執(zhí)行權(quán)限:

    $ chmod 777 bin

     

    master作為namenod需要執(zhí)行如下腳本:

    $HADOOP_HOME/bin/hadoop namenode –format

     

    完成后執(zhí)行 $HADOOP_HOME/bin/start-all.sh

     

    5、問(wèn)題檢查

    Hadoop根目錄下的logs文件中,檢查各個(gè)服務(wù)日志的啟動(dòng)情況

     

     

    6、其他情況說(shuō)明:

    Q: $HADOOP_HOME is deprecated

    A: 基本不會(huì)產(chǎn)生任何影響。由于腳本啟動(dòng)時(shí)設(shè)置了該環(huán)境變量,就會(huì)提示用戶原有環(huán)境變量失效??梢匀∠h(huán)境變量設(shè)置,或者直接去bin/hadoop中找到這句話,去掉即可

     

    Q: 無(wú)效的選項(xiàng) -jvm / Unrecognized option: -jvm

    A: 在使用root用戶登錄時(shí) bin/hadoop 腳本就會(huì)進(jìn)行判斷,加上-jvm參數(shù)。此處是為了進(jìn)入jsvchttp://commons.apache.org/daemon/jsvc.html),此處并不確定是否bug,也不再進(jìn)行詳細(xì)的追溯,解決方法就是進(jìn)入 bin/hadoop 腳本中 找到 jvm 參數(shù)并去掉。

     

     

     

     

     

     

     

    posted @ 2012-07-04 07:38 一酌散千憂 閱讀(594) | 評(píng)論 (0)編輯 收藏

    公司里有同事時(shí)常抱怨,項(xiàng)目的用戶體驗(yàn)太差,常常挨領(lǐng)導(dǎo)的罵。大家都認(rèn)為是在用戶體驗(yàn)的設(shè)計(jì)方面,公司人員的能力和經(jīng)驗(yàn)都不足引起的。發(fā)牢騷的時(shí)候也會(huì)說(shuō),如果公司能夠請(qǐng)得起“淘寶”的UI設(shè)計(jì)師,咱們的系統(tǒng)肯定會(huì)更上一層樓。我之前也一直認(rèn)為如此,即我們的設(shè)計(jì)是影響項(xiàng)目體驗(yàn)的重要原因。最近被領(lǐng)導(dǎo)調(diào)動(dòng)去協(xié)助一個(gè)項(xiàng)目,產(chǎn)生了一些不一樣的體會(huì)。

    項(xiàng)目背景,一個(gè)新的產(chǎn)品,小型項(xiàng)目,純開(kāi)發(fā)人員3-4人,2名熟練開(kāi)發(fā)人員,1名新手,偶爾會(huì)有協(xié)助人員。沒(méi)有技術(shù)經(jīng)理,項(xiàng)目經(jīng)理身負(fù)多個(gè)項(xiàng)目,對(duì)項(xiàng)目進(jìn)度關(guān)心不足,部門(mén)經(jīng)理會(huì)協(xié)助進(jìn)行工作和進(jìn)度管理。可以看到管理還是比較混亂。

    由于項(xiàng)目進(jìn)度太慢,領(lǐng)導(dǎo)要求從我這邊調(diào)一個(gè)熟練人員協(xié)助開(kāi)發(fā)。我也基本了解他們的項(xiàng)目狀況,為了不讓我的人進(jìn)去抓瞎,我就和他一起去了解項(xiàng)目情況。

    項(xiàng)目狀況比較糟糕,介入項(xiàng)目時(shí)已經(jīng)開(kāi)發(fā)了一段時(shí)間,保留的文檔只有兩份,一副數(shù)據(jù)庫(kù)說(shuō)明,一份非常粗略的需求說(shuō)明,而且還與開(kāi)發(fā)進(jìn)度不同步,就是沒(méi)有維護(hù)。

    我了解了一下項(xiàng)目目前的難度,開(kāi)發(fā)人員和我反映一個(gè)是人員熟練程度的問(wèn)題,二是需求變更的問(wèn)題。我整體了解了一下項(xiàng)目目前的需求和設(shè)計(jì),以及進(jìn)度。就挑了一個(gè)模塊詢問(wèn)他們的變更情況,這個(gè)模塊是一個(gè)關(guān)鍵詞匹配功能。結(jié)果是領(lǐng)導(dǎo)看了他們的頁(yè)面之后,嫌信息量太少,就要求提供一些更細(xì)化的數(shù)據(jù)展示。開(kāi)發(fā)人員問(wèn)我有什么意見(jiàn),我就簡(jiǎn)單講了一下頁(yè)面大概怎么構(gòu)建。其中有一個(gè)點(diǎn),是用于變更數(shù)據(jù)范圍,即查詢的表變更,我一開(kāi)始覺(jué)得使用下拉框就可以,產(chǎn)生了一些意見(jiàn)。有人建議分為不同子模塊,或者tab頁(yè),或者分為多塊并列展示。我想了想,就給他們講了我認(rèn)為幾種方案的優(yōu)點(diǎn)缺點(diǎn)及適用范圍。

     

    1.       多塊并行展示:

    多個(gè)不同范圍的數(shù)據(jù)在同一頁(yè)面中分為不同區(qū)域以相同形式展示。原因是由于多塊數(shù)據(jù)之間有一定的關(guān)聯(lián)因果關(guān)系,或值得對(duì)比。適用范圍:如購(gòu)物網(wǎng)站中的多個(gè)物品比較。

    2.       Tab頁(yè):

    同一個(gè)頁(yè)面的多個(gè)tab頁(yè),表示多個(gè)tab頁(yè)中的數(shù)據(jù)可能在一定的領(lǐng)域概念之下有一定的關(guān)聯(lián),但關(guān)聯(lián)度不強(qiáng)。因?yàn)?/span>tab頁(yè)更重要的是強(qiáng)調(diào)一個(gè)同步工作的狀態(tài),即A tab頁(yè)查看一定信息,會(huì)打開(kāi)B tab頁(yè)查看其他信息,中途還會(huì)切回A tab頁(yè)。適用范圍:如郵箱中,收件箱和草稿箱。

    3.       下拉框

    下拉框作為查詢條件的一部分,常用于有著常規(guī)或固定的可選擇內(nèi)容中(如性別,月份),更多是以過(guò)濾的形態(tài)出現(xiàn),即下拉框更適合針對(duì)某表的某個(gè)字段過(guò)濾,如果針對(duì)的是數(shù)據(jù)范圍或是對(duì)用戶需要直觀了解的重要業(yè)務(wù)條件則不太合適。適用范圍:如在考試成績(jī)中使用下拉框過(guò)濾“男女”或“及格不及格”。

    4.       單選框

    單選框與下拉框的作用范圍相似,但是不同之處在于將被選項(xiàng)全部展示,目的在于能夠讓用戶清楚的了解當(dāng)前數(shù)據(jù)顯示的實(shí)際范圍或條件,以及備選的其他范圍或條件。更適用于選項(xiàng)與實(shí)際業(yè)務(wù)及當(dāng)前展示數(shù)據(jù)關(guān)系重要,不同選項(xiàng)可能會(huì)引發(fā)用戶的不同行為。適用范圍:如銀行系統(tǒng)顯示了當(dāng)前用戶下綁定多個(gè)帳號(hào)時(shí),使用單選框。

     

    經(jīng)過(guò)上述討論,我們仔細(xì)分析了這個(gè)模塊中用戶的實(shí)際需求,以及可能后續(xù)操作,最終選擇的單選框的方案。

    目前還沒(méi)有后續(xù),但是我想我們基于用戶真是需求的挖掘和后續(xù)操作的認(rèn)真分析,會(huì)讓我們?cè)谂c領(lǐng)導(dǎo)進(jìn)行需求討論的時(shí)候有更加充分合理的依據(jù)。

    回來(lái)之后我又看了看淘寶的搜索頁(yè)面,比如就搜索“鞋子”來(lái)講,將品牌這欄設(shè)置為單選和下拉將是完全不同的效果,而確定方案的理由則是對(duì)于用戶的需求和實(shí)際行為的深入研究。這個(gè)應(yīng)該是需求分析和調(diào)研的結(jié)果。將搜索條件以tag的形式標(biāo)注于頁(yè)面上,并且可以直接點(diǎn)擊X按鈕進(jìn)行刪除,我覺(jué)得更加可以傾向?yàn)橛脩趔w驗(yàn)。滿足并充分考慮了用戶實(shí)際需求的是好的需求分析,能夠簡(jiǎn)化并引導(dǎo)用戶行為的是好的用戶體驗(yàn)。

    當(dāng)我們面臨的系統(tǒng)感覺(jué)非常難用的時(shí)候,往往這時(shí)候并非是用戶體驗(yàn)差,我們應(yīng)該檢討的是我們對(duì)用戶需求有沒(méi)有好好挖掘,做出來(lái)的是不是用戶想要、用戶能用的系統(tǒng)。

    posted @ 2012-05-22 05:02 一酌散千憂 閱讀(267) | 評(píng)論 (0)編輯 收藏

    Zookeeper的核心概念:

    ZNode

    Znode就是核心結(jié)構(gòu),Zookeeper服務(wù)中是由大量的Znode構(gòu)成。Znode一般是由客戶端建立和修改,作為信息或標(biāo)志的載體,甚至本身就是標(biāo)志。

    Znode可以設(shè)置為持久(PERSISTENT)或臨時(shí)(EPHEMERAL),區(qū)別在于臨時(shí)的節(jié)點(diǎn)若斷開(kāi)連接后就自動(dòng)刪除。建立節(jié)點(diǎn)時(shí)可選擇是否使用序列號(hào)命名(SEQUENTIAL),若啟用則會(huì)自動(dòng)在節(jié)點(diǎn)名后加入唯一序列編號(hào)。

    Session

    作為客戶端和Zookeeper服務(wù)之間交互的憑證。

    Watch

    當(dāng)客戶端對(duì)節(jié)點(diǎn)信息進(jìn)行查詢操作之后,可以選擇是否設(shè)置一個(gè)Watch。其作用就是當(dāng)本次查詢的數(shù)據(jù)在服務(wù)器端發(fā)生變化之后,會(huì)對(duì)設(shè)置Watch的客戶端發(fā)送通知。一次發(fā)送之后,就將刪除該Watch,以后的變更或不再設(shè)置Watch則不會(huì)通知。

    ACLs

    節(jié)點(diǎn)的權(quán)限限制使用ACL,如增刪改查操作。

    Zookeeper的服務(wù)器安裝:

    1、下載對(duì)應(yīng)版本號(hào)的tar.gz文件

    2、使用 tar xzvf zookeeper-3.4.2.tar.gz -C ./ 解壓

    3、設(shè)置,將conf/zoo.example.cfg復(fù)制到conf/zoo.cfg或者手動(dòng)建立一個(gè)新的。

    4、啟動(dòng)Zookeeper服務(wù):bin/zkServer.sh start

    5、啟動(dòng)客戶端連接:bin/zkCli.sh -server 127.0.0.1:2181(此處在本機(jī),且使用了默認(rèn)端口,且在Java環(huán)境中)

    6、使用命令:ls、get、set等。

    7、關(guān)閉Zookeeper服務(wù):bin/zkServer.sh stop

    Zookeeper代碼編寫(xiě):

    代碼編寫(xiě)部分比較簡(jiǎn)單,因?yàn)楸┞兜慕涌诤苌伲饕獜?fù)雜在于項(xiàng)目如何使用節(jié)點(diǎn)以及節(jié)點(diǎn)信息。

    啟動(dòng)Zookeeper服務(wù)之后,客戶端代碼進(jìn)行節(jié)點(diǎn)的增刪,Watch的設(shè)置,內(nèi)容的改查等。

    此處建議查看官方的《Programming with ZooKeeper - A basic tutorial》部分,當(dāng)中舉了兩個(gè)例子來(lái)模擬分布式系統(tǒng)的應(yīng)用。

    代碼基本沒(méi)有問(wèn)題,唯一需要注意的就是:若之間按照原版進(jìn)行調(diào)試時(shí),有可能在調(diào)用

     Stat s = zk.exists(root, false);

    這句代碼時(shí)會(huì)出現(xiàn)一個(gè)異常,當(dāng)中包括“KeeperErrorCode = ConnectionLoss for”。

    這個(gè)問(wèn)題引起的原因可以看一下代碼

                    System.out.println("Starting ZK:");
                    zk 
    = new ZooKeeper(address, 3000this);
                    mutex 
    = new Integer(-1);
                    System.out.println(
    "Finished starting ZK: " + zk);

    最后一行有打印出Zookeeper目前的信息,若未修改的原代碼,此處的State應(yīng)當(dāng)是CONECTING。連接中的時(shí)候去驗(yàn)證是否存在節(jié)點(diǎn)會(huì)報(bào)錯(cuò)。解決的方法也很簡(jiǎn)單,就是等到Zookeeper客戶端以及完全連接上服務(wù)器,State為CONECTED之后再進(jìn)行其他操作。給出代碼示例:

    // 使用了倒數(shù)計(jì)數(shù),只需要計(jì)數(shù)一次
    private CountDownLatch connectedSignal = new CountDownLatch(1); 
    SyncPrimitive(String address) {
        
    if(zk == null){
            
    try {
                System.out.println(
    "Starting ZK:");
                zk 
    = new ZooKeeper(address, 3000this);
                mutex 
    = new Integer(-1);
                connectedSignal.await(); 
    // 等待連接完成
                System.out.println("Finished starting ZK: " + zk);
            } 
    catch (IOException e) {
                System.out.println(e.toString());
                zk 
    = null;
            } 
    catch (InterruptedException e) {
                
    // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
    //else mutex = new Integer(-1);
    }
    synchronized public void process(WatchedEvent event) {
        
    // 此處設(shè)立在Watch中會(huì)在狀態(tài)變化后觸發(fā)事件
        if (event.getState() == KeeperState.SyncConnected) {
            connectedSignal.countDown();
    // 倒數(shù)-1
        }
        
            
    synchronized (mutex) {
                
    //System.out.println("Process: " + event.getType());
                mutex.notify();
            }
    }

    這樣就可以正確運(yùn)行代碼了。

    Zookeeper的應(yīng)用場(chǎng)景及方式:

    此處是為引用,原地址為(http://rdc.taobao.com/team/jm/archives/1232 

    ZooKeeper是一個(gè)高可用的分布式數(shù)據(jù)管理與系統(tǒng)協(xié)調(diào)框架?;趯?duì)Paxos算法的實(shí)現(xiàn),使該框架保證了分布式環(huán)境中數(shù)據(jù)的強(qiáng)一致性,也正是基于這樣的特性,使得zookeeper能夠應(yīng)用于很多場(chǎng)景。網(wǎng)上對(duì)zk的使用場(chǎng)景也有不少介紹,本文將結(jié)合作者身邊的項(xiàng)目例子,系統(tǒng)的對(duì)zk的使用場(chǎng)景進(jìn)行歸類介紹。 值得注意的是,zk并不是生來(lái)就為這些場(chǎng)景設(shè)計(jì),都是后來(lái)眾多開(kāi)發(fā)者根據(jù)框架的特性,摸索出來(lái)的典型使用方法。因此,也非常歡迎你分享你在ZK使用上的奇技淫巧。

    場(chǎng)景類別

    典型場(chǎng)景描述(ZK特性,使用方法)

    應(yīng)用中的具體使用

    數(shù)據(jù)發(fā)布與訂閱

    發(fā)布與訂閱即所謂的配置管理,顧名思義就是將數(shù)據(jù)發(fā)布到zk節(jié)點(diǎn)上,供訂閱者動(dòng)態(tài)獲取數(shù)據(jù),實(shí)現(xiàn)配置信息的集中式管理和動(dòng)態(tài)更新。例如全局的配置信息,地址列表等就非常適合使用。

    1. 索引信息和集群中機(jī)器節(jié)點(diǎn)狀態(tài)存放在zk的一些指定節(jié)點(diǎn),供各個(gè)客戶端訂閱使用。2. 系統(tǒng)日志(經(jīng)過(guò)處理后的)存儲(chǔ),這些日志通常2-3天后被清除。 

    3. 應(yīng)用中用到的一些配置信息集中管理,在應(yīng)用啟動(dòng)的時(shí)候主動(dòng)來(lái)獲取一次,并且在節(jié)點(diǎn)上注冊(cè)一個(gè)Watcher,以后每次配置有更新,實(shí)時(shí)通知到應(yīng)用,獲取最新配置信息。

    4. 業(yè)務(wù)邏輯中需要用到的一些全局變量,比如一些消息中間件的消息隊(duì)列通常有個(gè)offset,這個(gè)offset存放在zk上,這樣集群中每個(gè)發(fā)送者都能知道當(dāng)前的發(fā)送進(jìn)度。

    5. 系統(tǒng)中有些信息需要?jiǎng)討B(tài)獲取,并且還會(huì)存在人工手動(dòng)去修改這個(gè)信息。以前通常是暴露出接口,例如JMX接口,有了zk后,只要將這些信息存放到zk節(jié)點(diǎn)上即可。

    Name Service

    這個(gè)主要是作為分布式命名服務(wù),通過(guò)調(diào)用zk的create node api,能夠很容易創(chuàng)建一個(gè)全局唯一的path,這個(gè)path就可以作為一個(gè)名稱。

     

    分布通知/協(xié)調(diào)

    ZooKeeper中特有watcher注冊(cè)與異步通知機(jī)制,能夠很好的實(shí)現(xiàn)分布式環(huán)境下不同系統(tǒng)之間的通知與協(xié)調(diào),實(shí)現(xiàn)對(duì)數(shù)據(jù)變更的實(shí)時(shí)處理。使用方法通常是不同系統(tǒng)都對(duì)ZK上同一個(gè)znode進(jìn)行注冊(cè),監(jiān)聽(tīng)znode的變化(包括znode本身內(nèi)容及子節(jié)點(diǎn)的),其中一個(gè)系統(tǒng)update了znode,那么另一個(gè)系統(tǒng)能夠收到通知,并作出相應(yīng)處理。

    1. 另一種心跳檢測(cè)機(jī)制:檢測(cè)系統(tǒng)和被檢測(cè)系統(tǒng)之間并不直接關(guān)聯(lián)起來(lái),而是通過(guò)zk上某個(gè)節(jié)點(diǎn)關(guān)聯(lián),大大減少系統(tǒng)耦合。2. 另一種系統(tǒng)調(diào)度模式:某系統(tǒng)有控制臺(tái)和推送系統(tǒng)兩部分組成,控制臺(tái)的職責(zé)是控制推送系統(tǒng)進(jìn)行相應(yīng)的推送工作。管理人員在控制臺(tái)作的一些操作,實(shí)際上是修改了ZK上某些節(jié)點(diǎn)的狀態(tài),而zk就把這些變化通知給他們注冊(cè)Watcher的客戶端,即推送系統(tǒng),于是,作出相應(yīng)的推送任務(wù)。 

    3. 另一種工作匯報(bào)模式:一些類似于任務(wù)分發(fā)系統(tǒng),子任務(wù)啟動(dòng)后,到zk來(lái)注冊(cè)一個(gè)臨時(shí)節(jié)點(diǎn),并且定時(shí)將自己的進(jìn)度進(jìn)行匯報(bào)(將進(jìn)度寫(xiě)回這個(gè)臨時(shí)節(jié)點(diǎn)),這樣任務(wù)管理者就能夠?qū)崟r(shí)知道任務(wù)進(jìn)度。

    總之,使用zookeeper來(lái)進(jìn)行分布式通知和協(xié)調(diào)能夠大大降低系統(tǒng)之間的耦合。

    分布式鎖

    分布式鎖,這個(gè)主要得益于ZooKeeper為我們保證了數(shù)據(jù)的強(qiáng)一致性,即用戶只要完全相信每時(shí)每刻,zk集群中任意節(jié)點(diǎn)(一個(gè)zk server)上的相同znode的數(shù)據(jù)是一定是相同的。鎖服務(wù)可以分為兩類,一個(gè)是保持獨(dú)占,另一個(gè)是控制時(shí)序。 

    所謂保持獨(dú)占,就是所有試圖來(lái)獲取這個(gè)鎖的客戶端,最終只有一個(gè)可以成功獲得這把鎖。通常的做法是把zk上的一個(gè)znode看作是一把鎖,通過(guò)create znode的方式來(lái)實(shí)現(xiàn)。所有客戶端都去創(chuàng)建 /distribute_lock 節(jié)點(diǎn),最終成功創(chuàng)建的那個(gè)客戶端也即擁有了這把鎖。

    控制時(shí)序,就是所有視圖來(lái)獲取這個(gè)鎖的客戶端,最終都是會(huì)被安排執(zhí)行,只是有個(gè)全局時(shí)序了。做法和上面基本類似,只是這里 /distribute_lock 已經(jīng)預(yù)先存在,客戶端在它下面創(chuàng)建臨時(shí)有序節(jié)點(diǎn)(這個(gè)可以通過(guò)節(jié)點(diǎn)的屬性控制:CreateMode.EPHEMERAL_SEQUENTIAL來(lái)指定)。Zk的父節(jié)點(diǎn)(/distribute_lock)維持一份sequence,保證子節(jié)點(diǎn)創(chuàng)建的時(shí)序性,從而也形成了每個(gè)客戶端的全局時(shí)序。

     

    集群管理

    1. 集群機(jī)器監(jiān)控:這通常用于那種對(duì)集群中機(jī)器狀態(tài),機(jī)器在線率有較高要求的場(chǎng)景,能夠快速對(duì)集群中機(jī)器變化作出響應(yīng)。這樣的場(chǎng)景中,往往有一個(gè)監(jiān)控系統(tǒng),實(shí)時(shí)檢測(cè)集群機(jī)器是否存活。過(guò)去的做法通常是:監(jiān)控系統(tǒng)通過(guò)某種手段(比如ping)定時(shí)檢測(cè)每個(gè)機(jī)器,或者每個(gè)機(jī)器自己定時(shí)向監(jiān)控系統(tǒng)匯報(bào)“我還活著”。 這種做法可行,但是存在兩個(gè)比較明顯的問(wèn)題:1. 集群中機(jī)器有變動(dòng)的時(shí)候,牽連修改的東西比較多。2. 有一定的延時(shí)。 

    利用ZooKeeper有兩個(gè)特性,就可以實(shí)時(shí)另一種集群機(jī)器存活性監(jiān)控系統(tǒng):a. 客戶端在節(jié)點(diǎn) x 上注冊(cè)一個(gè)Watcher,那么如果 x 的子節(jié)點(diǎn)變化了,會(huì)通知該客戶端。b. 創(chuàng)建EPHEMERAL類型的節(jié)點(diǎn),一旦客戶端和服務(wù)器的會(huì)話結(jié)束或過(guò)期,那么該節(jié)點(diǎn)就會(huì)消失。

    例如,監(jiān)控系統(tǒng)在 /clusterServers 節(jié)點(diǎn)上注冊(cè)一個(gè)Watcher,以后每動(dòng)態(tài)加機(jī)器,那么就往 /clusterServers 下創(chuàng)建一個(gè) EPHEMERAL類型的節(jié)點(diǎn):/clusterServers/{hostname}. 這樣,監(jiān)控系統(tǒng)就能夠?qū)崟r(shí)知道機(jī)器的增減情況,至于后續(xù)處理就是監(jiān)控系統(tǒng)的業(yè)務(wù)了。
    2. Master選舉則是zookeeper中最為經(jīng)典的使用場(chǎng)景了。

    在分布式環(huán)境中,相同的業(yè)務(wù)應(yīng)用分布在不同的機(jī)器上,有些業(yè)務(wù)邏輯(例如一些耗時(shí)的計(jì)算,網(wǎng)絡(luò)I/O處理),往往只需要讓整個(gè)集群中的某一臺(tái)機(jī)器進(jìn)行執(zhí)行,其余機(jī)器可以共享這個(gè)結(jié)果,這樣可以大大減少重復(fù)勞動(dòng),提高性能,于是這個(gè)master選舉便是這種場(chǎng)景下的碰到的主要問(wèn)題。

    利用ZooKeeper的強(qiáng)一致性,能夠保證在分布式高并發(fā)情況下節(jié)點(diǎn)創(chuàng)建的全局唯一性,即:同時(shí)有多個(gè)客戶端請(qǐng)求創(chuàng)建 /currentMaster 節(jié)點(diǎn),最終一定只有一個(gè)客戶端請(qǐng)求能夠創(chuàng)建成功。

    利用這個(gè)特性,就能很輕易的在分布式環(huán)境中進(jìn)行集群選取了。

    另外,這種場(chǎng)景演化一下,就是動(dòng)態(tài)Master選舉。這就要用到 EPHEMERAL_SEQUENTIAL類型節(jié)點(diǎn)的特性了。

    上文中提到,所有客戶端創(chuàng)建請(qǐng)求,最終只有一個(gè)能夠創(chuàng)建成功。在這里稍微變化下,就是允許所有請(qǐng)求都能夠創(chuàng)建成功,但是得有個(gè)創(chuàng)建順序,于是所有的請(qǐng)求最終在ZK上創(chuàng)建結(jié)果的一種可能情況是這樣: /currentMaster/{sessionId}-1 , /currentMaster/{sessionId}-2 , /currentMaster/{sessionId}-3 ….. 每次選取序列號(hào)最小的那個(gè)機(jī)器作為Master,如果這個(gè)機(jī)器掛了,由于他創(chuàng)建的節(jié)點(diǎn)會(huì)馬上小時(shí),那么之后最小的那個(gè)機(jī)器就是Master了。

    1. 在搜索系統(tǒng)中,如果集群中每個(gè)機(jī)器都生成一份全量索引,不僅耗時(shí),而且不能保證彼此之間索引數(shù)據(jù)一致。因此讓集群中的Master來(lái)進(jìn)行全量索引的生成,然后同步到集群中其它機(jī)器。2. 另外,Master選舉的容災(zāi)措施是,可以隨時(shí)進(jìn)行手動(dòng)指定master,就是說(shuō)應(yīng)用在zk在無(wú)法獲取master信息時(shí),可以通過(guò)比如http方式,向一個(gè)地方獲取master。

    分布式隊(duì)列

    隊(duì)列方面,我目前感覺(jué)有兩種,一種是常規(guī)的先進(jìn)先出隊(duì)列,另一種是要等到隊(duì)列成員聚齊之后的才統(tǒng)一按序執(zhí)行。對(duì)于第二種先進(jìn)先出隊(duì)列,和分布式鎖服務(wù)中的控制時(shí)序場(chǎng)景基本原理一致,這里不再贅述。 

    第二種隊(duì)列其實(shí)是在FIFO隊(duì)列的基礎(chǔ)上作了一個(gè)增強(qiáng)。通常可以在 /queue 這個(gè)znode下預(yù)先建立一個(gè)/queue/num 節(jié)點(diǎn),并且賦值為n(或者直接給/queue賦值n),表示隊(duì)列大小,之后每次有隊(duì)列成員加入后,就判斷下是否已經(jīng)到達(dá)隊(duì)列大小,決定是否可以開(kāi)始執(zhí)行了。這種用法的典型場(chǎng)景是,分布式環(huán)境中,一個(gè)大任務(wù)Task A,需要在很多子任務(wù)完成(或條件就緒)情況下才能進(jìn)行。這個(gè)時(shí)候,凡是其中一個(gè)子任務(wù)完成(就緒),那么就去 /taskList 下建立自己的臨時(shí)時(shí)序節(jié)點(diǎn)(CreateMode.EPHEMERAL_SEQUENTIAL),當(dāng) /taskList 發(fā)現(xiàn)自己下面的子節(jié)點(diǎn)滿足指定個(gè)數(shù),就可以進(jìn)行下一步按序進(jìn)行處理了。

     

    posted @ 2012-05-15 11:02 一酌散千憂 閱讀(4844) | 評(píng)論 (0)編輯 收藏

    MongoDB介紹

    當(dāng)今NoSQL領(lǐng)域中有很多有力的競(jìng)爭(zhēng)者通過(guò)多種方式來(lái)處理海量數(shù)據(jù)問(wèn)題。其中重要的解決方案之一就是MongoDB。MongoDB是面向文檔的弱結(jié)構(gòu)化存儲(chǔ)方案,使用JSON格式來(lái)展現(xiàn)、查詢和修改數(shù)據(jù)。

    MongoDB文檔相當(dāng)完備,擴(kuò)展規(guī)模與安裝一樣簡(jiǎn)單。它提供冗余、切片、索引以及map/reduce等概念支持。MongoDB的開(kāi)源社區(qū)非常大且非常活躍。MongoDB在很多大型產(chǎn)品中被實(shí)際運(yùn)用,如:Disney, Craigslist, Foursquare, Github 和SourceForge。MongoDB是一個(gè)開(kāi)源項(xiàng)目,由10gen.com建立并維護(hù),該公司由DoubleClick的前任執(zhí)行人員創(chuàng)立。同時(shí),10gen也提供了極好的商業(yè)支持與參與建設(shè)。

    MongoDB NoSQL: 缺陷與優(yōu)勢(shì)

    MongoDB作為一個(gè)可用NoSQL方案具有很多優(yōu)勢(shì)。我剛開(kāi)始接觸NoSQL數(shù)據(jù)庫(kù)了解了一系列基于Java的方案,并且花了大量的時(shí)間來(lái)弄懂什么是列家族,Hadoop與HBase的關(guān)系,ZooKeeper到底是什么。當(dāng)我終于全部清楚之后,發(fā)現(xiàn)Cassandra與HBase確實(shí)是對(duì)于NoSQL領(lǐng)域非??煽?、可信賴的解決方案。但與其他的解決方案相比,MongoDB讓我在能夠開(kāi)始寫(xiě)代碼之前,不用理解那么多的概念。

    與其他軟件相似,MongoDB也存在缺陷。經(jīng)過(guò)一段時(shí)間使用MongoDB,我列舉經(jīng)歷過(guò)并需要注意的一些事情,我成為“Gotchas”:

    • 不要按照關(guān)系型數(shù)據(jù)庫(kù)來(lái)思考。這很明顯,MongoDB使得構(gòu)建和執(zhí)行復(fù)雜查詢變得非常容易。當(dāng)實(shí)際使用的時(shí)候,你會(huì)主要關(guān)注于效率問(wèn)題(像我一樣)。
    • MongoDB的索引是二進(jìn)制的樹(shù)。如果你不是很熟悉B-tree,可能需要了解一下。這些都涉及到構(gòu)建符合提供查詢條件需求的建立索引的方式。
    • 小心的設(shè)計(jì)索引結(jié)構(gòu)。這涉及到上面提到的B-tree。剛開(kāi)始我的索引包含文檔中的很多字段,以防我會(huì)使用到他們。不要犯同樣的錯(cuò)誤。我有一個(gè)很小集合的索引(大約1千萬(wàn)記錄)增長(zhǎng)到超過(guò)17GB的空間,比集合本身還大。你應(yīng)該不會(huì)想要索引一個(gè)包含成百上千個(gè)實(shí)體的列表字段。
    • MongoDB采用了非常有意思的方式來(lái)實(shí)現(xiàn)NoSQL:采用BSON作為存儲(chǔ),JSON作為展示,JavaScript用于管理和Map/Reduce。因此也引起了一些小問(wèn)題比如這個(gè) (破壞了Number和Long的相等操作),在MongoDB逐漸流行之后,可能會(huì)不斷的展示出來(lái)。

     

    MongoDB, 命令行與驅(qū)動(dòng)

    MongoDB基本是使用JavaScript客戶端命令行程序來(lái)進(jìn)行復(fù)雜任務(wù)管理的,如數(shù)據(jù)整合和簡(jiǎn)單信息處理,編程都是完全使用JavaScript語(yǔ)言來(lái)的。本文中,我們會(huì)展示命令行的使用示例?,F(xiàn)在有大量的MongoDB客戶端產(chǎn)品提供,并且由MongoDB社區(qū)來(lái)支持驅(qū)動(dòng)。通常每種編程語(yǔ)言都有驅(qū)動(dòng),并且所有流行的語(yǔ)言都有包括,一些不那么流行的也包含在內(nèi)。這篇文章展示了使用MongoDB的Java驅(qū)動(dòng),并使用一個(gè)ORM庫(kù)(MJORM)與之進(jìn)行比較。

    介紹 MJORM: MongoDBORM方案

    在解決的眾多有意思的問(wèn)題中,最近NoSQL數(shù)據(jù)存儲(chǔ)在開(kāi)發(fā)者中主要的問(wèn)題趨勢(shì)就是對(duì)象關(guān)系映射。對(duì)象關(guān)系映射就是將傳統(tǒng)中保存在關(guān)系型數(shù)據(jù)庫(kù)中的持久化數(shù)據(jù)映射為在應(yīng)用程序中使用的對(duì)象。這使得編程語(yǔ)言使用起來(lái)更加流暢和自然。

    MongoDB面向文檔的架構(gòu)使得它非常適合對(duì)象關(guān)系映射,因?yàn)槲臋n本身就是以對(duì)象形式存儲(chǔ)的。可惜沒(méi)有太多的MongoDB的Java對(duì)象關(guān)系映射庫(kù),但是還是有一些,如morphia-(A type-safe Java library for MongoDB), spring-data(SpringData項(xiàng)目的MongoDB實(shí)現(xiàn))

    這些ORM庫(kù)大量使用了注解,因?yàn)橐恍┰驅(qū)ξ也贿m合,其中最重要的就是這些被注解的對(duì)象在多個(gè)項(xiàng)目中的兼容性問(wèn)題。這讓我開(kāi)始了mongo-Java-orm 或者 "MJORM" (發(fā)音 me-yorm)項(xiàng)目,一個(gè)MongoDB的Java對(duì)象關(guān)系映射項(xiàng)目。MJORM是在MIT許可之下,并且在發(fā)布在了google code project。項(xiàng)目采用maven構(gòu)建,并且maven構(gòu)件倉(cāng)庫(kù)托管于google code版本控制服務(wù)器。MJORM的最新可用發(fā)布版本為0.15,已經(jīng)由一些項(xiàng)目使用與生產(chǎn)環(huán)境中。

    開(kāi)始使用ORM

    加入MJORM 庫(kù)

    Maven的使用者首先應(yīng)當(dāng)在pom.xml中加入MJORM的maven倉(cāng)庫(kù),使得MJORM構(gòu)件可用。

    <repository>
             <id>mjorm-webdav-maven-repo</id>
             <name>mjorm maven repository</name>
             <url>http://mongo-Java-orm.googlecode.com/svn/maven/repo/</url>
             <layout>default</layout>
    </repository>

    然后加入依賴:

    <dependency>
             <groupId>com.googlecode</groupId>
             <artifactId>mongo-Java-orm</artifactId>
             <version>0.15</version>
    </dependency>

    這樣就可以在應(yīng)用中引入MJORM代碼。假如沒(méi)有使用maven,則你需要手動(dòng)下載MJORM的pom.xml中列舉的所有依賴。

    建立 POJOs

    依賴已經(jīng)導(dǎo)入,可以開(kāi)始編碼了。我們從POJO開(kāi)始:

     
    class Author {
             private String firstName;
             private String lastName;
             // ... setters and getters ...
    }
     
    class Book {
             private String id;
             private String isbn;
             private String title;
             private String description;
             private Author author;
             // ... setters and getters ...
    }

    我們?cè)谶@個(gè)對(duì)象模型中的描述是,作者有ID、姓和名,書(shū)有ID、ISNB、標(biāo)題、描述和作者。

    你可能注意到書(shū)的id屬性是一個(gè)字符串,這是為了適應(yīng)MongoDB的對(duì)象ID類型。MongoDB的ID是一個(gè)12字節(jié)的二進(jìn)制值顯示為一個(gè)十六進(jìn)制的字符串。MongoDB要求集合中的每個(gè)文檔都必須有一個(gè)唯一id,但不要求一定要是ObjectId。目前MJORM只支持ObjectId,并且顯示為字符串。

    你也可能注意到了Author沒(méi)有id字段。這是因?yàn)锽ook是它的父文檔,因此不需要有id。記住,MongoDB只要求集合中的文檔在根級(jí)別的id。

    創(chuàng)建XML映射文件

    下一個(gè)步驟就是建立XML映射文件,MJORM能夠?qū)ongoDB文檔轉(zhuǎn)換為對(duì)象。我們?yōu)槊總€(gè)文檔創(chuàng)建一個(gè)對(duì)象作為示范,無(wú)論將所有的映射放在一個(gè)XML文件中還是分開(kāi)都是可以的。

    Author.mjorm.xml:

    <?xml version="1.0"?>
    <descriptors>
             <object class="Author">
                     <property name="firstName" />
                     <property name="lastName" />
             </object>
    </descriptors>

    Book.mjorm.xml:

    <?xml version="1.0"?>
    <descriptors>
             <object class="Book">
                     <property name="id" id="true" auto="true" />
                     <property name="isbn" />
                     <property name="title" />
                     <property name="description" />
                     <property name="author" />
             </object>
    </descriptors>

     

    這些映射文件能夠很好的自解釋。descriptors 元素是根元素,必須包含在每個(gè)映射文件中。在它下面是object元素定義了文檔與之對(duì)應(yīng)的類。Object包含的property 元素主要用于描述POJO中的屬性以及這些屬性如何與MongoDB中的文檔想對(duì)應(yīng)。property 元素至少必須包含一個(gè)name 屬性,這個(gè)元素就是POJO和MongoDB的文檔中的屬性名稱。column 屬性則是可選的,用于特定一個(gè)在MongoDB文檔中的可選屬性名稱。

    property 元素當(dāng)中的id屬性應(yīng)該是對(duì)象的唯一識(shí)別。一個(gè)對(duì)象只能有一個(gè)property 元素包含id屬性。auto 的設(shè)置會(huì)使得MJORM在持久化時(shí)為該屬性自動(dòng)生成一個(gè)值。

    可以在google code的MJORM項(xiàng)目主頁(yè)中查看XML映射文件的更多細(xì)節(jié)描述。

    整合POJOXML

    我們創(chuàng)建了數(shù)據(jù)模型以及映射文件,使得MJORM可以從MongoDB序列號(hào)以及反序列號(hào)POJO。我們可以進(jìn)行一些有意思的事情了,首先打開(kāi)MongoDB的鏈接:

    Mongo mongo = new Mongo(
             new MongoURI("mongodb://localhost/mjormIsFun")); // 10gen driver

    Mongo 對(duì)象是由10gen編寫(xiě)的Java驅(qū)動(dòng)提供的。示例中連接了一個(gè)本地的MongoDB實(shí)例中的mjormIsFun數(shù)據(jù)庫(kù)。接下來(lái)我們創(chuàng)建MJORM ObjectMapper 。目前ObjectMapper 在MJORM中的唯一實(shí)現(xiàn)就是XmlDescriptorObjectMapper,使用XML結(jié)構(gòu)描述信息??赡苤髸?huì)增加對(duì)注解或其他結(jié)構(gòu)定義的支持。

    XmlDescriptorObjectMapper objectMapper = new XmlDescriptorObjectMapper();
    mapper.addXmlObjectDescriptor(new File("Book.mjorm.xml"));
    mapper.addXmlObjectDescriptor(new File("Author.mjorm.xml"));

    建立好了XmlDescriptorObjectMapper 并且加入了映射文件。接下來(lái)建立由MJORM提供的MongoDao 對(duì)象的實(shí)例。

    DB db = mongo.getDB("mjormIsFun"); // 10gen driver
    MongoDao dao = new MongoDaoImpl(db, objectMapper);

    首先我們要獲得10gen驅(qū)動(dòng)提供的DB對(duì)象實(shí)例。然后使用DB和ObjectMapper 建立MongoDao 。我們準(zhǔn)備開(kāi)始持久化數(shù)據(jù),建立一個(gè)Book 然后保存到MongoDB中。

    Book book = new Book();
    book.setIsbn("1594743061");
    book.setTitle("MongoDB is fun");
    book.setDescription("...");
     
    book = dao.createObject("books", book);
    System.out.println(book.getId()); // 4f96309f762dd76ece5a9595

    首先建立Book 對(duì)象并且填值,然后調(diào)用MongoDao 的 createObject 方法,將Book 對(duì)象傳入"books" 的集合中。MJORM會(huì)按照之前的xml映射文件將Book 轉(zhuǎn)換為DBObject (這是10gen的Java驅(qū)動(dòng)使用的基本類型),并保存一個(gè)新的文檔進(jìn)"books" 集合。MJORM返回Book對(duì)象時(shí),id屬性會(huì)被填充。請(qǐng)注意,MongoDB默認(rèn)是不需要在使用前建立數(shù)據(jù)庫(kù)或集合的,系統(tǒng)會(huì)在需要時(shí)自動(dòng)創(chuàng)建,這可能會(huì)造成某些困擾。在MongoDB的命令行中查看Book對(duì)象大概如下:

    > db.books.find({_id:ObjectId("4f96309f762dd76ece5a9595")}).pretty()
    {
             "_id":          ObjectId("4f96309f762dd76ece5a9595"),
             "isbn":         "1594743061",
             "title":        "MongoDB is fun",
             "description": "..."
    }

     

    我們來(lái)看看假如不用MJORM而直接使用10gen的Java驅(qū)動(dòng),如何使用createObject 方法:

    Book book = new Book();
    book.setIsbn("1594743061");
    book.setTitle("MongoDB is fun");
    book.setDescription("...");
     
    DBObject bookObj = BasicDBObjectBuilder.start()
             .add("isbn",              book.getIsbn())
             .add("title",             book.getTitle())
             .add("description",       book.getDescription())
             .get();
     
    // 'db' is our DB object from earlier
    DBCollection col = db.getCollection("books");
    col.insert(bookObj);
     
    ObjectId id = ObjectId.class.cast(bookObj.get("_id"));
    System.out.println(id.toStringMongod()); // 4f96309f762dd76ece5a9595

     

    下面進(jìn)行對(duì)象的查詢:

    Book book = dao.readObject("books", "4f96309f762dd76ece5a9595", Book.class);
    System.out.println(book.getTitle()); // "MongoDB is fun"

    readObject 方法根據(jù)給定文檔的id從指定的集合中讀取文檔,轉(zhuǎn)換為對(duì)象(再次使用映射文件)并返回。

    敏銳的讀者會(huì)注意到Book還沒(méi)有指定Author,仍然保存了。這歸咎于MongoDB的結(jié)構(gòu)不敏感的特性。我們不能要求集合中的文檔包含所有屬性(id屬性是必須的),所有在MongoDB中沒(méi)有Author的Book是可以的。我們現(xiàn)在為Book添加一個(gè)Author并且更新一下:

    Author author = new Author();
    author.setFirstName("Brian");
    author.setLastName("Dilley");
     
    book.setAuthor(author);
     
    dao.updateObject("books", "4f96309f762dd76ece5a9595", book);

    現(xiàn)在Book就包含了Author,并且在MongoDB中持久化了。現(xiàn)在在命令行查看了Book:

    > db.books.find({_id:ObjectId("4f96309f762dd76ece5a9595")}).pretty()
    {
             "_id":          ObjectId("4f96309f762dd76ece5a9595"),
             "isbn":         "1594743061",
             "title":        "MongoDB is fun",
             "description": "..."
             "author": {
                 "firstName": "Brian",
                 "lastName": "Dilley"
             }
    }

    可以看到持久化的Book中已經(jīng)包含了author。不使用MJORM來(lái)操作一遍:

    Author author = new Author();
    author.setFirstName("Brian");
    author.setLastName("Dilley");
     
    book.setAuthor(author);
     
    DBObject bookObj = BasicDBObjectBuilder.start()
             .add("isbn",              book.getIsbn())
             .add("title",             book.getTitle())
             .add("description",       book.getDescription())
             .push("author")
                     .add("firstName",         author.getFirstName())
                     .add("lastName",  author.getLastName())
                     .pop()
             .get();
     
    DBCollection col = db.getCollection("books");
    col.update(new BasicDBObject("_id", bookObj.get("_id")), bookObj);
     

     

    對(duì)于MongoDao 方法的深入討論已經(jīng)超出了本文的范圍。對(duì)于將MJORM有興趣用于實(shí)際項(xiàng)目中的用戶強(qiáng)烈建議了解一下MJORM項(xiàng)目提供的相關(guān)文檔,或者MongoDao 接口提供的相關(guān)用法。

    總結(jié)

    希望這篇文章對(duì)MongoDB和MJORM的亮點(diǎn)有所展示。MongDB是一個(gè)優(yōu)秀的呃NoSQL數(shù)據(jù)存儲(chǔ),有著大量?jī)?yōu)秀的特性,會(huì)是NoSQL市場(chǎng)中長(zhǎng)期競(jìng)爭(zhēng)者。若你會(huì)在一個(gè)Java項(xiàng)目中使用MongoDB,希望你也能夠考慮使用MJORM作為你的ORM框架。十分歡迎大家提交特性需求、錯(cuò)誤異常報(bào)告、文檔和源碼修正。

     

    作者 Bio

    Brian Dilley 是一個(gè)經(jīng)驗(yàn)豐富的高級(jí)工程師以及項(xiàng)目領(lǐng)導(dǎo),在Java/Java EE /Spring Framework/Linux內(nèi)部結(jié)構(gòu)理解和管理有著超過(guò)13年的經(jīng)驗(yàn)。Brian對(duì)于創(chuàng)業(yè)公司有很多經(jīng)驗(yàn),推向市場(chǎng),構(gòu)建/維護(hù)產(chǎn)品等。他是IaascloudPHPLinux的專家,熟悉產(chǎn)品的采購(gòu)、安裝及配置定義,以及公司的軟硬件架構(gòu)包括負(fù)載均衡、數(shù)據(jù)庫(kù)、微博等??梢?/span>follow Brian Twitter

    posted @ 2012-05-09 13:46 一酌散千憂 閱讀(1549) | 評(píng)論 (0)編輯 收藏

    “企業(yè)信息集成(EII):實(shí)用方式”于2005年發(fā)布,描述了一套集成不同數(shù)據(jù)源的方法論,利用了當(dāng)時(shí)的先進(jìn)技術(shù),如面向服務(wù)架構(gòu)(SOA)、Web Services、XML、資源描述架構(gòu)(RDF)、基于XML的元數(shù)據(jù)格式以及數(shù)據(jù)提取、轉(zhuǎn)換和加載(ETL)。EII能夠基本為關(guān)系型數(shù)據(jù)元素提供統(tǒng)一視角,但在性能效率上缺乏能夠替代數(shù)據(jù)倉(cāng)庫(kù)和多維數(shù)據(jù)庫(kù)的能力。五年之后技術(shù)已經(jīng)得到了顯著提升,不僅在于對(duì)于分散數(shù)據(jù)的操作,還有簡(jiǎn)化了單一容器下不同數(shù)據(jù)的整合,以及對(duì)數(shù)據(jù)深入挖掘的能力。

    轉(zhuǎn)變了數(shù)據(jù)管理方式的技術(shù)正是虛擬化。低成本存儲(chǔ)、云計(jì)算、NoSQL數(shù)據(jù)庫(kù)以及Hadoop。當(dāng)我們提起虛擬化時(shí),已經(jīng)遠(yuǎn)遠(yuǎn)超出為一臺(tái)物理機(jī)器提供一套軟件實(shí)例這一概念。時(shí)至今日,我們可以虛擬化服務(wù)器、存儲(chǔ)以及網(wǎng)絡(luò)。所有這些虛擬化意味著我們不再被這些物理?xiàng)l件所限制,能夠迅速構(gòu)建物理環(huán)境以支持我們特定時(shí)刻的特定需求。當(dāng)面對(duì)GbTb、Pb等級(jí)數(shù)據(jù)量的處理需求時(shí),我們基本能擺脫結(jié)構(gòu)化的數(shù)據(jù)倉(cāng)庫(kù)。我們不在需要僅僅為了發(fā)掘業(yè)務(wù)的某一方面而建立一個(gè)特殊的環(huán)境了。

    低成本存儲(chǔ)在業(yè)務(wù)的數(shù)據(jù)存儲(chǔ)方面節(jié)省了開(kāi)支。高昂的存儲(chǔ)成本會(huì)使得企業(yè)尋找在限定規(guī)模的數(shù)據(jù)之上進(jìn)行關(guān)鍵業(yè)務(wù)分析的方案,這樣使得如何選擇最重要的數(shù)據(jù)變得十分關(guān)鍵,而且還限制了系統(tǒng)能夠處理的數(shù)據(jù)的質(zhì)量。

    負(fù)面影響便是業(yè)務(wù)最終可能面臨很少的選擇,因?yàn)闆](méi)有足夠的歷史數(shù)據(jù)提供從而識(shí)別一個(gè)有效關(guān)鍵模式?;蛘咭?yàn)楦甙旱耐度胧沟脴I(yè)務(wù)被停止,而使用常規(guī)慣例來(lái)識(shí)別模式。

    云計(jì)算為那些需要通過(guò)海量數(shù)據(jù)源在合理時(shí)間范圍內(nèi)產(chǎn)生結(jié)果的需求提供了一個(gè)可用的方式。海量數(shù)據(jù)處理需要兩點(diǎn):彈性存儲(chǔ),CPU。高速網(wǎng)絡(luò)很有幫助,但是待會(huì)我們會(huì)看到在發(fā)掘軟件在處理海量數(shù)據(jù)時(shí),它并非是系統(tǒng)的瓶頸。彈性存儲(chǔ)意味著企業(yè)不會(huì)在期望操作的數(shù)據(jù)規(guī)模或類型上受到限制,降低了使用數(shù)據(jù)倉(cāng)庫(kù)無(wú)法獲取最佳結(jié)果的風(fēng)險(xiǎn)。更多的CPU使得結(jié)果能夠在期望的時(shí)間范圍內(nèi)更快的被交付。

    NoSQL提供了海量數(shù)據(jù)的支持,但與傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)沒(méi)有關(guān)聯(lián)。而且大部分NoSQL數(shù)據(jù)庫(kù)是開(kāi)源的,無(wú)須支付購(gòu)買(mǎi)證書(shū)等費(fèi)用。NoSQL對(duì)于表結(jié)構(gòu)有著驚人的靈活性,無(wú)須隨著系統(tǒng)的改進(jìn)而不斷修改完善定義。NoSQL可以支持不同數(shù)據(jù)源的合并查看,從而成為EII之后另一個(gè)備選方案,這或許是NoSQL最重要的方面了。

    NoSQL內(nèi)置了數(shù)據(jù)冗余與分布式數(shù)據(jù)存儲(chǔ)機(jī)制。海量數(shù)據(jù)的最大問(wèn)題之一就是磁盤(pán)讀寫(xiě),NoSQL通過(guò)將數(shù)據(jù)分布至一系列節(jié)點(diǎn)來(lái)緩解這個(gè)問(wèn)題。當(dāng)一個(gè)查詢請(qǐng)求發(fā)出時(shí),這些節(jié)點(diǎn)能夠并行查詢自身節(jié)點(diǎn),而不是僅僅依靠一塊磁盤(pán),一個(gè)磁盤(pán)陣列或一條網(wǎng)絡(luò)連接等,數(shù)據(jù)查詢能夠在節(jié)省了讀寫(xiě)開(kāi)支之后變得更加迅速。

    最終,我們來(lái)討論Hadoop,集合了上述所有技術(shù)力量與一身的用于檢測(cè)和分析數(shù)據(jù)的框架。有些人可能認(rèn)為Hadoop是一項(xiàng)NoSQL技術(shù),實(shí)際上Hadoop是一個(gè)分布組件的java框架,用于分解“吃大象”(此處也雙關(guān)Hadoop是以創(chuàng)立者的兒子給自己的一個(gè)大象玩具起的名字)的工作——每次一口。

    Hadoop自身實(shí)際上與待處理數(shù)據(jù)是各自獨(dú)立的。它將大型查詢?nèi)蝿?wù)分解為小的并行查詢?nèi)蝿?wù),然后收集結(jié)果,并整合出答案返回給用戶。Hadoop相對(duì)于NoSQL來(lái)說(shuō)是一個(gè)并行查詢框架,通過(guò)云計(jì)算驅(qū)動(dòng)節(jié)點(diǎn),運(yùn)行在低成本存儲(chǔ)及虛擬化技術(shù)之上。

    Kicking的知識(shí)回顧

    當(dāng)EII第一次作為最佳實(shí)踐出現(xiàn)于2003-2004年,關(guān)鍵要素就是無(wú)需再移動(dòng)數(shù)據(jù)了。當(dāng)時(shí)大部分的數(shù)據(jù)中心仍然運(yùn)行于低速網(wǎng)絡(luò)中,有限的空間用于復(fù)制數(shù)據(jù)。之后,EII成為了當(dāng)時(shí)可用技術(shù)和問(wèn)題域中最優(yōu)秀的解決方案。EII的某些方面的優(yōu)秀即使在海量數(shù)據(jù)中也是很顯著的。

    EII的優(yōu)點(diǎn)之一就是將處理過(guò)程轉(zhuǎn)移到數(shù)據(jù)所在地。海量數(shù)據(jù)方案的關(guān)鍵架構(gòu)要素之一就是將處理過(guò)程轉(zhuǎn)移到數(shù)據(jù)所在地,而不是轉(zhuǎn)移數(shù)據(jù)。EII中的一個(gè)重要原則就是使用數(shù)據(jù)歸屬地的查詢功能。這項(xiàng)實(shí)踐就是構(gòu)建靠近數(shù)據(jù)源網(wǎng)絡(luò)的Web Service,能夠建立起通用查詢接口,但只針對(duì)本地?cái)?shù)據(jù)庫(kù)進(jìn)行查詢。我們通過(guò)開(kāi)放的基于Web的接口解決了數(shù)據(jù)的專有格式的問(wèn)題,從而使得多個(gè)數(shù)據(jù)子集能夠迅速的整合并以統(tǒng)一模式展示。

    有了低成本存儲(chǔ)和10G網(wǎng)絡(luò)之后,我們就不必那么擔(dān)心數(shù)據(jù)冗余與數(shù)據(jù)遷移,但還是有其他問(wèn)題存在的,數(shù)據(jù)倉(cāng)庫(kù)無(wú)法確保數(shù)據(jù)的原始性便是其中之一。在EII中,我們將從原始數(shù)據(jù)源獲取數(shù)據(jù)視為“黃金準(zhǔn)則”,這樣就能夠保證信息未被修改過(guò),且是準(zhǔn)確的。

    Big Data要求數(shù)據(jù)必須轉(zhuǎn)移到新的物理位置,這樣可信任度又成為了問(wèn)題。EII的那些獲取基線數(shù)據(jù)的最佳實(shí)踐仍然是相關(guān)而且重要的。實(shí)際上,那些為EII設(shè)計(jì)開(kāi)發(fā)的Web Services接口最終在Big Data的啟用中扮演主要角色。

    當(dāng)然,討論數(shù)據(jù)管理不能不涉及到安全問(wèn)題。EII在安全領(lǐng)域中還是超過(guò)了Big Data。技術(shù)上來(lái)說(shuō),Big Data在數(shù)據(jù)集成方面更加高效與敏捷,但是大部分缺少了固有的安全性,因?yàn)樵谠O(shè)計(jì)上會(huì)加大處理的難度。所以,可能要由源系統(tǒng)來(lái)?yè)?dān)任起數(shù)據(jù)訪問(wèn)安全方面的責(zé)任。因?yàn)?/span>EII直接在源系統(tǒng)中查詢數(shù)據(jù),所以必須要求有適當(dāng)?shù)氖跈?quán),否則查詢就將失敗。

    上述關(guān)于安全討論描述的是內(nèi)在的安全控制情況。將訪問(wèn)權(quán)限控制列表集成進(jìn)數(shù)據(jù)庫(kù)是非常合理的,這將確保安全能夠作為查詢的一部分進(jìn)行維護(hù)。然后,一旦能夠直接查詢NoSQL數(shù)據(jù)源,就意味著能夠自由的訪問(wèn)你所有的數(shù)據(jù)。

    總結(jié)

    引用老的Virginia Slims的廣告中的臺(tái)詞:“我們已經(jīng)歷很長(zhǎng)的路途了,寶貝兒!”文中討論到的技術(shù)的發(fā)展已經(jīng)對(duì)21世紀(jì)第二個(gè)10年中的的數(shù)據(jù)解決方案產(chǎn)生了巨大的影響。商業(yè)化與小型化掃除了一些思想體系上的障礙,使得架構(gòu)師能夠?qū)W⒂趩?wèn)題本身,而不是尋找一些實(shí)用及可實(shí)現(xiàn)的問(wèn)題解決方案。構(gòu)建10000個(gè)節(jié)點(diǎn)的處理引擎,能夠在數(shù)秒內(nèi)處理Pb級(jí)別的數(shù)據(jù)量,卻只消耗每小時(shí)幾便士,這就是數(shù)據(jù)處理的美好前景。

    有了這些新工具,我們就要重新考慮如何推進(jìn)數(shù)據(jù)管理。為何數(shù)據(jù)無(wú)法被很好地被維護(hù)整合,并且需要花費(fèi)數(shù)萬(wàn)美元。數(shù)據(jù)管理幾乎是每個(gè)大中型企業(yè)的心病。數(shù)據(jù)管理曾經(jīng)在存儲(chǔ)、管理、訪問(wèn)、整合以及查詢上花費(fèi)巨大,但是今后不再會(huì)是這樣了。

    關(guān)于作者

    JP Morgenthal 是在IT策略與云計(jì)算方面的世界級(jí)專家之一。他在企業(yè)復(fù)雜問(wèn)題域的解決方案實(shí)施上有著25年的經(jīng)驗(yàn)。JP Morgenthal以其在技術(shù)方面的深度和廣度,有利的支持他在企業(yè)問(wèn)題域中的敏感度。他在集成、軟件開(kāi)發(fā)和云計(jì)算是一位讓人尊敬的作者,同時(shí)也是InfoQ在引領(lǐng)云計(jì)算方面的編輯,并且參與了“云計(jì)算:評(píng)估風(fēng)險(xiǎn)”項(xiàng)目。 

     

    原文接:http://www.infoq.com/articles/DataIntegrationFromEIItoBigData

    posted @ 2012-04-19 07:15 一酌散千憂 閱讀(296) | 評(píng)論 (0)編輯 收藏

    項(xiàng)目經(jīng)理(Project manager)是項(xiàng)目的支柱與核心,維基百科的定義:項(xiàng)目經(jīng)理是項(xiàng)目管理方面的專家,負(fù)責(zé)項(xiàng)目的策劃、執(zhí)行和結(jié)束,即整個(gè)生命周期過(guò)程。項(xiàng)目經(jīng)理手中的“干將莫邪”便是軟件開(kāi)發(fā)過(guò)程方法(software development process/software development life cycle (SDLC)),可能采取的有RUPRational Unified Process),敏捷等。

    其實(shí)軟件開(kāi)發(fā)區(qū)分階段已經(jīng)廣為大家接受,普遍的概念即需要區(qū)分為分析、設(shè)計(jì)、實(shí)施、測(cè)試、發(fā)布,過(guò)程中會(huì)產(chǎn)生若干產(chǎn)物,如需求說(shuō)明書(shū)、概要設(shè)計(jì)、詳細(xì)設(shè)計(jì)等。若提及過(guò)程方法,如RUP的話,主要分為四大階段,先啟(Inception)、精華(Elaboration)、構(gòu)建(Construction)、交付(Transition)迭代的開(kāi)發(fā)方式,而Scrum的核心概念則是Sprint。

     

    Maven在項(xiàng)目管理中有那些幫助呢?Maven能夠從一個(gè)信息中心為項(xiàng)目提供構(gòu)建,報(bào)告,文檔編制等工作。在Maven官方介紹《What is maven》中介紹了maven的項(xiàng)目目標(biāo)(Objectives)(http://maven.apache.org/what-is-maven.html),如下:

    l         簡(jiǎn)化構(gòu)建過(guò)程

    l         提供統(tǒng)一的構(gòu)建系統(tǒng)

    l         提供項(xiàng)目質(zhì)量信息

    l         提供對(duì)于開(kāi)發(fā)最佳實(shí)踐的指導(dǎo)

    l         允許對(duì)于新特性的透明整合

     

    對(duì)于Maven影響最為深刻的就是它的構(gòu)建系統(tǒng),幾乎貫穿了整個(gè)實(shí)施階段。作為對(duì)比我們參考一下RUPConstruction階段,以及Scrum的單個(gè)Sprint過(guò)程。

     

    RUPConstruction階段的目標(biāo):

    這個(gè)階段的目標(biāo)是澄清需求并基于架構(gòu)基線完成開(kāi)發(fā)。

    l          通過(guò)優(yōu)化資源來(lái)縮減開(kāi)支,并避免無(wú)意義的爭(zhēng)執(zhí)與返工。

    l          實(shí)用性與質(zhì)量兼具。

    l          盡快發(fā)布可用版本。

    l          完成對(duì)所有功能的分析、設(shè)計(jì)、開(kāi)發(fā)、測(cè)試。

    l          采用增量迭代的模式完成開(kāi)發(fā)并準(zhǔn)備交付。

    l          檢查項(xiàng)目發(fā)布的所有資源是否已經(jīng)準(zhǔn)備完全。

    l          形成項(xiàng)目組之間的并行開(kāi)發(fā)。

     

    在《硝煙中的ScrumXP》一書(shū)中,介紹了作者實(shí)施Scrum的過(guò)程。在一個(gè)Sprint中,不是只有Sprint backlog、burn down chat等,實(shí)施過(guò)程中的敏捷思想也是其中的核心,我們來(lái)看看敏捷信奉的一部分最佳實(shí)踐:

    l          簡(jiǎn)單設(shè)計(jì)(Simple Design

    l          結(jié)對(duì)編程(Pair Programming

    l          測(cè)試驅(qū)動(dòng)(Test-Driven Development

    l          小規(guī)模發(fā)布(Small Releases

    l          持續(xù)集成(Continuous Integration

    l          集體擁有代碼(Collective Code Ownership

    l          編碼標(biāo)準(zhǔn)(Coding Standard

     

    Maven對(duì)于上述目標(biāo)中的質(zhì)量(實(shí)用性與質(zhì)量,以測(cè)試驅(qū)動(dòng))、可用(可用版本,小規(guī)模)、資源管理等均能發(fā)揮較大的作用。主要是其定義了一套完整優(yōu)秀的構(gòu)建生命周期機(jī)制,其基本階段如下:

    l         validate驗(yàn)證項(xiàng)目正確性及依賴有效性

    l         compile編譯項(xiàng)目源碼

    l         test使用合適的單元測(cè)試框架對(duì)編譯后的源碼進(jìn)行測(cè)試,測(cè)試代碼不會(huì)被打包或發(fā)布

    l         package將編譯后的代碼以規(guī)定格式打包,如Jar

    l         integration-test將打包后的代碼放置于環(huán)境中進(jìn)行集成測(cè)試

    l         verify檢查打包的有效性并驗(yàn)證質(zhì)量標(biāo)準(zhǔn)

    l         install將包裝載入本地倉(cāng)庫(kù),以提供與其他項(xiàng)目的依賴

    l         deploy將包發(fā)布至遠(yuǎn)程倉(cāng)庫(kù)中

    其上每一個(gè)階段實(shí)際都分為前中后三個(gè)階段,用戶可以定義在每一個(gè)階段前后進(jìn)行自定義的操作,打造自己的構(gòu)建流程(如在某個(gè)階段執(zhí)行前制定特殊的配置文件,完成后再改回默認(rèn))。對(duì)于階段的實(shí)際使用方式,如:validate可以項(xiàng)目所有依賴有效,test可以針對(duì)dao層進(jìn)行單元測(cè)試,intergration-test可以對(duì)完整業(yè)務(wù)流程或服務(wù)層等進(jìn)行集成測(cè)試。

     

    在項(xiàng)目中實(shí)際使用的經(jīng)驗(yàn),對(duì)于標(biāo)簽的使用心得:

    1.<dependency> - 依賴標(biāo)簽,最重要的標(biāo)簽,也是Maven的基礎(chǔ)功能。

                  <dependency>

                         <groupId>junit</groupId>

                         <artifactId>junit</artifactId>

                         <version>4.8.1</version>

                         <scope>test</scope>

                  </dependency>

     

    2. <repository> - 資源倉(cāng)庫(kù),可以包含多個(gè),常用的有Maven,Jboss等,如下是公司內(nèi)建的Nexus資源庫(kù)。

    <repository>

                         <id>Suntang's Maven Repository</id>

                         <name>Suntang Nexus Repository</name>

                         <url>http://10.10.10.33:8081/nexus/content/groups/public</url>

                  </repository>

    3. <profile> - 解釋為情景模式可能較為合適??梢杂卸鄠€(gè),在何種場(chǎng)景下會(huì)使用哪些屬性、插件等。如下例子便是當(dāng)缺失某個(gè)文件時(shí)激活。

    (感覺(jué)寫(xiě)的不錯(cuò)的一篇,http://blog.csdn.net/turkeyzhou/article/details/4894657

    <profile>

     <activation>

    <file>

    <missing>target/generated-sources/axistools/wsdl2java/org/apache/maven</missing>

    </file>

    </activation>

     </profile>

    4.<build> - 構(gòu)建過(guò)程。是進(jìn)行整個(gè)項(xiàng)目管理的核心標(biāo)簽。重點(diǎn)需要掌握的知識(shí)就是生命周期。

    http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference

    下面的例子就是制定了打包時(shí)的資源路徑,并且定義了最終打包的名稱。

     

           <build>

                  <resources>

                         <resource>

                                <directory>src/main/resources</directory>

                                <includes>

                                       <include>**/*</include>

                                </includes>

                         </resource>

                         <resource>

                                <directory>src/main/assembly</directory>

                                <includes>

                                       <include>**/*</include>

                                </includes>

                         </resource>

                  </resources>

     

                  <finalName>po</finalName>

           </build>

     

    4.< plugin > - 支持插件。如單元測(cè)試自動(dòng)化,之前提到的Ant的插件等。若有某些功能覺(jué)得不順手,可以嘗試官網(wǎng)找一下有沒(méi)有合適的插件(http://maven.apache.org/plugins/index.html)。

    下面的例子就是在集成測(cè)試中,只運(yùn)行后綴為TestSuitex.java的測(cè)試類

     

                         <plugin>

                                <groupId>org.apache.maven.plugins</groupId>

                                <artifactId>maven-surefire-plugin</artifactId>

                                <version>2.4.3</version>

                                <configuration>

                                       <junitArtifactName>junit:junit</junitArtifactName>

                                       <forkMode>once</forkMode>

                                </configuration>

                                <executions>

                                       <execution>

                                              <id>default-test</id>

                                              <phase>integration-test</phase>

                                              <goals>

                                                     <goal>test</goal>

                                              </goals>

                                              <configuration>

                                                     <skip>false</skip>

                                                     <includes>

                                                            <include>**/*TestSuitex.java</include>

                                                     </includes>

                                              </configuration>

                                       </execution>

                                </executions>

                         </plugin>

     

    posted @ 2012-04-06 14:48 一酌散千憂 閱讀(499) | 評(píng)論 (0)編輯 收藏

    環(huán)境背景:

    我作為項(xiàng)目經(jīng)理和技術(shù)架構(gòu)管理人員負(fù)責(zé)公司一條生產(chǎn)線。討論之后,首席架構(gòu)師希望我們能夠?qū)嵤?/span>TDD。在實(shí)施TDD的過(guò)程中,設(shè)計(jì)實(shí)施過(guò)程的整體思路就是:?jiǎn)卧獪y(cè)試用例文檔 - 實(shí)施單元測(cè)試 - 實(shí)施業(yè)務(wù)代碼修改業(yè)務(wù)代碼邏輯。實(shí)施人員需要參與每個(gè)環(huán)節(jié),按照規(guī)范編寫(xiě)單元測(cè)試用例文檔。單元測(cè)試我們按照模塊(模塊與人員基本沒(méi)有重合)劃分包(suite),保證實(shí)施起來(lái)不會(huì)產(chǎn)生干擾。。技術(shù)架構(gòu)決定采用:maven,junit,svn。

     

    技術(shù)背景:

    技術(shù)架構(gòu)設(shè)計(jì)上,我們封裝了dao層的實(shí)現(xiàn),所以實(shí)施人員基本無(wú)需涉及dao層的開(kāi)發(fā)。服務(wù)層我們采用了JAX-RS的服務(wù)規(guī)范,對(duì)外開(kāi)發(fā)服務(wù)接口。

    在測(cè)試覆蓋率方面,我們基本不要求對(duì)dao層的單元測(cè)試,但要求在服務(wù)層的單元測(cè)試達(dá)到100%。由于服務(wù)層是Restful WS的模式,所以我們采用了模擬HTTP請(qǐng)求的方式在測(cè)試服務(wù)層。

    由于需要模擬HTTP的請(qǐng)求,所以我們?cè)趩卧獪y(cè)試中采用了jetty作為內(nèi)嵌服務(wù)器,單元測(cè)試開(kāi)始時(shí)同一啟動(dòng),完成后關(guān)閉。

     

    實(shí)施過(guò)程:

    開(kāi)發(fā)過(guò)程中,實(shí)際實(shí)施的時(shí)候發(fā)現(xiàn)一個(gè)問(wèn)題,對(duì)于測(cè)試數(shù)據(jù)的管理問(wèn)題。即測(cè)試當(dāng)中需要一定的數(shù)據(jù)環(huán)境來(lái)驗(yàn)證業(yè)務(wù)邏輯。這個(gè)數(shù)據(jù)環(huán)境如何建立?

     

    方案一,使用dbunithsqldb。在測(cè)試啟動(dòng)時(shí)重建數(shù)據(jù)環(huán)境。

    否決,原因:

    1.與實(shí)際運(yùn)行環(huán)境差異較大。

    2.反復(fù)重建數(shù)據(jù)環(huán)境,效率上有缺失。

    3.技術(shù)架構(gòu)增加,學(xué)習(xí)和維護(hù)曲線較大。

     

    討論后決定使用

    方案二,獨(dú)立出一套測(cè)試數(shù)據(jù)庫(kù),完整數(shù)據(jù)環(huán)境??紤]到增刪改與查詢的沖突,制定默認(rèn)規(guī)則,如id20之內(nèi)的不允許進(jìn)行任何改動(dòng)。以盡量隔離增刪改的影響。

     

    針對(duì)方案二,有一個(gè)較大的問(wèn)題,如何在開(kāi)發(fā)過(guò)程中自由的切換數(shù)據(jù)庫(kù)配置呢?由于我們還是用了Hudson作為CI服務(wù)器,還要考慮到打包的過(guò)程。整體考慮之后,有兩個(gè)步驟需要注意:

    一、開(kāi)發(fā)過(guò)程。開(kāi)發(fā)過(guò)程中,我們將配置直接指向測(cè)試數(shù)據(jù)庫(kù)。

    二、打包過(guò)程。使用了maven,存在單元測(cè)試配置與最終產(chǎn)品配置的沖突。

    所以最終問(wèn)題的焦點(diǎn)集中在打包過(guò)程的maven配置方案。

     

    搜索之后比較好的資料有

    MAVEN:如何為開(kāi)發(fā)和生產(chǎn)環(huán)境建立不同的配置文件 --我的簡(jiǎn)潔方案

    http://www.tkk7.com/scud/archive/2010/10/27/336326.html

    這篇博客是介紹在maven 中使用mvn package -P test 這樣的自定義profile來(lái)實(shí)現(xiàn)的。這樣是可行的,但是在Hudson中無(wú)法實(shí)現(xiàn)一條命令切換兩套配置。

    于是繼續(xù)尋找,最終在maven的官方網(wǎng)站找到《Building For Different Environments with Maven 2》(http://maven.apache.org/guides/mini/guide-building-for-different-environments.html)看完文章之后發(fā)現(xiàn),實(shí)際maven提供了一個(gè)非常好的插件maven-antrun-plugin,以實(shí)現(xiàn)某些ant的功能。此處還需要了解的知識(shí)就是maven的構(gòu)建生命周期標(biāo)準(zhǔn)(http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html)?;谏鲜鰞蓚€(gè)知識(shí)點(diǎn),我們制定出如下方案,在項(xiàng)目中建立測(cè)試配置目錄及產(chǎn)品配置目錄,在mavenpackage階段開(kāi)始前,都使用測(cè)試配置,運(yùn)行集成測(cè)試,完成在package階段前將產(chǎn)品配置覆蓋至打包文件夾內(nèi),然后進(jìn)行打包。思路就是這樣,下面貼出pom文件的關(guān)鍵部分。

     

     

    POM.xml

    <!—profile 節(jié)點(diǎn)定義覆蓋文件的方式內(nèi)容 -->

           <profiles>

                  <profile>

                         <id>product</id>

                         <build>

                                <plugins>

                                       <plugin>

                                              <artifactId>maven-antrun-plugin</artifactId>

                                              <executions>

                                                     <execution>

                                                            <id>pre_product</id>

                                                            <phase>prepare-package</phase>

                                                            <goals>

                                                                   <goal>run</goal>

                                                            </goals>

                                                            <configuration>

    <!—此處與ant的任務(wù)相似 -->

                                                                   <tasks>

                                                                          <delete file="${project.build.outputDirectory}/spring/dataSourceContext.xml" />

                                                                          <delete file="${project.build.outputDirectory}/log4j.properties" />

                                                                          <copy file="src/product/assembly/log4j.properties" tofile="${project.build.outputDirectory}/log4j.properties" />

                                                                          <copy file="src/product/assembly/spring/dataSourceContext.xml" tofile="${project.build.outputDirectory}/spring/dataSourceContext.xml" />

                                                                   </tasks>

                                                            </configuration>

                                                     </execution>

                                              </executions>

                                       </plugin>

                                </plugins>

                         </build>

                  </profile>

           </profiles>

     

    <!—構(gòu)建過(guò)程 -->

           <build>

    <!—指定資源目錄 -->

                  <resources>

                         <resource>

                                <directory>src/test/resources</directory>

                                <includes>

                                       <include>**/*</include>

                                </includes>

                         </resource>

                         <resource>

                                <directory>src/test/assembly</directory>

                                <includes>

                                       <include>**/*</include>

                                </includes>

                         </resource>

                  </resources>

     

                  <finalName>po</finalName>

     

    <!—指定集成測(cè)試配置 -->

                  <plugins>

                         <plugin>

                                <groupId>org.apache.maven.plugins</groupId>

                                <artifactId>maven-surefire-plugin</artifactId>

                                <version>2.4.3</version>

                                <configuration>

                                       <junitArtifactName>junit:junit</junitArtifactName>

                                       <forkMode>once</forkMode>

                                </configuration>

                                <executions>

                                       <execution>

                                              <id>default-test</id>

                                              <phase>integration-test</phase>

                                              <goals>

                                                     <goal>test</goal>

                                              </goals>

                                              <configuration>

                                                     <skip>false</skip>

                                                     <includes>

                                                            <include>**/*TestSuitex.java</include>

                                                     </includes>

                                              </configuration>

                                       </execution>

                                </executions>

                         </plugin>

                  </plugins>

           </build>

    posted @ 2012-04-05 11:26 一酌散千憂 閱讀(1756) | 評(píng)論 (0)編輯 收藏

         摘要: Nosql企業(yè)之道 http://www.infoq.com/articles/nosql-in-the-enterprise   介紹 作為一個(gè)企業(yè)架構(gòu)師的好處,就是我一直在找一些新的有希望的概念或想法,能夠幫助我的企業(yè)用戶處理不同垂直行業(yè)之間的問(wèn)題。甚至在NoSQL這個(gè)詞被杜撰(錯(cuò)誤的杜撰?此處作者認(rèn)為NoSQL這個(gè)詞并不恰當(dāng),后面會(huì)提到)出來(lái)之前,因?yàn)樯鲜龅脑蛭以掷m(xù)...  閱讀全文

    posted @ 2012-04-04 21:41 一酌散千憂 閱讀(378) | 評(píng)論 (0)編輯 收藏

    《Hadoop in action》Manning出版,磕磕絆絆總算是看完了。書(shū)的內(nèi)容就不做介紹,主要講一下實(shí)踐的過(guò)程。并且在實(shí)踐過(guò)程中參考的書(shū)籍的部分也會(huì)簡(jiǎn)單介紹。

    灰色背景部分為一些介紹,或過(guò)程中出現(xiàn)問(wèn)題的描述,可以直接忽略。

    由于公司的業(yè)務(wù)需要,要在網(wǎng)絡(luò)收集網(wǎng)頁(yè)之后對(duì)網(wǎng)頁(yè)進(jìn)行結(jié)構(gòu)化的解析,這個(gè)結(jié)構(gòu)化過(guò)程希望能夠基于HDFS并且使用MR算法實(shí)現(xiàn)。

    我虛擬了一個(gè)需求,針對(duì)http://hadoop.apache.org/common/releases.html 頁(yè)面,假設(shè)已經(jīng)下載了頁(yè)面并入庫(kù)。要求最終體現(xiàn)的數(shù)據(jù)是 “版本號(hào)+完整鏈接(即a標(biāo)簽全部?jī)?nèi)容)” 的結(jié)構(gòu)。

     

    偽分布式環(huán)境搭建在虛擬機(jī)上,操作系統(tǒng)是centos5.5,hadoop的版本是1.0.0.

     

    插入書(shū)中的一些環(huán)境搭建的介紹

    書(shū)中的2.1節(jié)中介紹了每個(gè)進(jìn)程的作用。2.2節(jié)中的ssh設(shè)置也比較重要,否則好像會(huì)一直提示你輸入密碼。2.3.2節(jié)介紹了偽分布式的配置方式,對(duì)core-site.xmlmapred-site.xml,hdfs-site.xml進(jìn)行配置之后,需要對(duì)namenode節(jié)點(diǎn)進(jìn)行格式化。

    bin/hadoop namenode –format

     

    hadoop的根目錄為/usr/local/hadoop-1.0.0,直接啟動(dòng)start-all.sh

     

    [root@localhost hadoop-1.0.0]# ./bin/start-all.sh

     

    啟動(dòng)成功后使用jps命令

    jdk小工具jps介紹 

    jps(Java Virtual Machine Process Status Tool)JDK 1.5提供的一個(gè)顯示當(dāng)前所有java進(jìn)程pid的命令,簡(jiǎn)單實(shí)用,非常適合在linux/unix平臺(tái)上簡(jiǎn)單察看當(dāng)前java進(jìn)程的一些簡(jiǎn)單情況。 jps存放在JAVA_HOME/bin/jps

     

    [root@localhost hadoop-1.0.0]# jps

    5694 SecondaryNameNode

    5461 NameNode

    5578 DataNode

    6027 Jps

    5784 JobTracker

    5905 TaskTracker

    這幾個(gè)進(jìn)程是非常重要的。很多時(shí)候出現(xiàn)意外就是因?yàn)槟稠?xiàng)服務(wù)未啟動(dòng)或異常。可以看到上面的命令上打印出日志位置。出現(xiàn)異常后可以在日志中查看詳細(xì)的堆棧信息。

     

    至此,hadoop已經(jīng)啟動(dòng),環(huán)境已經(jīng)準(zhǔn)備就緒。

     

    下面準(zhǔn)備我們的測(cè)試數(shù)據(jù),將目標(biāo)頁(yè)面的html保存為news.txt,偽分布式也同樣支持hdfs,所以我們使用 fs –put news.txt存入hdfs中。

    [root@localhost hadoop-1.0.0]# ./bin/hadoop fs -put /mnt/hgfs/shared/news.txt /user/root

    [root@localhost hadoop-1.0.0]# ./bin/hadoop fs -lsr /userdrwxr-xr-x   - root supergroup          0 2012-04-01 11:22 /user/root

    -rw-r--r--   1 root supergroup       3935 2012-04-01 11:22 /user/root/news.txt

     

    實(shí)現(xiàn)的代碼在eclipse中使用maven打包,上傳至虛擬機(jī)。

    文件名com.suntang.analyse.hadoop-0.0.1.jar
    使用hadoop的中的jar命令調(diào)用該jar文件。


    [root@localhost hadoop-1.0.0]# ./bin/hadoop jar com.suntang.analyse.hadoop-0.0.1.jar com.suntang.analyse.hadoop.AnalyseJob /user/root/news.txt output_root_test

    12/04/01 14:40:04 INFO input.FileInputFormat: Total input paths to process : 1

    12/04/01 14:40:05 INFO mapred.JobClient: Running job: job_201204011420_0001

    12/04/01 14:40:06 INFO mapred.JobClient:  map 0% reduce 0%

    12/04/01 14:40:19 INFO mapred.JobClient:  map 100% reduce 0%

    12/04/01 14:40:31 INFO mapred.JobClient:  map 100% reduce 100%

    12/04/01 14:40:37 INFO mapred.JobClient: Job complete: job_201204011420_0001

     

     

    此處注意我犯的一個(gè)錯(cuò)誤:

    [root@localhost hadoop-1.0.0]# ./bin/hadoop jar com.suntang.analyse.hadoop-0.0.1.jar AnalyseJob -libjars hadoop-core-1.0.0.jar /user/root/news.txt output_root_test

    Exception in thread "main" java.lang.ClassNotFoundException: AnalyseJob

    提示找不到類,因?yàn)槲彝藢?xiě)完整類名,命令應(yīng)該改為

    ./bin/hadoop jar com.suntang.analyse.hadoop-0.0.1.jar com.suntang.analyse.hadoop.AnalyseJob -libjars hadoop-core-1.0.0.jar /user/root/news.txt output_root_test 即可。

     

    此處運(yùn)行可能出現(xiàn)另外一個(gè)錯(cuò)誤。在命令行中出現(xiàn)

    12/04/01 14:01:38 INFO mapred.JobClient: Task Id : attempt_201204011356_0001_m_000001_0, Status : FAILED

    java.lang.Throwable: Child Error

            at org.apache.hadoop.mapred.TaskRunner.run(TaskRunner.java:271)

    Caused by: java.io.IOException: Creation of symlink from /mnt/hgfs/shared/hadoop-1.0.0/libexec/../logs/userlogs/job_201204011356_0001/attempt_201204011356_0001_m_000001_0 to 。。。

    就不打全了,重點(diǎn)在與

    Creation of symlink,看詳細(xì)日志中hadoop-root-tasktracker-localhost.localdomain.log中提示org.apache.hadoop.fs.FileUtil: Command 'ln -s ....': Operation not supported,即ln操作不支持。google可知這個(gè)是由于vm中的共享區(qū)域的問(wèn)題,解決方法就是將hadoop完全轉(zhuǎn)移至linux目錄中。本例中從/mnt/hgfs/shared/hadoop-1.0.0轉(zhuǎn)移至/usr/local/hadoop-1.0.0。

     

    執(zhí)行完成后可在hdfs中查看結(jié)果,查看目錄結(jié)構(gòu)為

    -rw-r--r--   1 root supergroup          0 2012-04-01 14:40 /user/root/output_root_test/_SUCCESS

    drwxr-xr-x   - root supergroup          0 2012-04-01 14:40 /user/root/output_root_test/_logs

    drwxr-xr-x   - root supergroup          0 2012-04-01 14:40 /user/root/output_root_test/_logs/history

    -rw-r--r--   1 root supergroup      13634 2012-04-01 14:40 /user/root/output_root_test/_logs/history/job_201204011420_0001_1333262405103_root_ccAnalyseJob

    -rw-r--r--   1 root supergroup      20478 2012-04-01 14:40 /user/root/output_root_test/_logs/history/job_201204011420_0001_conf.xml

    -rw-r--r--   1 root supergroup       3580 2012-04-01 14:40 /user/root/output_root_test/part-r-00000

     

    /user/root/output_root_test/part-r-00000即為最終結(jié)果文件。

     

     =======================================================================

    附加AnalyseJob代碼

    package com.suntang.analyse.hadoop;

     

    import java.io.IOException;

     

    import org.apache.hadoop.conf.Configuration;

    import org.apache.hadoop.conf.Configured;

    import org.apache.hadoop.fs.Path;

    import org.apache.hadoop.io.LongWritable;

    import org.apache.hadoop.io.Text;

    import org.apache.hadoop.mapreduce.Job;

    import org.apache.hadoop.mapreduce.Mapper;

    import org.apache.hadoop.mapreduce.Reducer;

    import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;

    import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;

    import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

    import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;

    import org.apache.hadoop.util.Tool;

    import org.apache.hadoop.util.ToolRunner;

     

    public class AnalyseJob extends Configured implements Tool {

     

           public static class MapClass extends Mapper<LongWritable, Text, Text, Text> {

     

                  @Override

                  protected void map(LongWritable key, Text value, Context context)

                                throws IOException, InterruptedException {

                        

                        

                         // TODO Auto-generated method stub

                         // super.map(key, value, context);

                         if (value.toString().matches("<a[^>]*>.*?release.*?</a>"))

                                context.write(

                                              new Text(value.toString().substring(

                                                            value.toString().indexOf("release") + 8,

                                                            value.toString().indexOf("available") - 1)),

                                              value);

                  }

     

           }

     

           public static class ReduceClass extends Reducer<Text, Text, Text, Text> {

     

                  @Override

                  protected void reduce(Text arg0, Iterable<Text> arg1, Context arg2)

                                throws IOException, InterruptedException {

                         // TODO Auto-generated method stub

                         // super.reduce(arg0, arg1, arg2);

                         arg2.write(arg0, arg1.iterator().next());

                  }

     

           }

     

           public int run(String[] args) throws Exception {

                  Configuration conf = getConf();

     

                  Job job = new Job(conf, "myAnalyseJob");

                  job.setJarByClass(getClass());

     

                  Path in = new Path(args[0]);

                  Path out = new Path(args[1]);

                  FileInputFormat.setInputPaths(job, in);

                  FileOutputFormat.setOutputPath(job, out);

     

                  job.setMapperClass(MapClass.class);

                  job.setReducerClass(ReduceClass.class);

     

                  job.setInputFormatClass(TextInputFormat.class);

                  job.setOutputFormatClass(TextOutputFormat.class);

                  job.setOutputKeyClass(Text.class);

                  job.setOutputValueClass(Text.class);

     

                  System.exit(job.waitForCompletion(true) ? 0 : 1);

     

                  return 0;

           }

     

           public static void main(String[] args) throws Exception {

                  int res = ToolRunner.run(new Configuration(), new AnalyseJob(), args);

                  System.exit(res);

           }

    }

    posted @ 2012-04-01 15:00 一酌散千憂 閱讀(922) | 評(píng)論 (0)編輯 收藏

    主站蜘蛛池模板: 亚洲国产婷婷综合在线精品| 亚洲大码熟女在线观看| 欧亚精品一区三区免费| 免费毛片在线播放| 国产又大又长又粗又硬的免费视频 | 亚洲国产成人久久一区WWW| 国产亚洲人成A在线V网站| 亚洲av鲁丝一区二区三区| 欧洲 亚洲 国产图片综合| 亚州**色毛片免费观看| 永久看日本大片免费35分钟| 免费在线观看黄网站| 亚洲狠狠ady亚洲精品大秀| 美女免费视频一区二区三区| 五月亭亭免费高清在线| 老司机亚洲精品影视www| A级毛片成人网站免费看| 午夜精品在线免费观看| 亚洲精品在线观看视频| 美女羞羞喷液视频免费| 亚洲乱码日产精品a级毛片久久| 国产特黄一级一片免费| 久久亚洲精品成人| 欧美好看的免费电影在线观看| 99亚偷拍自图区亚洲| 亚洲av高清在线观看一区二区| 亚洲日韩乱码中文字幕| 免费成人福利视频| 国产精品亚洲lv粉色| 成视频年人黄网站免费视频| 亚洲人成网站在线播放影院在线| xxxxwww免费| 羞羞视频免费网站入口| 亚洲AV无码久久| 免费无码又爽又刺激网站直播 | 中文文字幕文字幕亚洲色| 日本免费xxxx| 黄人成a动漫片免费网站| 亚洲AV成人片色在线观看高潮| 成在人线AV无码免费| 亚洲国产情侣一区二区三区|