个人学习SpringBoot系列 Kafka篇
Github Link: https://github.com/panjianlong13/SpringBoot-SpringCloud/tree/master/spring-boot-kafka
Table of Contents
Kafka介绍
Kafka 是什么
Apache Kafka是分布式发布-订阅消息系统。它最初由LinkedIn公司开发,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。Kafka是一种快速、可扩展的、设计内在就是分布式的,分区的和可复制的提交日志服务。
主要设计目标如下:
1.以时间复杂度为O(1)的方式提供消息持久化能力,并保证即使对TB级以上数据也能保证常数时间的访问性能
2.高吞吐率。即使在非常廉价的商用机器上也能做到单机支持每秒100K条消息的传输
3.支持Kafka Server间的消息分区,及分布式消息消费,同时保证每个partition内的消息顺序传输
4.同时支持离线数据处理和实时数据处理
为什么要使用MQ:
1.解耦
在项目启动之初来预测将来项目会碰到什么需求,是极其困难的。消息队列在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口。这允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束
2.冗余
有时在处理数据的时候处理过程会失败。除非数据被持久化,否则将永远丢失。消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。在被许多消息队列所采用的”插入-获取-删除”范式中,在把一个消息从队列中删除之前,需要你的处理过程明确的指出该消息已经被处理完毕,确保你的数据被安全的保存直到你使用完毕。
3.扩展性
因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的;只要另外增加处理过程即可。不需要改变代码、不需要调节参数。扩展就像调大电力按钮一样简单。
4.灵活性 & 峰值处理能力
在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见;如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住增长的访问压力,而不是因为超出负荷的请求而完全崩溃。
5.可恢复性
当体系的一部分组件失效,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。而这种允许重试或者延后处理请求的能力通常是造就一个略感不便的用户和一个沮丧透顶的用户之间的区别。
6.送达保证
消息队列提供的冗余机制保证了消息能被实际的处理,只要一个进程读取了该队列即可。在此基础上,IronMQ提供了一个”只送达一次”保证。无论有多少进程在从队列中领取数据,每一个消息只能被处理一次。这之所以成为可能,是因为获取一个消息只是”预定”了这个消息,暂时把它移出了队列。除非客户端明确的表示已经处理完了这个消息,否则这个消息会被放回队列中去,在一段可配置的时间之后可再次被处理。
7.顺序保证
在许多情况下,数据处理的顺序都很重要。消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。IronMO保证消息浆糊通过FIFO(先进先出)的顺序来处理,因此消息在队列中的位置就是从队列中检索他们的位置。
8.缓冲
在任何重要的系统中,都会有需要不同的处理时间的元素。例如,加载一张图片比应用过滤器花费更少的时间。消息队列通过一个缓冲层来帮助任务最高效率的执行—写入队列的处理会尽可能的快速,而不受从队列读的预备处理的约束。该缓冲有助于控制和优化数据流经过系统的速度。
9.理解数据流
在一个分布式系统里,要得到一个关于用户操作会用多长时间及其原因的总体印象,是个巨大的挑战。消息系列通过消息被处理的频率,来方便的辅助确定那些表现不佳的处理过程或领域,这些地方的数据流都不够优化。
10.异步通信
很多时候,你不想也不需要立即处理消息。消息队列提供了异步处理机制,允许你把一个消息放入队列,但并不立即处理它。你想向队列中放入多少消息就放多少,然后在你乐意的时候再去处理它们。
几种分布式系统消息系统的对比:
Kafka与传统MQ区别:
1.更快!单机上万TPS,高吞吐量、低延迟、高并发
2.持久性、可靠性。传统的MQ,消息被消化掉后会被mq删除,而kafka中消息被消化后不会被删除,而是到配置的expire时间后,才删除
3.传统的MQ,消息的Offset是由MQ维护,而kafka中消息的Offset是由客户端自己维护
4.分布式,可扩展性容错性,把写入压力均摊到各个节点。可以通过增加节点降低压力
Kafka使用场景:
1.日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如Hadoop、Hbase、Solr等;
2.消息系统:解耦和生产者和消费者、缓存消息等;
3.用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到Hadoop、数据仓库中做离线分析和挖掘;
4.运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告;
5.流式处理:比如spark streaming和storm;
Kafka原理
Kafka基本架构
架构包括以下组件:
生产者将数据发送到Broker代理,Broker代理有多个话题topic,消费者从Broker获取数据。
1、话题(Topic):是特定类型的消息流。消息是字节的有效负载(Payload),话题是消息的分类名或种子(Feed)名;
2、生产者(Producer):是能够发布消息到话题的任何对象;
3、服务代理(Broker):已发布的消息保存在一组服务器中,它们被称为代理(Broker)或Kafka集群;
4、消费者(Consumer):可以订阅一个或多个话题,并从Broker拉数据,从而消费这些已发布的消息;
Kafka基本原理
我们将消息的发布(publish)称作 producer,将消息的订阅(subscribe)表述为 consumer,将中间的存储阵列称作 broker(代理),这样就可以大致描绘出这样一个场面,生产者将数据生产出来,交给 broker 进行存储,消费者需要消费数据了,就从broker中去拿出数据来,然后完成一系列对数据的处理操作。分布式Kafka情况下,多个 broker 协同合作,producer 和 consumer 部署在各个业务逻辑中被频繁的调用,三者通过 zookeeper管理协调请求和转发。这样一个高性能的分布式消息发布订阅系统就完成了。
Zookeeper在Kafka中的作用
1.无论是kafka集群,还是producer和consumer都依赖于zookeeper来保证系统可用性集群保存一些meta信息。
2.Kafka使用zookeeper作为其分布式协调框架,很好的将消息生产、消息存储、消息消费的过程结合在一起。
3.同时借助zookeeper,kafka能够生产者、消费者和broker在内的所以组件在无状态的情况下,建立起生产者和消费者的订阅关系,并实现生产者与消费者的负载均衡。
Kafka 实战
Kafka安装(Mac)
brew install kafka
#安装目录
#/usr/local/Cellar/kafka/XXX
#安装的配置文件位置
#/usr/local/etc/kafka/server.properties
#/usr/local/etc/kafka/zookeeper.properties
#进入安装目录
/usr/local/Cellar/kafka/2.1.0/bin
#启动ZooKeeper Service
zookeeper-server-start /usr/local/etc/kafka/zookeeper.properties &
#启动Kafka Service
kafka-server-start /usr/local/etc/kafka/server.properties &
#创建Topic
kafka-topics --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test
#查看创建的Topic
kafka-topics --list --zookeeper localhost:2181
test
#发送一些消息
kafka-console-producer --broker-list localhost:9092 --topic test
First
Second
#消费消息
kafka-console-consumer --bootstrap-server localhost:9092 --topic test --from-beginning
First
Second
新建SpringBoot项目
在pom.xml中添加依赖
配置文件application.properties
启动Kafka
/**
* @ClassName: KafkaConfiguration
* @Description: 启用 kafka
* @author Peter Pan
* @date 2018年12月6日
*
*/
@Configuration
@EnableKafka
public class KafkaConfiguration {
}
消息生产者
/**
* @ClassName: MsgProducer
* @Description: 消息生产者
* @author Peter Pan
* @date 2018年12月6日
*
*/
@Component
public class MsgProducer {
private static final Logger log = LoggerFactory.getLogger(MsgProducer.class);
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
public void sendMessage(String topicName, String jsonData) {
log.info("向本机kafka推送数据:[{}]", jsonData);
try {
kafkaTemplate.send(topicName, jsonData);
} catch (Exception e) {
log.error("发送数据出错!!!{}{}", topicName, jsonData);
log.error("发送数据出错=====>", e);
}
// 消息发送的监听器,用于回调返回信息
kafkaTemplate.setProducerListener(new ProducerListener<String, String>() {
@Override
public void onSuccess(String topic, Integer partition, String key, String value,
RecordMetadata recordMetadata) {
}
@Override
public void onError(String topic, Integer partition, String key, String value, Exception exception) {
}
@Override
public boolean isInterestedInSuccess() {
log.info("数据发送完毕");
return false;
}
});
}
消息消费者
/**
* @ClassName: MsgConsumer
* @Description: 消息消费者
* @author Peter Pan
* @date 2018年12月6日
*
*/
@Component
public class MsgConsumer {
@KafkaListener(topics = { "topic-1", "topic-2" })
public void processMessage(String content) {
System.out.println("消息被消费" + content);
}
}
启动服务
/**
* @ClassName: Startup
* @Description: 启动服务
* @author Peter Pan
* @date 2018年12月6日
*
*/
@SpringBootApplication
@ComponentScan(value = { "com.peter.springboot.kafka" })
public class Startup {
public static void main(String[] args) {
SpringApplication.run(Startup.class, args);
}
}
单元测试
/**
* @ClassName: BaseTest
* @Description: TODO
* @author Peter Pan
* @date 2018年12月6日
*
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Startup.class)
public class BaseTest {
@Autowired
private MsgProducer msgProducer;
@Test
public void test() throws Exception {
msgProducer.sendMessage("topic-1", "topic--------1");
msgProducer.sendMessage("topic-2", "topic--------2");
}
}