首页 > 数据库 > > Linux环境Spark安装配置及使用
MapReduce的核心是Shuffle(洗牌)。在整个Shuffle的过程中,至少会产生6次的I/O。
中间结果输出:基于MapReduce的计算引擎通常会将中间结果输出到磁盘上,进行存储和容错。另外,当一些查询(如:Hive)翻译到MapReduce任务时,往往会产生多个Stage(阶段),而这些串联的Stage又依赖于底层文件系统(如HDFS)来存储每一个Stage的输出结果,而I/O的效率往往较低,从而影响了MapReduce的运行速度。
tar -zxvf .tar.gz -C 目标目录mv spark-2.1.0-bin-hadoop2.7/ spark-2.1.0mv spark-env.sh.template spark-env.shvi spark-env.shexport JAVA_HOME=/opt/module/jdk1.8.0_144
export SPARK_MASTER_HOST=bigdata01
export SPARK_MASTER_PORT=7077
复制代码mv slaves.template slavesvi slavesbigdata02
bigdata03
复制代码vi /etc/profileexport SPARK_HOME=spark安装路径export PATH=$PATH:$SPARK_HOME/binexport PATH=$PATH:$SPARK_HOME/sbinsource /etc/profilescp -r spark-2.1.0/ bigdata02:$PWDscp -r spark-2.1.0/ bigdata03:$PWDstart-master.shstart-slaves.shspark-shellstop-master.shstop-slaves.sh主要用于开发或测试环境。
当spark提供目录保存spark Application和worker的注册信息,并将他们的恢复状态写入该目录中,一旦Master发生故障,就可以通过重新启动Master进程(sbin/start-master.sh),恢复已运行的spark Application和worker的注册信息。
基于文件系统的单点恢复,主要是在spark-env.sh里对SPARK_DAEMON_JAVA_OPTS设置
mkdir /opt/module/spark-2.1.0/recoveryvi spark-env.shexport SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=FILESYSTEM -Dspark.deploy.recoveryDirectory=/opt/module/spark-2.1.0/recovery"适用于现实生产。
ZooKeeper提供了一个Leader Election机制,利用这个机制可以保证虽然集群存在多个Master,但是只有一个是Active的,其他的都是Standby。当Active的Master出现故障时,另外的一个Standby Master会被选举出来。由于集群的信息,包括Worker,Driver和Application的信息都已经持久化到ZooKeeper,因此在切换的过程中只会影响新Job的提交,对于正在进行的Job没有任何的影响。加入ZooKeeper的集群整体架构如下图所示:
修改配置信息:
vi spark-env.shexport SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER -Dspark.deploy.zookeeper.url=bigdata01:2181,bigdata02:2181,bigdata03:2181 -Dspark.deploy.zookeeper.dir=/spark"export SPARK_MASTER_HOST和export SPARK_MASTER_PORT发送新的配置文件到集群其余节点:
scp spark-env.sh bigdata02:$PWDscp spark-env.sh bigdata03:$PWDspark-submit --master spark://XXXX:7077 (指明master地址)
--class org.apache.spark.examples.SparkPi (指明主程序的名字)
/XXXX/spark/examples/jars/spark-examples_2.11-2.1.0.jar(指明jar包地址)
100(指明运行次数)--master spark://XXXX:7077 指定Master的地址--executor-memory 2g 指定每个worker可用内存为2G--total-executor-cores 2 指定整个集群使用的cup核数为2个spark-shell(后面不接任何参数)spark-shell --master spark://XXXX:7077(指明master地址)spark-shellsc.textFile("/XXXX/WordCount.txt")(本地文件路径).flatMap(_.split(" "))(按照空格分割).map((_,1))(单词遍历).reduceByKey(_+_)(单词计数).collectspark-shell --master spark://XXXX:7077(指spark-shell scala> val rdd1 = sc.textFile("/root/sp_wc.txt")
rdd1: org.apache.spark.rdd.RDD[String] = /root/sp_wc.txt MapPartitionsRDD[1] at textFile at <console>:24
scala> rdd1.collect
res0: Array[String] = Array(I love Scala, I love Skark, 2019/5/8)
scala> val rdd2 = rdd1.flatMap(_.split(" "))
rdd2: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[2] at flatMap at <console>:26
scala> rdd2.collect
res1: Array[String] = Array(I, love, Scala, I, love, Skark, 2019/5/8)
scala> val rdd3 = rdd2.map((_,1))
rdd3: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[3] at map at <console>:28
scala> rdd3.collect
res2: Array[(String, Int)] = Array((I,1), (love,1), (Scala,1), (I,1), (love,1), (Skark,1), (2019/5/8,1))
scala> val rdd4 = rdd3.reduceByKey(_+_)
rdd4: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[4] at reduceByKey at <console>:30
scala> rdd4.collect
res3: Array[(String, Int)] = Array((2019/5/8,1), (love,2), (I,2), (Skark,1), (Scala,1))
复制代码import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
object WordCount {
def main(args: Array[String]): Unit = {
//创建一个Spark配置文件
val conf = new SparkConf().setAppName("Scala WordCount").setMaster("local")
//创建Spark对象
val sc = new SparkContext(conf)
val result = sc.textFile(args(0))
.flatMap(_.split(" "))
.map((_, 1))
.reduceByKey(_ + _)
.saveAsTextFile(args(1))
sc.stop()
}
}
复制代码import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.FlatMapFunction;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;
import parquet.format.PageHeader;
import scala.Tuple2;
public class WordCount {
public static void main(String[] args) {
// TODO Auto-generated method stub
SparkConf conf = new SparkConf()
.setAppName("JavaWordCount")
.setMaster("local") ;
//新建SparkContext对象
JavaSparkContext sc = new JavaSparkContext(conf) ;
//读入数据
JavaRDD<String> lines = sc.textFile("hdfs://XXXX:9000/WordCount.txt") ;
//分词 第一个参数表示读进来的话 第二个参数表示 返回值
JavaRDD<String> words = lines.flatMap(new FlatMapFunction<String, String>() {
@Override
public Iterator<String> call(String input) throws Exception {
return Arrays.asList(input.split(" ")).iterator() ;
}
}) ;
//每个单词记一次数
/*
* String, String, Integer
* input <key value>
*/
JavaPairRDD<String, Integer> ones = words.mapToPair(new PairFunction<String, String, Integer>() {
@Override
public Tuple2<String, Integer> call(String input) throws Exception {
return new Tuple2<String, Integer>(input, 1) ;
}
}) ;
//执行reduce操作
/*
* Integer, Integer, Integer
* nteger arg0, Integer arg1 返回值
*/
JavaPairRDD<String,Integer> counts = ones.reduceByKey(new Function2<Integer, Integer, Integer>() {
@Override
public Integer call(Integer arg0, Integer arg1) throws Exception {
// TODO Auto-generated method stub
return arg0 + arg1 ;
}
}) ;
//打印结果
List<Tuple2<String, Integer>> output = counts.collect() ;
for (Tuple2<String, Integer> tuple :output) {
System.out.println(tuple._1 + " : " + tuple._2) ;
}
sc.stop() ;
}
}
复制代码val rdd1 = sc.textFile(“hdfs://XXXX:9000/data.txt”)val rdd1 = sc.parallelize(Array(1,2,3,4,5,6,7,8))RDD的依赖关系
RDD和它依赖的父RDD(s)的关系有两种不同的类型,即窄依赖(narrow dependency)和宽依赖(wide dependency)。
窄依赖指的是每一个父RDD的Partition最多被子RDD的一个Partition使用
宽依赖指的是多个子RDD的Partition会依赖同一个父RDD的Partition
Spark任务中的Stage
练习1:
//通过并行化生成rdd
val rdd1 = sc.parallelize(List(5, 6, 4, 7, 3, 8, 2, 9, 1, 10))
//对rdd1里的每一个元素乘2然后排序
val rdd2 = rdd1.map(_ * 2).sortBy(x => x, true)
//过滤出大于等于十的元素
val rdd3 = rdd2.filter(_ >= 10)
//将元素以数组的方式在客户端显示
rdd3.collect
复制代码练习2:
val rdd1 = sc.parallelize(Array("a b c", "d e f", "h i j"))
//将rdd1里面的每一个元素先切分在压平
val rdd2 = rdd1.flatMap(_.split(' '))
rdd2.collect
复制代码练习3:
val rdd1 = sc.parallelize(List(5, 6, 4, 3))
val rdd2 = sc.parallelize(List(1, 2, 3, 4))
//求并集
val rdd3 = rdd1.union(rdd2)
//求交集
val rdd4 = rdd1.intersection(rdd2)
//去重
rdd3.distinct.collect
rdd4.collect
复制代码练习4:
val rdd1 = sc.parallelize(List(("tom", 1), ("jerry", 3), ("kitty", 2)))
val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
//求jion
val rdd3 = rdd1.join(rdd2)
rdd3.collect
//求并集
val rdd4 = rdd1 union rdd2
//按key进行分组
rdd4.groupByKey
rdd4.collect
复制代码练习5:
val rdd1 = sc.parallelize(List(("tom", 1), ("tom", 2), ("jerry", 3), ("kitty", 2)))
val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
//cogroup
val rdd3 = rdd1.cogroup(rdd2)
//注意cogroup与groupByKey的区别
rdd3.collect
复制代码练习6:
val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5))
//reduce聚合
val rdd2 = rdd1.reduce(_ + _)
rdd2.collect
复制代码练习7:
val rdd1 = sc.parallelize(List(("tom", 1), ("jerry", 3), ("kitty", 2), ("shuke", 1)))
val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 3), ("shuke", 2), ("kitty", 5)))
val rdd3 = rdd1.union(rdd2)
//按key进行聚合
val rdd4 = rdd3.reduceByKey(_ + _)
rdd4.collect
//按value的降序排序
val rdd5 = rdd4.map(t => (t._2, t._1)).sortByKey(false).map(t => (t._2, t._1))
rdd5.collect
复制代码def mapPartitionsWithIndex[U](f: (Int, Iterator[T]) ⇒ Iterator[U], preservesPartitioning: Boolean = false)(implicit arg0: ClassTag[U]): RDD[U]val rdd1 = sc.parallelize(List(1,2,3,4,5,6,7,8,9), 2)def func1(index:Int, iter:Iterator[Int]):Iterator[String] ={
iter.toList.map( x => "[PartID:" + index + ", value=" + x + "]" ).iterator
}
复制代码rdd1.mapPartitionsWithIndex(func1).collectval rdd1 = sc.parallelize(List(1,2,3,4,5), 2)
查看每个分区中的元素:
scala> rdd1.mapPartitionsWithIndex(fun1).collect
res4: Array[String] = Array(
[partId : 0 , value = 1 ], [partId : 0 , value = 2 ],
[partId : 1 , value = 3 ], [partId : 1 , value = 4 ], [partId : 1 , value = 5 ])
复制代码将每个分区中的最大值求和,注意初始值是0:
scala> rdd2.aggregate(0)(max(_,_),_+_)
res6: Int = 7
复制代码scala> rdd2.aggregate(100)(max(_,_),_+_)
res8: Int = 300
```
复制代码如果是求和,注意初始值是0:
scala> rdd2.aggregate(0)(_+_,_+_)
res9: Int = 15
复制代码scala> rdd2.aggregate(10)(_+_,_+_)
res10: Int = 45
复制代码e.g. —— 字符串:
val rdd2 = sc.parallelize(List("a","b","c","d","e","f"),2)def func2(index: Int, iter: Iterator[(String)]) : Iterator[String] = {
iter.toList.map(x => "[partID:" + index + ", val: " + x + "]").iterator
}
复制代码[partID:0, val: a], [partID:0, val: b], [partID:0, val: c],
[partID:1, val: d], [partID:1, val: e], [partID:1, val: f]
复制代码e.g.:
val rdd3 = sc.parallelize(List("12","23","345","4567"),2)
rdd3.aggregate("")((x,y) => math.max(x.length, y.length).toString, (x,y) => x + y)
复制代码结果可能是24,也可能是42
val rdd4 = sc.parallelize(List("12","23","345",""),2)
rdd4.aggregate("")((x,y) => math.min(x.length, y.length).toString, (x,y) => x + y)
复制代码结果是10,也可能是01
原因:注意有个初始值"",其长度0,然后0.toString变成字符串
val rdd5 = sc.parallelize(List("12","23","","345"),2)
rdd5.aggregate("")((x,y) => math.min(x.length, y.length).toString, (x,y) => x + y)
复制代码结果是11,原因同上。
准备数据:
val pairRDD = sc.parallelize(List( ("cat",2), ("cat", 5), ("mouse", 4),("cat", 12), ("dog", 12), ("mouse", 2)), 2)
def func3(index: Int, iter: Iterator[(String, Int)]) : Iterator[String] = {
iter.toList.map(x => "[partID:" + index + ", val: " + x + "]").iterator
}
复制代码两个分区中的元素:
e.g.:
scala> pairRDD.aggregateByKey(0)(math.max(_, _), _ + _).collect
res69: Array[(String, Int)] = Array((dog,12), (cat,17), (mouse,6))
复制代码scala> pairRDD.aggregateByKey(0)(_+_, _ + _).collect
res71: Array[(String, Int)] = Array((dog,12), (cat,19), (mouse,6))
复制代码scala> pairRDD.reduceByKey(_+_).collect
res73: Array[(String, Int)] = Array((dog,12), (cat,19), (mouse,6))
复制代码def func4(index: Int, iter: Iterator[(Int)]) : Iterator[String] = {
iter.toList.map(x => "[partID:" + index + ", val: " + x + "]").iterator
}
val rdd1 = sc.parallelize(List(1,2,3,4,5,6,7,8,9), 2)
下面两句话是等价的:
val rdd2 = rdd1.repartition(3)
val rdd3 = rdd1.coalesce(3,true) -> 如果是false,查看RDD的length依然是2
复制代码Tomcat的访问日志如下:
需求:找到访问量最高的两个网页,要求显示网页名称和访问量
步骤分析:
代码:
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
object TomcatLogCount {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local").setAppName("TomcatLogCount")
val sc = new SparkContext(conf)
/*
* 读入日志并解析
*
* 192.168.88.1 - - [30/Jul/2017:12:54:37 +0800] "GET /MyDemoWeb/oracle.jsp HTTP/1.1" 200 242
* */
val rdd1 = sc.textFile(" ").map(
line => {
//解析字符串,得到jsp的名字
//1. 解析两个引号间的字符串
val index1 = line.indexOf("\"")
val index2 = line.lastIndexOf("\"")
//line1 = GET /MyDemoWeb/oracle.jsp HTTP/1.1
val line1 = line.substring(index1 + 1, index2)
val index3 = line1.indexOf(" ")
val index4 = line1.lastIndexOf(" ")
//line2 = /MyDemoWeb/oracle.jsp
val line2 = line1.substring(index3 + 1, index4)
//得到jsp的名字 oracle.jsp
val jspName = line2.substring(line2.lastIndexOf("/"))
(jspName, 1)
}
)
//统计每个jsp的次数
val rdd2 = rdd1.reduceByKey(_+_)
//使用Value排序
val rdd3 = rdd2.sortBy(_._2, false)
//得到次数最多的两个jsp
rdd3.take(2).foreach(println)
sc.stop()
}
}
复制代码生成的分区文件
如:part-00000文件中的内容:只包含了web.jsp的访问日志
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import scala.collection.mutable.HashMap
object TomcatLogPartitioner {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local").setAppName("TomcatLogPartitioner")
val sc = new SparkContext(conf)
/*
* 读入日志并解析
*
* 192.168.88.1 - - [30/Jul/2017:12:54:37 +0800] "GET /MyDemoWeb/oracle.jsp HTTP/1.1" 200 242
* */
val rdd1 = sc.textFile(" ").map(
line => {
//解析字符串,得到jsp的名字
//1. 解析两个引号间的字符串
val index1 = line.indexOf("\"")
val index2 = line.lastIndexOf("\"")
//line1 = GET /MyDemoWeb/oracle.jsp HTTP/1.1
val line1 = line.substring(index1 + 1, index2)
val index3 = line1.indexOf(" ")
val index4 = line1.lastIndexOf(" ")
//line2 = /MyDemoWeb/oracle.jsp
val line2 = line1.substring(index3 + 1, index4)
//得到jsp的名字 oracle.jsp
val jspName = line2.substring(line2.lastIndexOf("/"))
(jspName, line)
}
)
//得到不重复的jsp名字
val rdd2 = rdd1.map(_._1).distinct().collect()
//创建分区规则
val wepPartitioner = new WepPartitioner(rdd2)
val rdd3 = rdd1.partitionBy(wepPartitioner)
//输出rdd3
rdd3.saveAsTextFile(" ")
}
//定义分区规则
class WepPartitioner(jspList : Array[String]) extends Partitioner {
/*
* 定义集合来保存分区条件:
* String 代表jsp的名字
* Int 代表序号
* */
val partitionMap = new HashMap[String, Int]()
//初始分区号
val partID = 0
//填值
for (jsp <- jspList) {
patitionMap.put(jsp, partID)
partID += 1
}
//返回分区个数
def numPartitioners : Int = partitionMap.size
//根据jsp,返回对应的分区
def getPartition(key : Any) : Int = partitionMap.getOrElse(key.toString(), 0)
}
}
复制代码JdbcRDD参数说明:
从上面的参数说明可以看出,JdbcRDD有以下两个缺点:
代码:
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import java.sql.Connection
import java.sql.DriverManager
import java.sql.PreparedStatement
/*
* 把Spark结果存放到mysql数据库中
*
*/
object TomcatLogCountToMysql {
def main(args: Array[String]): Unit = {
//创建SparkContext
val conf = new SparkConf().setMaster("local").setAppName("MyTomcatLogCountToMysql")
val sc = new SparkContext(conf)
/*
*
* 读入日志 解析:
*
* 192.168.88.1 - - [30/Jul/2017:12:54:37 +0800] "GET /MyDemoWeb/oracle.jsp HTTP/1.1" 200 242
*/
val rdd1 = sc.textFile("H:\\tmp_files\\localhost_access_log.txt")
.map(
line => {
//解析字符串,得到jsp的名字
//1、解析两个引号之间的字符串
val index1 = line.indexOf("\"")
val index2 = line.lastIndexOf("\"")
val line1 = line.substring(index1 + 1, index2) // GET /MyDemoWeb/oracle.jsp HTTP/1.1
//得到两个空格的位置
val index3 = line1.indexOf(" ")
val index4 = line1.lastIndexOf(" ")
val line2 = line1.substring(index3 + 1, index4) // /MyDemoWeb/oracle.jsp
//得到jsp的名字
val jspName = line2.substring(line2.lastIndexOf("/")) // oracle.jsp
(jspName, 1)
})
//
// try {
// /*
// * create table mydata(jsname varchar(50),countNumber Int)
// *
// * foreach 没有返回值,在本需求中,只需要写数据库,不需要返回新的RDD,所以用foreach即可
// *
// *
// * 运行 Task not serializable
// */
// conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/company?serverTimezone=UTC&characterEncoding=utf-8", "root", "123456")
// pst = conn.prepareStatement("insert into mydata values(?,?)")
//
// rdd1.foreach(f => {
// pst.setString(1, f._1)
// pst.setInt(2, f._2)
//
// pst.executeUpdate()
// })
// } catch {
// case t: Throwable => t.printStackTrace()
// } finally {
// if (pst != null) pst.close()
// if (conn != null) conn.close()
// }
//
// sc.stop()
// //存入数据库
// var conn: Connection = null
// var pst: PreparedStatement = null
// //第一种修改方法
// /*
// * 修改思路:
// * conn pst 让每一个节点都是用到,需要在不同的节点上传输,实现sericalizable接口
// */
// try {
// rdd1.foreach(f => {
// conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/company?serverTimezone=UTC&characterEncoding=utf-8", "root", "123456")
// pst = conn.prepareStatement("insert into mydata values(?,?)")
//
// pst.setString(1, f._1)
// pst.setInt(2, f._2)
//
// pst.executeUpdate()
// })
// } catch {
// case t: Throwable => t.printStackTrace()
// } finally {
// if (pst != null) pst.close()
// if (conn != null) conn.close()
// }
//
// sc.stop()
/*
* 第一种修改方式,功能上可以实现,但每条数据都会创建连接,对数据库造成很大压力
*
* 针对分区来操作:一个分区,建立一个连接即可
*/
rdd1.foreachPartition(saveToMysql)
sc.stop()
}
def saveToMysql(it: Iterator[(String, Int)]) = {
var conn: Connection = null
var pst: PreparedStatement = null
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/company?serverTimezone=UTC&characterEncoding=utf-8", "root", "123456")
pst = conn.prepareStatement("insert into mydata values(?,?)")
it.foreach(f => {
pst.setString(1, f._1)
pst.setInt(2, f._2)
pst.executeUpdate()
})
} catch {
case t: Throwable => t.printStackTrace()
} finally {
if (pst != null) pst.close()
if (conn != null) conn.close()
}
}
}
复制代码<1>. DataFrame
DataFrame是组织成命名列的数据集。它在概念上等同于关系数据库中的表,但在底层具有更丰富的优化。DataFrames可以从各种来源构建,
例如:
DataFrame API支持的语言有Scala,Java,Python和R。
从上图可以看出,DataFrame多了数据的结构信息,即schema。RDD是分布式的 Java对象的集合。DataFrame是分布式的Row对象的集合。DataFrame除了提供了比RDD更丰富的算子以外,更重要的特点是提升执行效率、减少数据读取以及执行计划的优化。
<2>. Datasets
<1>. 创建 DataFrames
case class Emp(empno:Int,ename:String,job:String,mgr:Int,hiredate:String,sal:Int,comm:Int,deptno:Int)val lines = sc.textFile("/XXXX/emp.csv").map(_.split(","))val allEmp = lines.map(x => Emp(x(0).toInt,x(1),x(2),x(3).toInt,x(4),x(5).toInt,x(6).toInt,x(7).toInt))val df1 = allEmp.toDFdf1.showimport org.apache.spark.sql.types._,import org.apache.spark.sql.Row import org.apache.spark.sql.types._
val myschema = StructType(
List(
StructField("empno",DataTypes.IntegerType),
StructField("ename",DataTypes.StringType),
StructField("job",DataTypes.StringType),
StructField("mgr",DataTypes.IntegerType),
StructField("hiredate",DataTypes.StringType),
StructField("sal",DataTypes.IntegerType),
StructField("comm",DataTypes.IntegerType),
StructField("deptno",DataTypes.IntegerType),
))
val allEmp = lines.map(x => Row(x(0).toInt,x(1),x(2),x(3).toInt,x(4),x(5).toInt,x(6).toInt,x(7).toInt))
import org.apache.spark.sql.Row
val df2 = spark.createDataFrame(allEmp,myschema)
复制代码val df3 = spark.read 读文件,默认是Parquet文件
val df3 = spark.read.json("/XXXX/people.json") 读json文件
df3.show
val df4 = spark.read.format("json").load("/XXXX/people.json")
复制代码<2>. DataFrame 操作
DataFrame操作也称为无类型的Dataset操作
a. DSL语句
b. SQL语句
df.createOrReplaceTempView("emp")spark.sql("select * from emp").showspark.sql("select * from emp where deptno=10").showspark.sql("select deptno,sum(sal) from emp group by deptno").show<1>. 普通视图(本地视图)——createOrReplaceTempView
<2>. 全局视图: ——createGlobalTempView
e.g.: ``` 创建一个新session,读取不到emp视图 spark.newSession.sql("select * from emp")
以下两种方式均可读到 全局视图 中的数据:
df1.createGlobalTempView("emp1")
spark.newSession.sql("select * from global_temp.emp1").show
spark.sql("select * from global_temp.emp1").show
复制代码
复制代码case class MyData(a:Int,b:String)val ds = Seq(MyData(1,"Tom"),MyData(2,"Mary")).toDSds.showcase class Person(name: String, gender: String)val df = spark.read.json(sc.parallelize("""{"gender": "Male", "name": "Tom"}""" :: Nil))df.as[Person].showdf.as[Person].collectval linesDS = spark.read.text("hdfs://XXXX:9000/XXXX/data.txt").as[String]val words = linesDS.flatMap(_.split(" ")).filter(_.length > 3)
words.show
words.collect
复制代码val result = linesDS.flatMap(_.split(" ")).map((_,1)).groupByKey(x => x._1).count
result.show
排序:result.orderBy($"value").show
复制代码val empDF = spark.read.json("/XXXX/emp.json")
查询工资大于3000的员工
empDF.where($"sal" >= 3000).show
复制代码case class Emp(empno:Long,ename:String,job:String,hiredate:String,mgr:String,sal:Long,comm:String,deptno:Long) val empDS = empDF.as[Emp]
查询工资大于3000的员工
empDS.filter(_.sal > 3000).show
查看10号部门的员工
empDS.filter(_.deptno == 10).show
复制代码 val deptRDD=sc.textFile("/XXXX/dept.csv").map(_.split(","))
case class Dept(deptno:Int,dname:String,loc:String)
val deptDS = deptRDD.map(x=>Dept(x(0).toInt,x(1),x(2))).toDS
复制代码复制代码case class Emp(empno:Int,ename:String,job:String,mgr:String,hiredate:String,sal:Int,comm:String,deptno:Int)
val empRDD = sc.textFile("/XXXX/emp.csv").map(_.split(","))
val empDS = empRDD.map(x => Emp(x(0).toInt,x(1),x(2),x(3),x(4),x(5).toInt,x(6),x(7).toInt)).toDS
复制代码val result = deptDS.join(empDS,"deptno")
另一种写法:注意有三个等号
val result = deptDS.joinWith(empDS,deptDS("deptno")=== empDS("deptno"))
joinWith和join的区别是连接后的新Dataset的schema会不一样
复制代码result.explain 读取 users.parquet 文件(Spark自带的示例文件)
val userDF = spark.read.load("/root/users.parquet")
查看结构:
userDF.printSchema
查看内容:
userDF.show
读取json文件:
val userDF = spark.read.load("/root/emp.json") ——>报错
正确方法:
val userDF = spark.read.format("json").load("/root/emp.json")
val userDF = spark.read.json("/root/emp.json")
保存parquet文件到本地路径:
userDF.select($"name",$"favorite_color").write.save("/root/parquet")
读取刚写入的文件:
val userDF1 = spark.read.load("/root/parquet/part-00000-888d505a-7d51-4a50-aaf5-2bbdb56e67a1.snappy.parquet") --> 不推荐
生产:(直接读目录)
val userDF2 = spark.read.load("/usr/local/tmp_files/parquet")
复制代码调用save函数的时候,可以指定存储模式,追加、覆盖等等
可以采用SaveMode执行存储操作,SaveMode定义了对数据的处理模式。需要注意的是,这些保存模式不使用任何锁定,不是原子操作。此外,当使用Overwrite方式执行时,在输出新数据之前原数据就已经被删除。SaveMode详细介绍如下表:
userDF2.write.save("/root/parquet") ——>报错
save的时候覆盖:
userDF2.write.mode("overwrite").save("/root/parquet")
将结果保存成表:
userDF2.select($"name").write.saveAsTable("table1")
查看数据:
spark.sql("select * from table2").show
也可以进行分区、分桶等操作:partitionBy、bucketBy
复制代码读入json格式的数据,将其转换成parquet格式,并创建相应的表来使用SQL进行查询。(把数据读进来,再写出去,就是Parquet文件)
读入文件:
val empDF = spark.read.json("/root/emp.json")
写出文件:
empDF.write.mode("overwrite").save("/root/parquet")
empDF.write.mode("overwrite").parquet("/root/parquet")
建表查询:
val emp1 = spark.read.parquet("/root/parquet")
emp1.createOrReplaceTempView("emp1")
spark.sql("select * from emp1").show
复制代码通过RDD来创建DataFrame:
val df1 = sc.makeRDD(1 to 5).map( i => (i,i*2)).toDF("single","double") ——>"single","double" 是表结构
df1.show
df1.write.mode("overwrite").save("/root/test_table/key=1")
val df2 = sc.makeRDD(6 to 10).map( i => (i,i*3)).toDF("single","triple")
df2.show
df2.write.mode("overwrite").save("/root/test_table/key=2")
合并两个部分:
val df3 = spark.read.parquet("/root/tmp_files/test_table")
val df3 = spark.read.option("mergeSchema",true).parquet("/root/tmp_files/test_table")
复制代码读取Json文件,生成DataFrame:
val peopleDF = spark.read.json("/usr/local/tmp_files/people.json")
打印Schema结构信息:
peopleDF.printSchema
创建临时视图:
peopleDF.createOrReplaceTempView("peopleView")
执行查询:
spark.sql("select * from peopleView").show
Spark SQL 支持统一的访问接口。对于不同的数据源,读取进来,生成DataFrame后,操作完全一样。
复制代码spark-shell --master spark://XXXX:7077 --jars /XXXX/.jar --driver-class-path /XXXX/.jar val mysqlDF = spark.read.format("jdbc").option("url","jdbc:mysql://XXXX:3306/company?serverTimezone=UTC&characterEncoding=utf-8").option("user","root").option("password","123456").option("driver","com.mysql.cj.jdbc.Driver").option("dbtable","emp").load
mysqlDF.show```
复制代码 import java.util.Properties
val mysqlProps = new Properties()
mysqlProps.setProperty("user","root")
mysqlProps.setProperty("password","123456")
val mysqlDF1 = spark.read.jdbc("jdbc:mysql://XXXX:3306/company?serverTimezone=UTC&characterEncoding=utf-8","emp",mysqlProps)
mysqlDF1.show ```
复制代码spark.sql("create table spark.emp1(empno Int,ename String,job String,mgr String,hiredate String,sal Int,comm String,deptno Int)row format delimited fields terminated by ','")spark.sql("load data local inpath '/root/emp.csv' overwrite into table spark.emp1")spark.sql("select * from spark.emp1").show package Demo
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.types.StructType
import org.apache.spark.sql.types.StructField
import org.apache.spark.sql.types.IntegerType
import org.apache.spark.sql.types.StringType
import org.apache.spark.sql.Row
import org.apache.log4j.Logger
import org.apache.log4j.Level
/*创建DataFrame StructType方式*/
object Demo01 {
def main(args: Array[String]): Unit = {
Logger.getLogger("org.apache.spark").setLevel(Level.ERROR)
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)
// 创建Spark Session对象
val spark = SparkSession.builder().master("local").appName("Demo1").getOrCreate()
// 从指定的地址创建RDD对象
/*1 Tom 12
*2 Mary 13
*3 Lily 15
* */
val personRDD = spark.sparkContext.textFile("/Users/apple/Documents/student.txt").map(_.split("\t"))
// 通过StructType方式指定Schema
val schema = StructType(
List(
StructField("id", IntegerType),
StructField("name", StringType),
StructField("age", IntegerType)))
// 将RDD映射到rowRDD上,映射到Schema上
val rowRDD = personRDD.map(p => Row(p(0).toInt, p(1), p(2).toInt))
val personDataFrame = spark.createDataFrame(rowRDD, schema)
// 注册视图
personDataFrame.createOrReplaceTempView("t_person")
//执行SQL语句
val df = spark.sql("select * from t_person order by age desc")
df.show()
spark.stop()
}
复制代码复制代码 package Demo
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.types.StructType
import org.apache.spark.sql.types.StructField
import org.apache.spark.sql.types.IntegerType
import org.apache.spark.sql.types.StringType
import org.apache.spark.sql.Row
import org.apache.log4j.Logger
import org.apache.log4j.Level
/*使用case Class来创建DataFrame*/
object Demo02 {
def main(args: Array[String]): Unit = {
Logger.getLogger("org.apache.spark").setLevel(Level.ERROR)
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)
//创建Spark Session对象
val spark = SparkSession.builder().master("local").appName("Demo2").getOrCreate()
//从指定的地址创建RDD对象
val lineRDD = spark.sparkContext.textFile("").map(_.split("\t"))
//把数据与case class做匹配
val studentRDD = lineRDD.map(x => Student(x(0).toInt, x(1), x(2).toInt))
//生成DataFrame
import spark.sqlContext.implicits._
val studentDF = studentRDD.toDF
//注册视图 执行SQL
studentDF.createOrReplaceTempView("student")
spark.sql("select * from student").show
spark.stop()
}
}
//定义case class
case class Student(stuId: Int, stuName: String, stuAge: Int)
复制代码package Demo
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.types.StructType
import org.apache.spark.sql.types.StructField
import org.apache.spark.sql.types.IntegerType
import org.apache.spark.sql.types.StringType
import org.apache.spark.sql.Row
import org.apache.log4j.Logger
import org.apache.log4j.Level
import java.util.Properties
/*写入MySQL*/
object Demo03 {
def main(args: Array[String]): Unit = {
Logger.getLogger("org.apache.spark").setLevel(Level.ERROR)
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)
//创建Spark Session对象
val spark = SparkSession.builder().master("local").appName("Demo3").getOrCreate()
//从指定的地址创建RDD对象
val lineRDD = spark.sparkContext.textFile("").map(_.split("\t"))
//通过StructType方式指定Schema
val schema = StructType(
List(
//字段与MySQL表中字段对应一致
StructField("personID", IntegerType),
StructField("personName", StringType),
StructField("personAge", IntegerType)))
//将RDD映射到rowRDD上,映射到Schema上
val rowRDD = lineRDD.map(p => Row(p(0).toInt,p(1),p(2).toInt))
val personDataFrame = spark.createDataFrame(rowRDD, schema)
personDataFrame.createOrReplaceTempView("myperson")
val result = spark.sql("select * from myperson")
result.show()
//把结果存入到mysql中
val props = new Properties()
props.setProperty("user", "root")
props.setProperty("password", "123456")
//append追加模式
result.write.mode("append").jdbc("jdbc:mysql://localhost:3306/company?serverTimezone=UTC&characterEncoding=utf-8", "student", props)
spark.stop()
}
}
复制代码package Demo
import org.apache.spark.sql.SparkSession
import java.util.Properties
/*使用Spark SQL 读取Hive中的数据,将计算结果存入mysql*/
//命令:./bin/spark-submit --master spark://node3:7077 --jars /usr/local/tmp_files/mysql-connector-java-8.0.11.jar --driver-class-path /usr/local/tmp_files/mysql-connector-java-8.0.11.jar --class day0410.Demo4 /usr/local/tmp_files/Demo4.jar
object Demo4 {
def main(args: Array[String]): Unit = {
//创建SparkSession
val spark = SparkSession.builder().appName("Demo4").enableHiveSupport().getOrCreate()
//执行SQL
val result = spark.sql("select deptno,count(1) from company.emp group by deptno")
//将结果保存到mysql中
val props = new Properties()
props.setProperty("user", "root")
props.setProperty("password", "123456")
result.write.mode("append").jdbc("jdbc:mysql://192.168.109.1:3306/company?serverTimezone=UTC&characterEncoding=utf-8", "emp_stat", props)
spark.stop()
}
}
复制代码<1>. 在内存中缓存数据
操作mysql,启动spark shell 时,需要:
./bin/spark-shell --master spark://node3:7077 --jars /usr/local/tmp_files/mysql-connector-java-8.0.11.jar --driver-class-path /usr/local/tmp_files/mysql-connector-java-8.0.11.jar
val mysqlDF = spark.read.format("jdbc").option("driver","com.mysql.jdbc.Driver").option("url","jdbc:mysql://192.168.109.1:3306/company?serverTimezone=UTC&characterEncoding=utf-8").option("user","root").option("password","123456").option("dbtable","emp").load
mysqlDF.show
mysqlDF.createOrReplaceTempView("emp")
spark.sqlContext.cacheTable("emp") ----> 标识这张表可以被缓存,数据还没有真正被缓存
spark.sql("select * from emp").show ----> 依然读取mysql
spark.sql("select * from emp").show ----> 从缓存中读取数据
spark.sqlContext.clearCache
清空缓存后,执行查询,会触发查询mysql数据库。
复制代码<2>. 性能优化相关参数
a. 将数据缓存到内存中的相关优化参数:
spark.sql.inMemoryColumnarStorage.compressed
spark.sql.inMemoryColumnarStorage.batchSize
b. 其他性能相关的配置选项(不过不推荐手动修改,可能在后续版本自动的自适应修改)
yum install nc.x86_64)nc -l 1234 (-l监听模式;1234端口号)run-example streaming.NetworkWordCount localhost 1234/**
*
* @ClassName: MyNetworkWordCount
* @Description
* @Author: YBCarry
* @Date2019-05-13 20:49
* @Version: V1.0
*
**/
import org.apache.spark.streaming.StreamingContext
import org.apache.spark.SparkConf
import org.apache.spark.streaming.Seconds
import org.apache.spark.storage.StorageLevel
import org.apache.log4j.Logger
import org.apache.log4j.Level
import org.apache.spark.internal.Logging
/*
* 自写流式计算程序
*
* 知识点:
* 1、创建一个StreamingContext对象 --> 核心:创建一个DStream
* 2、DStream的表现形式:就是一个RDD
* 3、使用DStream把连续的数据流变成不连续的RDD
*
* spark Streaming 最核心的内容
*/
object MyNetworkWordCount {
def main(args: Array[String]): Unit = {
//创建一个Streaming Context对象
//local[2] 表示开启了两个线程
val conf = new SparkConf().setAppName("MyNetworkWordCount").setMaster("local[2]")
//Seconds(3) 表示采样时间间隔
val ssc = new StreamingContext(conf, Seconds(3))
//创建DStream 从netcat服务器上接收数据
val lines = ssc.socketTextStream("172.16.194.128", 1234, StorageLevel.MEMORY_ONLY)
//lines中包含了netcat服务器发送过来的数据
//分词操作
val words = lines.flatMap(_.split(" "))
//计数
val wordPair = words.transform(x => x.map(x => (x, 1)))
//打印结果
wordPair.print()
//启动StreamingContext 进行计算
ssc.start()
//等待任务结束
ssc.awaitTermination()
}
}
复制代码transform(func)
updateStateByKey(func)
重写NetworkWordCount程序,累计每个单词出现的频率(注意:累计)
package test.Network
import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.storage.StorageLevel
import org.apache.spark.streaming.{Seconds, StreamingContext}
/**
*
* @ClassName: TotalNetworkWordCount
* @Description: 实现累加操作
* @Author: YBCarry
* @Date2019-05-15 16:05
* @Version: V1.0
*
**/
object TotalNetworkWordCount {
def main(args: Array[String]): Unit = {
Logger.getLogger("org.apache.spark").setLevel(Level.ERROR)
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)
//创建一个Streaming Context对象
//local[2] 表示开启了两个线程
val conf = new SparkConf().setAppName("MyNetworkWordCount").setMaster("local[2]")
//Seconds(3) 表示采样时间间隔
val ssc = new StreamingContext(conf, Seconds(3))
//设置检查点目录,保存之前都的状态信息
ssc.checkpoint("")
//创建DStream
val lines = ssc.socketTextStream("bigdata01", 1234, StorageLevel.MEMORY_ONLY)
//分割
val words = lines.flatMap(_.split(" "))
//计数
// val wordPair = words.map((_, 1))
val wordPair = words.transform( x => x.map(x => (x, 1)))
//定义一个值函数 ;累加计数
/*
* 接收两个参数
* currentValues —— 当前值
* previousValue ——历史值
* */
val addFunc = (currentValues : Seq[Int], previousValues : Option[Int]) => {
//累加当前的序列
val currrentTotal = currentValues.sum
//累加历史值
Some(currrentTotal + previousValues.getOrElse(0))
}
//累加运算
val total = wordPair.updateStateByKey(addFunc)
total.print()
ssc.start()
ssc.awaitTermination()
}
}
复制代码
复制代码Spark Streaming还提供了窗口计算功能,允许在数据的滑动窗口上应用转换操作。下图说明了滑动窗口的工作方式:
如图所示,每当窗口滑过originalDStream时,落在窗口内的源RDD被组合并被执行操作以产生windowed DStream的RDD。在上面的例子中,操作应用于最近3个时间单位的数据,并以2个时间单位滑动。这表明任何窗口操作都需要指定两个参数。
e.g.: 假设对之前的单词计数的示例进行扩展,每10秒钟对过去30秒的数据进行wordcount。则在最近30秒的pairs DStream数据中对(word, 1)键值对应用reduceByKey操作。这是通过使用reduceByKeyAndWindow操作完成的。
package test.NetworkByWindow
import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.storage.StorageLevel
import org.apache.spark.streaming.{Seconds, StreamingContext}
/**
*
* @ClassName: NetworkWordCountByWindow
* @Description: 每10秒读取过去30秒的数据
* @Author: YBCarry
* @Date2019-05-15 17:00
* @Version: V1.0
*
**/
object NetworkWordCountByWindow {
def main(args: Array[String]): Unit = {
Logger.getLogger("org.apache.spark").setLevel(Level.ERROR)
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)
//创建一个Streaming Context对象
//local[2] 表示开启了两个线程
val conf = new SparkConf().setAppName("MyNetworkWordCount").setMaster("local[2]")
//Seconds(3) 表示采样时间间隔
val ssc = new StreamingContext(conf, Seconds(3))
//设置检查点目录,保存之前都的状态信息
ssc.checkpoint("")
//创建DStream
val lines = ssc.socketTextStream("bigdata01", 1234, StorageLevel.MEMORY_ONLY)
//分割 每个单词计数
val words = lines.flatMap(_.split(" ")).map((_, 1))
/*
* 窗口操作
* 参数说明:要进行的操作 窗口的大小(30s) 窗口移动距离(12s) ——> 采样时间(3)的整数倍
* */
val result = words.reduceByKeyAndWindow((x : Int, y : Int) => (x + y), Seconds(30), Seconds(12))
}
}
复制代码<1>. 文件流:通过监控文件系统的变化,若有新文件添加,则将它读入并作为数据流
import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
/**
*
* @ClassName: FileStreaming
* @Description
* @Author: YBCarry
* @Date2019-05-16 09:24
* @Version: V1.0
*
**/
object FileStreaming {
def main(args: Array[String]): Unit = {
Logger.getLogger("org.apache.spark").setLevel(Level.ERROR)
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)
//创建一个Streaming Context对象
//local[2] 表示开启了两个线程
val conf = new SparkConf().setAppName("MyFileStreaming").setMaster("local[2]")
//Seconds(3) 表示采样时间间隔
val ssc = new StreamingContext(conf, Seconds(10))
//监控目录,读取产生的新文件
val lines = ssc.textFileStream("\\Users\\apple\\学习\\SparkFiles")
lines.print()
ssc.start()
ssc.awaitTermination()
}
}
复制代码<2>. RDD队列流
package test.RDDQueue
import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.{Seconds, StreamingContext}
import scala.collection.mutable.Queue
/**
*
* @ClassName: RDDQueueStream
* @Description: RDD队列流
* @Author: YBCarry
* @Date2019-05-16 10:48
* @Version: V1.0
*
**/
object RDDQueueStream {
def main(args: Array[String]): Unit = {
Logger.getLogger("org.apache.spark").setLevel(Level.ERROR)
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)
//创建一个Streaming Context对象
//local[2] 表示开启了两个线程
val conf = new SparkConf().setAppName("MyRDDQueueStream").setMaster("local[2]")
//Seconds(3) 表示采样时间间隔
val ssc = new StreamingContext(conf, Seconds(3))
//创建队列 RDD[Int]
val rddQueue = new Queue[RDD[Int]]()
//向队列里添加数据 (创建数据源)
for (i <- 1 to 3) {
rddQueue += ssc.sparkContext.makeRDD(1 to 10)
//便于观察
Thread.sleep(1000)
}
//从队列中接收数据,创建DStream
val inputDStream = ssc.queueStream(rddQueue)
//处理数据
val result = inputDStream.map(x => (x, x * 2))
result.print()
ssc.start()
ssc.awaitTermination()
}
}
复制代码<3>. 套接字流:通过监听Socket端口来接收数据
#定义agent名, source、channel、sink的名称
a4.sources = r1
a4.channels = c1
a4.sinks = k1
#具体定义source
a4.sources.r1.type = spooldir
a4.sources.r1.spoolDir = /usr/local/tmp_files/logs
#具体定义channel
a4.channels.c1.type = memory
a4.channels.c1.capacity = 10000
a4.channels.c1.transactionCapacity = 100
#具体定义sink
a4.sinks = k1
a4.sinks.k1.type = avro
a4.sinks.k1.channel = c1
a4.sinks.k1.hostname = bigdata01
a4.sinks.k1.port = 1234
#组装source、channel、sink
a4.sources.r1.channels = c1
a4.sinks.k1.channel = c1
复制代码package test.Flume
import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.flume.FlumeUtils
import org.apache.spark.streaming.{Seconds, StreamingContext}
/**
*
* @ClassName: MyFlumeStream
* @Description: flume将数据推送给Spark Streaming 使用push
* @Author: YBCarry
* @Date2019-05-16 14:01
* @Version: V1.0
*
**/
object MyFlumeStream01 {
def main(args: Array[String]): Unit = {
Logger.getLogger("org.apache.spark").setLevel(Level.ERROR)
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)
//创建一个Streaming Context对象
//local[2] 表示开启了两个线程
val conf = new SparkConf().setAppName("MyRDDQueueStream").setMaster("local[2]")
//Seconds(3) 表示采样时间间隔
val ssc = new StreamingContext(conf, Seconds(3))
//对接Flume
//创建一个Flumeevent从flume中接收puch来的数据(也是DStream)
//flume将数据push到localhost:1234,Spark Stream在这里监听
val flumeEventDStream = FlumeUtils.createStream(ssc, "bigdata01", 1234)
//将Flumeevent中的事件转换成字符串
val lineDStream = flumeEventDStream.map(e => {
new String(e.event.getBody.array)
})
//输出结果
lineDStream.print()
ssc.start()
ssc.awaitTermination()
}
}
复制代码flume-ng agent -n a4 -f Spark/MyFlumeStream01.conf -c conf -Dflume.root.logger=INFO,console**第一步:**Flume的配置文件
a1.channels = c1
a1.sinks = k1
a1.sources = r1
a1.sources.r1.type = spooldir
a1.sources.r1.spoolDir = /usr/local/tmp_files/logs
a1.channels.c1.type = memory
a1.channels.c1.capacity = 100000
a1.channels.c1.transactionCapacity = 100000
a1.sinks.k1.type = org.apache.spark.streaming.flume.sink.SparkSink
a1.sinks.k1.channel = c1
a1.sinks.k1.hostname = bigdata01
a1.sinks.k1.port = 1234
#组装source、channel、sink
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
复制代码**第二步:**Spark Streaming程序
复制代码package test.Flume
import org.apache.spark.streaming.StreamingContext import org.apache.spark.SparkConf import org.apache.spark.streaming.Seconds import org.apache.spark.storage.StorageLevel import org.apache.log4j.Logger import org.apache.log4j.Level import org.apache.spark.streaming.flume.FlumeUtils
/** *
**/ object FlumeLogPull {
def main(args: Array[String]): Unit = { Logger.getLogger("org.apache.spark").setLevel(Level.ERROR) Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF) //创建一个Streaming Context对象 //local[2] 表示开启了两个线程 val conf = new SparkConf().setAppName("FlumeLogPull").setMaster("local[2]") //Seconds(3) 表示采样时间间隔 val ssc = new StreamingContext(conf,Seconds(3))
//创建FlumeEvent的DStream,采用pull的方式
val flumeEvent = FlumeUtils.createPollingStream(ssc, "172.16.194.128",1234, StorageLevel.MEMORY_ONLY)
//将FlumeEvent的事件准换成字符串
val lineDStream = flumeEvent.map( e => {
new String(e.event.getBody.array)
})
//输出结果
lineDStream.print()
ssc.start()
ssc.awaitTermination()
}
复制代码
}
复制代码**第三步:**需要的jar包
spark-streaming-flume-sink_2.11-2.1.0.jar拷贝到Flume的lib目录下。**第四步:**测试
double wucha = 1.0
while ( wucha >= 0.00001 ) {
建模 wucha -= 某个值
}
模型计算完毕
复制代码来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/26692020/viewspace-3125379/ ,如需转载,请注明出处,否则将追究法律责任。