Kafka/Canal

1. 小小的知识点

1.kafka 发布与订阅的分布式(distributed)消息系统,让实时数据在之中排队,缓冲减压,存储实时数据
2.消息队列的应用场景:峰值压力缓冲
3.producer 数据的生产者
consumer 消息的消费者
broker kafka集群的server负责处理消息读写请求,存储数据
topic 消息队列,读写数据的单位
4.一个topic分成多个partition(为了做分布式),分区内部消息强有序分区之间是无序的,每个消息都有一个序号叫offset
5.一个partition中对应一个broker,一个broker可以管理多个partition(理解为一个磁盘文件)
6.消息不经过内内存,直接写入文件,对磁盘文件进行追加
7.根据时间策略删除数据,而不是消费完就删除,7天删除
8.生产者自己决定往哪一个分区内写数据,轮询写或者hash分区
9.消费者自己维护消费到哪个offset 顺序读写 0拷贝 批量处理
10.去中心化的架构,没有页面
11.同一个分区的副本不能放在同一个节点中,副本数不能大于Kafka节点数

2. Kafka搭建

2.1 上传解压修改环境变量

# 解压
tar -xvf kafka_2.11-1.0.0.tgz
# 配置环境变量
vim /etc/profile      
export KAFKA_HOME=/usr/local/soft/kafka_2.11-1.0.0
export PATH=$PATH:$KAFKA_HOME/bin
source /etc/profile

2.2 修改配置文件

vim config/server.properties
broker.id=0 每一个节点broker.id 要不一样
zookeeper.connect=master:2181,node1:2181,node2:2181
log.dirs=/usr/local/soft/kafka_2.11-1.0.0/data   数据存放的位置

2.3 将kafka文件同步到node1,node2

# 同步kafka文件
scp -r kafka_2.11-1.0.0/ node1:`pwd`
scp -r kafka_2.11-1.0.0/ node2:`pwd`

# 将master中的而环境变量同步到node1和node2中
scp /etc/profile node1:/etc/
scp /etc/profile node2:/etc/

#  在ndoe1和node2中执行source
source /etc/profile

2.4 修改node1和node2中的broker.id

vim config/server.properties
# node1
broker.id=1
# node2
broker.id=2

2.5 启动kafka

# 1、需要先穷zookeeper,  kafka使用zo保存元数据
# 需要在每隔节点中执行启动的命令
zkServer.sh start
# 查看启动的状体
zkServer.sh status
# 2、启动kafka,每个节点中都要启动(去中心化的架构)
# -daemon后台启动
kafka-server-start.sh -daemon /usr/local/soft/kafka_2.11-1.0.0/config/server.properties
kafka-server-stop.sh -daemon /usr/local/soft/kafka_2.11-1.0.0/config/server.properties

3. 使用Kafka

3.1 创建topic

--replication-factor  ---每一个分区的副本数量, 同一个分区的副本不能放在同一个节点,副本的数量不能大于kafak集群节点的数量
--partition   --分区数,  根据数据量设置
--zookeeper zk的地址,将topic的元数据保存在zookeeper中
命令:
kafka-topics.sh --create --zookeeper master:2181,node1:2181,node2:2181 --replication-factor 3 --partitions 3 --topic cars

在生产和消费数据时,如果topic不存在会自动创建一个分区为1,副本为1的topic

3.2 查看topic描述信息

kafka-topics.sh --describe  --zookeeper master:2181,node1:2181,node2:2181 --topic student

3.3 获取所有topic

__consumer_offsetsL kafka   用于保存消费偏移量的topic(不要用)
kafka-topics.sh --list  --zookeeper  master:2181,node1:2181,node2:2181

3.4 创建控制台生产者

kafka-console-producer.sh --broker-list master:9092,node1:9092,node2:9092 --topic cars

3.5 创建控制台消费者

--from-beginning   从头消费 如果不在执行消费的新的数据
kafka-console-consumer.sh --bootstrap-server  master:9092,node2:9092,node2:9092 --from-beginning --topic cars

4. Kafka保存数据的方式

# 1、保存的文件
/usr/local/soft/kafka_2.11-1.0.0/data

# 2,每一个分区每一个副本对应一个目录

# 3、每一个分区目录中可以有多个文件, 文件时滚动生成的,编号会往后加
00000000000000000000.log
00000000000000000001.log
00000000000000000002.log
# 4、滚动生成文件的策略
文件大小
log.segment.bytes=1073741824
时间
log.retention.check.interval.ms=300000

