Kafka理论笔记

1.消息中间件

1.1.定义

MOM(Message Oriented Middleware)是面向消息的中间件,使用消息传送提供者来协调消息传送操作。MOM 需要提供 API 和管理工具。客户端使用api调用,把消息发送到由提供者管理的目的地。在发送消息之后,客户端会继续执行其他工作,并且在接收方收到这个消息确认之前,提供者一直保留该消息。

1.2.主要解决的问题

设想一下没有消息中间件:因为没有第三方,所以生产者每生产一个消息,消费者就必须消费一个消息;
会出现以下问题:

消费者消费消息的时候,系统宕机了,生产者还在生产消息,那新生产的消息就丢失了。

生产者,在单位时间,生产消息大于消费者的消费速度,消费者就吃不消了(消息堵塞,最终导致系统超时),消费者拒绝再吃了,消息又丢失了。

2.JMS

2.2.1.背景知识

JMS全称叫做,Java消息服务(Java Message Service)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。

点对点与发布订阅最初是由JMS定义的。这两种模式主要区别或解决的问题就是发送到队列的消息能否重复消费(多订阅)。

2.2.点对点

消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息。这里要注意:

消息被消费以后,queue中不再有存储,所以消息消费者不可能消费到已经被消费的消息。

queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。

queue实现了负载均衡,一个消息只能被一个消费者接受,当没有消费者可用时,这个消息会被保存直到有 一个可用的消费者,一个queue可以有很多消费者,他们之间实现了负载均衡, 所以queue实现了一个可靠的负载均衡。

在这里插入图片描述

2.3.发布/订阅

消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。

topic实现了发布和订阅,当你发布一个消息,所有订阅这个topic的服务都能得到这个消息,所以从1到N个订阅者都能得到一个消息的拷贝, 只有在消息代理收到消息时有一个有效订阅时的订阅者才能得到这个消息的拷贝。
在这里插入图片描述

3.Kafka

3.3.1.背景

Kafka是由Apache软件基金会开发的一个开源处理平台,由Scala和Java编写。该项目的目标是为处理实时数据提供一个统一、高吞吐、低延迟的平台。其持久化层本质上是一个“按照分布式事务日志架构的大规模发布/订阅消息队列”,这使它作为企业级基础设施来处理流式数据非常有价值。

3.2.Kafka使用场景

消息队列(MQ)
在系统架构设计中,经常会使用消息队列(Message Queue)——MQ。MQ是一种跨进程的通信机制,用于上下游的消息传递,使用MQ可以使上下游解耦,消息发送上游只需要依赖MQ,逻辑上和物理上都不需要依赖其他下游服务。MQ的常见使用场景如流量削峰、数据驱动的任务依赖等等。在MQ领域,除了Kafka外还有传统的消息队列如ActiveMQ和RabbitMQ等。

追踪网站活动
Kafka最出就是被设计用来进行网站活动(比如PV、UV、搜索记录等)的追踪。可以将不同的活动放入不同的主题,供后续的实时计算、实时监控等程序使用,也可以将数据导入到数据仓库中进行后续的离线处理和生成报表等。

Metrics
Kafka经常被用来传输监控数据。主要用来聚合分布式应用程序的统计数据,将数据集中后进行统一的分析和展示等。

日志聚合
很多人使用Kafka作为日志聚合的解决方案。日志聚合通常指将不同服务器上的日志收集起来并放入一个日志中心,比如一台文件服务器或者HDFS中的一个目录,供后续进行分析处理。相比于Flume和Scribe等日志聚合工具,Kafka具有更出色的性能。

3.3.概念

概念说明
Message消息,是通信的基本单位,每个producer可以向一个topic(主题)发布一些消息
Producer消息生产者,就是向kafka broker发消息的客户端
Consumer消息消费者,向kafka broker取消息的客户端
ConsumerGroup消费者组,可以并行消费Topic中的partition的消息
TopicKafka处理资源的消息源(feeds of messages)的不同分类。本质是一个消息队列。
PartitionTopic物理上的分组,一个topic可以分为多个partion,每个partion是一个有序的队列。partion中每条消息都会被分配一个有序的Id(offset)
Broker缓存代理,Kafka集群中的一台或多台服务器统称broker。一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic。

备注:
1.kafka作为一个集群运行在一个或多个服务器上。
2.kafka集群存储的消息是以topic为类别记录的。
3.每个消息是由一个key,一个value和时间戳构成。

3.4.Topic& Partition的关系

在这里插入图片描述

每一个分区都是一个顺序的、不可变的消息队列, 并且可以持续的添加。分区中的消息都被分了一个序列号,称之为偏移量(offset),在每个分区中此偏移量都是唯一的。

在这里插入图片描述

Kafka集群保持所有的消息,直到它们过期,无论消息是否被消费了。实际上消费者所持有的仅有的元数据就是这个偏移量,也就是消费者在这个消息队列中的位置。这个偏移量由消费者控制:正常情况当消费者消费消息的时候,偏移量也线性的的增加。但是实际偏移量由消费者控制,消费者可以将偏移量重置为更老的一个偏移量,重新读取消息。可以看到这种设计对消费者来说操作自如,一个消费者的操作不会影响其它消费者对此的处理。

