Kafka入门

一:kafka基本组成部分

复制代码
官网地址:http://kafka.apache.org/


一:broker
Kafka 集群包含一个或多个服务器,服务器节点称为broker。
broker存储topic的数据。如果某topic有N个partition,集群有N个broker,那么每个broker存储该topic的一个partition。
如果某topic有N个partition,集群有(N+M)个broker,那么其中有N个broker存储该topic的一个partition,剩下的M个broker不存储该topic的partition数据。
如果某topic有N个partition,集群中broker数目少于N个,那么一个broker存储该topic的一个或多个partition。在实际生产环境中,尽量避免这种情况的发生,这种情况容易导致Kafka集群数据不均衡。

二:Topic
每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic。(物理上不同Topic的消息分开存储,逻辑上一个Topic的消息虽然保存于一个或多个broker上但用户只需指定消息的Topic即可生产或消费数据而不必关心数据存于何处)
类似于数据库的表名

三:Partition
topic中的数据分割为一个或多个partition。每个topic至少有一个partition。每个partition中的数据使用多个segment文件存储。partition中的数据是有序的,不同partition间的数据丢失了数据的顺序。如果topic有多个partition,消费数据时就不能保证数据的顺序。在需要严格保证消息的消费顺序的场景下,需要将partition数目设为1。

四:Producer
生产者即数据的发布者,该角色将消息发布到Kafka的topic中。broker接收到生产者发送的消息后,broker将该消息追加到当前用于追加数据的segment文件中。生产者发送的消息,存储到一个partition中,生产者也可以指定数据存储的partition。

五:Consumer
消费者可以从broker中读取数据。消费者可以消费多个topic中的数据。

六:Consumer Group
每个Consumer属于一个特定的Consumer Group(可为每个Consumer指定group name,若不指定group name则属于默认的group)。
复制代码

二:demo部分

2.1:AdminClient,kafka连接对象

复制代码
package com.jachs.kafka.clients.admin;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;

import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.AdminClientConfig;
import org.apache.kafka.clients.admin.DeleteTopicsOptions;
import org.apache.kafka.clients.admin.DeleteTopicsResult;
import org.apache.kafka.clients.admin.ListTopicsResult;
import org.apache.kafka.common.KafkaFuture;
import org.junit.Before;
import org.junit.Test;

import com.jachs.kafka.Console;

/***
 * 
 * @author zhanchaohan
 * @see org.apache.kafka.clients.admin.AdminClient
 * @see org.apache.kafka.clients.AdminClientConfig
 * 
 * 源码包kafka-client下  common.message包下有全量json
 */
public class AdminClientTest {
    AdminClient ac;
    
    /***
     * 初始化AdminClient對象
     */
    @Before
    public void init() {
//        Properties properties=new Properties();//读取配置文件方式
//        ac=AdminClient.create(properties);
        
        //代码Map加载配置方式
        Map<String, Object> conf=new HashMap<String, Object>();
        conf.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, Console.BOOTSTRAP_SERVERS_CONFIG);//服务地址
        
        ac=AdminClient.create(conf);
    }
    //打印全部的Topics
    @Test
    public void listTopics() throws InterruptedException, ExecutionException {
        ListTopicsResult listTopicsResult=ac.listTopics();
        KafkaFuture<Set<String>>sets=listTopicsResult.names();
        
        for (String name : sets.get()) {
            System.out.println(name);
        }
    }
    /***
     * 删除Topics,需要配置server.properties配置文件的delete.topic.enable=true
     */
    @Test
    public void deleteTopics() {
        Set<String>topics=new HashSet<String>();
        topics.add("topic1");
        topics.add("acs");
        topics.add("HelloWorld");
        
//        DeleteTopicsResult deleteTopicsResult=ac.deleteTopics(topics);
        
        //如果配额冲突应自动重试,则设置为true。
//        DeleteTopicsResult deleteTopicsResult=ac.deleteTopics(topics, new DeleteTopicsOptions().retryOnQuotaViolation(true));
        //保留此方法以保持与0.11的二进制兼容性
        DeleteTopicsResult deleteTopicsResult=ac.deleteTopics(topics, new DeleteTopicsOptions().timeoutMs(3000));
        
        
        for (String key : deleteTopicsResult.values().keySet()) {
            System.out.println(key);
            
            System.out.println("isCancelled:\t"+deleteTopicsResult.values().get(key).isCancelled());
            System.out.println("isCompletedExceptionally:\t"+deleteTopicsResult.values().get(key).isCompletedExceptionally());
            System.out.println("isDone:\t"+deleteTopicsResult.values().get(key).isDone());
            
            System.out.println("--------------------------------------------------------------");
        }
    }
}
复制代码

