初识kafka

Kafka 是一款分布式的基于 发布/订阅 模式的消息中间件。
相较于其他框架,kafka与大数据生态的组件更兼容(比如直接有Flink-kafka数据源DataStream、甚至Kafka Table(Flink SQL) 的API,)。(大数据组件也可以使用其他MQ)

  • 消息中间件主要作用:

    1. 异步通信
    2. 削峰
    3. 解耦:代替应用间接口调用,降低应用互相依赖
  • kafka用于数据实时采集流程举例

    1. log、csv文件 --> flume --> kafka -->ss / flink
    2. mysql / oracle --> maxwell / ogg
  • 大数据场景数据来源:

    1. 数据库:(关系型、非关系型)
    2. 文件:log文件,csv文件(方便程序之间转移的excel表格数据)

kafka 概述

kafka 官网:kafka.apache.org

Apache Kafka is an open-source distributed event streaming platform used by thousands of companies for high-performance data pipelines, streaming analytics, data integration, and mission-critical applications.
(distributed event streaming platform :分布式事件流平台 - -对标大数据流式组件 ss、flink、结构化流)

kafka 也不单纯做中间件,不甘心单单为ss \ flink 提供,也想有自己的流式平台streams,但很小众。

kafka 特点
  • HIGH THROUGHPUT : 高吞吐
  • SCALABLE : 可扩展
  • PERMANENT STORAGE : 持久化
  • HIGH AVAILABILITY :高可用
kafka 版本

下载:https://archive.apache.org/dist/kafka/

kafka 模式
  • 点对点模式
    一个生产者、一个消费者。消费者主动从Message Queue中拉取消息并消费,再发送确认给Message Queue,Message Queue收到消费者确认消费后会删除消息。
  • 发布/订阅模式
    可以有多个topic,且一个topic可以被多个消费者订阅并消费。消费者消费后不会删除消息(供其他消费者消费)。多个消费者互相独立。
架构
  • topic 主题,可以有多个主题。

  • partition 是topic的物理分组,代表进入topic的文件可以被切分,一个topic至少一个paitition,在partition中有序 。
    partition 的名称是以topic-编号组成。

  • replication 是备份的副本,防止每个partition的单点故障,每个partition(leader)个可以有多个follower。

    TopicA
    
    leader                follower
    TopicA-partition1     TopicA-repartition2
    TopicA-partition2     TopicA-repartition3
    TopicA-partition3     TopicA-repartition1
    

    生产和消费消息都只针对leader,只有leader故障时才将特定follower转换为leader。(如何找到leader,如何将follower转换为leader由zookeeper完成,(2.8.0以后可选不用ZK,kafka自己处理))

  • zookeeper 会保存两类数据:

    1. kafka集群中节点的在线情况
    leader是在ZK中以节点存在:
    /brokers/ids/[0,1,2]
    
    1. 所有leader与对应follower
    同时对每个partition还保存了哪些是leader,哪些是follower:
    /brokers/topics/first/partitions/0/state/
    "leader":0,"isr":[1,2]
    
  • acks
    acks 有三个值,分别对应3种处理方式
    acks = 0 : 生产者发送消息后不会等待kafka报告是否成功提交。
    acks = 1 :当主副本提交成功数据(记录到日志)就返回生产者,不关心其他节点成功与否。
    acks = all (等价 -1): 当主副本和所有备份副本都提交成功数据。最安全,但性能会有损耗。

部署使用

集群部署,三台机器
  1. 每台机器先配置hosts,如(hadoop001、hadoop002、hadoop003)

  2. 下载解压kafka后,进入 ./config 目录下,打开server.properties 做以下配置

    • 每一台机器是一个broker,通过指定 broker.id区分集群中每台机器。broker.id=0 (其余两台分别是1,2)

    • log.dirs=/tmp/kafka-logs (存储的是消息内容的数据,而不是kafka运行的日志。修改到非/tmp的目录)

    • log.retention.hours=168 [默认保留数据7天,按需修改]

    • zookeeper.connect=hadoop001:port,hadoop002:port,hadoop003:port/kafka (部署zookeeper集群的hostname:port) (/kafka 表示在三台ZK下创建一个/kafka目录统一存放)

    • 进kafka的数据,会经过压缩,默认保留7天

  3. 修改文件后,在 ./bin 目录找到启动命令

    kafka-server-start.sh [-daemon] 可选指定以后台进程的方法启动
    kafka-server-start.sh  -daemon  [../config/server.properties]  可选带配置文件启动
    

    启动后会有一个进程Kafka(zookeeper的进程是QuorumpeerMain),stop时先关闭kafka再关闭zk.

乱序

由于多个partition并行提交消息,不同partition之前的顺序不能保证全局有序的。(一个partition内是有序的)

