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

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

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

    csusky

    常用鏈接

    統(tǒng)計(jì)

    最新評(píng)論

    2008年4月10日 #

    異步IO的關(guān)閉事件

    JAVA SOCKET只定義了四種事件

    public static final int OP_READ = 1 << 0;
    public static final int OP_WRITE = 1 << 2;
    public static final int OP_CONNECT = 1 << 3;
    public static final int OP_ACCEPT = 1 << 4;

    是沒有關(guān)閉事件的,我們?cè)趺磁袛嘁粋€(gè)連接是否關(guān)閉呢?
    如果你的selector注冊(cè)了一個(gè)OP_READ事件,那么在連接關(guān)閉的時(shí)候?qū)?huì)產(chǎn)生一個(gè)OP_READ事件
    也就是說本來阻塞的selector此時(shí)將會(huì)被喚醒,但是如果試圖在此事件的通道中讀取數(shù)據(jù)將會(huì)返回-1
    如下:

    Set<SelectionKey> readyKeys = selector.selectedKeys();

    = readyKeys.iterator()

    SelectionKey key 
    = (SelectionKey)i.next();

    if (operation == SelectionKey.OP_READ &&
                             key.isReadable())
                    
    {
                        ReadableByteChannel incomingChannel 
    = (ReadableByteChannel)key.channel(); 
    //此時(shí)將會(huì)得到-1,表明該鏈接已關(guān)閉
    int n = incomingChannel.read(readBuffer);
    }
    此時(shí)我們需要取消該KEY 如下:
    if (n == -1)
                
    {
                    key.cancel();
                      //關(guān)閉輸入輸出 
                      sc.socket().shutdownOutput();
                      sc.socket().shutdownInput();
                       //關(guān)閉SOCKET
                       sc.socket().close();
                      //關(guān)閉通道
                       incomingChannel.close();
                }

    posted @ 2009-11-10 22:28 曉宇 閱讀(425) | 評(píng)論 (1)編輯 收藏

    ExecutorFilter

    1 . 用Executors構(gòu)造一個(gè)新的線程池
    ExecutorService executor = Executors.newCachedThreadPool();

    方法 newCachedThreadPool();
    創(chuàng)建一個(gè)可根據(jù)需要?jiǎng)?chuàng)建新線程的線程池,但是在以前構(gòu)造的線程可用時(shí)將重用它們,并在需要時(shí)使用提供的 ThreadFactory 創(chuàng)建新線程。
    2. 用構(gòu)造的線程池創(chuàng)建ExecutorFilter
    ExecutorFilter es= new ExecutorFilter(executor));

    在ExecutorFilter內(nèi)部:
    只需要將相應(yīng)的事件分發(fā)到到線程池的相應(yīng)線程即可,但是SessionCreated事件只能在主線程中,不能分發(fā)
    觸發(fā)方法
    1 .
    首先構(gòu)造一個(gè)IoFilterEvent,這個(gè)IoFilterEvent包含1、事件的類型,2、下一個(gè)過濾器
    然后觸發(fā)該時(shí)間的處理方法。
     if (eventTypes.contains(IoEventType.SESSION_OPENED)) {
                fireEvent(
    new IoFilterEvent(nextFilter, IoEventType.SESSION_OPENED,
                        session, 
    null));
            }

    2 .
    從線程池中取出一個(gè)線程執(zhí)行事件處理
    protected void fireEvent(IoFilterEvent event) {
            getExecutor().execute(event);
        }


    在構(gòu)造ExecutorFilter 時(shí)如果沒有傳入IoEventType則默認(rèn)只對(duì)如下幾種幾件感興趣
    EXCEPTION_CAUGHT
    MESSAGE_RECEIVED
    MESSAGE_SENT
    SESSION_CLOSED
    SESSION_IDLE
    SESSION_OPENED
    當(dāng)然還需要覆蓋相應(yīng)的事件處理方法 如上所示

    posted @ 2008-12-12 11:33 曉宇 閱讀(1559) | 評(píng)論 (0)編輯 收藏

    ORACLE的塊大小

    參數(shù)db_block_size;
    這個(gè)參數(shù)只能設(shè)置成底層操作系統(tǒng)物理塊大小的整數(shù)倍,最好是2的n次方倍。
    如WINDOWS下4KB,8KB,16KB
    且該參數(shù)需要在建庫(kù)的時(shí)候指定,一旦指定不能更改。
    雖然在ORACLE9I以上可以指定表空間的數(shù)據(jù)庫(kù)大小,允許同時(shí)使用包括非默認(rèn)大小在內(nèi)的數(shù)據(jù)庫(kù)塊大小。不過需要設(shè)置指定大小數(shù)據(jù)塊的buffer_cache.

    小的塊:
    小的塊降低塊競(jìng)爭(zhēng),因?yàn)槊總€(gè)塊中的行較少.
    小的塊對(duì)于小的行有益.
    小的塊對(duì)于隨意的訪問較好.如果一個(gè)塊不太可能在讀入內(nèi)存后被修改,那么塊的大小越小使用buffer cache越有效率。當(dāng)內(nèi)存資源很珍貴時(shí)尤為重要,因?yàn)閿?shù)據(jù)庫(kù)的buffer cache是被限制大小的。
    劣勢(shì):
    小塊的管理消費(fèi)相對(duì)大.
    因?yàn)樾械拇笮∧憧赡苤辉趬K中存儲(chǔ)很小數(shù)目的行,這可能導(dǎo)致額外的I/O。
    小塊可能導(dǎo)致更多的索引塊被讀取

    大的塊
    好處:
    更少的管理消費(fèi)和更多存儲(chǔ)數(shù)據(jù)的空間.
    大塊對(duì)于有順序的讀取較好.  譬如說全表掃描
    大塊對(duì)很大的行較好
    大塊改進(jìn)了索引讀取的性能.大的塊可以在一個(gè)塊中容納更多的索引條目,降低了大的索引級(jí)的數(shù)量.越少的index level意味著在遍歷索引分支的時(shí)候越少的I/O。
    劣勢(shì):
    大塊不適合在OLTP中用作索引塊,因?yàn)樗鼈冊(cè)黾恿嗽谒饕~塊上的塊競(jìng)爭(zhēng)。
    如果你是隨意的訪問小的行并有大的塊,buffer cache就被浪費(fèi)了。例如,8 KB的block size 和50 byte row size,你浪費(fèi)了7,950



     

    posted @ 2008-11-25 15:45 曉宇 閱讀(1769) | 評(píng)論 (0)編輯 收藏

    TIPS

    將進(jìn)酒  杯莫停  -------> 亭名:  悲默亭

    全球通史

    《詩(shī)經(jīng)·采薇》

    昔我往矣,楊柳依依 今我來思,雨雪霏霏

    posted @ 2008-11-10 16:31 曉宇 閱讀(185) | 評(píng)論 (0)編輯 收藏

    SPRING整合IBMMQ實(shí)現(xiàn)全局事物

         摘要: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance...  閱讀全文

    posted @ 2008-10-27 17:01 曉宇 閱讀(2414) | 評(píng)論 (0)編輯 收藏

    Lucene的切詞 analysis包

    在搜索引擎中,切詞語(yǔ)是一個(gè)重要的部分,其中包括專有名詞的提取、詞的分割、詞的格式化等等。
    TokenStream 類幾乎是所有這些類的基類
    有兩個(gè)需要被子類實(shí)現(xiàn)的方法Token next() 和 close()
    首先來看analysis包,這個(gè)包主要是提供一些簡(jiǎn)單的詞匯化處理
    Tokenizer結(jié)尾的類是將要處理的字符串進(jìn)行分割成Token流,而根據(jù)分割的依據(jù)的又產(chǎn)生了以下幾個(gè)Tokenizer類
    首先Tokenizer類是所有以Tokenizer結(jié)尾的類的基
    然后是CharTokenizer,所有的以Tokenizer結(jié)尾的類都是從這個(gè)類繼承的
    這個(gè)類中有一個(gè)抽象方法
      protected abstract boolean isTokenChar(char c);
    另外一個(gè)需要被子類覆寫的方法
      protected char normalize(char c) {};
    是對(duì)單個(gè)字符進(jìn)行處理的方法譬如說將英文字母全部轉(zhuǎn)化為小寫

    還有一個(gè)變量
    protected Reader input;
    這個(gè)讀取器是這些類所處理的數(shù)據(jù)的   數(shù)據(jù)源
    輸入一個(gè)Reader ,產(chǎn)生一個(gè)Token流


    這個(gè)方法是是否進(jìn)行切分的依據(jù),依次讀取char流,然后用這個(gè)方法對(duì)每個(gè)char進(jìn)行檢測(cè),如果返回false則將預(yù)先存儲(chǔ)在
    詞匯緩沖區(qū)中的char數(shù)組作為一個(gè)Token返回
    LetterTokenizer :
          protected boolean isTokenChar(char c) {
                  return Character.isLetter(c);
          }
    WhitespaceTokenizer:
          protected boolean isTokenChar(char c) {
                  return !Character.isWhitespace(c);
          } 
    LowerCaseTokenizer extends LetterTokenizer:
    protected char normalize(char c) {
          return Character.toLowerCase(c);
       }

       在構(gòu)造函數(shù)中調(diào)用super(in);進(jìn)行和 LetterTokenizer同樣的操作,但是在詞匯化之前所有的詞都轉(zhuǎn)化為小寫了
     
    然后是以Filter結(jié)尾的類,這個(gè)類簇主要是對(duì)已經(jīng)詞匯化的Token流進(jìn)行進(jìn)一步的處理
     輸入是Token流 , 輸出仍然是Token流。
    TokenFilter extends TokenStream  是所有這些類的父類
    protected TokenStream input;
    在TokenFilter 中有一個(gè)TokenStream 變量,是Filter類簇處理的數(shù)據(jù)源,而Filter類簇又是繼承了TokenStream 類的
    有一個(gè)public final Token next()方法,這個(gè)方法以TokenStream.next()產(chǎn)生的Token流 為處理源,產(chǎn)生的仍然是Token流
    只不過中間有一些處理的過程
    LowerCaseFilter:將所有的Token流的轉(zhuǎn)化為小寫
         t.termText = t.termText.toLowerCase();
    StopFilter:過濾掉一些停止詞,這些停止詞由構(gòu)造函數(shù)指定
         for (Token token = input.next(); token != null; token = input.next())
          if (!stopWords.contains(token.termText))
            return token;


    比較一下Tokenizer類簇和Filter類簇,可以知道
    Tokenizer類簇主要是對(duì)輸入的Reader流,實(shí)際上是字符流按照一定的規(guī)則進(jìn)行分割,產(chǎn)生出Token流
    其輸入是字符串的Reader流形式,輸出是Token流

    Filter類簇主要是對(duì)輸入的Token流進(jìn)行更進(jìn)一步的處理,如去除停止詞,轉(zhuǎn)化為小寫
    主要為一些格式化操作。
    由于Filter類簇的輸入輸出相同,所以可以嵌套幾個(gè)不同的Filter類,以達(dá)到預(yù)期的處理目的。
    前一個(gè)Filter類的輸出作為后一個(gè)Filter類的輸入
    而Tokenizer類簇由于輸入輸出不同,所以不能嵌套







    posted @ 2008-05-30 14:47 曉宇 閱讀(1030) | 評(píng)論 (1)編輯 收藏

    JDK1.5的自動(dòng)裝箱功能

    在JAVA JDK1.5以后具有的自動(dòng)裝箱與拆箱的功能,所謂的自動(dòng)裝箱
    與拆箱也就是把基本的數(shù)據(jù)類型自動(dòng)的轉(zhuǎn)為封裝類型。

    如:自動(dòng)裝箱,它可以直接把基本類型賦值給封裝類型

    Integer num = 10 ;

    Double d = 2d ;

    自動(dòng)拆箱,它可以把封裝類型賦值給基本類型

    int num = new Integer(10);

    double d = new Double(2d);

    自動(dòng)裝箱與拆箱的功能事實(shí)上是編譯器來幫您的忙,編譯器在編譯時(shí)期依您所編寫的語(yǔ)法,決定是否進(jìn)行裝箱或拆箱動(dòng)作。在自動(dòng)裝箱時(shí)對(duì)于值從-128到127之間的值,它們被裝箱為Integer對(duì)象后,會(huì)存在內(nèi)存中被重用,所以范例4.6中使用==進(jìn)行比較時(shí),i1 與 i2實(shí)際上參考至同一個(gè)對(duì)象。如果超過了從-128到127之間的值,被裝箱后的Integer對(duì)象并不會(huì)被重用,即相當(dāng)于每次裝箱時(shí)都新建一個(gè)Integer對(duì)象,所以范例4.7使用==進(jìn)行比較時(shí),i1與i2參考的是不同的對(duì)象。所以不要過分依賴自動(dòng)裝箱與拆箱,您還是必須知道基本數(shù)據(jù)類型與對(duì)象的差異。

        public void testBoxingUnboxing() {

            int i = 10;

            Integer inta = i;

            inta++;

            inta += 1;

            int j = inta;

            assertTrue(j == inta);結(jié)果是:true//junit里面的方法

            assertTrue(j == new Integer(j)); 結(jié)果是:true

            assertTrue(10000 == new Integer(10000)); 結(jié)果是:true

        }

    Integer i = 100.相當(dāng)于編譯器自動(dòng)為您作以下的語(yǔ)法編譯:

    Integer i = new Integer(100).所以自動(dòng)裝箱與拆箱的功能是所謂的“編譯器蜜糖”(Compiler Sugar),雖然使用這個(gè)功能很方便,但在程序運(yùn)行階段您得了解Java的語(yǔ)義。例如下面的程序是可以通過編譯的:

    Integer i = null.int j = i.這樣的語(yǔ)法在編譯時(shí)期是合法的,但是在運(yùn)行時(shí)期會(huì)有錯(cuò)誤,因?yàn)檫@種寫法相當(dāng)于:

    Integer i = null.int j = i.intValue().null表示i沒有參考至任何的對(duì)象實(shí)體,它可以合法地指定給對(duì)象參考名稱。由于實(shí)際上i并沒有參考至任何的對(duì)象,所以也就不可能操作intValue()方法,這樣上面的寫法在運(yùn)行時(shí)會(huì)出現(xiàn)NullPointerException錯(cuò)誤。

    自動(dòng)裝箱、拆箱的功能提供了方便性,但隱藏了一些細(xì)節(jié),所以必須小心。再來看范例4.6,您認(rèn)為結(jié)果是什么呢?

    Ü. 范例4.6 AutoBoxDemo2.java

    public class AutoBoxDemo2 {

    public static void main(String[] args) {
    Integer i1 = 100;

    Integer i2 = 100;

    if (i1 == i2)

    System.out.println("i1 == i2");

    else

    System.out.println("i1 != i2").

    }

    }

    從自動(dòng)裝箱與拆箱的機(jī)制來看,可能會(huì)覺得結(jié)果是顯示i1 == i2,您是對(duì)的。那么范例4.7的這個(gè)程序,您覺得結(jié)果是什么?

    Ü. 范例4.7 AutoBoxDemo3.java

    public class AutoBoxDemo3 {

    public static void main(String[] args) {

    Integer i1 = 200;

    Integer i2 = 200;

    if (i1 == i2)

    System.out.println("i1 == i2");

    else

    System.out.println("i1 != i2");

    }

    }

    結(jié)果是顯示i1 != i2,這有些令人驚訝,兩個(gè)范例語(yǔ)法完全一樣,只不過改個(gè)數(shù)值而已,結(jié)果卻相反。

    其實(shí)這與==運(yùn)算符的比較有關(guān),在第3章中介紹過==是用來比較兩個(gè)基本數(shù)據(jù)類型的變量值是否相等,事實(shí)上==也用于判斷兩個(gè)對(duì)象引用名稱是否參考至同一個(gè)對(duì)象。

    在自動(dòng)裝箱時(shí)對(duì)于值從–128127之間的值,它們被裝箱為Integer對(duì)象后,會(huì)存在內(nèi)存中被重用,所以范例4.6中使用==進(jìn)行比較時(shí),i1 i2實(shí)際上參考至同一個(gè)對(duì)象。如果超過了從–128127之間的值,被裝箱后的Integer對(duì)象并不會(huì)被重用,即相當(dāng)于每次裝箱時(shí)都新建一個(gè)Integer對(duì)象,所以范例4.7使用==進(jìn)行比較時(shí),i1i2參考的是不同的對(duì)象。

    所以不要過分依賴自動(dòng)裝箱與拆箱,您還是必須知道基本數(shù)據(jù)類型與對(duì)象的差異。范例4.7最好還是依正規(guī)的方式來寫,而不是依賴編譯器蜜糖(Compiler Sugar)。例如范例4.7必須改寫為范例4.8才是正確的。

    Ü. 范例4.8 AutoBoxDemo4.java

    public class AutoBoxDemo4 {
    public static void main(String[] args) {

    Integer i1 = 200;

    Integer i2 = 200;

    if (i1.equals(i2))

    System.out.println("i1 == i2");

    else

    System.out.println("i1 != i2");

    }

    }

    結(jié)果這次是顯示i1 == i2使用這樣的寫法,相信也會(huì)比較放心一些,對(duì)于這些方便但隱藏細(xì)節(jié)的功能到底要不要用呢?基本上只有一個(gè)原則:如果您不確定就不要用。

    posted @ 2008-05-16 11:33 曉宇 閱讀(447) | 評(píng)論 (0)編輯 收藏

    關(guān)于IndexWriter中的3個(gè)性能參數(shù)

    IndexWriter中有3個(gè)重要的性能參數(shù)
    mergeFactor           默認(rèn)為10
    minMergeDocs      默認(rèn)為10
    maxMergeDocs     默認(rèn)為Integer.maxValue

    maxMergeDocs     一個(gè)段中所能包含的最大的doc數(shù),達(dá)到這個(gè)數(shù)目即不再將段進(jìn)行合并 一般不改變這個(gè)值
    minMergeDocs      是指在RAMDirectory中保存的Doc的個(gè)數(shù),達(dá)到minMergeDocs 個(gè)即要合并到硬盤上去(在硬盤上新建一個(gè)段)
    mergeFactor           合并因子,是控制硬盤上的段的合并的,每次在硬盤上新建一個(gè)段之后即執(zhí)行
                                     targetMergeDocs*=mergeFactor(一開始targetMergeDocs=minMergeDocs) 如果硬盤上的doc數(shù)目大于等于                            targetMergeDocs則將硬盤上最后建立的mergeFactor個(gè)段進(jìn)行合并成一個(gè)段

    拿默認(rèn)的參數(shù)舉例:
    如果硬盤上面已經(jīng)有9個(gè)段  每個(gè)段分別存儲(chǔ)了10個(gè)Document,共(90個(gè)DOC),這時(shí)候如果程序再向硬盤合并一個(gè)新的段(含10個(gè)DOC),合并完之后targetMergeDocs=10*10  程序檢查已經(jīng)合并的最后(按照創(chuàng)建的時(shí)間先后順序)mergeFactor個(gè)段的document的總和100是否大于等于targetMergeDocs(這里是100,剛好滿足要求)于是程序又將硬盤上面的后10個(gè)段合并為一個(gè)新的段。

    另外一個(gè)例子:
    doc數(shù)目            段數(shù)目
      1000---------------9個(gè)
      100-----------------9個(gè)
      10   ----------------9個(gè)
    這時(shí)如果再象硬盤中新建一個(gè)新的包含了10個(gè)doc的段
        doc數(shù)目            段數(shù)目
      (1) 1000----------------9個(gè)

      (2)  100-----------------9個(gè)

      (3)   10  ----------------9個(gè)
                                         
      (4)    10 ----------------1個(gè)
    這時(shí)候(3)(4)首先合并成一個(gè)新的段(3-4)包含100個(gè)doc
     然后(2)(3-4)和并成一個(gè)新段(2-3-4)包含1000個(gè)doc
    然后(1)(2-3-4)合并成一個(gè)新的段  包含10000個(gè)doc
    最后合并成一個(gè)段


    private final void maybeMergeSegments() throws IOException {
        
    long targetMergeDocs = minMergeDocs;
        
    while (targetMergeDocs <= maxMergeDocs) {
          
    // find segments smaller than current target size
          int minSegment = segmentInfos.size();
          
    int mergeDocs = 0;
          
    while (--minSegment >= 0{
            SegmentInfo si 
    = segmentInfos.info(minSegment);
            
    if (si.docCount >= targetMergeDocs)
              
    break;
            mergeDocs 
    += si.docCount;
          }


          
    if (mergeDocs >= targetMergeDocs)          // found a merge to do
            mergeSegments(minSegment+1);
          
    else
            
    break;

          targetMergeDocs 
    *= mergeFactor;        // increase target size
          System.out.println("- -- - -targetMergeDocs:"+targetMergeDocs);
          
    try {Thread.sleep(5000);} catch(Exception e) {};
        }

      }

    posted @ 2008-05-15 19:27 曉宇 閱讀(1431) | 評(píng)論 (0)編輯 收藏

    HIBERNATE的一對(duì)多和多對(duì)一關(guān)聯(lián)

    HIBERNATE一多對(duì)關(guān)聯(lián)中  要求在持久化類中定義集合類屬性時(shí),必須把屬性聲明為接口,因?yàn)镠IBERNATE在調(diào)用持久化類的SET/GET方法時(shí)傳遞的是HIBERNATE自己定義的集合類。
    在定義集合時(shí),一般先初始化為集合實(shí)現(xiàn)類的一個(gè)實(shí)例 : private Set orders=new HashSet(),這樣可以避免訪問空集合出現(xiàn)NullPointerException.

    posted @ 2008-05-14 11:01 曉宇 閱讀(238) | 評(píng)論 (0)編輯 收藏

    Lucene索引文件的格式

    segments文件的格式: (段的信息)
    int:  =-1    查看文件是否是Lucene合法的文件格式
    long:        版本號(hào),每更新一次該文件將會(huì)將版本號(hào)加1
    int:         用來命名新段
    int:         段的數(shù)目
    String + int 段的信息 String是段的名稱  int是段中所含的doc數(shù)目
    String + int 同上


    .fnm的文件格式:   (Field的信息)
    int:               Field的個(gè)數(shù),最少為1,最少有一個(gè)Field("",false),在初始化的時(shí)候?qū)懭?暫時(shí)不知道原因); 名稱為空字符串,未索引,        未               向           量化。readVInt()讀取
    String: byte      String是 Field的名稱  byte指示該Field 是否被索引,是否向量化 (值有:11,10,01)第一個(gè)1代表被索引,第二個(gè)代表被向量化
    String: byte Field 同上
         

     

    .fdx的文件格式:主要是提供對(duì).fdt中存儲(chǔ)的document的隨即讀取
    long :       第一個(gè)document在.fdt文件中的位置
    long:        第二個(gè)document在.fdt文件中的位置


    .fdt的文件格式:  .fdt文件存儲(chǔ)了一系列document的信息
    VInt:        該document中的isStored屬性為true的域的個(gè)數(shù)
    (VInt:)      如果該field的isStored屬性為true則得到該field的fieldNumber,暫時(shí)不知道這個(gè)fieldNumber是怎么產(chǎn)生的,有什么用,初步估計(jì)是按照field創(chuàng)建的順序產(chǎn)生的,每次再上一個(gè)field的fieldNumber基礎(chǔ)上加1。
    byte:        如果該field的isTokenized屬性為true寫入1否則寫入false。
    String:      該field的stringValue()值。
    一個(gè)document結(jié)束,下面的數(shù)據(jù)將會(huì)開始一個(gè)新的document,每個(gè)新的document的開始點(diǎn)的文件位置都會(huì)在.fdx中有記載,便于隨即訪問

     

    posted @ 2008-04-21 17:52 曉宇 閱讀(483) | 評(píng)論 (0)編輯 收藏

    org.apache.lucene.index.SegmentInfos

    final class SegmentInfos extends Vector
    可以看出該類實(shí)際上是一個(gè)Vector   以及封裝了對(duì)該Vevtor的一些操作
    實(shí)際上封裝的是對(duì)segments文件的一些讀寫操作
    先來看下segments文件的格式

    segments文件的格式:
    int:  =-1         文件是否是Lucene合法的文件格式正常情況下為 -1
    long:             版本號(hào),每更新一次該文件將會(huì)將版本號(hào)加1
    int:                用來命名新段
    int:                段的數(shù)目
    String + int  段的信息 String是段的名稱  int是段中所含的doc數(shù)目
    String + int  同上

    所以用Lucene的API,我們可以簡(jiǎn)單的打印出其segments的所有信息

    try {
       //DataInputStream fis = new DataInputStream(new FileInputStream("C:\\sf\\snow\\segments"));
       FSDirectory dir=FSDirectory.getDirectory("C:/sf/snow", false);
        InputStream input = dir.openFile("segments");
       System.out.println("Format:"+input.readInt());             //得到文件標(biāo)志,是否為正常的segments文件
       System.out.println("version:"+input.readLong());        //得到版本號(hào)
       System.out.println("name:"+input.readInt());                //得到用來重命名新段的int,暫時(shí)不知道有什么用
       int n=input.readInt();                                                          //段的數(shù)目
       System.out.println("SegmentNum:"+n);                          
       for(int i=0;i<n;i++) {                                                           //用循環(huán)打印出所有段的信息 名稱和長(zhǎng)度
        System.out.println("segment "+i+" - name:"+input.readString()+" num:"+input.readInt());
       }
      } catch (Exception e) {

      }
    當(dāng)然,該類提供了更為復(fù)雜的訪問和更新segments文件的方法
     final void read(Directory directory)    將所有的段信息保存在本vector中
    final void write(Directory directory)    跟新該segment文件的內(nèi)容,主要是為了添加段,
    主要是更新 版本號(hào) 段的數(shù)目,跟新完這些后即可往segment文件后添加新段的信息。

    posted @ 2008-04-18 17:02 曉宇 閱讀(365) | 評(píng)論 (0)編輯 收藏

    org.apache.lucene.index.SegmentInfo

    segment(段)的信息
    該類比較簡(jiǎn)單,貼出其全部代碼

    import org.apache.lucene.store.Directory;

    final class SegmentInfo {
      public String name;        //在索引目錄中唯一的名稱 
      public int docCount;      // 該段中doc的數(shù)目
      public Directory dir;      // 該段所存在的Dirrectory

      public SegmentInfo(String name, int docCount, Directory dir) {
        this.name = name;
        this.docCount = docCount;
        this.dir = dir;
      }
    }

    posted @ 2008-04-18 16:45 曉宇 閱讀(265) | 評(píng)論 (0)編輯 收藏

    org.apache.lucene.store.RAMInputStream

    該類是從RAMFile中讀數(shù)據(jù)用的
    最重要的一個(gè)方法:
    該方法存在著從RAMFile的多個(gè)byte[1024]中讀取數(shù)據(jù)的情況,所以應(yīng)該在循環(huán)中進(jìn)行處理

     public void readInternal(byte[] dest, int destOffset, int len) {
        int remainder = len;
        int start = pointer;
        while (remainder != 0) {
          int bufferNumber = start/BUFFER_SIZE; //  buffer的序號(hào)
          int bufferOffset = start%BUFFER_SIZE; //    buffer偏移量
          int bytesInBuffer = BUFFER_SIZE - bufferOffset;// 在當(dāng)前buffer中剩下的字節(jié)數(shù)
          //如果緩沖區(qū)中剩余的字節(jié)大于len,則讀出len長(zhǎng)度的字節(jié),如果不夠則讀出剩余的字節(jié)數(shù)
          // bytesToCopy表示實(shí)際讀出的字節(jié)數(shù)
          int bytesToCopy = bytesInBuffer >= remainder ? remainder : bytesInBuffer;
          byte[] buffer = (byte[])file.buffers.elementAt(bufferNumber);
          System.arraycopy(buffer, bufferOffset, dest, destOffset, bytesToCopy);
          destOffset += bytesToCopy;       //增加已經(jīng)復(fù)制的byte數(shù)據(jù)長(zhǎng)度 到  dest中的偏移量
          start += bytesToCopy;                 //RAMFile文件指針,用來確定bufferNumber 和bytesInBuffer   相當(dāng)于內(nèi)存中的分頁(yè)
          remainder -= bytesToCopy;       //剩余的還未復(fù)制的字節(jié)數(shù)
        }
        pointer += len;//文件指針位置
      }

    posted @ 2008-04-18 11:45 曉宇 閱讀(225) | 評(píng)論 (0)編輯 收藏

    org.apache.lucene.store.RAMOutputStream

    這是OutputStream的一個(gè)子類,其輸出設(shè)備是內(nèi)存,準(zhǔn)確來說是RAMFile,即將數(shù)據(jù)寫入到RAMFile的Vector中去。
    該類有一個(gè)最重要的方法,現(xiàn)在把它整個(gè)貼出來

    public void flushBuffer(byte[] src, int len) {
        int bufferNumber = pointer/BUFFER_SIZE;   //buffer序列,即當(dāng)前所寫B(tài)uffer在RAMFile中的Vector中的序列號(hào)
        int bufferOffset = pointer%BUFFER_SIZE;   //偏移量,即當(dāng)前所寫字節(jié)在當(dāng)前Buffer中的偏移量。
        int bytesInBuffer = BUFFER_SIZE - bufferOffset; //當(dāng)前Buffer的剩余可寫字節(jié)數(shù)
       //bytesToCopy是實(shí)際寫入的字節(jié)數(shù),如果當(dāng)前Bufer的剩余字節(jié)數(shù)大于需要寫的字節(jié)的總數(shù)則寫入所有字節(jié)
       //否則,將當(dāng)前Buffer寫滿即可,剩余的字節(jié)將寫入下一個(gè)Buffer
        int bytesToCopy = bytesInBuffer >= len ? len : bytesInBuffer;

        if (bufferNumber == file.buffers.size())
          file.buffers.addElement(new byte[BUFFER_SIZE]); //在RAMFile中添加新的byte[1024]元素

        byte[] buffer = (byte[])file.buffers.elementAt(bufferNumber);
        System.arraycopy(src, 0, buffer, bufferOffset, bytesToCopy);

        if (bytesToCopy < len) {     // not all in one buffer,
          int srcOffset = bytesToCopy;
          bytesToCopy = len - bytesToCopy;    // remaining bytes 剩余的未寫入的字節(jié)數(shù)
          bufferNumber++;                         //將buffer數(shù)增加1
          if (bufferNumber == file.buffers.size()) 
            file.buffers.addElement(new byte[BUFFER_SIZE]);
          buffer = (byte[])file.buffers.elementAt(bufferNumber); //剩余字節(jié)寫入下一個(gè)Buffer
          System.arraycopy(src, srcOffset, buffer, 0, bytesToCopy);
        }
        pointer += len;
        if (pointer > file.length)
          file.length = pointer;        //移位文件指針          在原有的基礎(chǔ)上加上實(shí)際寫入的字節(jié)總數(shù)

        file.lastModified = System.currentTimeMillis(); //修改文件的最后修改時(shí)間為當(dāng)前時(shí)間
      }

    從指定的字節(jié)數(shù)組復(fù)制指定長(zhǎng)度的字節(jié)到RAMFile中去。由于RAMFile中Vector的元素是byte[1024]所以可能存在做一次該操作
    要操作兩個(gè)Vector元素的情況。即先將當(dāng)前byte[1024]數(shù)組填滿,再新建一個(gè)元素裝載剩余的字節(jié)。

    另外還有一個(gè)writeTo(OutputStream out)方法,將RAMFile中的數(shù)據(jù)輸出到另一個(gè)輸出流



    posted @ 2008-04-18 11:38 曉宇 閱讀(231) | 評(píng)論 (0)編輯 收藏

    org.apache.lucene.store.RAMFile

    這個(gè)類比較簡(jiǎn)單
    import java.util.Vector;
    class RAMFile {
      Vector buffers = new Vector();
      long length;
      long lastModified = System.currentTimeMillis();
    }

    可以理解為一個(gè)存儲(chǔ)在內(nèi)存中的文件,buffers是存儲(chǔ)數(shù)據(jù)的容器,length是容器中數(shù)據(jù)的總的字節(jié)數(shù)
    lastModified 是最后修改時(shí)間。

    在實(shí)際使用過程中容器buffers存放的對(duì)象是一個(gè)byte[1024]數(shù)組。

    posted @ 2008-04-18 11:23 曉宇 閱讀(289) | 評(píng)論 (0)編輯 收藏

    org.apache.lucene.store.OutputStream

    OutputStream
    這是一個(gè)Abstract類,是Lucene自己的一個(gè)文件輸出流的基類
    BUFFER_SIZE = 1024  緩沖區(qū) 大小為 1024bit
    bufferStart = 0 文件位置指針
    bufferPosition = 0 內(nèi)存緩沖區(qū)指針

     public final void writeByte(byte b) throws IOException {
        if (bufferPosition >= BUFFER_SIZE)
          flush();
        buffer[bufferPosition++] = b;
      }
    幾乎所有的寫入函數(shù)都要調(diào)用這個(gè)函數(shù),如果緩沖區(qū)的當(dāng)前容量已經(jīng)等于他的最大容量,則將緩沖區(qū)中的數(shù)據(jù)寫入文件。

    public final void writeBytes(byte[] b, int length) throws IOException
    批量寫byte進(jìn)入內(nèi)存緩沖

    public final void writeInt(int i) throws IOException
    寫入整形數(shù)據(jù)

    public final void writeLong(long i) throws IOException
    寫入長(zhǎng)整型數(shù)據(jù),即結(jié)合移位運(yùn)算調(diào)用兩次writeInt(int i)

    另外,最值得注意的是在該類中有兩個(gè)最特殊的函數(shù)
    writeVInt(int i) /   writeVLong(long i),
    先說
    writeVInt(int  i )   {
     while ((i & ~0x7F) != 0) {                               
          writeByte((byte)((i & 0x7f) | 0x80));
          i >>>= 7;
        }
        writeByte((byte)i);
    }
    ~0x7F==~(0111 1111)==(1000 0000)==0x80
    ((i & ~0x7F) != 0) 這一句判斷i是否大于0x80,如果不是則說明該int只有一個(gè)字節(jié)的有效數(shù)據(jù),其他字節(jié)都是0,直接轉(zhuǎn)化為Byte寫入。
    如果大于0x80則
    (i & 0x7f) | 0x80
    i&0x7f 只對(duì)后7位進(jìn)行處理,|0x80將第8位置1,與前面的7個(gè)bit構(gòu)成一個(gè)字節(jié),置1的原因是說明該字節(jié)并不是一個(gè)完整的整形數(shù),需要與其他的字節(jié)合起來才能構(gòu)成一個(gè)整形數(shù)字。
    這個(gè)算法相當(dāng)于將一個(gè)32bit的整形數(shù)字按照每7位編碼成一個(gè)字節(jié)進(jìn)行存儲(chǔ),將按照整形數(shù)的大小存儲(chǔ)1-5個(gè)字節(jié)。

    writeVLong(long i)方法大致與其相同。

    final void writeChars(String s, int start, int length)
    將字符串轉(zhuǎn)化成UTF-8編碼的格式進(jìn)行存儲(chǔ)。
    附:

    UNICODE值 UTF-8編碼
    U-00000000 - U-0000007F: 0xxxxxxx
    U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
    U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx

    可見對(duì)于在 0x00-0x7F范圍內(nèi)的UNICODE值(最大有效數(shù)位:7位),將會(huì)編碼成單字節(jié)的,會(huì)大大節(jié)約存儲(chǔ)空間。
    對(duì)于在           0x80-0x7FF范圍內(nèi)的UNICODE(最大有效數(shù)位:11位),會(huì)編碼成雙字節(jié)的。先存儲(chǔ)原字節(jié)低5位的數(shù)位,且將最高位和次高位都置1,再次高位置0(writeByte((byte)(0xC0 | (code >> 6)));)。然后存儲(chǔ)后6位的字節(jié),將前兩位置10(writeByte((byte)(0x80 | (code & 0x3F)));)
    對(duì)于其他的UNICODE值則
    writeByte((byte)(0xE0 | (code >>> 12)));               4位
     writeByte((byte)(0x80 | ((code >> 6) & 0x3F)));   5位
     writeByte((byte)(0x80 | (code & 0x3F)));            3- 5位

    final void writeString(String s) throws IOException
    該函數(shù)首先用s.length()判斷該String總共有多少個(gè)字符
    然后首先調(diào)用writeVInt寫入這個(gè)字符長(zhǎng)度
    再調(diào)用writeChars(s,s.length())寫入字符


    在inputStream中的readString()方法則與其相反,首先用readVInt()方法讀取字符長(zhǎng)度len 然后讀取len長(zhǎng)度的字符

    protected final void flush() throws IOException
    該方法調(diào)用另外一個(gè)方法flushBuffer將緩沖區(qū)中的數(shù)據(jù)輸出,然后清空緩沖區(qū);

    abstract void flushBuffer(byte[] b, int len) throws IOException
    可見flushBuffer方法是abstract的,即需要其子類對(duì)該方法進(jìn)行覆寫,以定位該輸出流的輸出方式。

    final long getFilePointer() throws IOException
    得到文件指針的位置,即得到輸出流已經(jīng)輸出的字節(jié)數(shù)。

    public void seek(long pos) throws IOException
    輸出緩沖區(qū)的內(nèi)容,然后將文件指針定位到long所指示的文件位置。

    abstract long length() throws IOException
    返回文件中已有的字節(jié)數(shù)。需要子類實(shí)現(xiàn)。

















    posted @ 2008-04-16 21:24 曉宇 閱讀(219) | 評(píng)論 (0)編輯 收藏

    org.apache.lucene.store.FSDirectory

    FSDirectory繼承了abstract類Directory
    在該類中既有該類的一些初始化操作,又有對(duì)FSDirectory對(duì)象本身的一些操作,這是為什么把其構(gòu)造函數(shù)設(shè)置為私有的一部分原因

    static final Hashtable DIRECTORIES = new Hashtable();
    每新建一個(gè)FSDirectory都會(huì)將其加入到該Hashtable中來。名稱是FSDirectory對(duì)應(yīng)的File   值是該FSDirectory。
    注意:final對(duì)象并非是不可更改的

    static final String LOCK_DIR =
        System.getProperty("org.apache.lucene.lockdir",
          System.getProperty("java.io.tmpdir"));
    首先看用戶是否注冊(cè)了"org.apache.lucene.lockdir"屬性,如果沒有則用JAVA虛擬機(jī)固有的"java.io.tmpdir"屬性
    這個(gè)屬性是一個(gè)路徑,代表lucene的鎖文件鎖放的位置。

    static final boolean DISABLE_LOCKS =
          Boolean.getBoolean("disableLuceneLocks") || Constants.JAVA_1_1;
    如果用戶注冊(cè)了"disableLuceneLocks"屬性且為false,或者JAVA的版本是1.1則無法使用鎖。

    static FSDirectory getDirectory(String path, boolean create)
    static FSDirectory getDirectory(File file, boolean create)
    從得到一個(gè)指定路徑或者文件的FSDirectory如果在則取出,如果不存在則用其私有的構(gòu)造函數(shù)構(gòu)造一個(gè)
    該類還有3個(gè)非static的類變量
      private File directory = null;      索引目錄
      private int refCount;                   鎖目錄
      private File lockDir;                    索引目錄數(shù)目
    實(shí)際上,初始化一個(gè)FSDirectory只需要初始化這3個(gè)變量即可
    如果create的值為true 則:如果索引目錄是已經(jīng)存在的目錄,則會(huì)遍歷該目錄然后刪除每一個(gè)文件,如果鎖目錄是已存在的也會(huì)用list返回所有的文件然后調(diào)用file.delete() 刪除。 如果目錄不存在則創(chuàng)建一個(gè)新的。

    注意:list()方法   會(huì)先用文件名進(jìn)行排序然后返回(a.txt會(huì)比b.txt先返回)    且delete方法刪除文件夾時(shí),只能刪除空文件夾。如果失敗則跳出程序,不會(huì)刪除在該文件夾之后返回的文件。(如果有aa.txt , ab/b.txt , b.txt , 則刪除時(shí)候由于a文件夾非空刪除失敗,則b.txt由于前面刪除失敗跳出程序,也不會(huì)被刪除,但是aa.txt被正常刪除)

    private FSDirectory(File path, boolean create) throws IOException
    私有的構(gòu)造函數(shù)

    private synchronized void create() throws IOException
    創(chuàng)建新的directory /lockDir目錄,當(dāng)目錄已存在時(shí)即清空該目錄,不存在即創(chuàng)建新的目錄。

    final String[] list() throws IOException
    以字符串文件名的形式返回索引目錄的所有文件

    final boolean fileExists(String name) throws IOException
    在索引目錄是否存在指定文件名的文件

    final long fileModified(String name) throws IOException
    static final long fileModified(File directory, String name)
    返回該文件的最后修改時(shí)間,directory參數(shù)為相對(duì)路徑,第一個(gè)函數(shù)的相對(duì)路徑為索引目錄

    void touchFile(String name) throws IOException
    將該文件的最后修改時(shí)間設(shè)置為當(dāng)前時(shí)間

    final long fileLength(String name) throws IOException
    返回該文件的長(zhǎng)度

    final void deleteFile(String name) throws IOException
    刪除該文件

    final synchronized void renameFile(String from, String to) throws IOException
    重命名該文件
    該方法會(huì)首先檢測(cè)新的文件名命名的文件是否已經(jīng)存在如果存在即刪除該文件,然后再將文件重新命名為新的文件名。
    doug cutting在該方法的注釋上寫到:
    1.刪除操作和重命名的操作不是原子的。
    2.重命名操作在有些虛擬機(jī)上面不能正確的工作,如果重命名失敗則會(huì)采用手動(dòng)copy的方法。使用輸入輸出流將舊的文件的內(nèi)容寫入到新的文件中去,然后刪除舊的文件。
    注意:該方法必須是同步的。

    final OutputStream createFile(String name) throws IOException
    用指定的文件名創(chuàng)建一個(gè)新的可寫的空文件  實(shí)際上返回的是FSOutputStream,注意這里的OutputStream并不是java的基礎(chǔ)類。而是doug cutting自己寫的一個(gè)文件隨即訪問類。同理FSInputStream和InputStream也是Lucene自己的類。

    final InputStream openFile(String name) throws IOException
    從一個(gè)存在的文件打開一個(gè)輸入流

    getLockPrefix()
    在FSDirectory中還有
     private static MessageDigest DIGESTER;這個(gè)靜態(tài)變量是提供加密功能的
    DIGESTER=MessageDigest.getInstance("MD5"),-----MD5加密算法
    或者可以DIGESTER=MessageDigest.getInstance("SHA"),-----SHA加密算法
    用于對(duì)鎖目錄的   文件名的加密
    用法如下:
    digest = DIGESTER.digest(dirName.getBytes());  dirName是需要被加密的字符串,這里是索引文件的目錄名,
    在FSContext中,其應(yīng)用在 getLockPrefix() 該方法是為某個(gè)索引目錄創(chuàng)建其對(duì)應(yīng)的鎖目錄文件名。
    首先返回經(jīng)過加密后的byte[] 數(shù)組digest,然后將digest按照每4個(gè)bit轉(zhuǎn)化為一個(gè)16進(jìn)制的字符,存進(jìn)一個(gè)StringBuffer中
    其轉(zhuǎn)化類似與Base64編碼方式,不過要簡(jiǎn)單得多。

    方法
    Lockl  makeLock(String name)
    是從Directory中擴(kuò)展而來的,該方法返回一個(gè)Lock對(duì)象,該對(duì)象將會(huì)在介紹完Lucene的輸入輸出流之后介紹。
    該方法比較簡(jiǎn)單,首先是調(diào)用了getLockPrefix() 方法,返回文件鎖的部分對(duì)象名,然后在該名稱后面加上鎖的特征名
    譬如說讀寫鎖 事務(wù)鎖
    其名稱類似于下:
    lucene-12c90c2c381bc7acbc4846b4ce97593b-write.lock
    lucene-12c90c2c381bc7acbc4846b4ce97593b-commit.lock
    這兩種鎖機(jī)制將會(huì)在后面介紹
    最后通過一個(gè)匿名的內(nèi)部類返回一個(gè)經(jīng)過重載的Lock對(duì)象,該內(nèi)部類中的方法有鎖的創(chuàng)建,得到,釋放,以及檢測(cè),另外還有一個(gè)toString()方法返回鎖的名稱。



    在FSDirectory類中有OutputStream和InputStream的實(shí)現(xiàn)類,這兩個(gè)虛類只是定義了一些操作,并沒有定義輸入或者輸出的設(shè)備。
    Lucene在輸入輸出的設(shè)計(jì)上,將會(huì)由子類定義輸入輸出的設(shè)備。
    FSOutputStream
    在FSOutputStream中有一個(gè) RandomAccess File=new RandomAccessFile(path, "rw");
    在對(duì)該輸出流的操作將用調(diào)用該file的相應(yīng)方法實(shí)現(xiàn)
    最重要的
      public final void flushBuffer(byte[] b, int size) throws IOException {
        file.write(b, 0, size);
      }
    flushBuffer的調(diào)用將會(huì)將byte中的0--size范圍的數(shù)據(jù)寫入到文件path中去。


    FSInputStream
    最重要的
    protected final void readInternal(byte[] b, int offset, int len)
           throws IOException {
        synchronized (file) {
          long position = getFilePointer();     //得到該當(dāng)前文件指針
          if (position != file.position) {
            file.seek(position);
            file.position = position;
          }
          int total = 0;
          do {

       //從文件中讀取指定長(zhǎng)度的字節(jié)到字節(jié)數(shù)組
       // 在其基類InputStream中的refill()方法  將會(huì)調(diào)用  readInternal(buffer, 0, bufferLength);首先從文件中讀取字節(jié)到緩沖數(shù)組。
      //  在InputStream中每次讀取操作都會(huì)調(diào)用readInternal方法,或者通過refill()方法間接調(diào)用該方法。
            int i = file.read(b, offset+total, len-total);       //將文件中的數(shù)據(jù)讀到緩沖區(qū)
            if (i == -1)
              throw new IOException("read past EOF");
            file.position += i;
            total += i;
          } while (total < len);
        }
      }





















    posted @ 2008-04-10 21:35 曉宇 閱讀(1098) | 評(píng)論 (0)編輯 收藏

    org.apache.lucene.document.DateField

    該類提供了日期和字符串之間的相互轉(zhuǎn)化,實(shí)際上是 long型和String型的相互轉(zhuǎn)化,轉(zhuǎn)化時(shí)用到了一個(gè)不常用的
    Long.toString(long,int);方法。是按指定的方式對(duì)long型進(jìn)行轉(zhuǎn)化
    第一個(gè)參數(shù)是要轉(zhuǎn)化的long,第二個(gè)參數(shù)是轉(zhuǎn)化時(shí)候的基數(shù),如果基數(shù)是10就相當(dāng)于方法Long.toString(long);
    這里使用的參數(shù)是最大值,即36== 10個(gè)數(shù)字+26個(gè)英文字母。這樣轉(zhuǎn)化出來的字符串長(zhǎng)度比較短,占用比較少的空間,
    另外,在轉(zhuǎn)化時(shí),統(tǒng)一了轉(zhuǎn)化后的字符串長(zhǎng)度,如果不足9位(日期的long轉(zhuǎn)化后最高為9位,1970之后的日期可正確轉(zhuǎn)換),
    統(tǒng)一長(zhǎng)度后的字符串可以通過比較字符串來比較日期的大小。


    日期轉(zhuǎn)化成的字符串類似于
    0fev8eza3
    本來應(yīng)該是fev8eza3 采取了不足9位補(bǔ)0的方法。

      private static int DATE_LEN = Long.toString(1000L*365*24*60*60*1000,
                Character.MAX_RADIX).length();
    計(jì)算出從1970年開始后1000年的時(shí)間轉(zhuǎn)化為字符串后的長(zhǎng)度,所有轉(zhuǎn)化后的時(shí)間都不應(yīng)超過這個(gè)長(zhǎng)度,如果不足則在前面補(bǔ)0

    可以通過字符串轉(zhuǎn)化為日期的函數(shù)計(jì)算出能表示的最大日期為
    stringToTime("zzzzzzzzz");
    打印出來是 Fri Apr 22 19:04:28 CST 5188  
    所以該函數(shù)能轉(zhuǎn)化的日期范圍為 1970-1-1~~5188-4-22


    日期轉(zhuǎn)化為字符串
    public static String timeToString(long time)

    字符串轉(zhuǎn)化為日期
    public static long stringToTime(String s)


    實(shí)際上 函數(shù) LongToString(long i,int radix) 相當(dāng)于  先將i轉(zhuǎn)化為radix進(jìn)制的整數(shù),然后再用函數(shù)
    LongToString(i)轉(zhuǎn)化為字符串。所以radix的值應(yīng)該在2--36之間如果不是 則按照10進(jìn)制計(jì)算。

    posted @ 2008-04-10 19:26 曉宇 閱讀(342) | 評(píng)論 (0)編輯 收藏

    主站蜘蛛池模板: 美女被免费网站在线视频免费| 亚洲精品第一国产综合野| 久久久久无码精品亚洲日韩| 亚洲国产国产综合一区首页| 亚洲国产成人精品青青草原| 国产精品亚洲AV三区| 在线观看免费视频一区| 91免费国产在线观看| 免费亚洲视频在线观看| 国产亚洲美女精品久久久久狼| 亚洲高清免费在线观看| 亚洲日韩AV一区二区三区中文| 一级做a爱过程免费视频高清| 日韩精品内射视频免费观看| 成人毛片视频免费网站观看| 亚洲综合区小说区激情区| 亚洲高清无在码在线无弹窗| 在线观看国产一区亚洲bd| 久久久精品免费国产四虎| 天天操夜夜操免费视频| 亚洲精品白浆高清久久久久久| 亚洲视频无码高清在线| a一级爱做片免费| 在线a级毛片免费视频| 丁香五月亚洲综合深深爱| 亚洲av无码片在线观看| 在线观看片免费人成视频播放| 成年女人视频网站免费m| 亚洲日韩中文无码久久| 亚洲最大的成人网站| 亚洲?V无码成人精品区日韩| 五月天网站亚洲小说| 国产成人亚洲精品播放器下载 | 亚洲网红精品大秀在线观看| 国产成人va亚洲电影| 18禁美女黄网站色大片免费观看 | 中国国产高清免费av片| 成年在线网站免费观看无广告| 亚洲熟妇无码八AV在线播放| 亚洲中文字幕久久精品无码A| 好久久免费视频高清|