spark+mongodb + quartz

由于业务数据量大,使用普通查询统计已不能满足需求,所以使用spark+mongodb进行聚合统计,两种方案

1使用quartz调度spark,定时进行业务数据统计

2使用crontab调度spark,定时进行业务数据统计


 为了便于管理最终使用方案1

 quartz调度代码这里就不展示了,只写spark+mongodb调用

1 引入maven依赖

          <dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.10</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.mongodb.spark</groupId>
<artifactId>mongo-spark-connector_2.10</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_2.10</artifactId>
<version>1.6.2</version>
</dependency> 


第一种方式:使用mongodb做聚合,spark将数据转换成document,然后保存

public class UserFlowCount
{

public static void main(String args[]) throws IOException
{
//spark mongodb 连接配置
SparkConf sc = new SparkConf()
.setMaster("spark://10.1.12.4228:2000") //spark master 地址
.setAppName("MongoSparkConnectorTour") //应用名称
.set("spark.mongodb.input.uri",
"mongodb://bssd:[email protected]:34234/bss.AccountDeductionDetailRecord_bak")//mongodb input 连接
.set("spark.mongodb.output.uri",
"mongodb://bssdb:[email protected]:234234/bss.myCollection")//mongodb output 连接
.set("spark.mongodb.input.partitioner", "MongoPaginateBySizePartitioner")
.set("spark.cores.max", "12").set("spark.driver.port", "20898")
.set("spark.fileserver.port", "20896").set("spark.broadcast.port", "20895")
.set("spark.blockManager.port", "20894").set("spark.executor.port", "20893")
.set("spark.replClassServer.port", "20892");

//依赖的jar
String[] jars = new String[] {
"D:\\maven\\org\\mongodb\\mongo-java-driver\\3.2.2\\mongo-java-driver-3.2.2.jar",
"D:\\maven\\org\\mongodb\\spark\\mongo-spark-connector_2.10\\1.0.0\\mongo-spark-connector_2.10-1.0.0.jar",
"E:\\DEV-PLATFORM\\ucl-platform\\code\\demo\\spark\\target\\spark-example-0.0.1-SNAPSHOT.jar"};


sc.setJars(jars);


JavaSparkContext jsc = new JavaSparkContext(sc); // Create a Java Spark

        //设置mongodb 读配置
Map<String, String> readOverrides = new HashMap<String, String>();
readOverrides.put("collection", "AccountDeductionDetailRecord_bak");// 读取的collection
readOverrides.put("readPreference.name", "secondaryPreferred");//读从节点优先
ReadConfig readConfig = ReadConfig.create(jsc).withOptions(readOverrides);


//读取collection,过滤条件{ $match: { createDate : { $gte : 1466956800000,$lte : 1467043200000} } }
JavaRDD<Document> customRdd = MongoSpark.load(jsc, readConfig).withPipeline(
java.util.Collections.singletonList(Document
.parse("{ $match: { createDate : { $gte : 1466956800000,$lte : 1467043200000} } }")));


//根据userName 聚合flowSize
JavaPairRDD<String, Long> result = customRdd.mapToPair(
new PairFunction<Document, String, Long>() {


public Tuple2<String, Long> call(Document arg0) throws Exception
{
// TODO Auto-generated method stub


return new Tuple2(arg0.getString("userName"), arg0.getLong("flowSize"));
}


}).cache();

//如果连起来写会出现执行时间特别长,还未弄清楚原因

//为何需要缓存当你在Spark代码中多次对一个RDD做了算子操作后,恭喜,你已经实现Spark作业第一步的优化了,也就是尽可能复用RDD。此时就该在这个基础之上,进行////第二步优化了,也就是要保证对一个RDD执行多次算子操作时,这个RDD本身仅仅被计算一次。

//Spark中对于一个RDD执行多次算子的默认原理是这样的:每次你对一个RDD执行一个算子操作时,都会重新从源头处计算一遍,计算出那个RDD来,然后再对这个RDD执//行你的算子操作。这种方式的性能是很差的。

//因此对于这种情况,我们的建议是:对多次使用的RDD进行持久化。此时Spark就会根据你的持久化策略,将RDD中的数据保存到内存或者磁盘中。以后每次对这个RDD进行//算子操作时,都会直接从内存或磁盘中提取持久化的RDD数据,然后执行算子,而不会从源头处重新计算一遍这个RDD,再执行算子操作

result  = result .reduceByKey(new Function2<Long, Long, Long>() {


public Long call(Long arg0, Long arg1) throws Exception
{
// TODO Auto-generated method stub
return arg0 + arg1;
}


});

//将聚合结果转化为Document
JavaRDD<Document> saveR = result.map(new Function<Tuple2<String,Long>,Document>(){


public Document call(Tuple2<String, Long> arg0) throws Exception
{
// TODO Auto-generated method stub
return Document.parse("{userName:\""+arg0._1+"\",flowSize:"+arg0._2+"}");
}

});

//将结果保存回mongodb
Map<String, String> writeOverrides = new HashMap<String, String>();
writeOverrides.put("collection", "spark");
writeOverrides.put("writeConcern.w", "majority");
WriteConfig writeConfig = WriteConfig.create(jsc).withOptions(writeOverrides);
MongoSpark.save(saveR,writeConfig);


System.out.println("success");
//System.out.println(customRdd.first().toJson());
}   


