kafka安装与使用

文章目录Apache Kafka一、概述架构应用场景核心概念Topic和Log二、环境搭建准备工作安装配置环境配置启动服务三、基础使用命令行操作Topic使用发布和订阅JAVA DriverMaven依赖准备工作生产者消费者四、高级部分偏移量控制消费方式订阅(Subscribe)指定消费分区重置消费位置消费组自定义对象类型的传输序列化接口自定义对象导入工具包的依赖jar包自定义编解码器类测试生产者...
摘要由CSDN通过智能技术生成

文章目录

Apache Kafka

一、概述

Apache Kafka是一个分布式的流数据平台,代表三层含义:

  • Publish/Subscribe: 消息队列系统 MQ(Message Queue)

  • Process: 流数据的实时处理(Stream Process)

  • Store: 流数据会以一种安全、容错冗余存储机制存放到分布式集群中

架构

在这里插入图片描述

应用场景

  • 构建实时的流数据管道,在系统和应用之间进行可靠的流数据传输
  • 构建实时的流数据处理应用,对流数据进行转换和加工处理

核心概念

  • Cluster: kafka支持一到多个服务构成的分布式集群,每一个服务实例成为Broker
  • Topic: 某一个分类的消息的集合,如:订单的topic、商品的topic等
  • Partition: 一个Topic有若干个分区(Partition)构成,分区的数量在创建Topic时手动指定
  • Replication: 分区副本,是Partition的冗余备份分区,当Partition不可用时,ZooKeeper会自动将Replication(Follower)分区升级为Partition(Leader)分区
  • Offset: 分区中的Record的位置标示,每一个消费者都会记录自己的消费位置(offset)

Topic和Log

Each partition is an ordered, immutable sequence of records that is continually appended to—a structured commit log

Kafka的每一个分区(Partition),都是一个有序、不可变的持续追加的记录序列,Kafka会以一种结构化的提交日志保存分区中的数据。
在这里插入图片描述
注意:在分区中写入数据时,会在队列的末尾进行追加,每一个消费者都维护的有一个自己的消费位置(offset)
在这里插入图片描述

二、环境搭建

完全分布式的Kafka集群

准备工作

  • 分布式集群中时钟同步

    [root@HadoopNodeX ~]# yum -y install ntpdate
    [root@HadoopNodeX ~]# ntpdate -u ntp.api.bz
    25 Sep 11:19:26 ntpdate[1749]: step time server 114.118.7.163 offset 201181.363384 sec
    [root@HadoopNodeX ~]# date
    Wed Sep 25 11:19:52 CST 2019
    
  • JDK1.8以上

  • 开启ssh免密登陆

    [root@HadoopNodeX ~]# ssh-keygen -t rsa   # 先在所有集群夫妻武器都运行此命令,在运行下面的
    [root@HadoopNodeX ~]# ssh-copy-id HadoopNode01
    [root@HadoopNodeX ~]# ssh-copy-id HadoopNode02
    [root@HadoopNodeX ~]# ssh-copy-id HadoopNode03
    
  • ZooKeeper集群服务运行正常

    [root@HadoopNode0* zookeeper-3.4.6]# vi conf/zoo.cfg
    tickTime=2000
    initLimit=10
    syncLimit=5
    dataDir=/home/zk/data
    clientPort=2181
    server.1=hadoopnode01:2887:3887
    server.2=hadoopnode02:2888:3888
    server.3=hadoopnode03:2889:3889
    
    [root@HadoopNode0* zookeeper-3.4.6]# cd /home/zk
    [root@HadoopNode0* zk]# mkdir data
    [root@HadoopNode0* zk]# vi data/myid
    # Node01
    1
    # Node02
    2
    # Node03
    3
    [root@HadoopNode0* zookeeper-3.4.6]# bin/zkServer.sh start conf/zoo.cfg
    
    # 如何判断zookeeper集群服务正常
    [root@HadoopNode03 zookeeper-3.4.6]# bin/zkServer.sh status conf/zoo.cfg
    JMX enabled by default
    Using config: conf/zoo.cfg
    Mode: leader  (一主两从)
    