# 5、文件删除的策略,默认是7天,以文件为单位删除
log.retention.hours=168

# 6、节点挂了后重启会进行副本数据同步

5. flink消费和生产kafka案例

5.1 消费kafka中数据

导入依赖:(读什么数据导入什么依赖)
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-connector-kafka</artifactId>
            <version>${flink.version}</version>
        </dependency>

object Demo5kafka {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment =StreamExecutionEnvironment.getExecutionEnvironment
    val source: KafkaSource[String] = KafkaSource
      .builder[String]()
      .setBootstrapServers("master:9092,node1:9092,node2:9092") //kafka集群broker列表
      .setTopics("test_topic2") //指定topic
      .setGroupId("my-group") //指定消费者组,一条数据在一个组内只被消费一次
      .setStartingOffsets(OffsetsInitializer.earliest()) //读取数据的位置,earliest:读取所有的数据,latest:读取最新的数据
      .setValueOnlyDeserializer(new SimpleStringSchema()) //反序列的类
      .build
    //使用kafka source
    val kafkaDS: DataStream[String] = env.fromSource(source, WatermarkStrategy.noWatermarks(), "Kafka Source")
    kafkaDS.print()
    env.execute()
  }
}

5.2 生产数据到kafka

读取数据将DataStream保存到Kafka2中:
有唯一一次和至少一次两种模式
object Demo6KafkaSInk {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    val studentDS: DataStream[String] = env.readTextFile("data/students.json")
     // 将数据保存到kafka中 --- kafka sink
     //  DeliveryGuarantee.EXACTLY_ONCE: 唯一一次
    // DeliveryGuarantee.AT_LEAST_ONCE: 至少一次,默认
    val sink: KafkaSink[String] = KafkaSink
      .builder[String]()
      .setBootstrapServers("master:9092,node1:9092,node2:9092") //broker地址
      .setRecordSerializer(
        KafkaRecordSerializationSchema
          .builder[String]()
          .setTopic("students_json") //topic
          .setValueSerializationSchema(new SimpleStringSchema())
          .build())
      //.setDeliverGuarantee(DeliveryGuarantee.AT_LEAST_ONCE) //唯一一次
      .build()
    //使用kafka sink
    studentDS.sinkTo(sink)
    env.execute()
  }
}

6. 通过JavaAPI生产数据

6.1 单个单词生产数据示例(控制台消费)

object Demo1KafkaProducer {
  def main(args: Array[String]): Unit = {
    /**
     * 创建生产者
     */
    val properties=new Properties()
    //指定Kafka的broker地址
properties.setProperty("bootstrap.servers","master:9092,node1:9092,node2:9092")
    //设置key  value的序列化类
    properties.setProperty("key.serializer","org.apache.kafka.common.serialization.StringSerializer")
    properties.setProperty("value.serializer","org.apache.kafka.common.serialization.StringSerializer")
    val producer=new KafkaProducer[String,String](properties)

    /**
     * 向指定的Kafka中生产数据
     */
    val record=new ProducerRecord[String,String]("test_topic2","flink")
    //生产者向消费者发送数据
    producer.send(record)
    producer.flush()
    producer.close()
  }
}

6.2 读取学生表批量生产数据(控制台消费)

    //读取学生表将数据批量写入Kafka中
     val studentList: List[String] =Source.fromFile("data/students.txt").getLines().toList
     //for循环批量写入数据
    for(student<-studentList){
      val record = new ProducerRecord[String, String]("student", student)
      producer.send(record)
      producer.flush()
    }
    producer.close()

6.3 创建JavaAPI消费者(API自产自消)

earliest
当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
latest  默认
当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产认值生的该分区下的数据
none
topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常
--------------------------------------------------------------------------------------
object Demo3StudentConsumer {
  def main(args: Array[String]): Unit = {
    /**
     * 创建消费者
     */
    val properties = new Properties()
    //指定broker的地址
    properties.setProperty("bootstrap.servers","master:9092,node1:9092,node2:9092")
    //添加key和value的反序列化类,消费者组,读取数据的位置
    properties.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer")
    properties.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer")
    properties.setProperty("group.id", "asdasd")
    properties.setProperty("auto.offset.reset", "earliest")
    val consumer = new KafkaConsumer[String, String](properties)
    /**
     * 订阅一个topic
     */
    val topics=new util.ArrayList[String]()
    topics.add("student")
    consumer.subscribe(topics)

    /**
     * 消费数据
     */
      while(true){
        print("正在消费")
    val consumerRecords: ConsumerRecords[String, String] =
      consumer.poll(Duration.ofSeconds(2))
    //解析数据
    val records: lang.Iterable[ConsumerRecord[String, String]] = consumerRecords.records("student")
    val iterRecord: util.Iterator[ConsumerRecord[String, String]] =records.iterator()
    while(iterRecord.hasNext){
      val record: ConsumerRecord[String, String] = iterRecord.next()
      val topic: String = record.topic()
      val offSet: Long = record.offset()   //数据偏移量
      val key: String = record.key()       //数据的key,未指定时为null
      val value: String = record.value()   //保存的数据
      val ts: Long = record.timestamp()    //时间戳,默认为存入的时间
      println(s"$topic\t$offSet\t$key\t$value\t$ts")
    }
    }
  }
}

