1. kafka介绍
1.1 什么是Kafka
Apache Kafka是一个分布式发布 - 订阅消息系统和一个强大的队列,可以处理大量的数据,并使能够将消息从一个端点传递到另一个端点。 Kafka适合离线和在线消息消费。 Kafka消息保留在磁盘上,并在群集内复制以防止数据丢失,Kafka构建在ZooKeeper同步服务之上。
Kafka专为分布式高吞吐量系统而设计。与其他消息传递系统相比,Kafka具有更好的吞吐量,内置分区,复制和固有的容错能力,这使得它非常适合大规模消息处理应用程序。
ZooKeeper是什么?
ZooKeeper是一个集中式服务,用于维护配置信息,命名,提供分布式同步和提供组服务。所有这些类型的服务以分布式应用程序的某种形式或另一种形式使用。每次他们被实现,有很多工作,以修复错误和竞争条件是不可避免的。由于实现这些服务的难度,应用程序最初通常嘲弄它们,这使得它们在变化的存在下变得脆弱并且难以管理。即使正确地完成,这些服务的不同实施导致在应用被部署时的管理复杂性。
打个比喻,其它分布式系统,就好比我们人的身体,有心脏,胃,有呼吸道系统。腿,手……等等。这么多的子系统处于分布式环境,怎么协调呢?那就是我们大脑(zookeeper),大家可以把zookeepr想成大脑,本身没有其它功能,只有负责协调,联络子系统的功能,具体有哪些协调功能大家自己去百度查吧。
1.2 Kafka的架构
Kafka架构,主要包括主题(Topic)、经纪人(Broker)、生产者(Producer)或者发布者,以及消费者(Consumer)或者订阅者等主要术语。
Broker
每个kafka server称为一个Broker,多个borker组成kafka cluster(集群)。
一个机器上可以部署一个或者多个Broker,这多个Broker连接到相同的ZooKeeper就组成了Kafka集群。
Topic
Kafka是一个发布订阅消息系统,它的逻辑结构如下:
Topic 就是消息类别名,一个topic中通常放置一类消息。每个topic都有一个或者多个消息的消费者consumer。
Producer(生产者)将消息推送到topic,由订阅该topic的consumer从topic中拉取消息。
谈到kafka的存储,就不得不提到分区,即partitions,创建一个topic时,同时可以指定分区数目,分区数越多,其吞吐量也越大,但是需要的资源也越多,同时也会导致更高的不可用性,kafka在接收到生产者发送的消息之后,会根据均衡策略将消息存储到不同的分区中。
在每个分区中,消息以顺序存储,最晚接收的的消息会最后被消费。
Topic 与broker
一个Broker上可以创建一个或者多个Topic。同一个topic可以在同一集群下的多个Broker中分布。
Producer
Producer作为消息的生产者,在生产完消息后需要将消息投送到指定的目的地(某个topic的某个partition)。Producer可以根据指定选择partition的算法或者是随机方式来选择发布消息到哪个partition。
生产者在向kafka集群发送消息的时候,可以通过指定分区来发送到指定的分区中
也可以通过指定均衡策略来将消息发送到不同的分区中
如果不指定,就会采用默认的随机均衡策略,将消息随机的存储到不同的分区中
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使用offset来记录当前消费的位置
在kafka的设计中,可以有多个不同的group来同时消费同一个topic下的消息,如图,我们有两个不同的group同时消费,他们的的消费的记录位置offset各不项目,不互相干扰。
对于一个group而言,消费者的数量不应该多余分区的数量,因为在一个group中,每个分区至多只能绑定到一个消费者上,即一个消费者可以消费多个分区,一个分区只能给一个消费者消费。因此,若一个group中的消费者数量大于分区数量的话,多余的消费者将不会收到任何消息。
1.3 Kafka的优点和缺点
与其他MQ相比较,Kafka有一些优缺点,主要如下:
优点:
- 可扩展。Kafka集群可以透明的扩展,增加新的服务器进集群。
- 高性能。Kafka性能远超过传统的ActiveMQ、RabbitMQ等,Kafka支持Batch操作。
- 容错性。Kafka每个Partition数据会复制到几台服务器,当某个Broker失效时,Zookeeper将通知生产者和消费者从而使用其他的Broker
- 完全的分布式系统,Broker、Producer、Consumer都原生自动支持分布式,自动实现负载均衡
- 性能卓越,单机写入TPS约在百万条/秒,消息大小10个字节
- 支持批量操作。
缺点:
- 重复消息。Kafka保证每条消息至少送达一次,虽然几率很小,但一条消息可能被送达多次。
- 消息乱序。Kafka某一个固定的Partition内部的消息是保证有序的,如果一个Topic有多个Partition,partition之间的消息送达不保证有序。
- 复杂性。Kafka需要Zookeeper的支持,Topic一般需要人工创建,部署和维护比一般MQ成本更高。
- 消费失败不支持重试。
现有的一些消息队列的介绍:
RabbitMQ
遵循AMQP实现,传统的messaging queue系统实现,基于Erlang语言开发,用在对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量还在其次。支持协议还包括XMPP、SMTP、STOMP,是一款重量级MQ,更适合于企业级的开发。实现Broker构架,消息在发送给客户端时先在中心队列排队。对路由、负载均衡及数据持久化都有良好的支持。
ZeroMQ
只是一个网络编程的Pattern库,将常见的网络请求形式模式化、组件化。ZeroMQ能实现RabbitMQ不擅长的高级复杂队列,但开发人员需要自己组合多种技术框架,技术复杂度是一个挑战。仅提供非持久性的队列,如果宕机,数据将丢失。
Redis
Key-Value的NoSQL数据库,本身也支持MQ功能,可以完全当做一个轻量级的队列使用。Redis在数据量大的时候入队较慢,Redis出队则无论数据量大小性能都不错。
2.Kafka的下载
下载地址:http://kafka.apache.org/downloads.html
3. Kafka的安装
首先确保你的机器上安装了jdk,kafka需要java运行环境,以前的kafka还需要zookeeper,新版的kafka已经内置了一个zookeeper环境,所以我们可以直接使用
如果只需要进行最简单的尝试的话我们只需要解压到任意目录即可,这里我们将kafka压缩包解压到/kafka目录
解压:tar -xzvf kafka_2.12-2.1.0.tgz
3.1 kafka的配置
在kafka解压目录下下有一个config的文件夹,里面放置的是我们的配置文件
zookeeper.properties 不用动默认端口
consumer.properites 消费者配置,这个配置文件用于配置开启的消费者,修改zookeeper.connect=192.168.26.128:2181。
server.properties kafka服务器的配置,此配置文件用来配置kafka服务器,目前仅介绍几个最基础的配置。
- broker.id 申明当前kafka服务器在集群中的唯一ID,需配置为integer,并且集群中的每一个kafka服务器的id都应是唯一的,这里采用默认配置即可(0)
- listeners 申明此kafka服务器需要监听的端口号,如果是在本机上跑虚拟机运行可以不用配置本项,默认会使用localhost的地址,如果是在远程服务器上运行则必须配置。
3. zookeeper.connect 申明kafka所连接的zookeeper的地址 ,需配置为zookeeper的地址,由于本次使用的是kafka高版本中自带zookeeper,使用默认配置即可zookeeper.connect=192.168.26.128:2181
4. Kafka的配置说明
server.properties配置:
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# see kafka.server.KafkaConfig for additional details and defaults
############################# Server Basics #############################
# The id of the broker. This must be set to a unique integer for each broker.
# 代理的id。必须为每个代理设置一个惟一的整数。
broker.id=0
port=9092
host.name=192.168.26.128
#有时候程序启动报错,会和这个id有关
#集群的搭建,broker.id不相同就可以。
############################# Socket Server Settings #############################
#listeners 申明此kafka服务器需要监听的端口号,如果是在本机上跑虚拟机运行可以不用配置本项,默认会使用localhost的地址,
#如果是在远程服务器上运行则必须配置,例如: listeners=PLAINTEXT:// 192.168.180.128:9092。并确保服务器的9092端口能够访问
#手动删除topic删除不掉,需要配置此项
delete.topic.enable=true
#开启自动创建配置:
# auto.create.topics.enable=true
# Hostname and port the broker will advertise to producers and consumers. If not set,
# it uses the value for "listeners" if configured. Otherwise, it will use the value
# returned from java.net.InetAddress.getCanonicalHostName().
#advertised.listeners=PLAINTEXT://your.host.name:9092
# Maps listener names to security protocols, the default is for them to be the same. See the config documentation for more details
# 将侦听器名称映射到安全协议,默认情况下它们是相同的。有关更多细节,请参阅配置文档
#listener.security.protocol.map=PLAINTEXT:PLAINTEXT,SSL:SSL,SASL_PLAINTEXT:SASL_PLAINTEXT,SASL_SSL:SASL_SSL
# The number of threads that the server uses for receiving requests from the network and sending responses to the network
# 服务器用于接收来自网络的请求和向网络发送响应的线程数
num.network.threads=3
# The number of threads that the server uses for processing requests, which may include disk I/O
#服务器用于处理请求的线程数,可能包括磁盘I/O
num.io.threads=8
# The send buffer (SO_SNDBUF) used by the socket server
# 服务器使用的发送缓冲区(SO_SNDBUF)
socket.send.buffer.bytes=102400
# The receive buffer (SO_RCVBUF) used by the socket server
socket.receive.buffer.bytes=102400
# The maximum size of a request that the socket server will accept (protection against OOM)
socket.request.max.bytes=104857600
############################# Log Basics #############################
# A comma separated list of directories under which to store log files
# 用于存储日志文件的目录
log.dirs=/root/kafka/kafka_2.12-2.1.0/logs
# The default number of log partitions per topic. More partitions allow greater
# parallelism for consumption, but this will also result in more files across
# the brokers.
# 每个主题的默认日志分区数量。更多的分区允许更大的
#并行性,但这也会导致更多的文件跨
#经纪人
num.partitions=1
# The number of threads per data directory to be used for log recovery at startup and flushing at shutdown.
# This value is recommended to be increased for installations with data dirs located in RAID array.
num.recovery.threads.per.data.dir=1
############################# Internal Topic Settings #############################
# The replication factor for the group metadata internal topics "__consumer_offsets" and "__transaction_state"
# For anything other than development testing, a value greater than 1 is recommended for to ensure availability such as 3.
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1
############################# Log Flush Policy #############################
# Messages are immediately written to the filesystem but by default we only fsync() to sync
# the OS cache lazily. The following configurations control the flush of data to disk.
# There are a few important trade-offs here:
# 1. Durability: Unflushed data may be lost if you are not using replication.
# 2. Latency: Very large flush intervals may lead to latency spikes when the flush does occur as there will be a lot of data to flush.
# 3. Throughput: The flush is generally the most expensive operation, and a small flush interval may lead to excessive seeks.
# The settings below allow one to configure the flush policy to flush data after a period of time or
# every N messages (or both). This can be done globally and overridden on a per-topic basis.
# The number of messages to accept before forcing a flush of data to disk
#log.flush.interval.messages=10000
# The maximum amount of time a message can sit in a log before we force a flush
#每间隔1秒钟时间,刷数据到磁盘
#log.flush.interval.ms=1000
############################# Log Retention Policy #############################
# The following configurations control the disposal of log segments. The policy can
# from the end of the log.
# A size-based retention policy for logs. Segments are pruned from the log unless the remaining
# segments drop below log.retention.bytes. Functions independently of log.retention.hours.
#log.retention.bytes=1073741824
# The maximum size of a log segment file. When this size is reached a new log segment will be created.
# 日志文件的最大大小。当达到这个大小时,将创建一个新的日志
log.segment.bytes=1073741824
# The interval at which log segments are checked to see if they can be deleted according
# to the retention policies
log.retention.check.interval.ms=300000
############################# Zookeeper #############################
# Zookeeper connection string (see zookeeper docs for details).
# This is a comma separated host:port pairs, each corresponding to a zk
# server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002".
# You can also append an optional chroot string to the urls to specify the
# root directory for all kafka znodes.
zookeeper.connect=192.168.26.128:2181
#集群的搭建,多台配置地址指向同一个zookeeper的url
# Timeout in ms for connecting to zookeeper
#超时时间
zookeeper.connection.timeout.ms=6000
########################GroupCoordinatorSettings##########################
# The following configuration specifies the time, in milliseconds, that the GroupCoordinator will delay the initial consumer rebalance.
# The rebalance will be further delayed by the value of group.initial.rebalance.delay.ms as new members join the group, up to a maximum of max.poll.interval.ms.
# The default value for this is 3 seconds.
# We override this to 0 here as it makes for a better out-of-the-box experience for development and testing.
# However, in production environments the default value of 3 seconds is more suitable as this will help to avoid unnecessary, and potentially expensive, rebalances during application startup.
#下面的配置以毫秒为单位指定GroupCoordinator将延迟初始使用者再平衡的时间。
#随着新成员的加入,再平衡将被进一步推迟,最大可达max.poll.interval.ms。
#默认值是3秒。
#我们在这里将其重写为0,因为它为开发和测试提供了更好的开箱即用体验。
#但是,在生产环境中,缺省值3秒更合适,因为这将有助于避免在应用程序启动期间进行不必要的、可能代价高昂的重新平衡。
group.initial.rebalance.delay.ms=0
kafka的其他配置,看官网:
https://kafka.apache.org/08/documentation#configuration
5. Kafka的使用
5.1 启动zookeeper
./bin/zookeeper-server-start.sh config/zookeeper.properties &
5.2 启动kafka
./bin/kafka-server-start.sh config/server.properties &
5.3 创建Topic 它只有一个分区,一个副本。
/root/kafka/kafka_2.12-2.1.0/bin/kafka-topics.sh --create --zookeeper 192.168.26.128:2181 --replication-factor 1 --partitions 1 --topic topic-producer-to-consumer
5.4 列出所有Topic
/root/kafka/kafka_2.12-2.10/bin/kafka-topics.sh --list --zookeeper 192.168.26.128:2181
5.5 删除Topic
/root/kafka/kafka_2.12-2.10/bin/kafka-topics.sh --delete --zookeeper 192.168.26.128:2181 --topic test
如果没有在配置里设置彻底删除Topic,此处则只是将该Topic标志为删除消费数据
5.6 发送数据
/root/kafka/kafka_2.12-2.10/bin/kafka-console-producer.sh --broker-list 192.168.26.128:9092 --topic topic-producer-to-consumer
5.7 消费数据
/root/kafka/kafka_2.12-2.10/bin/kafka-console-consumer.sh --zookeeper 192.168.26.128:2181 --topic topic-producer-to-consumer
6. java程序调用kafka
6.1 Pom文件的引入
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_2.12</artifactId>
<version>0.10.2.2</version>
</dependency>
6.2 生产者的配置
public class ProductorTest {
/**
* @param args
*/
public static void main(String[] args) {
producer_test();
}
private static void producer_test() {
Properties props = new Properties();
// bootstrap.servers --设置生产者需要连接的kafka地址
props.put("bootstrap.servers", "192.168.26.128:9092");
// acks --回令类型
props.put("acks", "all");
// retries --重试次数
props.put("retries", 0);
// batch.size --批量提交大小
props.put("batch.size", 16384);
// linger.ms --提交延迟等待时间(等待时间内可以追加提交)
props.put("linger.ms", 1);
// buffer.memory --缓存大小
props.put("buffer.memory", 33554432);
// key.serializer|value.serializer --序列化方法
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
Producer<String, String> producer = new KafkaProducer<String, String>(props);
String str = "我怎么这么好看!";
for (int i = 0; i < 10; i++) {
producer.send(new ProducerRecord<String, String>("topic-producer-to-consumer", str + Integer.toString(i)));
System.out.println("发送数据" + str + Integer.toString(i));
}
producer.close();
}
}
6.3 消费者的配置
/**
* 消费者
*/
public class ConsumerTest {
/**
* @param args
*/
public static void main(String[] args) {
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.26.128:9092");
props.put(ConsumerConfig.GROUP_ID_CONFIG, "test");
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");
props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
@SuppressWarnings("resource")
KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(props);
consumer.subscribe(Arrays.asList("topic-producer-to-consumer"));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(100);
for (ConsumerRecord<String, String> record : records) {
System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
}
}
}
}