任務要求如下:
- 實現Spark和HIVE與HDFS之間的通信
- 利用Spark.sql從HIVE中獲取數據,並進行預處理以符合Spark機器學習庫中輸入文件的libsvm格式
- LinearRegression線性迴歸模型建模
- 將得到的預測結果寫回HDFS
現在明確了我們的任務目標,就從頭開始進行。
配置Spark和Hive之間的通信
Spark內部可以直接讓SparkContext從hdfs上獲取數據(保證和集羣具有相同的網絡環境)。
//舉例
//創建sparkConf對象,設置spark應用的配置信息
SparkConf conf = new SparkConf()
.setAppName("WordCount")
.setMaster("local"); //spark應用程序要連接的spark集羣的master節點的url,local代表的是本地運行
//.setMaster("spark://ip:port");
//創建JavaSparkContext對象
JavaSparkContext sc = new JavaSparkContext(conf);
//針對輸入源(hdfs文件、本地文件等)創建一個初始的RDD
JavaRDD<String> lines = sc.textFile("hfs://master:9000/wordcount.txt");
在Spark1.6開始之後增加了DataSet數據類型,我們注意到通過SparkContext獲得的數據爲RDD類型,而通過SparkSession獲得的數據爲DataSet數據類型。
val sparkSession = SparkSession.builder.
master("local")
.appName("example")
.getOrCreate()
val data = sparkSession.read.text("hdfs://master:9000/wordcount.txt").as[String]
這裏的data是DataSet類型的,如果要轉化爲java可操作的數據類型List或者Array,之後會介紹。因此對於Spark通信hdfs不需要在配置上花費時間。
只需要實現Spark到Hive之間的通信,需要將Hive/conf目錄下的hive-site.xml配置文件複製到Spark/Conf目錄下(這裏假設Hive已經配置好,能和hdfs交互,這裏不贅述);同時還要將JDBC連接器copy到Spark/jars下面,例如mysql-connector-5.4.7.jar類似的,如果連接器版本過高要下載更低版本的。如果出現其他問題,就在Google吧。
Spark從Hive中獲取數據
這裏直接粘一下我的項目代碼
SparkConf conf = new SparkConf().setAppName("sterilizedmilk").setMaster("local");;
SparkSession spark = SparkSession
.builder()
.appName("sterilizedmilk")
.config(conf)
.enableHiveSupport() //支持hive
.getOrCreate();
String querySql = "SELECT * FROM myth.sterilizedmilk";
Dataset<Row> data = spark.sql(querySql);
data_sterilizedmilk = data.collectAsList();
之前說過Spark封裝了自己的sql函數,跟其他的sql語法一樣,只需要寫成字符串執行就可。查詢到的結果爲DataSet<Row>類型的數據,這個類型自帶了兩個函數:
- collect():可以將數據類型返回Array格式
- collectAsList():將數據類型返回List格式
獲得的結果可以直接java遍歷操作。下面就需要將數據以Spark.ml算法官方文檔中使用的文件結構存儲。libsvm格式如下:
Label1 para1:value1 para2:value1
Label2 para1:value2 para2:value2
這裏就不沾代碼了,直接按照格式寫入文件就成。
構建LinearRegression模型
這部分沒什麼好說的,直接去看官方文檔給的示例吧!很清楚也很簡潔Spark.ml傳送門
將預測結果寫會HDFS
我這裏選擇的是追加的方式直接將結果寫回hdfs上,也可以保存在服務器本地,再上傳至hdfs(個人覺得麻煩)。
public static void write_prediction_hdfs(List<Row> prediction, String path) throws Exception{
//獲取precition數據
int interval = (int)(data_regression.size() * 0.9);
for(int i = interval; i < data_regression.size(); i++){
ArrayList<Float> temp = data_regression.get(i);
temp.add(Float.parseFloat(prediction.get(i-interval).get(2).toString()));
data_prediction.add(temp);
}
//將prediction數據寫入hdfs中
Configuration conf = new Configuration();
conf.set("fs.defaultFS","hdfs://master:9000");
FileSystem fs = FileSystem.get(conf);
FSDataOutputStream out = fs.create(new Path(path));
//寫出
for(int i = 0; i < data_prediction.size(); i++) {
String re = "";
for(int j = 0;j<data_prediction.get(i).size();j++) {
if(j == data_prediction.get(i).size()-1){
re = re + data_prediction.get(i).get(j).toString() + "\n";
}
else{
re = re + data_prediction.get(i).get(j).toString() + " ";
}
}
out.write(re.getBytes("UTF-8"));
}
out.close();
}
可以看到通過追加的方式寫hdfs,就需要配置項,也就是Configuration,這個是hadoop.conf包下的Configuration,通過這個來鏈接hdfs,傳入的path就是hdfs下的目錄,文件將要寫入的位置。for循環裏面的都是我爲了數據格式好看一些做的一些操作,嫌麻煩可以直接把數據每行toString()之後就寫。
項目的完整代碼已經提交到Github上,需要自取:傳送門