Kafka学习笔记1--初步使用

参考文章:

https://baike.baidu.com/item/Kafka/17930165?fr=aladdin

https://blog.csdn.net/qq_29186199/article/details/80827085

http://www.linkedkeeper.com/detail/blog.action?bid=1016

一、基础知识

概述

  Kafka是Apache基金会旗下的开源流处理软件,使用Scala和Java编写的高吞吐量的基于日志的分布式发布订阅消息系统,通常Kafka被用来当作消息广播以及消息队列来使用。

  Kafka的程序包很小,很轻量,所以很适合用来封装集成进行二次开发。

相关术语

broker: Kafka集群包含一个或多个服务器(Kafka Server),这些服务器在Kafka中就被称为broker。

topic: 消息的主题,每条消息都属于一个主题,消费者订阅消息时即按照主题订阅。

partition: 一个topic对应多个partition,topic是逻辑层面的概念,partition是物理层面的概念,一个partition对应一个目录,目录中可以有很多文件(segment),主要是索引文件和日志文件,发布的消息就存储在这些日志文件中,再通过topic组织到一个消息主题中,供生产者和消费者读写。

segment file: 一个partition可以包含很多个segment file,每个segement file由两部分构成,分别是.index和.log,两者成对出现,index文件中存储索引数据,即每条消息在当前log中是第几条,并指向该条消息对应的物理偏移量,从而得以在log文件中快速定位读取消息,log文件中就是存储的消息数据。每当segment file达到一定的大小,就会产生新的segment file,这样设计的目的是为了避免单个日志文件太大,导致Kafka的读写性能急剧下降。

producer: 生产者,即产生消息的用户,负责发布消息到broker。

consumer: 消费者,即获取并使用的消息的用户,消费通过向服务器订阅topic来获取对应的消息。

consumer group: 消费分组,这是Kafka中一个比较独特的概念,每个消费者都属于一个特定的消费分组,订阅消息是以组为单位进行的,一个group订阅一个或多个topic,一个topic中的一条消息只会被一个group中的一个consumer接收到。所以基于这种特性,当我们需要使用的是消息队列,就将多个consumer关联到同一个group中,这样可以保证消息只会被消费一次。如果我们需要使用的是消息广播,那么一个group就只关联一个consumer,这样就可以保证所有的consumer都能够接收到消息。

offset:

  读取消息的位置偏移量,这也是Kafka中一个很独特的概念。

  Kafka与传统的消息队列不同,传统的消息队列消息被消费后,就会被删除,即由服务器来控制消息的分发,保证消息只被消费一次,而Kafka中消息的分发并不由服务器端控制,Kafka Server将接收到的消息写入日志文件,即partition中,consumer消费时就拿着offset来向Kafka Server请求,offset就指示了consumer要获取的消息位于partition中的位置信息,Kafka Server根据该位置信息取出数据并返回给consumer。

  从这种模式可以看出,Kafka和传统的消息队列还有一个不同的地方就是,Kafka的消息其实可以重复消费,甚至通过设置offset可以从任意指定位置消费,而传统的消息队列是无法重复消费的。

  offset由consumer管理,通过这种模式,Kafka将控制消息分发的压力转移给了客户端,而在传统的消息队列中,服务器为了控制消息分发,为了保证每个消息只被消费一次,需要采用同步机制,从而出现性能瓶颈,这也是为什么Kafka性能如此之高的一个原因。

replication: partition的副本,当使用Kafka集群时,一个partition会有多个副本,其中最先创建的是leader,其它均为follower,producer和consumer只和leader交互,follower只负责从leader中fetch同步数据,当leader宕机后会从follower中选举一个新的leader,从而尽量保证数据不丢失。

原理

工作流程

工作原理图如下:
在这里插入图片描述

  • producer选择一个topic,发送消息到Kafka Server,Kafka Server通过一定的分配策略,将消息append到某个partition末尾,一次生产消息的过程即结束;
  • Kafka Server接收到producer的消息后,将消息写入日志文件,并记录offset;
  • consumer group中某一个consumer,选择一个topic,请求指定offset的消息数据;

持久化原理

  Kafka是通过日志文件实现持久化的,也就是前面术语介绍中的segment file,topic可以包含多个partition,一个partition对应一个目录,目录下可以包含很多个segment file,segment file又包含.index和.log两个文件,当一个segment file达到一定大小时,会自动生成新的segment file,避免单个文件过大。

  详情可参看术语介绍中的topic、partition、segment、offset。

二、Kafka常用命令

以下命令均基于windows,linux下也大同小异。