3.5.点对点&发布订阅

在这里插入图片描述

如何通过消费者组来模拟传统点对点和发布订阅:
点对点:所有消费者均在一个消费者组。
发布订阅:所有消费者都在且唯一在一个不同的消费者组。
即类似发布订阅的方式到达每一个消费者组,消费者内部则采用点对点。

在这里插入图片描述

一个topic 可以配置几个partition,produce发送的消息分发到不同的partition中,consumer接受数据的时候是按照group来接受,kafka确保每个partition只能同一个group中的同一个consumer消费,如果想要重复消费,那么需要其他的组来消费。

所以导致了:相同的消费者组中不能有比分区更多的消费者,否则多出的消费者一直处于空等待,不会收到消息。

3.6.顺序的保持

传统的队列模型保持消息,并且保证它们的先后顺序不变。但是,尽管服务器保证了消息的顺序,消息还是异步的发送给各个消费者,消费者收到消息的先后顺序不能保证了。这也意味着并行消费将不能保证消息的先后顺序。用过传统的消息系统的同学肯定清楚,消息的顺序处理很让人头痛。如果只让一个消费者处理消息,又违背了并行处理的初衷。 在这一点上Kafka做的更好,尽管并没有完全解决上述问题。 Kafka采用了一种分而治之的策略:分区。 因为Topic分区中消息只能由消费者组中的唯一一个消费者处理,所以消息肯定是按照先后顺序进行处理的。但是它也仅仅是保证Topic的一个分区顺序处理,不能保证跨分区的消息先后处理顺序。 所以,如果你想要顺序的处理Topic的所有消息,那就只提供一个分区。

3.7.分布式

分区被分布到集群中的多个服务器上。每个服务器处理它分到的分区。 根据配置每个分区还可以复制到其它服务器作为备份容错。 每个分区有一个leader,零或多个follower。Leader处理此分区的所有的读写请求,而follower被动的复制数据。如果leader宕机,其它的一个follower会被推举为新的leader。 一台服务器可能同时是一个分区的leader,另一个分区的follower。 这样可以平衡负载,避免所有的请求都只让一台或者某几台服务器处理。

4.Kafka 环境搭建

4.1.启动zk服务:

bin/zookeeper-server-start.sh config/zookeeper.properties

4.2.启动kafka服务:

bin/kafka-server-start.sh config/server.properties

备注:server.properties文件解析:

其中:集群必须的修改的:

配置项说明
broker.id =0每一个broker在集群中的唯一表示,要求是正数,kafka及其根据id来识别broker机器。当该服务器的IP地址发生改变时,broker.id没有变化,则不会影响consumers的消息情况
log.dirs=/kafka/kafka-logskafka数据的存放地址,多个地址的话用逗号分割/kafka/kafka-logs-1,/kafka/kafka-logs-2
Listeners= PLAINTEXT://127.0.0.1:9092listeners配置kafka的host和port,如果不配置ip,kafka会使用默认的hostname去访问,所以伪集群中port必须不一样
port =9092broker server服务端口
host.name=localhost节点需要绑定的主机名称。如果没有设置,服务器会绑定到所有接口。

其他重要元素:

配置项说明
zookeeper.connect = localhost:2181zookeeper集群的地址,可以是多个,多个之间用逗号分割。
message.max.bytes =6525000单个消息的最大上限,单位是字节。
log.cleanup.policy = delete日志清理策略选择有:delete和compact主要针对过期数据的处理,或是日志文件达到限制的额度,会被 topic创建时的指定参数覆盖。
log.retention.minutes=60*24数据存储的最大时间超过这个时间会根据log.cleanup.policy设置的策略处理数据,也就是消费端能够多久去消费数据。
log.cleaner.min.cleanable.ratio=0.5日志清理的频率控制,越大意味着更高效的清理,同时会存在一些空间上的浪费,会被topic创建时的指定参数覆盖。
zookeeper.session.timeout.ms=6000ZooKeeper的最大超时时间,就是心跳的间隔,若是没有反映,那么认为已经死了,不易过大。

详情参考:https://www.orchome.com/472

4.3.关于Topic:

创建Topic:

bin/kafka-topics.sh  --create 
--zookeeper localhost:2181 
--replication-factor 3 
--partitions 4 
--topic kafka

以上为必须的参数:
Zookeeper 为zk地址,replication-factor为备份数量,partitions为分区数量,topic为topic名字

某条的详细信息:

bin/kafka-topics.sh  --describe
--zookeeper localhost:2181 
--topic test

在这里插入图片描述
在这里插入图片描述

输出信息说明:
第一行为:详细信息,4个分区,3个备份。
之后缩进行,为每一个分区的循环。依次为:
分区编号 主节点 应该有的节点(不论它是否活着) 实际上没有失效的节点。
(显示均为上述的Broker.id)

查看所有Topic:

bin/kafka-topics.sh --list 
--zookeeper localhost:2181

