Kafka

1.0 Kafka 概述

1.1 kafka 定义

Kafka 是一个分布式的基于发布/订阅模式消息队列(Message Queue),主要应用于大数据实时处理领域。

1.2 消息队列

1.2.1 传统消息队列的应用场景

01.png

使用消息队列的好处

  1. 解耦(类似Spring的IOC)
    • 允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。
  2. 可恢复性
    • 系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
  3. 缓冲
    • 有助于控制和优化数据流经过系统的速度, 解决生产消息和消费消息的处理速度不一致的情况。
  4. 灵活性 & 峰值处理能力(削峰)
    • 在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
  5. 异步通信
    • 很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
1.2.2 消费模式

​ 消息队列的两种模式

  1. 点对点模式:

    一对一,消费者主动拉取数据,消息收到后消息清除

    消息生产者生产消息发送到Queue中,然后消息消费者从Queue中取出并且消费消息。

    消息被消费以后, queue 中不再有存储,所以消息消费者不可能消费到已经被消费的消息。

    Queue 支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。

02.png

  1. 发布/订阅模式:

    一对多,消费者消费数据之后不会清除消息,但是保存数据有时效。

​ 消息生产者(发布)将消息发布到 topic 中,同时有多个消息消费者(订阅)消费该消息。

​ 和点对点方式不同,发布到 topic 的消息会被所有订阅者消费。

img

1.3 基础架构

04.png

  1. Producer : 消息生产者,就是向 Kafka ;

  2. Consumer : 消息消费者,向 Kafka broker 取消息的客户端;

  3. Consumer Group (CG): 消费者组,由多个 consumer 组成。 消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费;消费者组之间互不影响。 所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。

    一个分区只能被一个消费者组里面的某一个消费者消费

  4. Broker :经纪人 一台 Kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker可以容纳多个 topic。

  5. Topic : 话题,可以理解为一个队列, 生产者和消费者面向的都是一个 topic

  6. Partition: 为了实现扩展性,一个非常大的 topic 可以分布到多个 broker(即服务器)上,一个 topic 可以分为多个 partition,每个 partition 是一个有序的队列;

  7. Replica: 副本(Replication),为保证集群中的某个节点发生故障时, 该节点上的 partition 数据不丢失,且 Kafka仍然能够继续工作, Kafka 提供了副本机制,一个 topic 的每个分区都有若干个副本,一个 leader 和若干个 follower

  8. Leader: 每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对象都是 leader。

  9. Follower: 每个分区多个副本中的“从”,实时从 leader 中同步数据,保持和 leader 数据的同步。 leader 发生故障时,某个 Follower 会成为新的 leader。(类似于备份)

replica 英 [ˈreplɪkə] 美 [ˈreplɪkə] n.复制品;仿制品

topic 英 [ˈtɒpɪk] 美 [ˈtɑːpɪk] n.话题;题目;标题

2.0 Kafka 快速入门

2.1 Kafka 安装&启动&关闭

  1. kafka 0.11.0.0 下载

  2. 解压

  3. 修改配置文件 cd /usr/kafka/config

修改 server.properties

delete.topic.enable=true

#kafka 运行日志存放的路径
log.dirs=/usr/kafka/kafka/logs

#配置连接 Zookeeper 集群地址
zookeeper.connect=master:2181,slave1:2181,slave2:2181

#broker 的全局唯一编号,不能重复
broker.id=0
#删除 topic 功能使能
delete.topic.enable=true
#处理网络请求的线程数量
num.network.threads=3
#用来处理磁盘 IO 的现成数量
num.io.threads=8
#发送套接字的缓冲区大小
socket.send.buffer.bytes=102400
#接收套接字的缓冲区大小
socket.receive.buffer.bytes=102400
#请求套接字的缓冲区大小
socket.request.max.bytes=104857600
#kafka 运行日志存放的路径
log.dirs=/usr/kafka/kafka/logs
#topic 在当前 broker 上的分区个数
num.partitions=1
#用来恢复和清理 data 下数据的线程数量
num.recovery.threads.per.data.dir=1
#segment 文件保留的最长时间,超时将被删除
log.retention.hours=168
#配置连接 Zookeeper 集群地址
zookeeper.connect=master:2181,slave1:2181,slave2:2181
  1. 配置环境变量

    #KAFKA_HOME
    export KAFKA_HOME=/usr/kafka/kafka
    export PATH=$PATH:$KAFKA_HOME/bin
    
  2. 启动 zookeeper

    bin/zkServer.sh start

  3. 启动 kafka

    bin/kafka-serve-start.sh -daemon config/server.properties

  4. 写一个群启脚本

    #!/bin/bash
    case $! in
    "start"){
    for i in master slave1 slave2
    do
    echo "========== $i ==========" 
    ssh $i '/usr/kafka/kafka/bin/kafka-server-start.sh -daemon /usr/kafka/kafka/config/server.properties'
    done
    };;
    "stop"){
    for i in master slave1 slave2
    do
    echo "========== $i ==========" 
    ssh $i '/usr/kafka/kafka/bin/kafka-server-stop.sh'
    done
    };;
    esac
    