2.2:Producer,生成者对象

复制代码
import java.util.Properties;

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;

import com.jachs.kafka.Console;

/***
 * 
 * @author zhanchaohan
 *
 */
public class ProducerDemo {
    public static void main(String[] args) {
        Properties properties = new Properties();
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, Console.BOOTSTRAP_SERVERS_CONFIG);
        properties.put(ProducerConfig.ACKS_CONFIG, "all");
        properties.put(ProducerConfig.RETRIES_CONFIG, 0);
        properties.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
        properties.put(ProducerConfig.LINGER_MS_CONFIG, 1);
        properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.StringSerializer");
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.StringSerializer");

        Producer<String, String> helloProducer = null;
        try {
            helloProducer = new KafkaProducer<String, String>(properties);
            for (int i = 0; i < 100; i++) {
                String msg = "This is Message " + i;
                if (i < 20) {
                    helloProducer.send(new ProducerRecord<String, String>("A", msg));
                } else if (i >= 20 && i > 60) {
                    helloProducer.send(new ProducerRecord<String, String>("B", msg));
                } else if (i >= 60 && i > 80) {
                    helloProducer.send(new ProducerRecord<String, String>("C", msg));
                } else {
                    helloProducer.send(new ProducerRecord<String, String>("D", msg));
                }
                System.out.println("Sent:" + msg);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            helloProducer.close();
        }

    }
}
复制代码

2.3:KafkaConsumer,消费者对象,消费2.2生产者生成的信息

复制代码
import java.time.Duration;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

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 com.jachs.kafka.Console;

/***
 * @author zhanchaohan
 */
public class ConsumerDemo {
    
    public static void main(String[] args) throws InterruptedException {
        Properties properties = new Properties();
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, Console.BOOTSTRAP_SERVERS_CONFIG);
        properties.put(ConsumerConfig.GROUP_ID_CONFIG, "group-1");
        properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");
        properties.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");
        properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
        properties.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000");
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");

        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<String, String>(properties);
        
        Set<String>topicSet=new HashSet<String>();
        topicSet.add("A");
        kafkaConsumer.subscribe(topicSet);
//        ConsumerRecords<String, String> pollMap=kafkaConsumer.poll(Duration.ofHours(1) );//超时时间小时单位
//        ConsumerRecords<String, String> pollMap=kafkaConsumer.poll(Duration.ofMinutes(1) );//超时时间分钟单位
        
        ConsumerRecords<String, String> pollMap=kafkaConsumer.poll(50000 );//超时时间毫秒单位
        for (ConsumerRecord<String, String> consumerRecord : pollMap) {
            System.out.println(consumerRecord.toString());
        }
        
    }

}
复制代码

2.4:传输字节流测试传输文件,生产者代码

复制代码
package com.jachs.kafka.pac.file.part1;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;

import com.jachs.kafka.Console;

/***
 * 
 * @author zhanchaohan
 *
 */
public class ProducerDemo {
    private static String topic = "files";
    private static File fis=new File("e:\\f.xls");
    