解决乱序:

  1. 牺牲吞吐量,只设置一个partition,缺失了使用kafka的意义。
  2. 消费后先做全局排序,性能开销很大。
  3. 将部分有关联的,有顺序要求的数据指定到同一个Key,保证进入同一个partition。
    那么如何关联让partition认这个key呢:
    代码层面:send消息的时候指定key, 源码算法就是取这个key做hash运算以确定partition,key为空才取其他的内容做hash
    配置层面:如果使用maxwell,可以有参数 producer_partition_by 指定如按数据库主键为key等,保证对同一个主键数据的操作是按顺序的

047 kafka-04 J哥解决乱序的过程和配置,这套配置仍在使用

kafka.acks = all
producer_partition_by =  primary_key
kafka.retries = 100
max.in.flight.requests.per.connection = 1 

补充,如果仍出现数据差异,在业务低谷时,对比大数据和RDBMS,对有差异的数据多退少补。做补偿机制,

常用命令
topic
  • 创建topic
    ./kafka-topic.sh \
    --create \
    --zookeeper hostname:port,hostname:port,... \
    --topic test_topic \
    --partitions 3 \
    --replication-factor 3 
    
  1. 注意:如果kafka配置zookeeper.connect保持时指定了根目录/kafka,则这里–zookeeper 的内容也要指定到/kafka。也就是要和配置文件server.properties中zookeeper.connect保持一致。
    kafka-topics.sh \
    --create \
    --zookeeper localhost:2181/kafka \
    --replication-factor 1 \
    --partitions 1 \
    --topic test
    
  • 查看所有topic

    ./kafka-topic.sh \
    --list \
    --zookeeper hostname:port,hostname:port,...
    
  • 描述(也可验证topic是否正常创建)

    ./kafka-topic.sh \
    --describe \
    --zookeeper hostname:port,hostname:port,...
    
    描述中的内容代表的意义
    Partition: 0  代表分区的number
    Leader: 0  和 Repicas:0,1,2  代表当前分区所在的机器的broker.id
    
  • 修改topic (尽量一次创建好,不得已才修改)

    ./kafka-topic.sh \
    --alter \
    --zookeeper hostname:port,hostname:port,... \
    --topic test_topic \
    --partitions 3   // 不能往小了调
    

    建议值 partitions 8,replication-factor 3 。
    为什么要调整 partitions ,kafka partitions : spark partitions == 1:1; 临时加了机器。

  • 删除topic

    ./kafka-topics.sh \
    --delete \
    --zookeeper ruozedata001:2181,ruozedata002:2181,ruozedata003:2181/kafka \
    --topic test_topic
    

    删除时出现:
    Topic jj is marked for deletion. --仅仅时打了’删除’的标签
    Note: This will have no impact if delete.topic.enable is not set to true.

    delete.topic.enable=true 是受这个配置决定,(不要删除是最保险的,不打算用的话等着7天回收就行)

  • 迁移(机器、磁盘)
    反复验证后使用
    http://kafka.apache.org/documentation/#basic_ops_automigrate

    // 创建一个json文件
    cat topics-to-move.json
    {"topics": [{"topic": "foo1"}],
    "version":1
    }
    
    bin/kafka-reassign-partitions.sh \
    --zookeeper localhost:2181 \
    --topics-to-move-json-file topics-to-move.json \
    --broker-list "5,6" \
    --generate
    
    bin/kafka-reassign-partitions.sh \
    --zookeeper localhost:2181 \
    --reassignment-json-file expand-cluster-reassignment.json \
    --execute
    

关于topic创建:

  1. kafka topic名称规范: 英文字母小写
  2. 在建topic之前,名字想好了 真的想好了再建
  3. 不要轻易删除topic 就算生产上这个topic不用了 且 数据量较大, 也没关系 7天后数据会自己删除
  4. 不要有强迫症,不要随意修改、删除
生产/消费者
  • 创建生产者和消费者 (用于验证是否生产、消费)
    ./kafka-console-producer.sh \
    --zookeeper hostname:port,hostname:port,... \
    --topic test_topic 
    
    ./kafka-console-consumer.sh \
    --zookeeper hostname:port,hostname:port,... \
    --topic test_topic 
    

Kafka容错机制的理解

kafka为保证高可用做了很多处理,以副本机制在多个服务端节点上对每个主题分区的日志进行复制,副本的单位是按主题下的分区,即对每个主题的每个分区都有副本。
其中一个为主副本Leader,其余多个备份副本,主副本负责响应客户端的读写请求,备份副本则从主副本拉取数据,保持和主副本的同步。
基于多副本冗余设计,kafka就有了容错的保证。即当部分节点宕机时,仍然保证系统正常对外提供服务,且外界无感知。
当主副本宕机时,kafka会在备份副本中选择一个作为主副本,对客户端提供响应。
(为尽量保证主副本和备份副本不同时宕机,主副本应该均匀的分布在各个服务器上,且互相做备份副本,即一台机器做其中一个分区的主副本,同时还做另一分区的备份副本)
当一个备份副本宕机(没有和ZK节点保持会话)、或备份进度落后太多时,主副本就会将其从同步副本集合中移除,反之,如果备份副本重新赶上主副本,它就会加入到主副本的同步集合中。
(一组主副本和其备份副本,成为一组 In Sync Replicas 即ISR)