2.2 Kafka命令行操作

  1. 查看当前服务器中的所有 topic

    bin/kafka-topics.sh --list --zookeeper master:2181

  2. 创建topic(主题)

    bin/kafka-topics.sh --zookeeper
    hadoop102:2181 --create --replication-factor 3 --partitions 1 --
    topic first

    创建名为first的主题。保存在logs 里面 名叫" first-0 " 0 是分区的 id

    选项说明:

    –topic 定义 topic 名

    –replication-factor 定义副本数

    –partitions 定义分区数

    副本数 > 分区数

  3. 删除topic

    bin/kafka-topics.sh --delete --zookeeper master:2181 --topic first

  4. 显示信息

    bin/kafka-topics.sh --describe --topic first --zookeeper master:2181

  5. 发送消息

    bin/kafka-console-producer.sh --topic first --broker-list master:9092

  6. 消费者:消费信息

    bin/kafka-console-consumer.sh --topic first --zookeeper master:2181

​ 控制台:

bin/kafka-console-consumer.sh --topic first --zookeeper master:2181 --from-beginning

–from-beginning:会把主题中以往所有的数据都读取出来。

​ 前两个过时了,现在使用bootstrap-server

bin/kafka-console-producer.sh --topic first --boostarp-server master:9092

2.2.1 数据日志分离
  • 删除日志文件logs:rm -rf logs
  • 进入zk删除:``bin/zk.Cli.shls /rmr /cluster` 或者把 zkdata 中的 version 删掉

3.0 案例

3.1 Flume读取日志数据并写入到Kafka,ConsoleConsumer进行实时消费
  1. 编写用于实时产生日志的shell文件

    1. 创建临时存放日志文件的目录

      
      mkdir -p /tmp/flumetokafka/logs
      mkdir -p /tmp/flumetokafka/testdata
      
    2. 接下来编写shell文件

      vim output.sh
      #写入
      for((i=5612; i<6000; i++));
      do
        touch $PWD/testdata/20170913-jangzhangz-$i.log
        echo 'When we will see you again. Put a little sunshine in your life.----'+$i >> $PWD/testdata/20170913-jangzhangz-$i.log
        mv $PWD/testdata/20170913-jangzhangz-$i.log $PWD/logs/
      done
      

      tmp/flumetokafka/logs/就是flume进行监听日志文件的数据目录

  2. 为flume构建agent

    首先进入flume所在目录下的conf目录,编写构建agent的配置文件(flume2kafka.properties):

    
    Flume2KafkaAgent.sources=mysource
    Flume2KafkaAgent.channels=mychannel
    Flume2KafkaAgent.sinks=mysink
    
    Flume2KafkaAgent.sources.mysource.type=spooldir
    Flume2KafkaAgent.sources.mysource.channels=mychannel
    Flume2KafkaAgent.sources.mysource.spoolDir=/tmp/flumetokafka/logs
    
    Flume2KafkaAgent.sinks.mysink.channel=mychannel
    Flume2KafkaAgent.sinks.mysink.type=org.apache.flume.sink.kafka.KafkaSink
    Flume2KafkaAgent.sinks.mysink.kafka.bootstrap.servers=master:9092,slave1:9092,slave2:9092
    Flume2KafkaAgent.sinks.mysink.kafka.topic=flume-data
    Flume2KafkaAgent.sinks.mysink.kafka.batchSize=20
    Flume2KafkaAgent.sinks.mysink.kafka.producer.requiredAcks=1
    
    Flume2KafkaAgent.channels.mychannel.type=memory
    Flume2KafkaAgent.channels.mychannel.capacity=30000
    Flume2KafkaAgent.channels.mychannel.transactionCapacity=100
    
  3. 求证成功

    1. 启动 flume agent

      bin/flume-ng agent -c conf -f conf/flume2kafka.properties -n Flume2KafkaAgent -Dflume.root.logger=INFO,console

    2. 启动 kafka消费者

      bin/kafka-console-consumer.sh --zookeeper master:2181 --topic flume-data --from-beginning

      bin/kafka-console-consumer.sh --zookeeper master:2181 --topic flume-data --from-beginning

  4. 生产日志

    在tmp/flumetokafka/ 下执行 ./output.sh

  5. 查看 terminal显示的消费记录情况

    正常执行后,结果会出现类似情况:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fGS60dBF-1640089765175)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211208155301387.png)]

3.2 使用Flume监控本机的端口,将数据发送给Kafka

使用Flume监控本机的6666端口,将数据发送给Kafka,并启动Kafka的消费者,将数据打印到控制台,其中Kafka的topic自定义

  1. 首先启动 zk 和 kafka

  2. 然后 创建一个主题

    bin/kafka-topics.sh --create --zookeeper hadoop102:2181 --replication-factor 1 --partitions 1 --topic mytopic

  3. 启动一个消费者

    bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic mytopic

  4. 启动一个生产者(测试)

    bin/kafka-console-producer.sh --broker-list hadoop102:9092 --topic mytopic

  5. 然后启动flume监控

    bin/flume-ng agent --conf conf --conf-file job/flume-kafka.conf --name a1 -Dflume.root.logger=INFO,console

  6. 配置文件

    #flume-kafka.conf
    
    a1.channels = c1
    a1.sources = r1
    a1.sinks = k1
    
    # Describe/configure the source
    a1.sources.r1.type = netcat
    a1.sources.r1.bind = localhost
    a1.sources.r1.port = 6666
    
    # Describe the sink
    a1.sinks.k1.type = logger
    a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink
    #主题mytopic和创建的topic一样
    a1.sinks.k1.kafka.topic = mytopic
    a1.sinks.k1.kafka.bootstrap.servers = master:9092
    
    # Use a channel which buffers events in memory
    a1.channels.c1.type = memory
    a1.channels.c1.capacity = 1000
    a1.channels.c1.transactionCapacity = 100
    
    # Bind the source and sink to the channel
    a1.sources.r1.channels = c1
    a1.sinks.k1.channel = c1
    
  7. 最后使用nc来输入数据 (kafka消费者会读取数据)

    nc localhost 6666

    kafka消费者 打印出数据即为成功

3.3 对接案例:Flume实时读取数据导入到Kafka的mytopic主题中

文件配置:

agent1.sources = logsrc
agent1.channels = memcnl
agent1.sinks = kafkasink
 
#source p
agent1.sources.logsrc.type = exec
agent1.sources.logsrc.command = tail -F /tmp/root/hive.log
agent1.sources.logsrc.shell = /bin/sh -c
agent1.sources.logsrc.batchSize = 50
agent1.sources.logsrc.channels = memcnl
 
# Each sink's type must be defined
agent1.sinks.kafkasink.type = org.apache.flume.sink.kafka.KafkaSink
agent1.sinks.kafkasink.brokerList=master:9092, slave1:9092,slave2:9092
agent1.sinks.kafkasink.topic=mytopic
agent1.sinks.kafkasink.requiredAcks = 1
agent1.sinks.kafkasink.batchSize = 20
agent1.sinks.kafkasink.channel = memcnl
# Each channel's type is defined.
agent1.channels.memcnl.type = memory
agent1.channels.memcnl.capacity = 1000
  • 启动Flume节点

    bin/flume-ng agent -c conf/ -n agent -f job/flume3kafka.conf -Dflume.mointoring.type=http -Dflume.monitoring.port=10100

3.4 使用flume监控对应的目录数据,将实时日志数据采集到kafka中
  1. 启动Kafka , 创建好Flume监控数据导入的 topic logtopic

  2. 将日志采集接口部署到linux节点上,启动日志采集接口

    java -jar 日志采集接口文件

  3. 启动 Flume 监控对应的日志目录,将“用户登录日志”采集到kafka topic 中

    my.properites

    #设置source名称
    a.sources = r1
    #设置channel的名称
    a.channels = c1
    #设置sink的名称
    a.sinks = k1
    
    # For each one of the sources, the type is defined
    #设置source类型为TAILDIR,监控目录下的文件
    #Taildir Source可实时监控目录一批文件,并记录每个文件最新消费位置,agent进程重启后不会有重复消费的问题
    a.sources.r1.type = TAILDIR
    #文件的组,可以定义多种
    a.sources.r1.filegroups = f1 f2
    #第一组监控的是test1文件夹中的什么文件:.log文件
    a.sources.r1.filegroups.f1 = /software/logs/common/.*log
    #第二组监控的是test2文件夹中的什么文件:以.txt结尾的文件
    #a.sources.r1.filegroups.f2 = /software/logs/system/.*txt
    
    # The channel can be defined as follows.
    #设置source的channel名称
    a.sources.r1.channels = c1
    a.sources.r1.max-line-length = 1000000
    #a.sources.r1.eventSize = 512000000
    
    # Each channel's type is defined.
    #设置channel的类型
    a.channels.c1.type = memory
    # Other config values specific to each type of channel(sink or source)
    # can be defined as well
    # In this case, it specifies the capacity of the memory channel
    #设置channel道中最大可以存储的event数量
    a.channels.c1.capacity = 1000
    #每次最大从source获取或者发送到sink中的数据量
    a.channels.c1.transcationCapacity=100
    
    # Each sink's type must be defined
    #设置Kafka接收器
    a.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink
    #设置Kafka的broker地址和端口号
    a.sinks.k1.brokerList=master1:9092,slave1:9092,slave2:9092
    #设置Kafka的Topic
    a.sinks.k1.topic=logtopic
    #设置序列化方式
    a.sinks.k1.serializer.class=kafka.serializer.StringEncoder 
    #Specify the channel the sink should use
    #设置sink的channel名称
    a.sinks.k1.channel = c1
    
  4. 生成数据访问日志采集接口,查看topic数据

flume-ng agent 

-n a

-f /software/apache-flume-1.9.0-bin/conf/flume-conf-file-kafka.properties

-Dflume.root.logger=INFO,console

kafka启动:
bin/kafka-console-consumer.sh --bootstrap-server master:9092,slave1:9092,slave2:9092
--topic logtopic

3.5 Flink消费Kafka

package com.atguigu.bigdata.spark.sql.Spark和Flink代码

import java.util.Properties

case class Test(aa: Double)

object Flinkstream {

  val REDISIP = extractjob.RedisIP //redisip
  val ZKUrl = extractjob.XKIP + ":2181" //zk url+ip
  val KAFKAURL = extractjob.KafkaIP + ":9092" //kafka ip
  val TOPIC = "topictest"
  val GROUPID = "Test1"

  def main(args: Array[String]): Unit = {

    var env = StreamExecutionEnvironment.getExecutionEnvironment
    if ((args.length > 0 && args(0).equals("local")) || args.length == 0) {
      env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI()
    }
    val properties = new Properties()
    properties.setProperty("zookeeper.connect", ZKUrl)
    properties.setProperty("bootstrap.servers", KAFKAURL)
    properties.setProperty("group.id", GROUPID)
    env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime)
    env.setParallelism(6)
    env.enableCheckpointing(5000)
    env.getCheckpointConfig.setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE)
    val kafkaSource = new FlinkKafkaConsumer(TOPIC, new SimpleStringSchema, properties)


    val datastream: DataStream[Test] = env.addSource(kafkaSource).filter(x => {
      "正确值".equals(x.split(":")(0)) //过滤数据
    }).map(x => {
      //获取到想要的数据并转成 Test类,方便凑走
      Test(x.split(":")(1).split(",")(3).toDouble)
    })


    val config: FlinkJedisPoolConfig = new FlinkJedisPoolConfig.Builder().setHost(REDISIP).build()


    //数据进行累加计算,在插入redis
    datastream.map(x => {
      ("aaTest", x.aa)
    }).keyBy(_._1).sum(1).map(x => (x._1, x._2.toString))
      .addSink(new RedisSink[(String, String)](config, new MyRedisMapper) {})


  }

}

class MyRedisMapper extends RedisMapper[(String, String)] {
  //  方法用于指定对接收来的数据进行什么操作
  override def getCommandDescription: RedisCommandDescription = {
    new RedisCommandDescription(RedisCommand.SET)
  }


  //  于指定接收到的数据中哪部分作为key
  override def getKeyFromData(data: (String, String)): String = {
    data._1
  }

  //  方法用于指定接收到的数据中哪部分作为value
  override def getValueFromData(data: (String, String)): String = {
    data._2
  }
}

new RedisSink[(String, String)](config, new MyRedisMapper) {})

}

}

class MyRedisMapper extends RedisMapper[(String, String)] {
// 方法用于指定对接收来的数据进行什么操作
override def getCommandDescription: RedisCommandDescription = {
new RedisCommandDescription(RedisCommand.SET)
}

// 于指定接收到的数据中哪部分作为key
override def getKeyFromData(data: (String, String)): String = {
data._1
}

// 方法用于指定接收到的数据中哪部分作为value
override def getValueFromData(data: (String, String)): String = {
data._2
}
}


















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喵内噶.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值