    public static void main(String[] args) throws Exception {
        InputStream is = new FileInputStream(fis);
        ByteArrayOutputStream  baos=new ByteArrayOutputStream();
        byte []buffer=new byte[1024];
        
        while(is.read(buffer)!=-1) {
            baos.write(buffer, 0, buffer.length);
        }
        
        Properties properties = new Properties();
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, Console.BOOTSTRAP_SERVERS_CONFIG);
        properties.put(ProducerConfig.ACKS_CONFIG, "all");
        properties.put(ProducerConfig.RETRIES_CONFIG, 0);
        properties.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
        properties.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
        properties.put(ProducerConfig.LINGER_MS_CONFIG, 1);
        properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.ByteArraySerializer");
        
        
        Producer<String, byte[]> producer = null;
        try {
            producer = new KafkaProducer<String, byte[]>(properties);
            producer.send(new ProducerRecord<String, byte[]>(topic,fis.getName(), baos.toByteArray()));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            producer.close();
            is.close();
        }

    }
}
复制代码

2.5:传输字节流测试传输文件,消费者代码。

复制代码
package com.jachs.kafka.pac.file.part1;

import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;

import com.jachs.kafka.Console;

/***
 * 
 * @author zhanchaohan
 *
 */
public class ConsumerDemo {
    private static String filepath = "e:\\log\\";

    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();
        properties.put("bootstrap.servers", Console.BOOTSTRAP_SERVERS_CONFIG);
        properties.put("group.id", "group-1");
        properties.put("enable.auto.commit", "true");
        properties.put("auto.commit.interval.ms", "1000");
        properties.put("auto.offset.reset", "earliest");
        properties.put("session.timeout.ms", "30000");
        properties.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        properties.put("value.deserializer", "org.apache.kafka.common.serialization.ByteArrayDeserializer");

        KafkaConsumer<String, byte[]> kafkaConsumer = new KafkaConsumer<String, byte[]>(properties);

        Set<String> topics = new HashSet<String>();
        topics.add("files");
        kafkaConsumer.subscribe(topics);
        ConsumerRecords<String, byte[]> records = kafkaConsumer.poll(50000);//等50秒
        for (ConsumerRecord<String, byte[]> record : records) {
            OutputStream os = new FileOutputStream(filepath+record.key());

            os.write(record.value());
            os.close();
        }
    }

}
复制代码

2.6:源码部分序列化传输的代码在org.apache.kafka.common.serialization包下如下图

自定义序列化代码,继承Deserializer,Serializer接口实现自定义代码

 

三:配置文件详情

3.1:server

复制代码
#http://kafka.apache.org/documentation/#producerapi