当然这样的设计会对数据的同步和可靠性增加管理难度。
当acks = all,一条消息只有被所有副本都运用到本地的日志文件,才会认为消息被成功提交到kafka。只有已经提交的消息才能被消费者消费。
当acks = all,对于生产者所发送的一条消息已经写入到主副本中,但是此时备份副本还没来及进行数据copy时,主副本就挂掉的情况,那么此时消息提交不成功,生产者需要重新发送该消息。

acks 有三个值,分别对应3种处理方式
acks = 0 : 生产者发送消息后不会等待kafka报告是否成功提交。
acks = 1 :当主副本提交成功数据(记录到日志)就返回生产者,不关心其他节点成功与否。
acks = all (等价 -1): 当主副本和所有备份副本都提交成功数据。最安全,但性能会有损耗。
生产环境会在 1 和 all 之间根据业务类型选择。
当 acks = all ,但Partition只有一个主副本Leader,没有任何Follower,则设置all与1并无区别,不能保证数据一定安全。
并且在有Follower情况下,如果副本放置不合理如一组ISR都在同一台机器导致全部宕机,也会丢失。

kafka集群部署

准备工作,zookeeper集群已提前搭建:
172.16.69.207|172.16.69.206|172.16.69.208

现搭建由4台主机组成的kafka集群:
172.16.69.205|172.16.69.207|172.16.69.206|172.16.69.208

下载 kafka_2.13-2.7.2.tgz,分别上传到集群每台主机,解压,配置环境变量等过程省略。

分别对4台主机修改配置文件 config/server.properties

broker.id=0
host.name=172.16.69.205
port=9092

listeners=PLAINTEXT://172.16.69.205:9092

log.dirs=/tmp/kafka-logs

zookeeper.connect=172.16.69.207:2181,172.16.69.206:2181,172.16.69.208:2181/kafka
  • 每一台机器是一个broker,通过指定 broker.id区分集群中每台机器。broker.id=0 (其余3台分别是1,2,3)
  • host.name=172.16.69.205。本节点的ip
  • port=9092 配置文件中没有端口默认配置项,但默认端口就是9092,可改成其他端口
  • listeners=PLAINTEXT://172.16.69.208:9092. 监听本节点的host.name和port,不然生产消息时就会报错:Error while fetching metadata with correlation id : {LEADER_NOT_AVAILABLE}
  • log.dirs=/tmp/kafka-logs (存储的是消息内容的数据,而不是kafka运行的日志。修改到非/tmp的目录)
  • log.retention.hours=168 [默认保留数据7天,按需修改]
  • zookeeper.connect=node1:port,node2:port,node3:port/kafka (部署zookeeper集群的hostname:port) (/kafka 表示在三台ZK下创建一个/kafka目录统一存放)
  • 其他配置项按需修改

其他3台主机:

broker.id=1
host.name=172.16.69.207
port=9092
listeners=PLAINTEXT://172.16.69.207:9092
log.dirs=/tmp/kafka-logs
zookeeper.connect=172.16.69.207:2181,172.16.69.206:2181,172.16.69.208:2181/kafka
broker.id=2
host.name=172.16.69.206
port=9092
listeners=PLAINTEXT://172.16.69.206:9092
log.dirs=/tmp/kafka-logs
zookeeper.connect=172.16.69.207:2181,172.16.69.206:2181,172.16.69.208:2181/kafka
broker.id=3
host.name=172.16.69.208
port=9092
listeners=PLAINTEXT://172.16.69.208:9092
log.dirs=/tmp/kafka-logs
zookeeper.connect=172.16.69.207:2181,172.16.69.206:2181,172.16.69.208:2181/kafka

分别修改好配置文件后,4台主机依次启动

kafka-server-start.sh  -daemon  ../config/server.properties
  • 在任意一台主机下,创建topic
./kafka-topics.sh \
--create \
--zookeeper 172.16.69.207:2181,172.16.69.206:2181,172.16.69.208:2181/kafka \
--topic test_topic \
--partitions 4 \
--replication-factor 1 
  • 创建消费者
kafka-console-consumer.sh \
--bootstrap-server 172.16.69.207:9092,172.16.69.206:9092,172.16.69.208:9092,172.16.69.205:9092 \
--topic test_topic 

创建后会保持会话,等待消息到来
Kafka常用命令之kafka-console-consumer.sh :https://blog.csdn.net/qq_29116427/article/details/80206125

  • 创建生产者