7. Canal 实时采集数据

1.阿里云开源的数据采集工具,功能单一
2.Canal监控MySQL的binarylog将其变更的数据(插入删除更新)采集后以json格式将数据存到Kafka中


8. Flume(测试)

指定flume配置文件,监控文件(目录,端口),将采集到的数据保存到hdfs,kafka中(示例:控制台消费者监控)

1.传配置文件,指定相应配置文件启动flume(会占用命令行,出现start说明成功启动)
flume-ng agent -n agent -f ./FlumeToKafka.properties -Dflume.root.logger=DEBUG,console

2.创建监控的文件,打数据
mkdir /usr/flume
echo 'java' >> /usr/flume/log.log

3.控制台消费
kafka-console-consumer.sh --zookeeper  master:2181,node1:2181,node2:2181  --from-beginning --topic flume

9. process (Flink底层王者级算子)

9.1不用窗口版示例(只有Java的语法)

示例:读一个socket,计算后统计到控制台
1.process  一行行处理数据,可以返回一行或多行,可以替代map,filter,flatMap,是一个底层的函数
2.processFunction是一个抽象的类,需要实现之,所以匿名内部类重processElement方法:将DS中的数据一条条传给pe,再其中进行处理,可以输出多条数据,相当于flatMap(还可以重写open()  close() 方法)
3.重写后的三个参数:  
i  相当于一行数据
context相当于上下文对象  (context.timestamp() 可以获取到时间)
collector  用于数据发送到下游

代码:
object ProcessFun {
  def main(args: Array[String]): Unit = {
  //构建flink环境,设置并行度,连接一个socket
    val env: StreamExecutionEnvironment =StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)
    val linesDS: DataStream[String] = env.socketTextStream("master", 8888)
    val processDS: DataStream[(String, Int)] =linesDS
      .process(new ProcessFunction[String,(String,Int)] {
      override def processElement(i: String,
                                  context: ProcessFunction[String, (String, Int)]#Context,
                                  collector: Collector[(String, Int)]): Unit = {
        val clazz: String =i.split(",")(4)
        collector.collect((clazz,1))
      }
    })
    processDS.print()
    env.execute()
  }
}

9.2 keyBy之后使用process算子

说明:将单词数量和次数放在一个可变的HashMap中,从context中拿到key,返回集合中的单词数量,有则次数加一后更新集合后返回,没有则先返回0再加一后更新集合再返回,使用put方法更新单词数量,收集数据后发送给下游
object KeyByProcess {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)
    val linesDS: DataStream[String] = env.socketTextStream("master", 8888)
    val wordsDS: DataStream[String] =linesDS.flatMap(line=>line.split(","))
    val kvDS: DataStream[(String, Int)] =wordsDS.map(word=>(word,1))
    val keyByDS: KeyedStream[(String, Int), String] =kvDS.keyBy(kv=>kv._1)
    val countDS: DataStream[(String, Int)] =keyByDS
      .process(new KeyedProcessFunction[String,(String,Int),(String,Int)]{

      val countMap: mutable.Map[String, Int] =new mutable.HashMap[String,Int]()
      override def processElement(i: (String, Int),
                                  context: KeyedProcessFunction[String, (String, Int), (String, Int)]#Context,
                                  collector: Collector[(String, Int)]): Unit = {
        val key: String =context.getCurrentKey
        //从map中获取单词的数量,如果有就返回,如果没有就返回0
        var count: Int =countMap.getOrElse(key,0)
        //做一个累计
        count += 1
        //更新map中单词的数量
        countMap.put(key,count)
        //将数据发送到下游
        collector.collect(key,count)
      }
    })
    countDS.print()
    env.execute()
  }
}

9.3 window中的process


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值