#每一个broker在集群中的唯一表示,要求是正数。当该服务器的IP地址发生改变时,broker.id没有变化,则不会影响consumers的消息情况
broker.id =0
#kafka数据的存放地址,多个地址的话用逗号分割,多个目录分布在不同磁盘上可以提高读写性能
log.dirs=/data0/kafka_data
#broker server服务端口
port =9092
#broker处理消息的最大线程数,一般情况下数量为cpu核数
num.network.threads =4
#broker处理磁盘IO的线程数,数值为cpu核数2倍
num.io.threads =8
#一些后台任务处理的线程数,例如过期消息文件的删除等,一般情况下不需要去做修改
background.threads =4
#等待IO线程处理的请求队列最大数,若是等待IO的请求超过这个数值,那么会停止接受外部消息
queued.max.requests =500
#broker的主机地址,若是设置了,那么会绑定到这个地址上,若是没有,会绑定到所有的接口上,并将其中之一发送到ZK,一般不设置
host.name=
#socket的发送缓冲区,socket的调优参数SO_SNDBUFF
socket.send.buffer.bytes=100*1024
#socket的接受缓冲区,socket的调优参数SO_RCVBUFF
socket.receive.buffer.bytes =100*1024
#socket请求的最大数值,防止serverOOM,message.max.bytes必然要小于socket.request.max.bytes,会被topic创建时的指定参数覆盖
socket.request.max.bytes =100*1024*1024
#topic的分区是以一堆segment文件存储的,这个控制每个segment的大小,会被topic创建时的指定参数覆盖
log.segment.bytes =1024*1024*1024
#这个参数会在日志segment没有达到log.segment.bytes设置的大小,也会强制新建一个segment会被 topic创建时的指定参数覆盖
log.roll.hours =24*7
#日志清理策略选择有:delete和compact主要针对过期数据的处理,或是日志文件达到限制的额度,会被 topic创建时的指定参数覆盖
log.cleanup.policy = delete
#数据文件保留多长时间,存储的最大时间超过这个时间会根据log.cleanup.policy设置数据清除策略log.retention.bytes和log.retention.minutes或log.retention.hours任意一个达到要求,都会执行
log.retention.minutes=300 或 log.retention.hours=24
#topic每个分区的最大文件大小,一个topic的大小限制 = 分区数*log.retention.bytes。-1没有大小限log.retention.bytes和log.retention.minutes任意一个达到要求,都会执行删除,会被topic创建时的指定参数覆盖
log.retention.bytes=-1
#是否开启日志压缩
log.cleaner.enable=false
#日志压缩线程
log.cleaner.threads = 2
#日志清理时候用到的IO块大小一般不需要修改
log.cleaner.io.buffer.size=512*1024
#日志清理中hash表的扩大因子一般不需要修改
log.cleaner.io.buffer.load.factor =0.9
#日志清理的频率控制,越大意味着更高效的清理,同时会存在一些空间上的浪费,会被topic创建时的指定参数覆盖
log.cleaner.min.cleanable.ratio=0.5
#对于压缩的日志保留的最长时间,也是客户端消费消息的最长时间,同log.retention.minutes的区别在于一个控制未压缩数据,一个控制压缩后的数据。会被topic创建时的指定参数覆盖
log.cleaner.delete.retention.ms =1day
#对于segment日志的索引文件大小限制,会被topic创建时的指定参数覆盖
log.index.size.max.bytes =10*1024*1024
#当执行一个fetch操作后,需要一定的空间来扫描最近的offset大小,设置越大,代表扫描速度越快,但是也更好内存,一般情况下不需要搭理这个参数
log.index.interval.bytes =4096
#log文件”sync”到磁盘之前累积的消息条数,因为磁盘IO操作是一个慢操作,但又是一个”数据可靠性"的必要手段,所以此参数的设置,需要在"数据可靠性"与"性能"之间做必要的权衡.如果此值过大,将会导致每次"fsync"的时间较长(IO阻塞),如果此值过小,将会导致"fsync"的次数较多,这也意味着整体的client请求有一定的延迟.物理server故障,将会导致没有fsync的消息丢失
log.flush.interval.messages=1000
#是否允许自动创建topic,若是false,就需要通过命令创建topic
auto.create.topics.enable =true
#每个topic的分区个数,若是在topic创建时候没有指定的话会被topic创建时的指定参数覆盖
num.partitions =1
复制代码

3.2:producer生产者配置详情

复制代码
#http://kafka.apache.org/documentation/#producerapi

