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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)