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

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

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

    ivaneeo's blog

    自由的力量,自由的生活。

      BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
      669 Posts :: 0 Stories :: 64 Comments :: 0 Trackbacks

    #

    在Android的客戶端編程中(特別是SNS 類型的客戶端),經(jīng)常需要實(shí)現(xiàn)注冊功能Activity,要用戶輸入用戶名,密碼,郵箱,照片后注冊。但這時(shí)就有一個(gè)問題,在HTML中用form表單就 能實(shí)現(xiàn)如上的注冊表單,需要的信息會自動(dòng)封裝為完整的HTTP協(xié)議,但在Android中如何把這些參數(shù)和需要上傳的文件封裝為HTTP協(xié)議呢?

    我們可以先做個(gè)試驗(yàn),看一下form表單到底封裝了什么樣的信息。

    第一步:編寫一個(gè)Servlet,把接收到的HTTP信息保存在一個(gè)文件中,代碼如下:

    1.     public void doPost(HttpServletRequest request, HttpServletResponse response)
    2.  
    3.            throws ServletException, IOException {
    4.  
    5.  
    6.  
    7.        //獲取輸入流,是HTTP協(xié)議中的實(shí)體內(nèi)容
    8.  
    9.        ServletInputStream  sis=request.getInputStream();
    10.  
    11.      
    12.  
    13.        //緩沖區(qū)
    14.  
    15.        byte buffer[]=new byte[1024];
    16.  
    17.       
    18.  
    19.        FileOutputStream fos=new FileOutputStream("d:\\file.log");
    20.  
    21.       
    22.  
    23.        int len=sis.read(buffer, 0, 1024);
    24.  
    25.       
    26.  
    27.        //把流里的信息循環(huán)讀入到file.log文件中
    28.  
    29.        while( len!=-1 )
    30.  
    31.        {
    32.  
    33.            fos.write(buffer, 0, len);
    34.  
    35.            len=sis.readLine(buffer, 0, 1024);
    36.  
    37.        }
    38.  
    39.       
    40.  
    41.        fos.close();
    42.  
    43.        sis.close();
    44.  
    45.       
    46.  
    47.     }
    48.  
    49.  

    第二步:實(shí)現(xiàn)如下一個(gè)表單頁面, 詳細(xì)的代碼如下:

    1. &lt;form action="servlet/ReceiveFile" method="post" enctype="multipart/form-data"&gt;
    2.  
    3.     第一個(gè)參數(shù)&lt;input type="text" name="name1"/&gt; &lt;br/&gt;
    4.  
    5.     第二個(gè)參數(shù)&lt;input type="text" name="name2"/&gt; &lt;br/&gt;
    6.  
    7.     第一個(gè)上傳的文件&lt;input type="file" name="file1"/&gt; &lt;br/&gt;
    8.  
    9.     第二個(gè)上傳的文件&lt;input type="file" name="file2"/&gt; &lt;br/&gt;
    10.  
    11.     &lt;input type="submit" value="提交"&gt;
    12.  
    13. &lt;/form&gt;

    注意了,由于要上傳附件,所以一定要設(shè)置enctype為multipart/form-data,才可以實(shí)現(xiàn)附件的上傳。

    第三步:填寫完信息后按“提交”按鈕后,在D盤下查找file.log文件用記事本打開,數(shù)據(jù)如下:

    —————————–7d92221b604bc

    Content-Disposition: form-data; name=”name1″

    hello

    —————————–7d92221b604bc

    Content-Disposition: form-data; name=”name2″

    world

    —————————–7d92221b604bc

    Content-Disposition: form-data; name=”file1″; filename=”C:\2.GIF”

    Content-Type: image/gif

    GIF89a

          €   € €€   €€ € €€€€€覽?                                                                                     3  f       3  33 3f 3 3 3 f  f3 ff f f f   ? 檉 櫃 櫶 ?   ? 蘤 虣 燙 ?   3 f   3  3 33 f3 ? ? 33 33333f33?3?33f 3f33ff3f?f?f3 3?3檉3櫃3櫶3?3 3?3蘤3虣3燙3?3 333f3??f  f 3f ff 檉 蘤 f3 f33f3ff3檉3蘤3ff ff3fffff檉f蘤ff f?f檉f櫃f櫶f?f f?f蘤f虣f燙f?f f3fff檉蘤   3 f 櫃 虣 ? ?3?f?櫃3虣3檉 檉3檉f檉櫃f虣f櫃 櫃3櫃f櫃櫃櫶櫃櫶 櫶3櫶f櫶櫃燙櫶? ?3?f?櫃虣   3 f 櫶 燙 ? ?3?f?櫶3燙3蘤 蘤3蘤f蘤櫶f燙f虣 虣3虣f虣櫶櫶虣燙 燙3燙f燙櫶燙燙? ?3?f?櫶燙   3 f ? ? 3 333f3?3?3f f3fff?f?f ?檉櫃櫶??蘤虣燙? 3f??!?   ,   

      e   ??羵Q鸚M!C囑lH馉脝遠(yuǎn)5荑p釩?3R?R愣?MV39V5?談re琷?試   3??qn?薵Q燚c?獖i鄲EW艗赥戟j ;

    —————————–7d92221b604bc

    Content-Disposition: form-data; name=”file2″; filename=”C:\2.txt”

    Content-Type: text/plain

    hello everyone!!!

    —————————–7d92221b604bc–

           從表單源碼可知,表單上傳的數(shù)據(jù)有4個(gè):參數(shù)name1和name2,文件file1和file2

    首先從file.log觀察兩個(gè)參數(shù)name1和name2的情況。這時(shí)候使用UltraEdit打開file.log(因?yàn)橛行┳址谟浭卤纠镲@示不出來,所以要用16進(jìn)制編輯器)

           結(jié)合16進(jìn)制數(shù)據(jù)和記事本顯示的數(shù)據(jù)可知上傳參數(shù)部分的格式規(guī)律:

    1.       第一行是“—————————–7d92221b604bc”作為分隔符,然后是“\r\n”(即16進(jìn)制編輯器顯示的0D 0A)回車換行符。

    2.       第二行

    (1)       首先是HTTP中的擴(kuò)展頭部分“Content-Disposition: form-data;”,表示上傳的是表單數(shù)據(jù)。

    (2)       “name=”name1″”參數(shù)的名稱。

    (3)       “\r\n”(即16進(jìn)制編輯器顯示的0D 0A)回車換行符。

    3.       第三行:“\r\n”(即16進(jìn)制編輯器顯示的0D 0A)回車換行符。

    4.       第四行:參數(shù)的值,最后是“\r\n”(即16進(jìn)制編輯器顯示的0D 0A)回車換行符。

    由觀察可得,表單上傳的每個(gè)參數(shù)都是按照以上1—4的格式構(gòu)造HTTP協(xié)議中的參數(shù)部分。

    結(jié)合16進(jìn)制數(shù)據(jù)和記事本顯示的數(shù)據(jù)可知上傳文件部分的格式規(guī)律:

    1.       第一行是“—————————–7d92221b604bc”作為分隔符,然后是“\r\n”(即16進(jìn)制編輯器顯示的0D 0A)回車換行符。

    2.       第二行:

    a)         首先是HTTP中的擴(kuò)展頭部分“Content-Disposition: form-data;”,表示上傳的是表單數(shù)據(jù)。

    b)        “name=”file2″;”參數(shù)的名稱。

    c)        “filename=”C:\2.txt””參數(shù)的值。

    d)        “\r\n”(即16進(jìn)制編輯器顯示的0D 0A)回車換行符。

    3.       第三行:HTTP中的實(shí)體頭部分“Content-Type: text/plain”:表示所接收到得實(shí)體內(nèi)容的文件格式。計(jì)算機(jī)的應(yīng)用中有多種多種通用的文件格式,人們?yōu)槊糠N通用格式都定義了一個(gè)名稱,稱為 MIME,MIME的英文全稱是”Multipurpose Internet Mail Extensions” (多功能Internet 郵件擴(kuò)充服務(wù))

    4.       第四行:“\r\n”(即16進(jìn)制編輯器顯示的0D 0A)回車換行符。

    5.       第五行開始:上傳的內(nèi)容的二進(jìn)制數(shù)。

    6.       最后是結(jié)束標(biāo)志“—————————–7d92221b604bc–”,注意:這個(gè)結(jié)束標(biāo)志和分隔符的區(qū)別是最后多了“–”部分。

    但現(xiàn)在還有一個(gè)問題,就是分隔符“—————————–7d92221b604bc”是怎么確定的呢?是不是一定要“7d92221b604bc”這串?dāng)?shù)字?

            我們以前的分析只是觀察了HTTP請求的實(shí)體部分,可以借用工具觀察完整的HTTP請求看一看有沒有什么線索?

      在IE下用HttpWatch,在Firefox下用Httpfox這個(gè)插件,可以實(shí)現(xiàn)網(wǎng)頁數(shù)據(jù)的抓包,從圖4可看出,原來在Content-Type部分指定了分隔符所用的字符串。

     
    根據(jù)以上總結(jié)的注冊表單中的參數(shù)傳遞和文件上傳的規(guī)律,我們可以能寫出Android中實(shí)現(xiàn)一個(gè)用戶注冊功能(包括個(gè)人信息填寫和上傳圖片部分)的工具類,

    首先,要有一個(gè)javaBean類FormFile封裝文件的信息:

    1. public class FormFile {
    2.  /* 上傳文件的數(shù)據(jù) */
    3.  private byte[] data;
    4.  /* 文件名稱 */
    5.  private String filname;
    6.  /* 表單字段名稱*/
    7.  private String formname;
    8.  /* 內(nèi)容類型 */
    9.  private String contentType = "application/octet-stream"; //需要查閱相關(guān)的資料
    10.  
    11.  public FormFile(String filname, byte[] data, String formname, String contentType) {
    12.   this.data = data;
    13.   this.filname = filname;
    14.   this.formname = formname;
    15.   if(contentType!=null) this.contentType = contentType;
    16.  }
    17.  
    18.  public byte[] getData() {
    19.   return data;
    20.  }
    21.  
    22.  public void setData(byte[] data) {
    23.   this.data = data;
    24.  }
    25.  
    26.  public String getFilname() {
    27.   return filname;
    28.  }
    29.  
    30.  public void setFilname(String filname) {
    31.   this.filname = filname;
    32.  }
    33.  
    34.  public String getFormname() {
    35.   return formname;
    36.  }
    37.  
    38.  public void setFormname(String formname) {
    39.   this.formname = formname;
    40.  }
    41.  
    42.  public String getContentType() {
    43.   return contentType;
    44.  }
    45.  
    46.  public void setContentType(String contentType) {
    47.   this.contentType = contentType;
    48.  }
    49.  
    50. }
    51.  

     
    實(shí)現(xiàn)文件上傳的代碼如下:

    /** 
     * 直接通過HTTP協(xié)議提交數(shù)據(jù)到服務(wù)器,實(shí)現(xiàn)表單提交功能 
     * @param actionUrl 上傳路徑 
     * @param params 請求參數(shù) key為參數(shù)名,value為參數(shù)值 
     * @param file 上傳文件 
     */ 
    public static String post(String actionUrl, Map<String, String> params, FormFile[] files) {  
        try {             
            String BOUNDARY = “———7d4a6d158c9″; //數(shù)據(jù)分隔線  
            String MULTIPART_FORM_DATA = “multipart/form-data”;  
              
            URL url = new URL(actionUrl);  
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
            conn.setDoInput(true);//允許輸入  
            conn.setDoOutput(true);//允許輸出  
            conn.setUseCaches(false);//不使用Cache  
            conn.setRequestMethod(”POST”);            
            conn.setRequestProperty(”Connection”, “Keep-Alive”);  
            conn.setRequestProperty(”Charset”, “UTF-8″);  
            conn.setRequestProperty(”Content-Type”, MULTIPART_FORM_DATA + “; boundary=” + BOUNDARY);  
     
            StringBuilder sb = new StringBuilder();  
              
            //上傳的表單參數(shù)部分,格式請參考文章  
            for (Map.Entry<String, String> entry : params.entrySet()) {//構(gòu)建表單字段內(nèi)容  
                sb.append(”–”);  
                sb.append(BOUNDARY);  
                sb.append(”\r\n”);  
                sb.append(”Content-Disposition: form-data; name=\”"+ entry.getKey() + “\”\r\n\r\n”);  
                sb.append(entry.getValue());  
                sb.append(”\r\n”);  
            }  
            DataOutputStream outStream = new DataOutputStream(conn.getOutputStream());  
            outStream.write(sb.toString().getBytes());//發(fā)送表單字段數(shù)據(jù)  
             
            //上傳的文件部分,格式請參考文章  
            for(FormFile file : files){  
                StringBuilder split = new StringBuilder();  
                split.append(”–”);  
                split.append(BOUNDARY);  
                split.append(”\r\n”);  
                split.append(”Content-Disposition: form-data;name=\”"+ file.getFormname()+”\”;filename=\”"+ file.getFilname() + “\”\r\n”);  
                split.append(”Content-Type: “+ file.getContentType()+”\r\n\r\n”);  
                outStream.write(split.toString().getBytes());  
                outStream.write(file.getData(), 0, file.getData().length);  
                outStream.write(”\r\n”.getBytes());  
            }  
            byte[] end_data = (”–” + BOUNDARY + “–\r\n”).getBytes();//數(shù)據(jù)結(jié)束標(biāo)志           
            outStream.write(end_data);  
            outStream.flush();  
            int cah = conn.getResponseCode();  
            if (cah != 200) throw new RuntimeException(”請求url失敗”);  
            InputStream is = conn.getInputStream();  
            int ch;  
            StringBuilder b = new StringBuilder();  
            while( (ch = is.read()) != -1 ){  
                b.append((char)ch);  
            }  
            outStream.close();  
            conn.disconnect();  
            return b.toString();  
        } catch (Exception e) {  
            throw new RuntimeException(e);  
        }  

    posted @ 2011-06-09 16:26 ivaneeo 閱讀(3322) | 評論 (0)編輯 收藏

    一. 相同配置(set....)的 Configuration 可以考慮只在整個(gè) Application 中共享同一個(gè)實(shí)例: 

    Create a configuration instance

    First you have to create a freemarker.template.Configuration instance and adjust its settings. A Configuration instance is a central place to store the application level settings of FreeMarker. Also, it deals with the creation and caching of pre-parsed templates.

    Probably you will do it only once at the beginning of the application (possibly servlet) life-cycle:

    二. 具有不同配置(set....)的 Configuration 應(yīng)該建立相互獨(dú)立的實(shí)例:

    From now you should use this single configuration instance. Note however that if a system has multiple independent components that use FreeMarker, then of course they will use their own private Configuration instance.

    三. 共享的 Configuration 實(shí)例有利于開啟 MRU Cache 功能:

    Multithreading

    In a multithreaded environment Configuration instances, Template instances and data models should be handled as immutable (read-only) objects. That is, you create and initialize them (for example with set... methods), and then you don't modify them later (e.g. you don't call set...). This allows us to avoid expensive synchronized blocks in a multithreaded environment. Beware with Template instances; when you get a Template instance with Configuration.getTemplate, you may get an instance from the template cache that is already used by other threads, so do not call its set... methods (calling process is of course fine).

    The above restrictions do not apply if you access all objects from the same single thread only.

    四. 開啟 MRU Cache 策略

    Template caching

    FreeMarker caches templates (assuming you use the Configuration methods to create Template objects). This means that when you call getTemplate, FreeMarker not only returns the resulting Template object, but stores it in a cache, so when next time you call getTemplate with the same (or equivalent) path, it just returns the cached Template instance, and will not load and parse the template file again.

    cfg.setCacheStorage(new freemarker.cache.MruCacheStorage(20, 250))  

    Or, since MruCacheStorage is the default cache storage implementation:

    cfg.setSetting(Configuration.CACHE_STORAGE_KEY, "strong:20, soft:250");  

    When you create a new Configuration object, initially it uses an MruCacheStorage where maxStrongSize is 0, and maxSoftSize is Integer.MAX_VALUE (that is, in practice, infinite). But using non-0 maxStrongSize is maybe a better strategy for high load servers, since it seems that, with only softly referenced items, JVM tends to cause just higher resource consumption if the resource consumption was already high, because it constantly throws frequently used templates from the cache, which then have to be re-loaded and and re-parsed.

    五. MRU (Most Recently Used) Cache 自動(dòng)更新模板內(nèi)容的特性

    If you change the template file, then FreeMarker will re-load and re-parse the template automatically when you get the template next time. However, since checking if the file has been changed can be time consuming, there is a Configuration level setting called ``update delay''. This is the time that must elapse since the last checking for a newer version of a certain template before FreeMarker will check that again. This is set to 5 seconds by default. If you want to see the changes of templates immediately, set it to 0. Note that some template loaders may have problems with template updating. For example, class-loader based template loaders typically do not notice that you have changed the template file.

    六. MRU Cache 的兩級緩存策略

    A template will be removed from the cache if you call getTemplate and FreeMarker realizes that the template file has been removed meanwhile. Also, if the JVM thinks that it begins to run out of memory, by default it can arbitrarily drop templates from the cache. Furthermore, you can empty the cache manually with the clearTemplateCache method of Configuration.

    The actual strategy of when a cached template should be thrown away is pluggable with the cache_storage setting, by which you can plug any CacheStorage implementation. For most users freemarker.cache.MruCacheStorage will be sufficient. This cache storage implements a two-level Most Recently Used cache. In the first level, items are strongly referenced up to the specified maximum (strongly referenced items can't be dropped by the JVM, as opposed to softly referenced items). When the maximum is exceeded, the least recently used item is moved into the second level cache, where they are softly referenced, up to another specified maximum. The size of the strong and soft parts can be specified with the constructor. For example, set the size of the strong part to 20, and the size of soft part to 250:

    posted @ 2011-06-09 15:50 ivaneeo 閱讀(727) | 評論 (0)編輯 收藏

    今早一來,突然發(fā)現(xiàn)使用-put命令往HDFS里傳數(shù)據(jù)傳不上去了,抱一大堆錯(cuò)誤,然后我使用bin/hadoop dfsadmin -report查看系統(tǒng)狀態(tài)

    admin@adw1:/home/admin/joe.wangh/hadoop-0.19.2>bin/hadoop dfsadmin -report
    Configured Capacity: 0 (0 KB)
    Present Capacity: 0 (0 KB)
    DFS Remaining: 0 (0 KB)
    DFS Used: 0 (0 KB)
    DFS Used%: ?%

    -------------------------------------------------
    Datanodes available: 0 (0 total, 0 dead)

    使用bin/stop-all.sh關(guān)閉HADOOP

    admin@adw1:/home/admin/joe.wangh/hadoop-0.19.2>bin/stop-all.sh
    stopping jobtracker
    172.16.197.192: stopping tasktracker
    172.16.197.193: stopping tasktracker
    stopping namenode
    172.16.197.193: no datanode to stop
    172.16.197.192: no datanode to stop

    172.16.197.191: stopping secondarynamenode

    哦,看到了吧,發(fā)現(xiàn)datanode前面并沒有啟動(dòng)起來。去DATANODE上查看一下日志

    admin@adw2:/home/admin/joe.wangh/hadoop-0.19.2/logs>vi hadoop-admin-datanode-adw2.hst.ali.dw.alidc.net.log

    ************************************************************/
    2010-07-21 10:12:11,987 ERROR org.apache.hadoop.hdfs.server.datanode.DataNode: java.io.IOException: Incompatible namespaceIDs in /home/admin/joe.wangh/hadoop/data/dfs.data.dir: namenode namespaceID = 898136669; datanode namespaceID = 2127444065
            at org.apache.hadoop.hdfs.server.datanode.DataStorage.doTransition(DataStorage.java:233)
            at org.apache.hadoop.hdfs.server.datanode.DataStorage.recoverTransitionRead(DataStorage.java:148)
            at org.apache.hadoop.hdfs.server.datanode.DataNode.startDataNode(DataNode.java:288)
            at org.apache.hadoop.hdfs.server.datanode.DataNode.<init>(DataNode.java:206)
            at org.apache.hadoop.hdfs.server.datanode.DataNode.makeInstance(DataNode.java:1239)
            at org.apache.hadoop.hdfs.server.datanode.DataNode.instantiateDataNode(DataNode.java:1194)
            at org.apache.hadoop.hdfs.server.datanode.DataNode.createDataNode(DataNode.java:1202)
            at org.apache.hadoop.hdfs.server.datanode.DataNode.main(DataNode.java:1324)
    ......

    錯(cuò)誤提示namespaceIDs不一致。

    下面給出兩種解決辦法,我使用的是第二種。

    Workaround 1: Start from scratch

    I can testify that the following steps solve this error, but the side effects won't make you happy (me neither). The crude workaround I have found is to:

    1.     stop the cluster

    2.     delete the data directory on the problematic datanode: the directory is specified by dfs.data.dir in conf/hdfs-site.xml; if you followed this tutorial, the relevant directory is /usr/local/hadoop-datastore/hadoop-hadoop/dfs/data

    3.     reformat the namenode (NOTE: all HDFS data is lost during this process!)

    4.     restart the cluster

    When deleting all the HDFS data and starting from scratch does not sound like a good idea (it might be ok during the initial setup/testing), you might give the second approach a try.

    Workaround 2: Updating namespaceID of problematic datanodes

    Big thanks to Jared Stehler for the following suggestion. I have not tested it myself yet, but feel free to try it out and send me your feedback. This workaround is "minimally invasive" as you only have to edit one file on the problematic datanodes:

    1.     stop the datanode

    2.     edit the value of namespaceID in <dfs.data.dir>/current/VERSION to match the value of the current namenode

    3.     restart the datanode

    If you followed the instructions in my tutorials, the full path of the relevant file is /usr/local/hadoop-datastore/hadoop-hadoop/dfs/data/current/VERSION (background: dfs.data.dir is by default set to ${hadoop.tmp.dir}/dfs/data, and we set hadoop.tmp.dir to /usr/local/hadoop-datastore/hadoop-hadoop).

    If you wonder how the contents of VERSION look like, here's one of mine:

    #contents of <dfs.data.dir>/current/VERSION

    namespaceID=393514426

    storageID=DS-1706792599-10.10.10.1-50010-1204306713481

    cTime=1215607609074

    storageType=DATA_NODE

    layoutVersion=-13

     

    原因:每次namenode format會重新創(chuàng)建一個(gè)namenodeId,而tmp/dfs/data下包含了上次format下的id,namenode format清空了namenode下的數(shù)據(jù),但是沒有晴空datanode下的數(shù)據(jù),導(dǎo)致啟動(dòng)時(shí)失敗,所要做的就是每次fotmat前,清空tmp一下 的所有目錄.

    posted @ 2011-06-09 14:20 ivaneeo 閱讀(563) | 評論 (0)編輯 收藏

    1. private void buildZK() {  
    2.         System.out.println("Build zk client");  
    3.         try {  
    4.             zk = new ZooKeeper(zookeeperConnectionString, 10000, this);  
    5.             Stat s = zk.exists(rootPath, false);  
    6.             if (s == null) {  
    7.                 zk.create(rootPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);  
    8.                 zk.create(rootPath + "/ELECTION", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);  
    9.             }  
    10.             String value = zk.create(rootPath + "/ELECTION/n_", hostAddress, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);  
    11.         } catch (Exception e) {  
    12.             e.printStackTrace();  
    13.             System.err.println("Error connect to zoo keeper");  
    14.         }  
    15.     }  
    16.   
    17.   
    18.     public void process(WatchedEvent event) {  
    19.         System.out.println(event);  
    20.         if (event.getState() == Event.KeeperState.Disconnected || event.getState() == Event.KeeperState.Expired) {  
    21.             System.out.println("Zookeeper connection timeout.");  
    22.             buildZK();  
    23.         }  
    24.   
    25.     }  
    26.  
    posted @ 2011-06-09 13:38 ivaneeo 閱讀(452) | 評論 (0)編輯 收藏

    修改配置


    復(fù)制conf/zoo_sample.cfg文件為conf/zoo.cfg,修改其中的數(shù)據(jù)目錄。

    # cat /opt/apps/zookeeper/conf/zoo.cfg  tickTime=2000 initLimit=5 syncLimit=2 dataDir=/opt/zkdata clientPort=2181 

    相關(guān)配置如下:

    • tickTime:這個(gè)時(shí)間作為Zookeeper服務(wù)器之間或者服務(wù)器與客戶端之間維護(hù)心跳的時(shí)間,時(shí)間單位毫秒。
    • initLimit:選舉leader的初始延時(shí)。由于服務(wù)器啟動(dòng)加載數(shù)據(jù)需要一定的時(shí)間(尤其是配置數(shù)據(jù)非常多),因此在選舉 Leader后立即同步數(shù)據(jù)前需要一定的時(shí)間來完成初始化。可以適當(dāng)放大一點(diǎn)。延時(shí)時(shí)間為initLimit*tickTime,也即此數(shù)值為 tickTime的次數(shù)。
    • syncLimit:此時(shí)間表示為Leader與Follower之間的最大響應(yīng)時(shí)間單元,如果超時(shí)此時(shí)間(syncLimit*tickTime),那么Leader認(rèn)為Follwer也即死掉,將從服務(wù)器列表中刪除。

    如果是單機(jī)模式的話,那么只需要tickTime/dataDir/clientPort三個(gè)參數(shù)即可,這在單機(jī)調(diào)試環(huán)境很有效。

    集群環(huán)境配置


    增加其他機(jī)器的配置

    # cat /opt/apps/zookeeper/conf/zoo.cfg  tickTime=2000 initLimit=5 syncLimit=2 dataDir=/opt/zkdata clientPort=2181 server.1=10.11.5.202:2888:3888 server.2=192.168.105.218:2888:3888 server.3=192.168.105.65:2888:3888 

    其中server.X的配置是每一個(gè)機(jī)器的相關(guān)參數(shù)。X代表唯一序號,例如1/2/3等,值是IP:PORT:PORT。其中IP是 zookeeper服務(wù)器的IP地址或者域名,第一個(gè)PORT(例如2888)是服務(wù)器之間交換數(shù)據(jù)的端口,也即Follower連接Leader的端 口,而第二個(gè)端口(例如3888)是各服務(wù)器選舉Leader的端口。單機(jī)配置集群的話可以通過不同的端口來實(shí)現(xiàn)。

    同步文件目錄

    # rsync --inplace -vzrtLp --delete-after --progress /opt/apps/zookeeper root@192.168.105.218:/opt/apps # rsync --inplace -vzrtLp --delete-after --progress /opt/apps/zookeeper root@192.168.106.65:/opt/apps 

    建立每一個(gè)服務(wù)器的id

    注意,此id需要和zoo.cfg中的配置對應(yīng)起來

    ssh root@10.11.5.202 'echo 1 > /opt/zkdata/myid' ssh root@192.168.105.218 'echo 2 > /opt/zkdata/myid' ssh root@192.168.106.65 'echo 3 > /opt/zkdata/myid' 

    啟動(dòng)服務(wù)器


    ssh root@10.11.5.202 '/opt/apps/zookeeper/bin/zkServer.sh start' ssh root@192.168.105.218 '/opt/apps/zookeeper/bin/zkServer.sh start' ssh root@192.168.106.65 '/opt/apps/zookeeper/bin/zkServer.sh start' 

    防火墻配置


    如果開啟了iptables防火墻,則需要在文件/etc/sysconfig/iptables文件下增加如下配置

    -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 2181 -j ACCEPT -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 2888 -j ACCEPT -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 3888 -j ACCEPT 

    重啟防火墻:

    service iptables restart 
    posted @ 2011-06-08 18:07 ivaneeo 閱讀(1197) | 評論 (0)編輯 收藏

    最近經(jīng)常對自己提一些問題,然后自己通過google、讀代碼、測試尋求答案來解決疑惑,可能這些問題也能給其他人帶來一些幫助。

    quora是個(gè)不錯(cuò)的問答型網(wǎng)站,興趣去看一下自己感興趣的話題吧~

    1)HBase中的TTL參數(shù)什么意思?
    TTL == "Time To Live".  You can specify how long a cell lives in hbase.
    Onces its "TTL" has expired, its removed.
    2)影響read性能的配置參數(shù)有哪些?
    hbase-env.xml:
    export HBASE_HEAPSIZE=4000
    hbase-default.xml:
    hfile.block.cache.size
    3)HBase在寫操作的時(shí)候會更新LruBlockCache嗎?
    從代碼上看寫的時(shí)候不會更新lruBlockCache!
    4)如何將一個(gè)HBase CF指定為IN_MEMORY?
    創(chuàng)建table的時(shí)候可以指定CF的屬性,create 'taobao', {NAME => 'edp', IN_MEMORY => true}
    5)HBase cache每次load的最小單位是block
    6)如果每次load一個(gè)block到cache中,而以后不會再讀取這個(gè)block,則這個(gè)block對block cache
    hit ratio沒有貢獻(xiàn)啊,但是為什么block cache hit ratio有60%+呢?(這個(gè)我當(dāng)初的錯(cuò)誤理解,漏
    洞還是很多的)
    注意block cache hit ratio的最小計(jì)量單位應(yīng)該是record,cache的最小單位才是block, 因?yàn)閎lock
    下面有很多record,后面的record借助了讀第一個(gè)record帶來的cache福利,所以block cache hit ratio
    才會有60%+


    7)如果只有一行一個(gè)cf,寫入很大量的數(shù)據(jù)會不會發(fā)生region split?

    1. <property>  
    2.   <name>hbase.hregion.max.filesize</name>  
    3.   <value>67108864</value>  
    4.   <description>  
    5.   Maximum HStoreFile size. If any one of a column families' HStoreFiles has  
    6.   grown to exceed this value, the hosting HRegion is split in two.  
    7.   Default: 256M.  
    8.   </description>  
    9. </property>  

    測試: 將參數(shù)hbase.hregion.max.filesize設(shè)置成64M以后,然后create table的時(shí)候只創(chuàng)建一個(gè)CF,測試的時(shí)候只往一個(gè)row + CF 下面塞入數(shù)據(jù),數(shù)據(jù)量大概在80M左右,在web上顯示的數(shù)目是107M,但是沒有發(fā)生region split。這說明region split最小單位應(yīng)該是row key級別,因?yàn)檫@里只有一個(gè)row,即使數(shù)據(jù)量已經(jīng)上去了,但是還是沒有發(fā)生region split.

    posted @ 2011-06-08 18:02 ivaneeo 閱讀(739) | 評論 (0)編輯 收藏

    Hi all.
    I've a thinkpad T60 and 9.10 installed. I did some search on the forums and found the workaround with tpb package to fix thinkpad volume buttons issue.
    My problems with that fix are:
    -tbp package depens on xosd (or whatever like that, NOT Notify-OSD) so the result is not the best...
    -tpb package is not neccessary at all, because thinkpad_acpi module can take care about volume buttons as well, you just have to enable the hotkey mask! http://www.thinkwiki.org/wiki/Thinkpad-acpi

    So my workaround on T60 (in terminal):
    9.04 jaunty:
    Code:
    sudo echo enable,0x00ffffff > /proc/acpi/ibm/hotkey
    9.10 karmic: (using sysfs): (also works on 10.04 and 10.10 as well...)
    Code:
    sudo cp /sys/devices/platform/thinkpad_acpi/hotkey_all_mask /sys/devices/platform/thinkpad_acpi/hotkey_mask
    Update:
    The solutions only works till next reboot or suspend/resume cycle.
    you should put the commands in:
    /etc/rc.local
    without sudo of course, to make it permanent.


    Please confirm if the solution works on other thikpad models.

    As soon as I find solution for all the things I need on my T60 I will put it up on Thinkwiki and paste the link here.
    (Active protection - hdaps)
    (Trackpoint additional functions - you just have to install the: gpointing-device-settings package)
    (fingerprint reader - thinkfinger)

    Hope it helped for someone.
    posted @ 2011-05-31 15:16 ivaneeo 閱讀(305) | 評論 (0)編輯 收藏

    Distributed File Systems (DFS) are a new type of file systems which provides some extra features over normal file systems and are used for storing and sharing files across wide area network and provide easy programmatic access. File Systems like HDFS from Hadoop and many others falls in the category of distributed file systems and has been widely used and are quite popular.

    This tutorial provides a step by step guide for accessing and using distributed file system for storing and retrieving data using j\Java. Hadoop Distributed File System has been used for this tutorial because it is freely available, easy to setup and is one of the most popular and well known Distributed file system. The tutorial demonstrates how to access Hadoop distributed file system using java showing all the basic operations.

    Introduction
    Distributed File Systems (DFS) are a new type of file systems which provides some extra features over normal file systems and are used for storing and sharing files across wide area network and provide easy programmatic access. 

    Distributed file system is used to make files distributed across multiple servers appear to users as if they reside in one place on the network. Distributed file system allows administrators to consolidate file shares that may exist on multiple servers to appear as if they all are in the same location so that users can access them from a single point on the network. 
    HDFS stands for Hadoop Distributed File System and is a distributed file system designed to run on commodity hardware. Some of the features provided by Hadoop are:
    •    Fault tolerance: Data can be replicated, so if any of the servers goes down, resources still will be available for user.
    •    Resource management and accessibility: Users does not require knowing the physical location of the data; they can access all the resources through a single point. HDFS also provides web browser interface to view the contents of the file.
    •    It provides high throughput access to application data.

    This tutorial will demonstrate how to use HDFS for basic distributed file system operations using Java. Java 1.6 version and Hadoop driver has been used (link is given in Pre-requisites section). The development environment consists of Eclipse 3.4.2 and Hadoop 0.19.1 on Microsoft Windows XP – SP3.


    Pre-requisites

    1.      Hadoop-0.19.1 installation - here and here -

    2.      Hadoop-0.19.1-core.jar file

    3.      Commons-logging-1.1.jar file

    4.      Java 1.6

    5.      Eclipse 3.4.2



    Creating New Project and FileSystem Object

    First step is to create a new project in Eclipse and then create a new class in that project. 
    Now add all the jar files to the project, as mentioned in the pre-requisites.
    First step in using or accessing Hadoop Distributed File System (HDFS) is to create file system object.
    Without creating an object you cannot perform any operations on the HDFS, so file system object is always required to be created.
    Two input parameters are required to create object. They are “Host name” and “Port”. 
    Code below shows how to create file system object to access HDFS. 

    Configuration config = new Configuration();

    config.set("fs.default.name","hdfs://127.0.0.1:9000/");

    FileSystem dfs = FileSystem.get(config);


    Here Host name = “127.0.0.1” & Port = “9000”.

    Various HDFS operations

    Now we will see various operations that can be performed on HDFS.

    Creating Directory

    Now we will start with creating a directory.
    First step for using HDFS is to create a directory where we will store our data. 
    Now let us create a directory named “TestDirectory”.

    String dirName = "TestDirectory";

    Path src = new Path(dfs.getWorkingDirectory()+"/"+dirName);

    dfs.mkdirs(src);

    Here dfs.getWorkingDirectory() function will return the path of the working directory which is the basic working directory and all the data will be stored inside this directory. mkdirs() function accepts object of the type Path, so as shown above Path object is created first. Directory is required to be created inside basic working directory, so Path object is created accordingly. dfs.mkdirs(src)function will create a directory in the working folder with name “TestDirectory”.

    Sub directories can also be created inside the “TestDirectory”; in that case path specified during creation of Path object will change. For example a directory named “subDirectory” can be created inside directory “TestDirectory” as shown in below code.

    String subDirName = "subDirectory";

    Path src = new Path(dfs.getWorkingDirectory()+"/TestDirectory/"+ subDirName);

    dfs.mkdirs(src);

    Deleting Directory or file

    Existing directory in the HDFS can be deleted. Below code shows how to delete the existing directory.

    String dirName = "TestDirectory";

    Path src = new Path(dfs.getWorkingDirectory()+"/"+dirName);

    Dfs.delete(src);


    Please note that delete() method can also be used to delete files. What needs to be deleted should be specified in the Path object.

    Copying file to/from HDFS from/to Local file system

    Basic aim of using HDFS is to store data, so now we will see how to put data in HDFS.
    Once directory is created, required data can be stored in HDFS from the local file system.
    So consider that a file named “file1.txt” is located at “E:\HDFS” in the local file system, and it is required to be copied under the folder “subDirectory” (that was created earlier) in HDFS.
    Code below shows how to copy file from local file system to HDFS.

    Path src = new Path("E://HDFS/file1.txt");

    Path dst = new Path(dfs.getWorkingDirectory()+"/TestDirectory/subDirectory/");

    dfs.copyFromLocalFile(src, dst);


    Here src and dst are the Path objects created for specifying the local file system path where file is located and HDFS path where file is required to be copied respectively. copyFromLocalFile() method is used for copying file from local file system to HDFS.

    Similarly, file can also be copied from HDFS to local file system. Code below shows how to copy file from HDFS to local file system.

    Path src = new Path(dfs.getWorkingDirectory()+"/TestDirectory/subDirectory/file1.txt");

    Path dst = new Path("E://HDFS/");

    dfs.copyToLocalFile(src, dst);

    Here copyToLocalFile() method is used for copying file from HDFS to local file system.

    CIO, CTO & Developer Resources

    Creating a file and writing data in it

    It is also possible to create a file in HDFS and write data in it. So if required instead of directly copying the file from the local file system, a file can be first created and then data can be written in it.
    Code below shows how to create a file name “file2.txt” in HDFS directory.

    Path src = new Path(dfs.getWorkingDirectory()+"/TestDirectory/subDirectory/file2.txt");

    dfs.createNewFile(src);


    Here createNewFile() method will create the file in HDFS based on the input provided in src object.

    Now as the file is created, data can be written in it. Code below shows how to write data present in the “file1.txt” of local file system to “file2.txt” of HDFS.

    Path src = new Path(dfs.getWorkingDirectory()+"/TestDirectory/subDirectory/file2.txt");

    FileInputStream fis = new FileInputStream("E://HDFS/file1.txt");

    int len = fis.available();

    byte[] btr = new byte[len];

    fis.read(btr);

    FSDataOutputStream fs = dfs.create(src);

    fs.write(btr);

    fs.close();


    Here write() method of FSDataOutputStream is used to write data in file located in HDFS.

    Reading data from a file

    It is always necessary to read the data from file for performing various operations on data. It is possible to read data from the file which is stored in HDFS. 
    Code below shows how to retrieve data from the file present in the HDFS. Here data is read from the file (file1.txt) which is present in the directory (subDirectory) that was created earlier.

    Path src = new Path(dfs.getWorkingDirectory()+"/TestDirectory/subDirectory/file1.txt");

    FSDataInputStream fs = dfs.open(src);

    String str = null;

    while ((str = fs.readline())!= null)
    {
    System.out.println(str);
    }


    Here readline() method of FSDataInputStream is used to read data from the file located in HDFS. Also src is the Path object used to specify the path of the file in HDFS which has to be read.

    Miscellaneous operations that can be performed on HDFS

    Below are some of the basic operations that can be performed on HDFS.

    Below is the code that can be used to check whether particular file or directory exists in HDFS. If it exists, it returns true and if it doesn’t exists it returns false.dfs.exists() method is used for this.

    Path src = new Path(dfs.getWorkingDirectory()+"/TestDirectory/HDFS/file1.txt");

    System.out.println(dfs.exists(src));

    Below is the code that can be used to check the default block size in which file would be split. It returns block size in terms of Number of Bytes.dfs.getDefaultBlockSize() method is used for this.

    System.out.println(dfs.getDefaultBlockSize());

    To check for the default replication factor, as shown belowdfs.getDefaultReplication() method can be used.

    System.out.println(dfs.getDefaultReplication());

    To check whether given path is HDFS directory or file, as shown belowdfs.isDirectory() or dfs.isFile() methods can be used.

    Path src = new Path(dfs.getWorkingDirectory()+"/TestDirectory/subDirectory/file1.txt");
    System.out.println(dfs.isDirectory(src));
    System.out.println(dfs.isFile(src));

    Conclusion
    So we just learned some of the basics about Hadoop Distributed File System, how to create and delete directory, how to copy file to/from HDFS from/to local file system, how to create and delete file into directory, how to write data in file, and how to read data from file. We also learned various other operations that can be performed on HDFS. Thus from what we have done we can say that, HDFS is easy to use for data storage and retrieval.

    References:
    http://hadoop.apache.org/common/docs/current/hdfs_design.html

    http://en.wikipedia.org/wiki/Hadoop

    posted @ 2011-05-17 10:43 ivaneeo 閱讀(570) | 評論 (0)編輯 收藏

    本文主要介紹zookeeper中zookeeper Server leader的選舉,zookeeper在選舉leader的時(shí)候采用了paxos算法(主要是fast paxos),這里主要介紹其中兩種:LeaderElection 和FastLeaderElection.

    我們先要清楚以下幾點(diǎn)

    • 一個(gè)Server是如何知道其它的Server


    在zookeeper中,一個(gè)zookeeper集群有多少個(gè)Server是固定,每個(gè)Server用于選舉的IP和PORT都在配置文件中

    • 除了IP和PORT能標(biāo)識一個(gè)Server外,還有沒有別的方法

    每一個(gè)Server都有一個(gè)數(shù)字編號,而且是唯一的,我們根據(jù)配置文件中的配置來對每一個(gè)Server進(jìn)行編號,這一步在部署時(shí)需要人工去做,需要在存儲數(shù)據(jù)文件的目錄中創(chuàng)建一個(gè)文件叫myid的文件,并寫入自己的編號,這個(gè)編號在處理我提交的value相同很有用

    • 成為Leader的必要條件

    獲得n/2 + 1個(gè)Server同意(這里意思是n/2 + 1個(gè)Server要同意擁有zxid是所有Server最大的哪個(gè)Server)

    • zookeeper中選舉采用UDP還是TCP

    zookeeper中選舉主要是采用UDP,也一種實(shí)現(xiàn)是采用TCP,在這里介紹的兩種實(shí)現(xiàn)采用的是UDP

    • zookeeper中有哪幾種狀態(tài)

    LOOKING 初始化狀態(tài)

    LEADING  領(lǐng)導(dǎo)者狀態(tài)

    FOLLOWING  跟隨者狀態(tài)

    • 如果所有zxid都相同(例如: 剛初始化時(shí)),此時(shí)有可能不能形成n/2+1個(gè)Server,怎么辦

    zookeeper中每一個(gè)Server都有一個(gè)ID,這個(gè)ID是不重復(fù)的,而且按大小排序,如果遇到這樣的情況時(shí),zookeeper就推薦ID最大的哪個(gè)Server作為Leader

    • zookeeper中Leader怎么知道Fllower還存活,F(xiàn)llower怎么知道Leader還存活

    Leader定時(shí)向Fllower發(fā)ping消息,F(xiàn)llower定時(shí)向Leader發(fā)ping消息,當(dāng)發(fā)現(xiàn)Leader無法ping通時(shí),就改變自己的狀態(tài)(LOOKING),發(fā)起新的一輪選舉

    名詞解釋

    zookeeer Server: zookeeper中一個(gè)Server,以下簡稱Server

    zxid(zookeeper transtion id): zookeeper 事務(wù)id,他是選舉過程中能否成為leader的關(guān)鍵因素,它決定當(dāng)前Server要將自己這一票投給誰(也就是我在選舉過程中的value,這只是其中一個(gè),還有id)

    myid/id(zookeeper server id): zookeeper server id ,他也是能否成為leader的一個(gè)因素

    epoch/logicalclock:他主要用于描述leader是否已經(jīng)改變,每一個(gè)Server中啟動(dòng)都會有一個(gè)epoch,初始值為0,當(dāng) 開始新的一次選舉時(shí)epoch加1,選舉完成時(shí) epoch加1。

    tag/sequencer:消息編號

    xid:隨機(jī)生成的一個(gè)數(shù)字,跟epoch功能相同

    Fast Paxos消息流向圖與Basic Paxos的對比

    消息流向圖

    • basic paxos 消息流向圖
    Client   Proposer      Acceptor     Learner
    |         |          |  |  |       |  |
    X-------->|          |  |  |       |  |  Request
    |         X--------->|->|->|       |  |  Prepare(N)//向所有Server提議
    |         |<---------X--X--X       |  |  Promise(N,{Va,Vb,Vc})//向提議人回復(fù)是否接受提議(如果不接受回到上一步)
    |         X--------->|->|->|       |  |  Accept!(N,Vn)//向所有人發(fā)送接受提議消息
    |         |<---------X--X--X------>|->|  Accepted(N,Vn)//向提議人回復(fù)自己已經(jīng)接受提議)
    |<---------------------------------X--X  Response
    |         |          |  |  |       |  |
    
    • fast paxos消息流向圖

    沒有沖突的選舉過程

    Client    Leader         Acceptor      Learner
    |         |          |  |  |  |       |  |
    |         X--------->|->|->|->|       |  |  Any(N,I,Recovery)
    |         |          |  |  |  |       |  |
    X------------------->|->|->|->|       |  |  Accept!(N,I,W)//向所有Server提議,所有Server收到消息后,接受提議
    |         |<---------X--X--X--X------>|->|  Accepted(N,I,W)//向提議人發(fā)送接受提議的消息
    |<------------------------------------X--X  Response(W)
    |         |          |  |  |  |       |  |
    

    第一種實(shí)現(xiàn): LeaderElection

    LeaderElection是Fast paxos最簡單的一種實(shí)現(xiàn),每個(gè)Server啟動(dòng)以后都詢問其它的Server它要投票給誰,收到所有Server回復(fù)以后,就計(jì)算出zxid最大的哪個(gè)Server,并將這個(gè)Server相關(guān)信息設(shè)置成下一次要投票的Server


    每個(gè)Server都有一個(gè)response線程和選舉線程,我們先看一下每個(gè)線程是做一些什么事情

    response線程

    它主要功能是被動(dòng)的接受對方法的請求,并根據(jù)當(dāng)前自己的狀態(tài)作出相應(yīng)的回復(fù),每次回復(fù)都有自己的Id,以及xid,我們根據(jù)他的狀態(tài)來看一看他都回復(fù)了哪些內(nèi)容

    LOOKING狀態(tài):

    自己要推薦的Server相關(guān)信息(id,zxid)

    LEADING狀態(tài)

    myid,上一次推薦的Server的id

    FLLOWING狀態(tài):

    當(dāng)前Leader的id,以及上一次處理的事務(wù)ID(zxid)

    選舉線程

    選舉線程由當(dāng)前Server發(fā)起選舉的線程擔(dān)任,他主要的功能對投票結(jié)果進(jìn)行統(tǒng)計(jì),并選出推薦的Server。選舉線程首先向所有Server發(fā)起一次詢問(包括自己),被詢問方,根據(jù)自己當(dāng)前的狀態(tài)作相應(yīng)的回復(fù),選舉線程收到回復(fù)后,驗(yàn)證是否是自己發(fā)起的詢問(驗(yàn)證 xid是否一致),然后獲取對方的id(myid),并存儲到當(dāng)前詢問對象列表中,最后獲取對方提議的leader相關(guān)信息(id,zxid),并將這些 信息存儲到當(dāng)次選舉的投票記錄表中,當(dāng)向所有Server都詢問完以后,對統(tǒng)計(jì)結(jié)果進(jìn)行篩選并進(jìn)行統(tǒng)計(jì),計(jì)算出當(dāng)次詢問后獲勝的是哪一個(gè) Server,并將當(dāng)前zxid最大的Server設(shè)置為當(dāng)前Server要推薦的Server(有可能是自己,也有可以是其它的Server,根據(jù)投票 結(jié)果而定,但是每一個(gè)Server在第一次投票時(shí)都會投自己),如果此時(shí)獲勝的Server獲得n/2 + 1的Server票數(shù), 設(shè)置當(dāng)前推薦的leader為獲勝的Server,將根據(jù)獲勝的Server相關(guān)信息設(shè)置自己的狀態(tài)。每一個(gè)Server都重復(fù)以上流程,直到選出 leader

    了解每個(gè)線程的功能以后,我們來看一看選舉過程

    • 選舉過程中,Server的加入

    當(dāng)一個(gè)Server啟動(dòng)時(shí)它都會發(fā)起一次選舉,此時(shí)由選舉線程發(fā)起相關(guān)流程,那么每個(gè)Server都會獲得當(dāng)前zxid最大的哪個(gè)Server是誰,如果當(dāng)次最大的Server沒有獲得n/2+1個(gè)票數(shù),那么下一次投票時(shí),他將向zxid最大的Server投票,重復(fù)以上流程,最后一定能選舉出一個(gè)Leader

    • 選舉過程中,Server的退出

    只要保證n/2+1個(gè)Server存活就沒有任何問題,如果少于n/2+1個(gè)Server存活就沒辦法選出Leader

    • 選舉過程中,Leader死亡

    當(dāng)選舉出Leader以后,此時(shí)每個(gè)Server應(yīng)該是什么狀態(tài)(FLLOWING)都已經(jīng)確定,此時(shí)由于Leader已經(jīng)死亡我們就不管它,其它的Fllower按正常的流程繼續(xù)下去,當(dāng)完成這個(gè)流程以后,所有的Fllower都會向Leader發(fā)送Ping消息,如果無法ping通,就改變自己的狀態(tài)為(FLLOWING ==> LOOKING),發(fā)起新的一輪選舉

    • 選舉完成以后,Leader死亡

    這個(gè)過程的處理跟選舉過程中Leader死亡處理方式一樣,這里就不再描述

    第二種實(shí)現(xiàn): FastLeaderElection

    fastLeaderElection是標(biāo)準(zhǔn)的fast paxos的實(shí)現(xiàn),它首先向所有Server提議自己要成為leader,當(dāng)其它Server收到提議以后,解決epoch和zxid的沖突,并接受對方的提議,然后向?qū)Ψ桨l(fā)送接受提議完成的消息

    數(shù)據(jù)結(jié)構(gòu)

    本地消息結(jié)構(gòu):

    static public class Notification {
    long leader;  //所推薦的Server id

    long zxid;      //所推薦的Server的zxid(zookeeper transtion id)

    long epoch;   //描述leader是否變化(每一個(gè)Server啟動(dòng)時(shí)都有一個(gè)logicalclock,初始值為0)

    QuorumPeer.ServerState state;   //發(fā)送者當(dāng)前的狀態(tài)
    InetSocketAddress addr;            //發(fā)送者的ip地址
    }

    網(wǎng)絡(luò)消息結(jié)構(gòu):

    static public class ToSend {

    int type;        //消息類型
    long leader;  //Server id
    long zxid;     //Server的zxid
    long epoch;  //Server的epoch
    QuorumPeer.ServerState state; //Server的state
    long tag;      //消息編號

    InetSocketAddress addr;

    }

    Server具體的實(shí)現(xiàn)

    每個(gè)Server都一個(gè)接收線程池(3個(gè)線程)和一個(gè)發(fā)送線程池 (3個(gè)線程),在沒有發(fā)起選舉時(shí),這兩個(gè)線程池處于阻塞狀態(tài),直到有消息到來時(shí)才解除阻塞并處理消息,同時(shí)每個(gè)Server都有一個(gè)選舉線程(可以發(fā)起 選舉的線程擔(dān)任);我們先看一下每個(gè)線程所做的事情,如下:

    被動(dòng)接收消息端(接收線程池)的處理:

    notification: 首先檢測當(dāng)前Server上所被推薦的zxid,epoch是否合法(currentServer.epoch <= currentMsg.epoch && (currentMsg.zxid > currentServer.zxid || (currentMsg.zxid == currentServer.zxid && currentMsg.id > currentServer.id))) 如果不合法就用消息中的zxid,epoch,id更新當(dāng)前Server所被推薦的值,此時(shí)將收到的消息轉(zhuǎn)換成Notification消息放入接收隊(duì)列中,將向?qū)Ψ桨l(fā)送ack消息

    ack:   將消息編號放入ack隊(duì)列中,檢測對方的狀態(tài)是否是LOOKING狀態(tài),如果不是說明此時(shí)已經(jīng)有Leader已經(jīng)被選出來,將接收到的消息轉(zhuǎn)發(fā)成Notification消息放入接收對隊(duì)列

    主動(dòng)發(fā)送消息端(發(fā)送線程池)的處理:

    notification: 將要發(fā)送的消息由Notification消息轉(zhuǎn)換成ToSend消息,然后發(fā)送對方,并等待對方的回復(fù),如果在等待結(jié)束沒有收到對方法回復(fù),重做三次,如果重做次還是沒有收到對方的回復(fù)時(shí)檢測當(dāng)前的選舉(epoch)是否已經(jīng)改變,如果沒有改變,將消息再次放入發(fā)送隊(duì)列中,一直重復(fù)直到有Leader選出或者收到對方回復(fù)為止

    ack: 主要將自己相關(guān)信息發(fā)送給對方

    主動(dòng)發(fā)起選舉端(選舉線程)的處理:

    首先自己的epoch 加1,然后生成notification消息,并將消息放入發(fā)送隊(duì)列中,系統(tǒng)中配置有幾個(gè)Server就生成幾條消息,保證每個(gè)Server都能收到此消息,如果當(dāng)前Server的狀態(tài)是LOOKING就一直循環(huán)檢查接收隊(duì)列是否有消息,如果有消息,根據(jù)消息中對方的狀態(tài)進(jìn)行相應(yīng)的處理。

    LOOKING狀態(tài):

    首先檢測消息中epoch是否合法,是否比當(dāng)前Server的大,如果比較當(dāng)前Server的epoch大時(shí),更新epoch,檢測是消息中的zxid,id是否比當(dāng)前推薦的Server大,如果是更新相關(guān)值,并新生成notification消息放入發(fā)關(guān)隊(duì)列,清空投票統(tǒng)計(jì)表; 如果消息小的epoch則什么也不做; 如果相同檢測消息中zxid,id是否合法,如果消息中的zxid,id大,那么更新當(dāng)前Server相關(guān)信息,并新生成notification消息放入發(fā)送隊(duì)列,將收到的消息的IP和投票結(jié)果放入統(tǒng)計(jì)表中,并計(jì)算統(tǒng)計(jì)結(jié)果,根據(jù)結(jié)果設(shè)置自己相應(yīng)的狀態(tài)

    LEADING狀態(tài):

    將收到的消息的IP和投票結(jié)果放入統(tǒng)計(jì)表中(這里的統(tǒng)計(jì)表是獨(dú)立的),并計(jì)算統(tǒng)計(jì)結(jié)果,根據(jù)結(jié)果設(shè)置自己相應(yīng)的狀態(tài)

    FOLLOWING狀態(tài):

    將收到的消息的IP和投票結(jié)果放入統(tǒng)計(jì)表中(這里的統(tǒng)計(jì)表是獨(dú)立的),并計(jì)算統(tǒng)計(jì)結(jié)果,根據(jù)結(jié)果設(shè)置自己相應(yīng)的狀態(tài)

    了解每個(gè)線程的功能以后,我們來看一看選舉過程,選舉過程跟第一程一樣

    • 選舉過程中,Server的加入

    當(dāng)一個(gè)Server啟動(dòng)時(shí)它都會發(fā)起一次選舉,此時(shí)由選舉線程發(fā)起相關(guān)流程,通過將自己的zxid和epoch告訴其它Server,最后每個(gè)Server都會得zxid值最大的哪個(gè)Server的相關(guān)信息,并且在下一次投票時(shí)就投zxid值最大的哪個(gè)Server,重復(fù)以上流程,最后一定能選舉出一個(gè)Leader

    • 選舉過程中,Server的退出

    只要保證n/2+1個(gè)Server存活就沒有任何問題,如果少于n/2+1個(gè)Server存活就沒辦法選出Leader

    • 選舉過程中,Leader死亡

    當(dāng)選舉出Leader以后,此時(shí)每個(gè)Server應(yīng)該是什么狀態(tài) (FLLOWING)都已經(jīng)確定,此時(shí)由于Leader已經(jīng)死亡我們就不管它,其它的Fllower按正常的流程繼續(xù)下去,當(dāng)完成這個(gè)流程以后,所有的 Fllower都會向Leader發(fā)送Ping消息,如果無法ping通,就改變自己的狀態(tài)為(FLLOWING ==> LOOKING),發(fā)起新的一輪選舉

    • 選舉完成以后,Leader死亡

    這個(gè)過程的處理跟選舉過 程中Leader死亡處理方式一樣,這里就不再描述

    posted @ 2011-05-05 13:16 ivaneeo 閱讀(1264) | 評論 (1)編輯 收藏

         摘要: zookeeper簡介 zookeeper是一個(gè)開源分布式的服務(wù),它提供了分布式協(xié)作,分布式同步,配置管理等功能. 其實(shí)現(xiàn)的功能與google的chubby基本一致.zookeeper的官方網(wǎng)站已經(jīng)寫了一篇非常經(jīng)典的概述性文章,請大家參閱:ZooKeeper: A Distributed Coordination Service for Distributed Applications 在此我...  閱讀全文
    posted @ 2011-05-05 13:15 ivaneeo 閱讀(1655) | 評論 (0)編輯 收藏

    僅列出標(biāo)題
    共67頁: First 上一頁 13 14 15 16 17 18 19 20 21 下一頁 Last 
    主站蜘蛛池模板: 免费无码毛片一区二区APP| 免费人成在线视频| 日本免费高清一本视频| 久久乐国产精品亚洲综合| 亚洲国产日韩在线人成下载 | 亚洲情a成黄在线观看动漫尤物| 久久亚洲最大成人网4438| 一级特黄特色的免费大片视频| 精品成在人线AV无码免费看 | 亚洲五月综合缴情在线观看| 亚洲va乱码一区二区三区| caoporn国产精品免费| 免费精品国产自产拍在| 亚洲人成网77777亚洲色| 亚洲中文字幕AV每天更新| 大地资源中文在线观看免费版| 亚洲日韩中文字幕无码一区| 搡女人免费免费视频观看| 免费黄网在线观看| 亚洲第一视频网站| 深夜A级毛片视频免费| 国产一卡2卡3卡4卡2021免费观看| 亚洲综合另类小说色区| 亚洲av永久无码天堂网| 亚洲剧场午夜在线观看| 久久精品无码免费不卡| 黄网址在线永久免费观看| 久久精品亚洲一区二区三区浴池| 免费视频成人国产精品网站 | 亚洲最大激情中文字幕| 亚洲欧洲无码AV不卡在线| 久久精品毛片免费观看| 国产亚洲AV夜间福利香蕉149 | 亚洲精品成人网久久久久久| 亚洲乱码在线卡一卡二卡新区| 性色午夜视频免费男人的天堂| 亚洲无码黄色网址| 日本亚洲中午字幕乱码| 歪歪漫画在线观看官网免费阅读| 亚洲精品免费在线观看| 搡女人免费免费视频观看|