安装配置

  • 将安装包上传并复制到其它节点

    # 上传到某一个服务器,并拷贝给其它服务器
    
    [root@HadoopNode01 ~]# scp kafka_2.11-2.2.0.tgz root@hadoopnode02:~
    kafka_2.11-2.2.0.tgz                                                                         100%   61MB  61.0MB/s   00:00
    [root@HadoopNode01 ~]# scp kafka_2.11-2.2.0.tgz root@hadoopnode03:~
    kafka_2.11-2.2.0.tgz   
    
  • 安装Kafka

    [root@HadoopNode0* ~]# tar -zxf kafka_2.11-2.2.0.tgz -C /usr
    [root@HadoopNode01 kafka_2.11-2.2.0]# ll
    total 52
    drwxr-xr-x. 3 root root  4096 Mar 10  2019 bin  # 指令
    drwxr-xr-x. 2 root root  4096 Mar 10  2019 config  # 配置文件
    drwxr-xr-x. 2 root root  4096 Oct  9 08:56 libs  # 依赖jar包
    -rw-r--r--. 1 root root 32216 Mar 10  2019 LICENSE 
    -rw-r--r--. 1 root root   336 Mar 10  2019 NOTICE 
    drwxr-xr-x. 2 root root  4096 Mar 10  2019 site-docs  # 使用文档
    

环境配置

  • 修改kafka核心配置文件server.properties

    [root@HadoopNode01 kafka_2.11-2.2.0]# vi config/server.properties
    broker.id=0 | 1 | 2
    listeners=PLAINTEXT://HadoopNode0[1 | 2 | 3]:9092
    log.dirs=/usr/kafka_2.11-2.2.0/data
    zookeeper.connect=HadoopNode01:2181,HadoopNode02:2181,HadoopNode03:2181
    

启动服务

  • 启动

    [root@HadoopNode0* kafka_2.11-2.2.0]# bin/kafka-server-start.sh -daemon config/server.properties
    [root@HadoopNode0* kafka_2.11-2.2.0]# jps
    10386 Kafka
    10517 Jps
    3276 QuorumPeerMain
    
  • 关闭

    [root@HadoopNode0* kafka_2.11-2.2.0]# bin/kafka-server-stop.sh config/server.properties
    

三、基础使用

命令行操作

Topic使用
  • 新建Topic

    [root@HadoopNode01 kafka_2.11-2.2.0]# bin/kafka-topics.sh --bootstrap-server HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092 --topic t1 --partitions 3 --replication-factor 3 --create
    
  • 展示Topic列表

    [root@HadoopNode01 kafka_2.11-2.2.0]# bin/kafka-topics.sh --bootstrap-server HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092  --list
    
  • 删除Topic

    [root@HadoopNode02 kafka_2.11-2.2.0]# bin/kafka-topics.sh --bootstrap-server HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092  --delete --topic t2
    
  • 描述Topic

    [root@HadoopNode02 kafka_2.11-2.2.0]# bin/kafka-topics.sh --bootstrap-server HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092  --describe --topic t1
    Topic:t1        PartitionCount:3        ReplicationFactor:3     Configs:segment.bytes=1073741824
            Topic: t1       Partition: 0    Leader: 0       Replicas: 0,2,1 Isr: 0,2,1
            Topic: t1       Partition: 1    Leader: 2       Replicas: 2,1,0 Isr: 2,1,0
            Topic: t1       Partition: 2    Leader: 1       Replicas: 1,0,2 Isr: 1,0,2
            
    # 测试:kill掉Node03上的kafka服务实例  也就是broker【2】
    [root@HadoopNode02 kafka_2.11-2.2.0]# bin/kafka-topics.sh --bootstrap-server HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092  --describe --topic t1
    Topic:t1        PartitionCount:3        ReplicationFactor:3     Configs:segment.bytes=1073741824
            Topic: t1       Partition: 0    Leader: 0       Replicas: 0,2,1 Isr: 0,1
            Topic: t1       Partition: 1    Leader: 1       Replicas: 2,1,0 Isr: 1,0
            Topic: t1       Partition: 2    Leader: 1       Replicas: 1,0,2 Isr: 1,0
    
    # 测试:恢复运行Node03上的Kafka服务实例Broker[2],第三列信息不改变的原因:(分区的Leader都存在,不会触发ZK的故障转移),第五列信息不变
    [root@HadoopNode02 kafka_2.11-2.2.0]# bin/kafka-topics.sh --bootstrap-server HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092  --describe --topic t1
    Topic:t1        PartitionCount:3        ReplicationFactor:3     Configs:segment.bytes=1073741824
            Topic: t1       Partition: 0    Leader: 0       Replicas: 0,2,1 Isr: 0,1,2
            Topic: t1       Partition: 1    Leader: 1       Replicas: 2,1,0 Isr: 1,0,2
            Topic: t1       Partition: 2    Leader: 1       Replicas: 1,0,2 Isr: 1,0,2        
    

