一.消息列队
1.什么是消息列队
消息(Message)是指在应用间传送的数据。消息可以非常简单,比如只包含文本字符串,也可以更复杂,可能包含嵌入对象。消息队列(Message Queue)是一种应用间的通信方式,消息发送后可以立即返回,由消息系统来确保消息的可靠传递。消息发布者只管把消息发布到MQ中而不用管谁来取,消息使用者只管从 MQ 中取消息而不管是谁发布的。这样发布者和使用者都不用知道对方的存在。
2.消息列队的特征
(1)存储
与依赖于使用套接字的基本 TCP和 UDP 协议的传统请求和响应系统不同,消息队列通常将消息存储在某种类型的缓冲区中,直到目标进程读取这些消息或将其从消息队列中显式移除为止。
(2)异步
与请求和响应系统不同,消息队列通过缓冲消息可以在应用程序中实现一定程度的异步性,允许源进程发送消息并在队列中累积消息,而目标进程则可以挑选消息进行处理。 这样,应用程序就可以在某些故障情况下运行,例如连接断断续续或源进程或目标进程故障。
路由:消息队列还可以提供路由功能,其中多个进程可以在同一队列中读取或写入消息,从而实现广播或单播通信模式。
3.为何需要消息列队
(1)解耦
允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。
(2)冗余
消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的"插入-获取-删除"范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。
4.特点
• 高吞吐量:Kafka能够处理大量的消息,每秒可以处理数万条甚至更多的消息,适合处理大规模的数据流。
• 分布式架构:可以水平扩展,通过添加更多的服务器来增加系统的处理能力和存储容量。
• 消息持久化:消息被持久化到磁盘,即使服务器重启也不会丢失数据,保证了数据的可靠性。
• 支持多副本:每个分区可以有多个副本,分布在不同的服务器上,提高了系统的容错能力。
5.核心概念
• 主题(Topic):是消息的逻辑分类,每条消息都属于一个特定的主题。
• 分区(Partition):每个主题可以分为多个分区,分区是物理上的概念,消息会被存储在不同的分区中。
• 生产者(Producer):负责向Kafka主题发送消息的客户端应用程序。
• 消费者(Consumer):从Kafka主题读取消息的客户端应用程序。
• 消费者组(Consumer Group):由多个消费者组成,共同消费一个或多个主题的消息,每个分区只能被组内的一个消费者消费。
应用场景
• 日志收集:可以收集各种应用程序的日志信息,将日志发送到Kafka中,然后由其他应用程序进行处理和分析。
• 消息传递:用于不同应用程序之间的消息传递,实现解耦和异步处理。
• 实时数据处理:可以作为实时数据处理系统的数据源,将实时数据发送到Kafka中,然后由实时计算框架进行处理。
6.Kafka拓扑架构
一个典型的 Kafka 集群包含若干 Producer,若干 broker、若干 Consumer Group,以及一个 Zookeeper 集群。Kafka 通过 Zookeeper 管理集群配置,选举 leader,以及在 Consumer Group 发生变化时进行 rebalance。Producer 使用 push 模式将消息发布到 broker,Consumer 使用 pull 模式从 broker 订阅并消费消息。
从图中可以看出,典型的消息系统有生产者(Producer),存储系统(broker)和消费者(Consumer)组成,Kafka作为分布式的消息系统支持多个生产者和多个消费者,生产者可以将消息分布到集群中不同节点的不同 Partition 上,消费者也可以消费集群中多个节点上的多个Partition。在写消息时允许多个生产者写到同一个 Partition 中,但是读消息时一个 Partition 只允许被一个消费组中的一个消费者所消费,而一个消费者可以消费多个Partition。也就是说同一个消费组下消费者对 Partition 是互斥的,而不同消费组之间是共享的kafka 支持消息持久化存储,持久化数据保存在 kafka 的日志文件中,在生产者生产消息后,kafka不会直接把消息传递给消费者,而是先要在broker中进行存储,为了减少磁盘写入的次数,broker 会将消息暂时缓存起来,当消息的个数或尺寸、大小达到一定阀值时,再统一写到磁盘上,这样不但提高了 kafka的执行效率,也减少了磁盘 IO调用次数。
kafka 中每条消息写到 partition 中,是顺序写入磁盘的,这个很重要,因为在机械盘中如果是随机写入的话,效率将是很低的,但是如果是顺序写入,那么效率却是非常高,这种顺序写入磁盘机制是 kafka 高吞吐率的一个很重要的保证
7.Topic与partition
Kafka 中的 topic(主题)是以 partition 的形式存放的,每一个 topic 都可以设置它的 partition 数量,Partition 的数量决定了组成 topic 的 log 的数量。推荐 partition 的数量一定要大于同时运行的 consumer 的数量。另外,建议 partition 的数量要小于等于集群 broker 的数量,这样消息数据就可以均匀
的分布在各个 broker 中那么,Topic 为什么要设置多个 Partition 呢,这是因为 kafka 是基于文件存储的,通过配置多个 partition 可以将消息内容分散存储到多个 broker 上,这样可以避免文件尺寸达到单机磁盘的上限。同时,将一个 topic 切分成任意多个 partitions,可以保证消息存储、消息消费的效率,因为越多的 partitions可以容纳更多的 consumer,可有效提升 Kafka 的吞吐率。因此,将 Topic 切分成多个 partitions 的好处是可以将大量的消息分成多批数据同时写到不同节点上,将写请求分担负载到各个集群节点。
在存储结构上,每个 partition 在物理上对应一个文件夹,该文件夹下存储这个 partition 的所有消息和索引文件。partiton 命名规则为 topic 名称+序号,第一个 partiton 序号从0开始,序号最大值为 partitions 数量减 1。
在每个 partition(文件夹)中有多个大小相等的 segment(段)数据文件,每个segment 的大小是相同的,但是每条消息的大小可能不相同,因此segment<br/>数据文件中消息数量不一定相等。segment 数据文件有两个部分组成,分别为 index file 和 data file,此两个文件是一一对应,成对出现,后缀”.index“和“.log”分别表示为 segment 索引文件和数据文件。
8.Producer生产机制
Producer 是消息和数据的生产者,它发送消息到broker 时,会根据Paritition 机制选择将其存储到哪一个 Partition。如果 Partition 机制设置的合理,所有消息都可以均匀分布到不同的Partition 里,这样就实现了数据的负载均衡。如果一个 Topic 对应一个文件,那这个文件所在的机器 I/0 将会成为这个 Topic 的性能瓶颈,而有了 Partition后,不同的消息可以并行写入不同broker 的不同 Partition 里,极大的提高了吞吐率。
9.Consumer 消费机制
Kafka 发布消息通常有两种模式:队列模式(queuing)和发布/订阅模式(publish-subscribe)。在队列模式下,只有一个消费组,而这个消费组有多个消费者,一条消息只能被这个消费组中的一个消费者所消费;而在发布/订阅模式下,可有多个消费组,每个消费组只有一个消费者,同一条消息可被多个消费组消费。
Kafka 中的 Producer 和 consumer 采用的是 push、pull 的模式,即 producer 向broker 进行 push 消息,comsumer 从 bork 进行 pull 消息,push 和 pull 对于消息的生产和消费是异步进行的。pu11模式的一个好处是 consumer 可自主控制消费消息的速率,同时consumer 还可以自己控制消费消息的方式是批量的从broker 拉取数据还是逐条消费数据。
二.Zookeeper概念介绍
ZooKeeper 是一种分布式协调技术,所谓分布式协调技术主要是用来解决分布式环境当中多个进程之间的同步控制,让他们有序的去访问某种共享资源,防止造成资源竞争(脑裂)的后果。脑裂是指在主备切换时,由于切换不彻底或其他原因,导致客户端和 Slave 误以为出现两个 activemaster,最终使得整个集群处于混乱状态
这里首先介绍下什么是分布式系统,所谓分布式系统就是在不同地域分布的多个服务器,共同组成的一个应用系统来为用户提供服务,在分布式系统中最重要的是进程的调度,这里假设有一个分布在三个地域的服务器组成的一个应用系统,在第一台机器上挂载了一个资源,然后这三个地域分布的应用进程都要竞争这个资源,但我们又不希望多个进程同时进行访问,这个时候就需要一个协调器(锁),来让它们有序的来访问这个资源。这个协调器就是分布式系统中经常提到的那个锁,例如进程1在使用该资源的时候,会先去获得这把锁,进程1获得锁以后会对该资源保持独占,此时其它进程就无法访问该资源,进程1在用完该资源以后会将该锁释放掉,以便让其它进程来获得锁。由此可见,通过这个锁机制,就可以保证分布式系统中多个进程能够有序的访问该共享资源。这里把这个分布式环境下的这个锁叫作分布式锁。这个分布式锁就是分布式协调技术实现的核心内容。
目前,在分布式协调技术方面做得比较好的有Google 的 Chubby,还有 Apache的 ZooKeeper,它们都是分布式锁的实现者。ZooKeeper 所提供锁服务在分布式领域久经考验,它的可靠性、可用性都是经过理论和实践验证的。
ZooKeeper 是一种为分布式应用所设计的高可用、高性能的开源协调服务它提供了一项基本服务:分布式锁服务,同时,也提供了数据的维护和管理机制,如:统一命名服务、状态同步服务、集群管理、分布式消息队列、分布式应用配置项的管理等等。
1.zookeeper应用举例
单点故障
单点故障,就是在一个主从的分布式系统中,主节点负责任务调度分发,从节点负责任务的处理,而当主节点发生故障时,整个应用系统也就瘫痪了,那么这种故障就称为单点故障。那我们的解决方法就是通过对集群 master 角色的选取,来解决分布式系统单点故障的问题。
2.zookeeper的工作原理
(1)master启动
在分布式系统中引入 Zookeeper 以后,就可以配置多个主节点,这里以配置两个主节点为例,假定它们是主节点A和主节点B,当两个主节点都启动后,它们都会向 ZooKeeper 中注册节点信息。我们假设主节点A注册的节点信息是master00001,主节点B注册的节点信息是 master00002 ,注册完以后会进行选举,选举有多种算法,这里以编号最小作为选举算法为例,编号最小的节点将在选举中获胜并获得锁成为主节点,也就是主节点A将会获得锁成为主节点,然后主节点B将被阻塞成为一个备用节点。这样,通过这种方式 Zookeeper 就完成了对两个 Master 进程的调度。完成了主、备节点的分配和协作。
(2)master故障
如果主节点 A 发生了故障,这时候它在 ZooKeeper 所注册的节点信息会被自动删除,ZooKeeper 会自动感知节点的变化,发现主节点A故障后,会再次发出选举,这时候 主节点B将在选举中获胜,替代主节点A 成为新的主节点,这样就完成了主、被节点的重新选举。
(3)master恢复
如果主节点恢复了,它会再次向 ZooKeeper 注册自身的节点信息,只不过这时候它注册的节点信息将会变成 master00003,而不是原来的信息。ZooKeeper会感知节点的变化再次发动选举,这时候,主节点B在选举中会再次获胜继续担任主节点,主节点A会担任备用节点。
zookeeper 就是通过这样的协调、调度机制如此反复的对集群进行管理和状态同步的。
3.zookeeper集群架构
zookeeper集群主要角色有server和client,其中server又分为leader、follower和observer三个角色
Leader:领导者角色,主要负责投票的发起和决议,以及更新系统状态。
follower:跟随着角色,用于接收客户端的请求并返回结果给客户端,在选举过程中参与投票。
observer:观察者角色,用户接收客户端的请求,并将写请求转发给1eader,同时同步 leader 状态,但是不参与投票。0bserver 目的是扩展系统,提高伸缩性。
client:客户端角色,用于向zookeeper 发起请求
4.zookeeper的工作流程
Zookeeper 修改数据的流程: Zookeeper 集群中每个 Server 在内存中存储了一份数据,在 Zookeeper 启动时,将从实例中选举一个Server 作为 leader,Leader 负责处理数据更新等操作,当且仅当大多数 Server 在内存中成功修改数据,才认为数据修改成功。
Zookeeper 写的流程为:客户端 Client 首先和一个 Server 或者 0bserve 通信,发起写请求,然后 Server 将写请求转发给Leader,Leader 再将写请求转发给其它 Server,其它 Server 在接收到写请求后写入数据并响应 Leader,Leader在接收到大多数写成功回应后,认为数据写成功,最后响应Client,完成一次写操作过程。
三.Zookeeper在Kafka中的作用
1.Broker注册
Broker 是分布式部署并且相互之间相互独立,但是需要有一个注册系统能够将整个集群中的 Broker 管理起来,此时就使用到了Zookeeper。在 Zookeeper 上会有一个专门用来进行 Broker 服务器列表记录的节点:/brokers/ids
每个 Broker 在启动时,都会到 Zookeeper 上进行注册,即到/brokers/ids 下创建属于自己的节点,如/brokers/ids/[0...N]。Kafka 使用了全局唯一的数字来指代每个 Broker 服务器,不同的 Broker 必须使用不同的 Broker ID 进行注册,创建完节点后,每个 Broker 就会将自己的 IP地址和端口信息记录到该节点中去。其中,Broker 创建的节点类型是临时节点,一旦 Broker 宕机,则对应的临时节点也会被自动删除。
2.Topic注册
在 Kafka 中,同一个 Topic 的消息会被分成多个分区并将其分布在多个 Broker上,这些分区信息及与 Broker 的对应关系也都是由 Zookeeper 在维护,由专门的节点来记录,如:/borkers/topics
Kafka 中每个Topic 都会以/brokers/topics/[topic]的形式被记录,如/brokers/topics/login和/brokers/topics/search 等。Broker 服务器启动后,会到对应 Topic 节点(/brokers/topics)上注册自己的 Broker ID 并写入针对该 Topic 的分区总数,如/brokers/topics/login/3->2,这个节点表示 Broker ID为3的一个 Broker 服务器,对于"login"这个 Topic 的消息,提供了2个分区进行消息存储,同样,这个分区节点也是临时节点。
3.生产者负载均衡
由于同一个 Topic 消息会被分区并将其分布在多个 Broker 上,因此,生产者需要将消息合理地发送到这些分布式的Broker上,那么如何实现生产者的负载均衡,Kafka 支持传统的四层负载均衡,也支持Zookeeper 方式实现负载均衡。
(1)四层负载均衡
根据生产者的 IP 地址和端口来为其确定一个相关联的 Broker。通常,一个生产者只会对应单个 Broker,然后该生产者产生的消息都发往该 Broker。这种方式逻辑简单,每个生产者不需要同其他系统建立额外的TCP连接,只需要和 Broker维护单个 TCP连接即可。但是,其无法做到真正的负载均衡,因为实际系统中的每个生产者产生的消息量及每个 Broker 的消息存储量都是不一样的,如果有些生产者产生的消息远多于其他生产者的话,那么会导致不同的 Broker 接收到的消息总数差异巨大,同时,生产者也无法实时感知到 Broker 的新增和删除。
(2)使用 Zookeeper 进行负载均衡
由于每个 Broker 启动时,都会完成 Broker 注册过程,生产者会通过该节点的变化来动态地感知到 Broker 服务器列表的变更,这样就可以实现动态的负载均衡机制。
4.消费者负载均衡
与生产者类似,Kafka中的消费者同样需要进行负载均衡来实现多个消费者合理地从对应的 Broker 服务器上接收消息,每个消费者分组包含若干消费者,每条消息都只会发送给分组中的一个消费者,不同的消费者分组消费自己特定的Topic 下面的消息,互不干扰。
5.记录消息分区与消费者的关系
消费组(Consumer Group)下有多个 Consumer(消费者)。对于每个消费者组(Consumer Group),Kafka 都会为其分配一个全局唯一的 Group ID,Group 内部的所有消费者共享该 ID。订阅的 topic 下的每个分区只能分配给某个 group 下的一个 consumer(当然该分区还可以被分配给其他 group)。同时,Kafka 为每个消费者分配一个 Consumer ID,通常采用"Hostname:UUID'形式表示。在 Kafka 中,规定了每个消息分区 只能被同组的一个消费者进行消费,因此,需要在 Zookeeper 上记录消息分区与 Consumer 之间的关系,每个消费者一旦确定了对一个消息分区的消费权力,需要将其 Consumer ID 写入到 Zookeeper 对应消息分区的临时节点上,例如./consumers/[group id]/owners/[topic]/[broker id-partition id]其中,[broker id-partition id]就是一个 消息分区 的标识,节点内容就是该消息分区 上 消费者的 Consumer ID。
6.消息消费进度offset记录
在消费者对指定消息分区进行消息消费的过程中,需要定时地将分区消息的消费进度 Offset 记录到 Zookeeper 上,以便在该消费者进行重启或者其他消费者重新接管该消息分区的消息消费后,能够从之前的进度开始继续进行消息消费。0ffset 在 Zookeeper 中由一个专门节点进行记录,其节点路径为:/consumers/Lgroup id]/offsets/[topic]/[broker id-partition id节点内容就是 Offset 的值。
7.消费者注册
消费者服务器在初始化启动时加入消费者分组的步骤如下:
(1)注册到消费者分组
每个消费者服务器启动时,都会到 Zookeeper 的指定节点下创建一个属于自己的消费者节点,例如/consumers/[group id]/ids/[consumer_id],完成节点创建后,消费者就会将自己订阅的 Topic 信息写入该临时节点。
(2)对 消费者分组中的消费者的变化注册监听
每个消费者都需要关注所属消费者分组中其他消费者服务器的变化情况,即对/consumers/[group id]/ids 节点注册子节点变化的 Watcher 监听,一旦发现消费者新增或减少,就触发消费者的负载均衡。
(3)对 Broker 服务器变化注册监听
消费者需要对/broker/ids/[0-N]中的节点进行监听,如果发现 Broker 服务器列表发生变化,那么就根据具体情况来决定是否需要进行消费者负载均衡。
(4)进行消费者负载均衡
为了让同一个 Topic 下不同分区的消息尽量均衡地被多个 消费者 消费而进行消费者 与 消息 分区分配的过程,通常,对于一个消费者分组,如果组内的消费者服务器发生变更或 Broker 服务器发生变更,会发出消费者负载均衡。
四.单节点部署Kafka
1.基础环境
更改主机名并添加地址映射
hostnamectl set-hostname kafka1
echo '192.168.10.101 kafka1' >> /etc/hosts
2.安装Zookeeper
Zookeeper运行依赖java环境
yum -y install java
tar zxvf apache-zookeeper-...
mv apache-zookeeper-.... /etc/zookeeper
cd /etc/zookeerper/conf
mv zoo_samlpe.cfg zoo.cfg
编辑zookeeper配置文件
vi zoo.cfg
dataDir=/etc/zookeeper/zookeeper-data
启动并验证zookeeper
cd /etc/zookeeper/
mkdir zookeeper-data
./bin/zkServer.sh start
./bin/zkServer.sh status
3.安装Kafka
tar zxvf kafk_.....
mv kafka_.... /etc/kafka
cd /etc/kafka
修改配置文件
vi config/server.properties
log.dirs=/etc/kafka/kafka-logs
启动kafka
mkdir /etc/kafka/kafka.logs
nohup bin/kafka-server-start.sh config/server.properties &
检查两个端口的开启状态
netstat -anpt | grep 2181
netstat -anpt | grep 9092
注意:启动时先启动zookeeper,关闭时先关闭kafka
如果要关闭zookeeper
/etc/zookeeper/binzkServer.sh stop
如果关闭kafka
/etc/kafka/bin/kafka-server-stop.sh
如果关不了,就kill杀死该进程
4.测试
创建topic
bin/kafka-topics.sh --create --zookeeper kafka1:2181 --replication-factor 1 --partitions 1 --topoc test
列出topic
bin/kafka-topics.sh --list --zookeeper kafka1:2181
查看topic
bin/kafka-topics.sh --describe --zookeeper kafka1:2181 --topoc test
生产信息
bin/kafka-console-producer.sh --broker-list kafka1:9092 -topic test
消费信息(打开另一个终端,一边生产消息,一边查看消费信息)
bin/kafka-console-consumer.sh --bootstrap-server kafka1:9092 --topic test
删除topic
bin/kafka-topics.sh --delete --zookeeper kafka1:2181 --topic test