一、概述
1.1 消息队列-Message Queue
1.1.1 消息:
消息是系统间通信载体,是分布式应用不可或缺一部分。目前系统间发送消息的方式有两种。
1.1.1.1 同步消息
即使消息:打电话、表单提交、WebService(SOAP)、Dubbo/SpringCloud
要求消息发送方和接收方必须同时在线,一般都需要和接收方建立会话。
1.1.1.2 异步消息
发动方不接受对象是否在线,一般不需要和接收方建立会话,在接收方上线后,一般会自获取发送方发送的消息。
离线消息:发短信、发邮件、写信—消息队列
1.1.2 Queue
消息队列
FIFO先进先出
1.1.3使用场景
1.1.3.1 异步消息
1.1.3.2 系统间解耦
1.1.3.3 削峰填谷
1.2 Kafka
1.2.1 简介
http://kafka.apache.org
Kafka® is used for building real-time data pipelines and streaming apps. It is horizontally scalable, fault-tolerant, wicked fast, and runs in production in thousands of companies.
1.2.2 kafka结构
1.2.3 Kafka组件说明
1.2.3.1 Broker
每个kafka server成为一个Broker,多个Broker组成kafka cluster
一个机器上可以部署一个或者多个Broker,这多个Broker连接到相同的ZK就组成了Kafka集群。
1.2.3.2 Topic
Kafka是一个发布订阅消息系统,它的逻辑如下
Topic就是消息类名,一个Topic中通常放置一类消息。每个Topic都有一个或者多个订阅者,也就是消费者Consumer。
Producer将消息推送到Topic,由订阅该Topic的Consumer从Topic中拉取消息。
1.2.3.3 Topic与Broker
一个Broker上可以创建一个或者多个Topic。同一个Topic可以在同一集群下的多个Broker。
1.2.3.4 Partion log
kafka会为每个topic维护了多个分区(partition),每个分区会映射到一个逻辑的日志(log)文件
每当一个message被发布到一个topic上的一个partition,broker应该会将message追加到这个逻辑log的租后一个segment上。这些segments会flush到磁盘上。Flush时可以按照时间进行,也可以按照message数来执行。
每个partition都是一个有序的、不可变的结构化的提交日志记录的序列。在每个partition中每一个日志记录都会被分配一个序号——通常称为offset,offset在partiton内是唯一的。论点逻辑文件会被化为多个文件segment(每个segment的大小一样的)
Broker集群将会保留所有已发布的message records,不管这些消息是否已被消费。保留时间依赖于一个可配的保留周期。例如:如果设置了保留策略是2day,那么每一条消息发布两天内是被保留的,在这个2day的保留时间内,消息是可以被消费的,过期后不再保留。
1.2.3.5 Partiton distribution
日志分区是分布式的存在于一个kafka集群的多个Broker。每个partiton会被复制多份存在于不同的Broker上。这样做是为了容灾。具体会复制几份,会复制到那些broker上,都是可以配置的。经过相关复制策略后,每个topic在每个broker上回驻留一到多个partiton。
对于同一个partiton,它所在任何一个broker,都有能扮演两种角色:leader、follower
每个partiton的Leader用于处理到该partiton的读写请求。
每个partiton的followers是用于异步的从它的leader中复制数据。
Kafka会动态维护一个与Leader保持一致的同步副本(in-sync replicas (ISR))集合,并且会将最新的同步副本(ISR )集合持久化到zookeeper。如果leader出现问题了,就会从该partition的followers中选举一个作为新的leader。
1.2.4 Kafka 角色
1.2.4.1 Producer
Producer作为消息的生产者,在生产消息后需要将消息投送到指定的目的地(某个topic的某个partition)。Producer可以根据指定选择partiton的算法或者是随机方式来选择发布消息到哪个partiton
1.2.4.2 Consumer
在Kafka中,同样有consumer group的概念,它是逻辑上将一些consumer分组。因为每个kafka consumer是一个进程。所以一个consumer group中的consumers将可能是由分布在不同机器上的不同的进程组成的。Topic中的每一条消息可以被多个consumer group消费,然而每个consumer group内只能有一个consumer来消费该消息。所以,如果想要一条消息被多个consumer消费,那么这些consumer就必须是在不同的consumer group中。所以也可以理解为consumer group才是topic在逻辑上的订阅者。
每个consumer可以订阅多个topic。
每个consumer会保留它读取到某个partition的offset。而consumer 是通过zookeeper来保留offset的
二、kafka安装
2.1 Kafka 基础环境
- 安装JDK,配置JAVA_HOME环境变量
- 安装Zookeeper集群|单机|伪集群 ,并且正常启动
- 必须配置主机名和IP的映射关系(很重要)
- 时钟需要同步|防火墙关闭
2.2 安装启动
集群模式
[root@HadoopNode01 ~]# mkdir /home/kafka
[root@HadoopNode01 ~]# tar -zxvf kafka_2.11-0.11.0.0.tgz -C /home/kafka/
[root@HadoopNode01 ~]# vi /home/kafka/kafka_2.11-0.11.0.0/config/server.properties
[root@HadoopNode01 ~]# cd /home/kafka/kafka_2.11-0.11.0.0/
[root@HadoopNode01 kafka_2.11-0.11.0.0]# ./bin/kafka-server-start.sh -daemon config/server.properties
版本
kafka_2.11-0.11.0.0
其中-
以前2.11表示Scala编译版本,后面0.11.0.0
表示kafka版本例如:
- Scala 2.11 - kafka_2.11-2.1.0.tgz (asc, sha512)
- Scala 2.12 - kafka_2.12-2.1.0.tgz (asc, sha512)
#创建topic
# 单节点下 可用的Broker 只有一个 所以副本数量必须是1个
# 副本数量受到了Broker数量的限制
[root@CentOSA kafka_2.11-0.11.0.0]# ./bin/kafka-topics.sh --zookeeper ZK01:2181,ZK02:2181,ZK03:2181 --create --topic topic01 --partitions 3 --replication-factor 3
//启动消费者
[root@CentOSA kafka_2.11-0.11.0.0]# ./bin/kafka-console-consumer.sh --bootstrap-server HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092 --topic topic01
//启动生产者
[root@CentOSB kafka_2.11-0.11.0.0]# ./bin/kafka-console-producer.sh --broker-list HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092 --topic topic01
>
#从开始消费
./bin/kafka-console-consumer.sh --bootstrap-server HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092 --topic topic01 --from-beginning
2.3 关闭
关闭 kafka服务
[root@CentOSA kafka_2.11-0.11.0.0]# vi bin/kafka-server-stop.sh
PIDS=$(jps | grep Kafka | awk '{print $1}')
if [ -z "$PIDS" ]; then
echo "No kafka server to stop"
exit 1
else
kill -s TERM $PIDS
fi
三、Topic管理
3.1 添加Topic
[root@CentOSA kafka_2.11-0.11.0.0]# ./bin/kafka-topics.sh --zookeeper ZK01:2181,ZK02:2181,ZK03:2181 --create --topic topic01 --partitions 3 --replication-factor 3
3.2 查看Topic详情
[root@HadoopNode02 kafka_2.11-0.11.0.0]# ./bin/kafka-topics.sh --zookeeper ZK01:2181,ZK02:2181,ZK03:2181 --describe --topic topic01
Topic:topic01 PartitionCount:3 ReplicationFactor:3 Configs:
Topic: topic01 Partition: 0 Leader: 2 Replicas: 2,1,0 Isr: 2,1,0
Topic: topic01 Partition: 1 Leader: 0 Replicas: 0,2,1 Isr: 0,2,1
Topic: topic01 Partition: 2 Leader: 1 Replicas: 1,0,2 Isr: 1,0,2
3.3 删除Topic信息
[root@CentOSA kafka_2.11-0.11.0.0]# ./bin/kafka-topics.sh --zookeeper ZK01:2181,ZK02:2181,ZK03:2181 --delete --topic topic01
3.4 修改分区信息
[root@CentOSA kafka_2.11-0.11.0.0]# ./bin/kafka-topics.sh --zookeeper ZK01:2181,ZK02:2181,ZK03:2181 --alter --topic topic01 --partitions 4
3.5 显示所有Topic
[root@HadoopNode02 kafka_2.11-0.11.0.0]# ./bin/kafka-topics.sh --zookeeper ZK01:2181,ZK02:2181,ZK03:2181 --list
四、Tpoic和日志
Topic以日志分区形式存储,分区中每一则Record都有个offset编号用于标示record的顺序.所有存储在kafka中record是允许用户重复消费的kafka通过log.retention.hours
控制Record存活时间.
kafka服务端只负责存储topic日志数据,kafka消费端独自维护一套分区信息和offset偏移量,一旦消费完成后消费端会自动提交消费分区的offset信息.下次在开始消费的时候只需要从上一次offset开始即可
删除一则已经发布的消息(很少)
[root@CentOSA kafka_2.11-0.11.0.0]# ./bin/kafka-delete-records.sh --bootstrap-server HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092 --offset-json-file /root/deleterecord.json
-----------------------------
{
"partitions":
[
{
"topic": "topic01", "partition": 1,"offset": 3}
]
}
删除topic01中分区1 的offset 2以前的所有的数据
五、消费者
组内
均分分区组间
广播
六、JavaAPI
6.1 依赖
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>0.11.0.0</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
6.2 删除Topic
@Test
public void del() {
Properties props = new Properties();
props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, "HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092");
//KafkaAdminClient创建AdminClient对象
AdminClient adminClient = KafkaAdminClient.create(props);
Collection<String> topics = Arrays.asList("topic01");
adminClient.deleteTopics(topics);
adminClient.close();
}
6.3 创建Topic
@Test
public void create() {
Properties props = new Properties();
props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, "HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092");
AdminClient adminClient = KafkaAdminClient.create(props);
Collection<NewTopic> topics = Arrays.asList(
new NewTopic("topic01", 3, (short) 3));
adminClient.createTopics(topics);
adminClient.close();
}
6.4 发布一则消息
@Test
public void publish() {
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.PARTITIONER_CLASS_CONFIG, DefaultPartitioner.class);
KafkaProducer<String, String> pd = new KafkaProducer<String, String>(props);
//封装record
for (int i = 0; i < 10; i++) {
ProducerRecord<String, String> record
= new ProducerRecord<String, String>("topic01", "00" + i + "0", "user0100 true 15000");
//发送消息
pd.send(record);
}
pd.flush();
pd.close();
}
6.5 订阅消息
@Test
public void subcribe() {
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "HadoopNode01:9092,HadoopNode02:9092,HadoopNode03:9092");
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.GROUP_ID_CONFIG, "g1");
System.out.println<