Giraph 編程實踐及源碼編譯調試

前言

本文主要總結了如何利用 Giraph 提供的 API 實現圖計算編程,並說明了將 Giraph 源碼導入 IDEA 進行調試的過程。

編程實踐

本部分通過實現最短路徑算法說明 Giraph 的編程流程

  1. 創建 Maven 工程在這裏插入圖片描述
    在這裏插入圖片描述

  2. 添加相關依賴

    <dependencies>
        <!--添加 Giraph 依賴-->>
        <dependency>
            <groupId>org.apache.giraph</groupId>
            <artifactId>giraph-core</artifactId>
            <version>1.2.0-hadoop2</version>
        </dependency>
    
        <!--添加 Hadoop 依賴-->>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>2.5.1</version>
        </dependency>
    
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>2.5.1</version>
        </dependency>
    </dependencies>
    
  3. 創建 ShortestPathComputation 類

    import org.apache.giraph.edge.Edge;
    import org.apache.giraph.graph.BasicComputation;
    import org.apache.giraph.graph.Vertex;
    import org.apache.hadoop.io.DoubleWritable;
    import org.apache.hadoop.io.FloatWritable;
    import org.apache.hadoop.io.LongWritable;
    
    import java.io.IOException;
    
    public class ShortestPathComputation extends BasicComputation<LongWritable, DoubleWritable,
            FloatWritable, DoubleWritable> {
        /**
         * 需要計算最短路徑的源頂點
         */
        private static final int SOURCE_VERTEX = 0;
        /**
         * 表示節點不可達
         */
        private static final double UNREACHABLE = Double.MAX_VALUE;
        
        /**
        * @param vertex 待處理的頂點
        * @param messages vertex 接收到的來自其餘頂點的 message
        */
        @Override
        public void compute(Vertex<LongWritable, DoubleWritable, FloatWritable> vertex,
                            Iterable<DoubleWritable> messages) throws IOException {
            if (getSuperstep() == 0) {
                //超步 0 時源頂點最短路徑設置爲 0,其餘頂點設置爲不可達,並且源頂點需要向其它頂點發送最短距離message
                if (vertex.getId().get() == SOURCE_VERTEX) {
                    vertex.setValue(new DoubleWritable(0));
                    sendDist(vertex);
                } else {
                    vertex.setValue(new DoubleWritable(UNREACHABLE));
                }
            } else {
                //遍歷處理從其餘頂點收到的 message,
                // 查看 message 中傳遞的最短距離是否小於當前的最短距離,如果是則進行更新
                for (DoubleWritable message : messages) {
                    if (message.get() < vertex.getValue().get()) {
                        vertex.setValue(message);
                        sendDist(vertex);
                    }
                }
            }
            //主動將頂點置於不活躍狀態,如果頂點收到 message,系統會將頂點再度激活
            vertex.voteToHalt();
        }
    
        /**
         * 發送頂點 vertex 到其鄰接頂點的最短距離
         */
        private void sendDist(Vertex<LongWritable,
                DoubleWritable, FloatWritable> vertex) {
            for (Edge<LongWritable, FloatWritable> edge : vertex.getEdges()) {
                double distance = vertex.getValue().get() + edge.getValue().get();
                sendMessage(edge.getTargetVertexId(), new DoubleWritable(distance));
            }
        }
    }
    

    ShortestPathComputation 繼承了 BasicComputation,對於 BasicComputation 中指定的四個類型參數,其含義依次爲

    • Vertex id

    • Vertex data

    • Edge data

    • Message

    整個計算的過程可以總結爲:

    1. Superstep 0 的時候進行初始化,然後源頭頂點向鄰接頂點發送可能的最短路徑 message
    2. 下一 Superstep 鄰接頂點處理接收到的 message 然後和頂點值進行比較,如果 message 小於頂點值則對頂點值進行更新,並向鄰接頂點發送可能的最短路徑 message。
    3. 重複 2 直到所有的頂點處於不活躍狀態,最後結束計算進行輸出。
  4. 提交驗證

    驗證部分基於 Pseudo-Distributed 模式的 Hadoop 進行

    • 上傳測試文件到 HDFS

      $HADOOP_HOME/bin/hdfs dfs -put giraph_data.txt <your_input_path>/shortestpath
      #giraph_data 中的數據,格式爲 [source_id,source_value,[[dest_id, edge_value],...]],
      # 如 [0,0,[[1,1],[3,3]]] 表示頂點序號爲 0,頂點值爲 0,存在序號 0 到 序號 1 的邊,值爲 1,存在序號 0 到序號 3 的邊,值爲 3。
      [0,100,[[1,1],[3,3]]]
      [1,20,[[0,1],[2,2],[3,1]]]
      [2,90,[[1,2],[4,4]]]
      [3,50,[[0,3],[1,1],[4,4]]]
      [4,80,[[3,4],[2,4]]]
      
    • 將工程打包爲 jar 文件,並提交到 Hadoop

      $HADOOP_HOME/bin/hadoop jar Examples.jar org.apache.giraph.GiraphRunner com.ikroal.shortestpath.ShortestPathComputation -vif org.apache.giraph.io.formats.JsonLongDoubleFloatDoubleVertexInputFormat -vip <your_input_path>/giraph_data.txt -vof org.apache.giraph.io.formats.IdWithValueTextOutputFormat -op <your_output>/shortestpaths -w 1
      

      提交任務的過程基本與一般的提交過程一致,只是還額外指定了 Giraph 運行所需要的參數,對於相關參數的解釋請參考 {% post_link Giraph環境搭建 Giraph 環境搭建%} 。

    • 查看結果

      運行之後將會在輸出路徑下看到輸出文件,其內容爲

      0	0.0
      1	1.0
      2	3.0
      3	2.0
      4	6.0
      