在这里插入图片描述

  • 修改Topic

    [root@HadoopNode02 kafka_2.11-2.2.0]# bin/kafka-topics.sh --bootstrap-server HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092  --alter --topic t1 --partitions 5
    
发布和订阅
  • 发布消息

    [root@HadoopNode01 kafka_2.11-2.2.0]# bin/kafka-console-producer.sh --broker-list HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092 --topic t1
    >Hello World
    >Hello Kafka
    >Hello Hadoop
    
  • 订阅消息

    [root@HadoopNode02 kafka_2.11-2.2.0]# bin/kafka-console-consumer.sh --topic t1 --bootstrap-server HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092
    

JAVA Driver

Maven依赖
<!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka-clients -->
<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
    <version>2.2.0</version>
</dependency>
准备工作
  • 配置Windows Hosts主机名和IP映射

    192.168.11.20 HadoopNode00
    192.168.11.21 HadoopNode01
    192.168.11.22 HadoopNode02
    192.168.11.23 HadoopNode03
    
生产者
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;

import java.util.Properties;
import java.util.UUID;

/**
 * kafka 生产者的测试类
 */
public class ProducerDemo {
   

    public static void main(String[] args) {
   
        //1. 准备Kafka生产者配置信息
        Properties properties = new Properties();
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092");
        // string 序列化(Object ---> byte[])器
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class);

        //2. 创建kafka生产者对象
        KafkaProducer<String, String> producer = new KafkaProducer<String, String>(properties);

        //3. 生产记录并将其发布
        ProducerRecord<String, String> record = new ProducerRecord<String, String>("t2", UUID.randomUUID().toString(),"Hello Kafka");

        producer.send(record);

        //4. 释放资源
        producer.flush();
        producer.close();
    }
}

1) Kafka的消息生产者,负责生产数据(Record K\V\Timestamp),最终发布(Publish)保存到Kafka集群

2)数据的保存策略:

  • 如果Record的Key不为Null,采用哈希算法:key.hashCode % numPartitions = 余数(分区序号)
  • 如果Record的Key为Null, 采用轮询策略
  • 手动指定存放的分区

3) 数据会以一种分布式的方式保存在Kafka集群中,每一个分区都会维护一个队列的数据结构,新产生的数据会追加到队列的末尾,并且分配offset,

4)数据在Kafka集群中默认最多保留7天(168Hours),不论是否消费,在保留周期到达后都会自动被删除。

5)数据在Kafka中可以进行重复消费,重置消费offset即可

消费者
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.StringDeserializer;

import java.time.Duration;
import java.util.Arrays;
import java.util.Properties;

/**
 * kafka消费者测试类
 * 1. 订阅 subscribe
 * 2. 拉取 pull
 */
public class ConsumerDemo {
   
    public static void main(String[] args) {
   
        //1. 指定kafka消费者的配置信息
        Properties properties = new Properties();
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092");
        // 反序列化器 byte[] ---> Object
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        // 消费组必须得指定
        properties.put(ConsumerConfig.GROUP_ID_CONFIG, "group1");

        //2. 创建kafka消费者对象
        KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(properties);

        //3. 订阅主体topic
        consumer.subscribe(Arrays.asList("t2"));

        //4. 拉取新产生的记录
        while (true) {
   
            ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(10));
            for (ConsumerRecord<String, String> record : records) {
   
                System.out.println(record.key() + "\t" + record.value() + "\t"
                        + record.topic() + "\t" + record.offset()
                        + "\t" + record.timestamp() + "\t" + record.partition());
            }
        }
    }
}

在这里插入图片描述

1)消费者并不是独立存在,kafka中消费者会以消费组的方式进行组织和管理

2)消费组符合特征: 组外广播、组内负载均衡

  • 组外广播: 保证不同的消费组,能够独立消费新产生的数据
  • 组内负载均衡: 消息只会被消费组中的一个消费着进行处理,多个消费组提高了Kafka并行处理能力

3)消费者可以订阅一个到多个感兴趣的Topic,一旦这些Topic有新的数据产生,消费者会自动拉取新产生的数据,进行相应的业务处理