 第2中方式,直接读取数据,用spark的sql做聚合

public class CdrCount
{
public static void main(String args[]) throws IOException
{
SparkConf sc = new SparkConf().setMaster("spark://10.1.123.123:20898").setAppName("MongoSparkConnectorTour")
.set("spark.mongodb.input.uri", "mongodb://bssdb:[email protected]:2000/bss.AccountDeductionDetailRecord_bak")
.set("spark.mongodb.output.uri", "mongodb://bssdb:[email protected]:2000/bss.myCollection")
.set("spark.mongodb.input.partitioner", "MongoPaginateBySizePartitioner")
.set("spark.cores.max", "12")
.set("spark.driver.port","20898")
.set("spark.fileserver.port","20896")
.set("spark.broadcast.port","20895")
.set("spark.blockManager.port", "20894")
.set("spark.executor.port", "20893")
.set("spark.replClassServer.port", "20892");
String[] jars = new String[] {
"D:\\maven\\org\\mongodb\\mongo-java-driver\\3.2.2\\mongo-java-driver-3.2.2.jar",
"D:\\maven\\org\\mongodb\\spark\\mongo-spark-connector_2.10\\1.0.0\\mongo-spark-connector_2.10-1.0.0.jar" };

sc.setJars(jars);


JavaSparkContext jsc = new JavaSparkContext(sc); // Create a Java Spark // Context

SQLContext sqlContext = SQLContext.getOrCreate(jsc.sc());
Map<String, String> readOverrides = new HashMap<String, String>();
readOverrides.put("collection", "AccountDeductionDetailRecord_bak");
readOverrides.put("readPreference.name", "secondaryPreferred");
ReadConfig readConfig = ReadConfig.create(jsc).withOptions(readOverrides);

//定义DataFrame
DataFrame df = MongoSpark.load(jsc,readConfig).toDF();
//过滤数据
df = df.filter(df.col("createDate").between(1466956800000L, 1467043200000L));
//映射表
df.registerTempTable("flowLog");
        //聚合sql
DataFrame centenarians = sqlContext.sql("SELECT userName,sum(flowSize) FROM flowLog group by userName");
//mongodb保存
MongoSpark.save(centenarians.write().option("collection", "hundredClub").mode("append"));

}


}


注意:1 、work节点函数入口为main函数,如果没有main函数不能进入

            2 、JavaSparkContext 设置成全局变量,在入口处初始化一次,切记不可以频繁初始化,如果频繁初始化会造成老年代占用内存过大,GC频繁回收,而且spark使用堆外内存导致机器内存过高被OOM-KILL杀掉。

                  如果设置驱动节点内存最大值,又导致堆内存溢出。

                 将需要使用的参数通过main函数的String[] args传入,如果是对象可以序列化成Json串传入(项目中使用dubbo接口去查询其他模块对象),在解析即可。

           3、spark连接mongos和单点可以正常运行,主从未测试

           4 、javaRdd执行isEmpty(),take()、count()、collect()会从从远程集群上获取数据到本地,非常慢不建议使用



弹性分布式数据集是Apache Spark的核心理念。它是由数据组成的不可变分布式集合,其主要进行两个操作:transformation和action。Transformation是类似在RDD上做 filter()、map()或union() 以生成另一个RDD的操作,而action则是count()、first()、take(n)、collect() 等促发一个计算并返回值到Master或者稳定存储系统的操作。Transformations一般都是lazy的,直到action执行后才会被执行。Spark Master/Driver会保存RDD上的Transformations。这样一来,如果某个RDD丢失(也就是salves宕掉),它可以快速和便捷地转换到集群中存活的主机上。这也就是RDD的弹性所在。


猜你喜欢

转载自blog.csdn.net/changyuan101/article/details/53257799