#消费者获取消息元信息(topics, partitions and replicas)的地址,配置格式是:host1:port1,host2:port2,也可以在外面设置一个vip
metadata.broker.list
#消息的确认模式,0:不保证消息的到达确认,只管发送,低延迟但是会出现消息的丢失,在某个server失败的情况下,有点像TCP,1:发送消息,并会等待leader 收到确认后,一定的可靠性,-1:发送消息,等待leader收到确认,并进行复制操作后,才返回,最高的可靠性
request.required.acks = 0
#消息发送的最长等待时间
request.timeout.ms = 10000
#socket的缓存大小
send.buffer.bytes=100*1024
#key的序列化方式,若是没有设置,同serializer.class
key.serializer.class
#分区的策略,默认是取模
partitioner.class=kafka.producer.DefaultPartitioner
#消息的压缩模式,默认是none,可以有gzip和snappy
compression.codec = none
#可以针对默写特定的topic进行压缩
compressed.topics=null
#消息发送失败后的重试次数
message.send.max.retries = 3
#每次失败后的间隔时间
retry.backoff.ms = 100
#生产者定时更新topic元信息的时间间隔 ,若是设置为0,那么会在每个消息发送后都去更新数据
topic.metadata.refresh.interval.ms = 600 * 1000
#用户随意指定,但是不能重复,主要用于跟踪记录消息
client.id=""
#生产者的类型 async:异步执行消息的发送 sync:同步执行消息的发送
producer.type=sync
#异步模式下,那么就会在设置的时间缓存消息,并一次性发送
queue.buffering.max.ms = 5000
#异步的模式下 最长等待的消息数
queue.buffering.max.messages = 10000
#异步模式下,进入队列的等待时间 若是设置为0,那么要么进入队列,要么直接抛弃
queue.enqueue.timeout.ms = -1
#异步模式下,每次发送的最大消息数,前提是触发了queue.buffering.max.messages或是queue.buffering.max.ms的限制
batch.num.messages=200
#消息体的系列化处理类 ,转化为字节流进行传输
serializer.class = kafka.serializer.DefaultEncoder
复制代码

3.3:consumer消费者配置详情

复制代码
#http://kafka.apache.org/documentation/#producerapi

#Consumer归属的组ID,broker是根据group.id来判断是队列模式还是发布订阅模式,非常重要
group.id=
#消费者的ID,若是没有设置的话,会自增
consumer.id
#一个用于跟踪调查的ID ,最好同group.id相同
client.id = group id value
#对于zookeeper集群的指定,可以是多个 hostname1:port1,hostname2:port2,hostname3:port3 必须和broker使用同样的zk配置
zookeeper.connect=localhost:2182
#zookeeper的心跳超时时间,查过这个时间就认为是dead消费者
zookeeper.session.timeout.ms = 6000
#zookeeper的等待连接时间
zookeeper.connection.timeout.ms = 6000
#zookeeper的follower同leader的同步时间
zookeeper.sync.time.ms = 2000
#当zookeeper中没有初始的offset时候的处理方式 。smallest :重置为最小值 largest:重置为最大值 anything else:抛出异常
auto.offset.reset = largest
#socket的超时时间,实际的超时时间是:max.fetch.wait + socket.timeout.ms.
socket.timeout.ms= 30 * 1000
#socket的接受缓存空间大小
socket.receive.buffer.bytes=64 * 1024
#从每个分区获取的消息大小限制
fetch.message.max.bytes = 1024 * 1024
#是否在消费消息后将offset同步到zookeeper,当Consumer失败后就能从zookeeper获取最新的offset
auto.commit.enable = true
#自动提交的时间间隔
auto.commit.interval.ms = 60 * 1000
#用来处理消费消息的块,每个块可以等同于fetch.message.max.bytes中数值
queued.max.message.chunks = 10
#当有新的consumer加入到group时,将会reblance,此后将会有partitions的消费端迁移到新,的consumer上,如果一个consumer获得了某个partition的消费权限,那么它将会向zk注册,"Partition Owner registry"节点信息,但是有可能此时旧的consumer尚没有释放此节点,此值用于控制,注册节点的重试次数.
rebalance.max.retries = 4
#每次再平衡的时间间隔
rebalance.backoff.ms = 2000
#每次重新选举leader的时间
refresh.leader.backoff.ms
#server发送到消费端的最小数据,若是不满足这个数值则会等待,知道满足数值要求
fetch.min.bytes = 1
#若是不满足最小大小(fetch.min.bytes)的话,等待消费端请求的最长等待时间
fetch.wait.max.ms = 100
#指定时间内没有消息到达就抛出异常,一般不需要改
consumer.timeout.ms = -1
复制代码

 

posted @   Jachs  阅读(140)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示
人生而自由,却无往不在枷锁中。