只需要zk地址

删除某条Topic:

bin/kafka-topics.sh --delete 
--zookeeper localhost:2181 
--topic test

zk地址和topic的名字

4.4.启动生产者

bin/kafka-console-producer.sh 
--broker-list localhost:9092 
--topic test

Kafka服务地址,topic的名字
效果可以再控制台录入消息。

4.5.启动消费者

bin/kafka-console-consumer.sh 
--bootstrap-server localhost:9092
--from-beginning 
--topic test
--group testgroup

Kafka服务地址,topic的名字
使consumer从kafka最早的消息开始消费。
指定的消费者组
效果控制台会输出生产者录入的消息

4.6.消费者组

bin/kafka-consumer-groups.sh
--bootstrap-server localhost:9092
––list

5.SpringBoot整合Kafka

Pom依赖:

	<dependency>
         <groupId>org.springframework.kafka</groupId>
         <artifactId>spring-kafka</artifactId>
         <version>1.1.1.RELEASE</version>
     </dependency>

Application.xml:

#============== kafka ===================
#指定kafka 代理地址,可以多个
spring.kafka.bootstrap-servers=192.168.153.135:9092

#=============== provider  =======================

spring.kafka.producer.retries=0
#每次批量发送消息的数量
spring.kafka.producer.batch-size=16384
spring.kafka.producer.buffer-memory=33554432

#指定消息key和消息体的编解码方式
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer

#=============== consumer  =======================
#指定默认消费者group id
spring.kafka.consumer.group-id=test-consumer-group

spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.consumer.enable-auto-commit=true
spring.kafka.consumer.auto-commit-interval=100

#指定消息key和消息体的编解码方式
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer

如果自动化配置无法满足需求:
1)通过@Configuration、@EnableKafka,声明Config并且打开KafkaTemplate能力。
2)通过@Value注入application.properties配置文件中的kafka配置。
3)生成bean,@Bean

生产者:

public class UserSSOApplicationTests {

   @Resource
    //注入kafkatemplete,这个由spring boot自动创建
   KafkaTemplate kafkaTemplate;
   @Test
   public void testKafkaSendMsg() {
       //发送消息
      kafkaTemplate.send("test", 0,12,"1222");
   }
}
//备注:
//kafkaTemplate的常用send方法:
ListenableFuture<SendResult<K, V>> sendDefault(V data);
ListenableFuture<SendResult<K, V>> sendDefault(K key, V data);
ListenableFuture<SendResult<K, V>> sendDefault(Integer partition, K key, V data);
ListenableFuture<SendResult<K, V>> sendDefault(Integer partition, Long timestamp, K key, V data);
ListenableFuture<SendResult<K, V>> send(String topic, V data);
ListenableFuture<SendResult<K, V>> send(String topic, K key, V data);

ListenableFuture<SendResult<K, V>> send(String topic, Integer partition, K key, V data);
ListenableFuture<SendResult<K, V>> send(String topic, Integer partition, Long timestamp, K key, V data);
ListenableFuture<SendResult<K, V>> send(ProducerRecord<K, V> record);
ListenableFuture<SendResult<K, V>> send(Message<?> message);
		//发送带有时间戳的消息
        kafkaTemplate.send("topic.quick.demo", 0, System.currentTimeMillis(), 0, "send message with timestamp");

        //使用ProducerRecord发送消息
        ProducerRecord record = new ProducerRecord("topic.quick.demo", "use ProducerRecord to send message");
        kafkaTemplate.send(record);

        //使用Message发送消息
        Map map = new HashMap();
        map.put(KafkaHeaders.TOPIC, "topic.quick.demo");
        map.put(KafkaHeaders.PARTITION_ID, 0);
        map.put(KafkaHeaders.MESSAGE_KEY, 0);
        GenericMessage message = new GenericMessage("use Message to send message",new MessageHeaders(map));
        kafkaTemplate.send(message);

备注:生产者可选的配置

消费者:

public class KafkaMessageReceiver2 {
    //指定监听的topic,当前消费者组id
    @KafkaListener(topics = {"test"}, groupId = "receiver")
    public void registryReceiver(ConsumerRecord<Integer, String> integerStringConsumerRecords) {
        log.info(integerStringConsumerRecords.value());
    }


}

备注:
KafkaListener的可选项:
id:消费者的id,当GroupId没有被配置的时候,默认id为GroupId
containerFactory:上面提到了@KafkaListener区分单数据还是多数据消费只需要配置一下注解的containerFactory属性就可以了,这里面配置的是监听容器工厂,也就是ConcurrentKafkaListenerContainerFactory,配置BeanName
topics:需要监听的Topic,可监听多个
topicPartitions:可配置更加详细的监听信息,必须监听某个Topic中的指定分区,或者从offset为200的偏移量开始监听
errorHandler:监听异常处理器,配置BeanName
groupId:消费组ID
idIsGroup:id是否为GroupId
clientIdPrefix:消费者Id前缀
beanRef:真实监听容器的BeanName,需要在 BeanName前加 “__”

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值