4)消费者在消费消息时,会维护一个消费的位置(offset),下一次消费时会自动从offset向后进行消费。

​ 在kafka中数据会有一个默认的保留周期(7天),在保留期内数据是可以进行重复消费的,只需要重置消费者消费的offset即可。

5)__consumer_offsets是一个特殊topic,主要记录了Kafka消费组的消费位置。

四、高级部分

偏移量控制

Kafka消费者在订阅Topic时,会自动拉取Topic中新产生的数据。首次消费时使用默认的偏移量消费策略lastest

偏移量消费策略:

  • lastest(默认):如果有已提交的offset,从已提交的offset之后消费消息。如果无提交的offset,从最后的offset之后消费数据

  • earliest:如果有已提交的offset,从已提交的offset之后消费消息。如果无提交的offset,从最早的offset消费消息

    // 注意:此配置项 修改偏移量消费策略的默认行为 
    properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"earliest");
    

Kafka消费者消费位置offset,默认采用自动提交的方式,将消费位置提交保存到特殊Topic__consumer_offsets

自动提交策略:

// 默认自动提交消费的位置offset
properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG,true);
// 默认每隔5秒提交一次消费位置
properties.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG,5000);

通常情况需要手动提交消费位置:

为什么需要手动提交消费位置(offset)的原因?

原因:如果自动提交消费位置,有可能在进行业务处理时出现错误,会造成数据没有被正确处理。

​ 手动提交消费位置,可以保证数据一定能够被完整的正确处理。

// 关闭消费位置offset的自动提交功能
properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG,false);

// 手动提交消费位置
consumer.commitSync();

消费方式

订阅(Subscribe)

消费者订阅1到N个感兴趣的Topic,一旦Topic中有新的数据产生,会自动拉取Topic分区内的所有数据

// 订阅(消费)Topic所有的分区
consumer.subscribe(Arrays.asList("t3"));
指定消费分区

消费者在消费数据时,可以只消费某个Topic特定分区内的数据

// 指定消费Topic的特定分区
consumer.assign(Arrays.asList(new TopicPartition("t3",0)));
重置消费位置

消费者在消费数据时,可以重置消费的offset,消费已消费的数据或者跳过不感兴趣的数据

consumer.assign(Arrays.asList(new TopicPartition("t3",0)));
// 重置消费位置
consumer.seek(new TopicPartition("t3",0),1);

消费组

(略)

自定义对象类型的传输

序列化接口
public interface Serializer<T> extends Closeable {
   
    void configure(Map<String, ?> var1, boolean var2);
	// 序列化方法
    byte[] serialize(String var1, T var2);

    default byte[] serialize(String topic, Headers headers, T data) {
   
        return this.serialize(topic, data);
    }

    void close();
}
反序列化接口
public interface Deserializer<T> extends Closeable {
   
    void configure(Map<String, ?> var1, boolean var2);
	
    // 反序列化方法
    T deserialize(String var1, byte[] var2);

    default T deserialize(String topic, Headers headers, byte[] data) {
   
        return this.deserialize(topic, data);
    }

    void close();
}
自定义对象
public class User implements Serializable {
   

    private Integer id;
    private String name;
    private Date birthday;
    // ...
}
导入工具包的依赖jar包
<dependency>
    <groupId>commons-lang</groupId>
    <artifactId>commons-lang</artifactId>
    <version>2.4</version>
</dependency>
自定义编解码器类
package transfer;

import org.apache.commons.lang.SerializationUtils;
import org.apache.kafka.common.serialization.Deserializer;
import org.apache.kafka.common.serialization.Serializer;

import java.io.Serializable;
import java.util.Map;

/**
 * 自定义对象的编解码器类
 */
public class ObjectCodec implements Serializer, Deserializer {
   

    /**
     * bytes[] ---> Object
     * @param s
     * @param bytes
     * @return
     */
    @Override
    public Object deserialize(String s, byte[] bytes) {
   
        return SerializationUtils.deserialize(bytes);
    }

    @Override
    public void configure(Map map, boolean b) {
   

    }

    /**
     * Object ---> bytes[]
     * @param s
     * @param o
     * @return
     */
    @Override
    public byte[] serialize(String s, Object o) {
   
        return SerializationUtils.serialize((Serializable) o);
    }

    @Override
    public void close() {
   

    }
}
测试

建议新创建Topic进行测试,避免旧的Topic中历史数据对我们产生干扰

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值