执行命令后,就会在控制台等待键入消息体。
回车表示触发“发送”操作;
退出生产者控制台,直接使用“Ctrl + c”退出。

Kafka常用命令之kafka-console-producer.sh:https://blog.csdn.net/qq_29116427/article/details/105912397

  • 创建生产者并发送消息(指定key)
kafka-console-producer.sh \
--bootstrap-server 172.16.69.207:9092,172.16.69.206:9092,172.16.69.208:9092,172.16.69.205:9092 \
--topic test_topic \
--property parse.key=true

默认消息key与消息value间使用“Tab键”进行分隔,切勿使用转义字符(\t),如下所示:

>Lei Li    Hello Kafka!
>Meimei Han    你好 kafka!
  • 创建生产者并发送消息(默认,无key)
kafka-console-producer.sh \
--bootstrap-server 172.16.69.207:9092,172.16.69.206:9092,172.16.69.208:9092,172.16.69.205:9092 \
--topic test_topic

控制台直接输入消息值(value)即可,每行表示一条消息,如下所示。

>Hello Kafka!

Spring Boot客户端整合kafka集群

上文测试了在集群中创建topic,创建消费者,创建生产者并发送消息都测试成功。(内网环境)

添加依赖

       <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
        </dependency>

添加配置文件 application.yml

spring:
  kafka:
    bootstrap-servers: 外网ip:9092,外网ip:9092,外网ip:9092,外网ip:9092
    consumer:
      group-id: my-group
      auto-offset-reset: earliest
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer

    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
    template:
      default-topic: test_topic

创建消费者

@Service
public class KafkaConsumerService {
    @KafkaListener(topics = "test_topic", groupId = "my-group")
    public void consume(String message) {
        System.out.println("Received message: " + message);
    }
}

创建生产者

@Service
public class KafkaProducer {
 
    private final KafkaTemplate<String, String> kafkaTemplate;
 
    public KafkaProducer(KafkaTemplate<String, String> kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }
 
    public void sendMessage(String topic, String message) {
        kafkaTemplate.send(topic, message);
    }
}

创建启动类

@SpringBootApplication
public class KafkaSpringBootApplication {
    public static void main(String[] args) {
        SpringApplication.run(KafkaSpringBootApplication.class, args);

        /** 
         *  Kafka 提供两个API
         * 操作topic的
         *      @Autowired
         *      private KafkaAdmin kafkaAdmin;
         *
         * 操作生产者的
         *      @Autowired
         *      private KafkaTemplate kafkaTemplate;
         *
         * 消费者直接使用注解
         *  @KafkaListener(topics = "test_topic", groupId = "my-group")
         *  public class KafkaConsumer{
         *  }
         * */
    }
}

启动便报错:
[Consumer clientId=consumer-my-group-1, groupId=my-group] Group coordinator 172.16.69.206:9092 (id: 2147483645 rack: null) is unavailable or invalid

其中这个172.16.69.206是内网ip,但java客户端现在是在外网环境,可是检查application.yml配置里,并没有使用内网ip,那么这个内网ip只可能是kafka配置里返回到java客户端的,而java客户端再使用内网ip去连接,于是连不上。

解决办法:再次回到kafka配置文件server.properties

vim ../config/server.properties

对集群中每一个主机的配置,都增加一项

advertised.listeners=PLAINTEXT://当前节点的外网ip:9092

重启kafka集群,再次启动KafkaSpringBootApplication,成功启动并消费了topic中消息。


上文部署集群时的配置文件不"完整"的,补充后配置文件:

broker.id=0
host.name=172.16.69.205
port=9092

listeners=PLAINTEXT://172.16.69.205:9092
advertised.listeners=PLAINTEXT://当前节点的外网ip:9092

log.dirs=/tmp/kafka-logs

zookeeper.connect=172.16.69.207:2181,172.16.69.206:2181,172.16.69.208:2181/kafka

查看kafka在zookeeper中的数据

启动zk客户端

[root@tidb1 data]# zkCli.sh
Connecting to localhost:2181

在zk客户端控制台,列出所有数据

[zk: localhost:2181(CONNECTED) 1] ls /
[kafka, zookeeper]
[zk: localhost:2181(CONNECTED) 3] ls /kafka
[admin, brokers, cluster, config, consumers, controller, controller_epoch, feature, isr_change_notification, latest_producer_id_block, log_dir_event_notification]
[zk: localhost:2181(CONNECTED) 4] ls /kafka/brokers 
[ids, seqid, topics]

[zk: localhost:2181(CONNECTED) 5] ls /kafka/brokers/ids
[0, 1, 2, 3]

[zk: localhost:2181(CONNECTED) 6] ls /kafka/brokers/topics 
[__consumer_offsets, multi_topic, test_topic]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值