第106讲: Spark Streaming电商广告点击综合案例黑名单过滤实现

有兴趣想学习国内整套Spark+Spark Streaming+Machine learning顶级课程的,可加我qq  471186150。共享视频,性价比超高!
package com.dt.streaming;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

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.function.FlatMapFunction;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.spark.api.java.function.VoidFunction;
import org.apache.spark.streaming.Durations;
import org.apache.spark.streaming.api.java.JavaDStream;
import org.apache.spark.streaming.api.java.JavaPairDStream;
import org.apache.spark.streaming.api.java.JavaPairInputDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext;
import org.apache.spark.streaming.kafka.KafkaUtils;

import kafka.serializer.StringDecoder;
import scala.Tuple2;
/**
 * 
 *第106讲: 在线处理广告点击流
 * 广告点击的基本数据格式:timestamp、ip、userID、adID、province、city
 * 
 * @author hp
 *
 */
public class AdClickedStreamingStats {

   public static void main(String[] args) {
      
  
      SparkConf conf = new SparkConf().setMaster("local[5]").
            setAppName("AdClickedStreamingStats");
      
      /*SparkConf conf = new SparkConf().setMaster("spark://Master:7077").
            setAppName("SparkStreamingOnKafkaReceiver");*/
      
  
      JavaStreamingContext jsc = new JavaStreamingContext(conf, Durations.seconds(10));
      
    
      
      /**
       * 创建Kafka元数据,来让Spark Streaming这个Kafka Consumer利用
       */
      Map<String, String> kafkaParameters = new HashMap<String, String>();
      kafkaParameters.put("metadata.broker.list", 
            "Master:9092,Worker1:9092,Worker2:9092");
      
      Set<String> topics =  new HashSet<String>();
      topics.add("AdClicked");
      
      JavaPairInputDStream<String, String> adClickedStreaming = KafkaUtils.createDirectStream(jsc, 
            String.class, String.class, 
            StringDecoder.class, StringDecoder.class,
            kafkaParameters, 
            topics);
      /*
       * 第四步:接下来就像对于RDD编程一样基于DStream进行编程!!!原因是DStream是RDD产生的模板(或者说类),在Spark Streaming具体
       * 发生计算前,其实质是把每个Batch的DStream的操作翻译成为对RDD的操作!!!
       *对初始的DStream进行Transformation级别的处理,例如map、filter等高阶函数等的编程,来进行具体的数据计算
        *     广告点击的基本数据格式:timestamp、ip、userID、adID、province、city
        */
   
      
      JavaPairDStream<String, Long> pairs = adClickedStreaming.mapToPair(new PairFunction<Tuple2<String,String>, String, Long>() {

         @Override
         public Tuple2<String, Long> call(Tuple2<String, String> t) throws Exception {
            String[] splited = t._2.split("\t");
            
            String timestamp = splited[0]; //yyyy-MM-dd,必须这种格式,才可能同一个key出现俩次,因为时间是唯一的
            String ip = splited[1];
            String userID = splited[2];
            String adID = splited[3];
            String province = splited[4];
            String city = splited[5];
            
            String clickedRecord = timestamp + "_" + ip + "_" + userID + "_" + adID + "_" 
                  + province + "_" + city;
            
            return new Tuple2<String, Long>(clickedRecord, 1L);
         }
      });
      
       /*
          * 第四步:对初始的DStream进行Transformation级别的处理,例如map、filter等高阶函数等的编程,来进行具体的数据计算
          *   计算每个Batch Duration中每个User的广告点击量
          */
      JavaPairDStream<String, Long> adClickedUsers = pairs.reduceByKey(new Function2<Long, Long, Long>(){

         @Override
         public Long call(Long v1, Long v2) throws Exception {
            // TODO Auto-generated method stub
            return v1 + v2;//加上时间维度,不可能同一个key出现俩次,因为时间是唯一的
         }
            
      });
      
      
      /**
       * 
       * 计算出什么叫有效的点击?
       * 1,复杂化的一般都是采用机器学习训练好模型直接在线进行过滤;
       * 2,简单的?可以通过一个Batch Duration中的点击次数来判断是不是非法广告点击,但是实际上讲非法广告
       * 点击程序会尽可能模拟真实的广告点击行为,所以通过一个Batch来判断是 不完整的,我们需要对例如一天(也可以是每一个小时)
       * 的数据进行判断!
       * 3,比在线机器学习退而求次的做法如下:
       *        例如:一段时间内,同一个IP(MAC地址)有多少个用户的帐号访问;
       *        例如:可以统计一天内一个用户点击广告的次数,如果一天点击同样的广告操作50次的话,就列入黑名单;
       * 
       * 黑名单有一个重点的特征:动态生成!!!所以每一个Batch Duration都要考虑是否有新的黑名单加入,此时黑名单需要存储起来
       * 具体存储在什么地方呢,存储在DB/Redis中即可;
       * 
       * 例如邮件系统中的“黑名单”,可以采用Spark Streaming不断的监控每个用户的操作,如果用户发送邮件的频率超过了设定的值,可以
       * 暂时把用户列入“黑名单”,从而阻止用户过度频繁的发送邮件。
*filter就是过滤出条件判断为true的数据,顾虑白名单
       */
       
      JavaPairDStream<String, Long>  filteredClickInBatch = adClickedUsers.filter(new Function<Tuple2<String,Long>, Boolean>() {
         
         @Override
         public Boolean call(Tuple2<String, Long> v1) throws Exception {
            if ( 1 < v1._2){
               //更新一下黑名单的数据表
               return false;
            } else {
               return true;
            }
            
         }
      });
      
      // Todo。。。。
      
      /*
       * 此处的print并不会直接出发Job的执行,因为现在的一切都是在Spark Streaming框架的控制之下的,对于Spark Streaming
       * 而言具体是否触发真正的Job运行是基于设置的Duration时间间隔的
       * 
       * 诸位一定要注意的是Spark Streaming应用程序要想执行具体的Job,对Dtream就必须有output Stream操作,
       * output Stream有很多类型的函数触发,类print、saveAsTextFile、saveAsHadoopFiles等,最为重要的一个
       * 方法是foraeachRDD,因为Spark Streaming处理的结果一般都会放在Redis、DB、DashBoard等上面,foreachRDD
       * 主要就是用用来完成这些功能的,而且可以随意的自定义具体数据到底放在哪里!!!
       *
       */
//    filteredClickInBatch.print();

      filteredClickInBatch.foreachRDD(new Function<JavaPairRDD<String,Long>, Void>() {

         @Override
         public Void call(JavaPairRDD<String, Long> rdd) throws Exception {
            rdd.foreachPartition(new VoidFunction<Iterator<Tuple2<String,Long>>>() {
               
               @Override
               public void call(Iterator<Tuple2<String, Long>> partition) throws Exception {
                  /**
                   * 在这里我们使用数据库连接池的高效读写数据库的方式把数据写入数据库MySQL;
                   * 由于传入的参数是一个Iterator类型的集合,所以为了更加高效的操作我们需要批量处理
                   * 例如说一次性插入1000条Record,使用insertBatch或者updateBatch类型的操作;
                   * 插入的用户信息可以只包含:useID、adID、clickedCount、time
                   * 这里面有一个问题:可能出现两条记录的Key是一样的,此时就需要更新累加操作
                   * Batch里面这个放进去的就是这个用户对这个广告点击的次数,如果发现数据库中有这个数据,,就累加,每10秒更新一次
                   */
               }
            });
            return null;
         }
      });
      
      //上面更新数据库,这是过滤历史上累计下来的所有的黑名单,上面是每个batch的黑名单。
//filter就是过滤出条件判断为true的数据
JavaPairDStream<String, Long> blackListBasedOnHistory = filteredClickInBatch.filter(new Function<Tuple2<String,Long>, Boolean>() { @Override public Boolean call(Tuple2<String, Long> v1) throws Exception { //这里可以拿到所有点击的历史数据,先提取出数据 //广告点击的基本数据格式:timestamp、ip、userID、adID、province、city String[] splited = v1._1.split("\t"); String date = splited[0]; String userID = splited[2]; String adID = splited[3]; /** * 接下来根据date、userID、adID等条件去查询用户点击广告的数据表,获得总的点击次数 * 这个时候基于点击次数判断是否属于黑名单点击 同一个user对同一个adId点击超过50, 就是黑名单 * */ int clickedCountTotalToday = 81;//这里假设从数据库中获得的总点击次数 if (clickedCountTotalToday > 50) { return true; } else { return false; } } }); /** * 必须对黑名单的整个RDD进行去重操作!!!因为不同partition里面可能包含同样的黑名单用户,RDD去重,则Partition肯定也去重。 * 下面2步,对Dstream中的RDD中的元素进行去重操作。 */ JavaDStream<String> blackListuserIDtBasedOnHistory = blackListBasedOnHistory.map(new Function<Tuple2<String,Long>, String>() { @Override public String call(Tuple2<String, Long> v1) throws Exception { // TODO Auto-generated method stub return v1._1.split("\t")[2];//取userId } }); //这里拿到去重后的内容 JavaDStream<String> blackListUniqueuserIDtBasedOnHistory = blackListuserIDtBasedOnHistory.transform(new Function<JavaRDD<String>, JavaRDD<String>>() { @Override public JavaRDD<String> call(JavaRDD<String> rdd) throws Exception { // TODO Auto-generated method stub return rdd.distinct(); } }); //下一步写入黑名单数据表中 //把数据保存在数据库中 blackListUniqueuserIDtBasedOnHistory.foreachRDD(new Function<JavaRDD<String>, Void>() { @Override public Void call(JavaRDD<String> rdd) throws Exception { // 遍历里面的数据,写入外部存储系统 rdd.foreachPartition(new VoidFunction<Iterator<String>>() { @Override public void call(Iterator<String> t) throws Exception { /**上面Iterator<String> t是一条完整的记录, * 在这里我们使用数据库连接池的高效读写数据库的方式把数据写入数据库MySQL; * 由于传入的参数是一个Iterator类型的集合,所以为了更加高效的操作我们需要批量处理 * 例如说一次性插入1000条Record,使用insertBatch或者updateBatch类型的操作; * 插入的用户信息可以只包含:useID * 此时直接插入黑名单数据表即可。 */ } }); return null; } }); /* * Spark Streaming执行引擎也就是Driver开始运行,Driver启动的时候是位于一条新的线程中的,当然其内部有消息循环体,用于 * 接受应用程序本身或者Executor中的消息; */ jsc.start(); jsc.awaitTermination(); jsc.close(); }}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值