總結

Giraph 的編程過程可以總結爲:

  • 繼承 BasicComputation,實現 compute 方法

    compute 方法主要完成頂點的計算更新和必要的消息發送

  • 命令行提交 Job

    由於系統已經提供了 GiraphRunner 的主類,所以大部分情況下用戶不需要編寫 Job 的配置和提交過程,但是如果 GiraphRunner 不滿足用戶需求,用戶也可以自定義主類然後命令行提交的時候進行指定

以上僅僅說明了最基本的 Giraph 編程過程,但如果想要實現性能最優的圖計算過程,則還需要考慮編程過程中利用 AggregatorsCombiners 機制,相關示例可以參考 giraph 源碼的 giraph-examples 部分。

源碼編譯調試

環境要求

Giraph 源碼的編譯調試要求 Java 1.8、Maven 3 以上版本以及 Hadoop2.5.1

導入源碼

  1. 下載源碼

    git clone https://github.com/apache/giraph.git
    
  2. 編譯源碼

    mvn -Phadoop_2 -DskipTests clean package
    

    成功後將會輸出

  3. 使用 IDEA 打開 giraph 源碼目錄

    在 Maven 的 Profies 頁面選擇 hadoop2 ( 默認是 hadoop1 )

增加自定義入口

Giraph 提供了一個位於 giraph-core/src/main/java/org/apache/giraph 下的入口類 GiraphRunner。但該類比較繁瑣並且不能自動刪除輸出文件,不太利於本地調試閱讀 Giraph 的源碼。因此最好是增加一個自定義的入口類。

  • 在 org.apache.giraph 包下創建 custom 包

  • 在 custom 包下創建用於測試的 Shortestpath 類(內容與編程實踐部分一致)

  • 在 custom 包下創建自定義入口類 CustomRunner

    import org.apache.giraph.conf.GiraphConfiguration;
    import org.apache.giraph.conf.GiraphConstants;
    import org.apache.giraph.io.formats.*;
    import org.apache.giraph.job.GiraphJob;
    import org.apache.hadoop.conf.Configuration;
    import org.apache.hadoop.fs.Path;
    
    import java.io.File;
    
    public class CustomRunner {
    
        /**
         * 輸入路徑
         */
        private static final String INPUT_PATH = "giraph-core/src/main/resources/input/graph_data.txt";
    
        /**
         * 輸出路徑
         */
        private static final String OUTPUT_PATH = "giraph-core/src/main/resources/output/shortestPath";
    
        public static void main(String[] args) throws Exception {
            GiraphConfiguration conf = new GiraphConfiguration(new Configuration());
            conf.setComputationClass(Shortestpath.class);
            //設置輸入和輸出格式
            conf.setVertexInputFormatClass(JsonLongDoubleFloatDoubleVertexInputFormat.class);
            conf.setVertexOutputFormatClass(IdWithValueTextOutputFormat.class);
            //設置本地運行模式
            conf.setLocalTestMode(true);
            //設置 worker 配置
            conf.setWorkerConfiguration(1, 1, 100);
            //可選,如果要學習 Checkpoint 機制應該設置
            conf.setCheckpointFrequency(4);
            GiraphConstants.SPLIT_MASTER_WORKER.set(conf, false);
    
            GiraphJob job = new GiraphJob(conf, Shortestpath.class.getSimpleName());
            //設置輸入和輸出路徑
            GiraphTextInputFormat.setVertexInputPath(conf, new Path(INPUT_PATH));
            GiraphTextOutputFormat.setOutputPath(job.getInternalJob(), new Path(OUTPUT_PATH));
            //刪除之前的輸出
            deletePath(OUTPUT_PATH, true);
            job.run(true);
        }
    
        /**
         * 用於刪除輸出目錄
         *
         * @param path 目錄路徑
         */
        public static void deletePath(String path, boolean isDirectory) {
            File file = new File(path);
            if (file.exists()) {
                //本地目錄遞歸刪除
                if (isDirectory) {
                    File[] subFiles = file.listFiles();
                    for (File subFile : subFiles) {
                        if (subFile.isFile()) {
                            subFile.delete();
                        } else {
                            deletePath(subFile.getPath(), true);
                        }
                    }
                }
                file.delete();
            }
        }
    }
    
  • 在 resources 文件夾創建 input 文件夾並放入編程實踐中的測試數據 graph_data.txt

  • 驗證

    修改配置並運行如果在 resources 文件夾下看到輸出文件,證明添加自定義入口成功,此時可以進行斷點調試

問題

  1. 運行時提示 TestYarnJob 中的 MiniYARNCluster 缺失問題

    對於 Test 部分的內容因爲不影響源碼閱讀,可以將出錯部分註釋掉

Thanks

  1. Introduction to Apache Giraph
  2. Building and Testing
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章