首先我们需要启动zookeeper,Kafka中默认集成了zookeeper,所以我们不需要额外下载安装,使用集成的即可,默认的zookeeper配置文件中默认端口是2181。

启动zookeeper的命令如下:

bin\windows\zookeeper-server-start.bat config\zookeeper.propertie

然后启动Kafka Server,默认配置文件中默认使用的端口是9092

启动Kafka Server命令如下:

bin\windows\kafka-server-start.bat config\server.properties

创建需要使用的topic,命令如下:

bin\windows\kafka-topics.bat --create --zookeeper <IP>:<port> --replication-factor 1 --partitions 1 --topic <topic>

–replication-factor为该topic需要备份的副本数量,应小于或等于集群中的broker数量,–partitions为该topic下的partition数量

修改topic的分区数量,命令如下:

bin\windows\kafka-topics.bat --zookeeper <IP>:<port> --partitions <num> --topic <topic> --alter

向broker-list中的broker下的某topic发送消息,控制台启动生产者命令如下:

bin\windows\kafka-console-producer.bat --broker-list <IP>:<port> --topic <topic>

从bootstrap-server罗列的broker中的某topic中接收消息,并声明当前consumer所属的group,控制台启动消费者命令如下:

bin\windows\kafka-console-consumer.bat --bootstrap-server <IP>:<port> --topic <topic> --group <group>

三、在Java中使用Kafka

通过Maven导入需要的依赖包,代码如下:

<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
    <version>2.2.0</version>
</dependency>

构建生产者,代码如下:

package com.dongrui.study.kafka;

import java.util.Properties;

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

public class KafkaProducerProxy<K, V> {
	private KafkaProducer<K, V> producer;

	public KafkaProducerProxy(Properties properties) {
		producer = new KafkaProducer<K, V>(properties);
	}
	
	public KafkaProducerProxy() {
		// 初始化生产者并设置属性
		Properties properties = new Properties();
		properties.put("bootstrap.servers", "172.24.108.223:9092");
		properties.put("session.timeout.ms", "5000");
		properties.put("acks", "all");
		properties.put("retries", "0");
		properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
		properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
		producer = new KafkaProducer<K, V>(properties);
	}

	public void sendMessage(String topic, V value) {
		try {
			// 发送消息
			producer.send(new ProducerRecord<K, V>(topic, value));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

构建消费者,代码如下:

package com.dongrui.study.kafka;

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

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

public class KafkaConsumerProxy<K,V> {
	private KafkaConsumer<K, V> kafkaConsumer;
	private HashSet<String> topics = new HashSet<>();
	
	public KafkaConsumerProxy(Properties properties,String...topics) {
		kafkaConsumer = new KafkaConsumer<K, V>(properties);
		for (String topic : topics) {
			this.topics.add(topic);
		}
		kafkaConsumer.subscribe(this.topics);
	}
	
	public KafkaConsumerProxy(String...topics) {
		// 设置初始化属性并初始化
		Properties properties = new Properties();
		properties.put("bootstrap.servers", "172.24.108.223:9092");
		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.StringDeserializer");
		kafkaConsumer = new KafkaConsumer<K, V>(properties);
		
		// 为该consumer订阅topic
		for (String topic : topics) {
			this.topics.add(topic);
		}
		kafkaConsumer.subscribe(this.topics);
	}
	
	public ConsumerRecords<K, V> received() {
		// 没有消息时,poll会阻塞设置的时长,直到收到消息
		return kafkaConsumer.poll(Duration.ofMinutes(1));
	}
}

完整demo源码位于 https://gitee.com/imdongrui/study-repo.git 仓库中kafka目录下,需要科自行获取

踩过的坑

windows powershell的坑

在windows powershell中执行创建生产者的命令,有不能加载出输入行的问题,开始一直以为是kafka server部署有问题,但经过折腾最终发现是window powershell的问题。

单个生产者发送消息不要太频繁

for (int i = 0; i < 10; i++) {
	value = format.format(new Date()) + " good boy kafka " + i;
	kafkaProducerProxy.sendMessage("study", value);
}

如上所示代码,由于for循环执行太快,导致消息不能成功发送,偶尔又能够成功发送,也是几经折腾,最终发现加上sleep后能够正常发送,暂不清楚是kafka-clien的问题还是kafka server的问题,修改后的代码如下:

for (int i = 0; i < 10; i++) {
	value = format.format(new Date()) + " good boy kafka " + i;
	kafkaProducerProxy.sendMessage("study", value);
	Thread.sleep(1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值