阻塞队列
kafka是一个消息队列的框架,底层做了深度的封装,我们其实也可以用阻塞队列的方式来实现消息队列,阻塞队列则较为简单,所以理解阻塞队列对理解kafka底层做了哪些事情很有帮助。
阻塞队列其实是java中的接口:BlockingQueue,主要用于解决线程之间的通信问题,当然我们也可以用Object类的wait和notify的方法来解决线程之间的通信,但是用其来实现消息队列更为原始所以也更为麻烦。
BlockingQueue主要靠put和take方法来解决问题,这两个方法都是阻塞的,主要采用的是生产者和消费者模式,Thread1作为生产者生产把数据放入阻塞队列中,Thread2作为消费者从阻塞队列中消费数据,BlockingQueue作为生产者和消费者之间的桥梁,避免了这两个线程之间直接通信。
如果生产者和消费者之间直接通信的话,假设有这样的场景,生产者生产数据的速度特别快,消费者消费的速度特别慢,如果数据没有做一个缓冲(生产线程没有被阻塞),Thread1生产者就会一直在生产(生产的数据又没有被消费掉),并占用cpu的资源,系统的性能就白白会浪费掉,如果此时加入一个阻塞队列,就能解决这样的问题,生产者将生产的数据放入到阻塞队列中,然后继续生产,直到把BlockingQueue堆满,put生产的方法就会阻塞,Thead-1不会占用系统的资源。
BlockingQueue是一个java接口,其主要的实现类如下:
- ArrayBlockingQueue 基于数组实现
- LinkedBlockingQueue 基于链表实现
- PriorityBlockingQueue,SyschronousQueue,DelayQueue等
代码演示:
在如下的代码中,创建了1个生产者线程和3个消费者线程,创建了一个容量为10的阻塞队列,生产者每隔20毫秒想阻塞队列容器中生产数据,总共生产100个数据,消费者则每隔一个随机的秒数从容器中获取数据:
package com.wsc.community;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueTests {
public static void main(String[] args) {
BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(10);
new Thread(new Producer(blockingQueue)).start();
new Thread(new Consumer(blockingQueue)).start();
new Thread(new Consumer(blockingQueue)).start();
new Thread(new Consumer(blockingQueue)).start();
}
}
class Producer implements Runnable {
private BlockingQueue<Integer> queue;
public Producer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
for (int i = 0; i < 100; i++) {
Thread.sleep(20);
queue.put(i);
System.out.println(Thread.currentThread().getName() + "生产:" + queue.size());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Consumer implements Runnable {
private BlockingQueue<Integer> queue;
public Consumer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
while (true) {
Thread.sleep(new Random().nextInt(1000));
queue.take();
System.out.println(Thread.currentThread().getName() + "消费:" + queue.size());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
程序运行的结果如下:
mac系统下Kakfa安装与基本操作
Kakfa简介
kafka是一个分布式的流媒体平台,起初只用于消息队列,后来做了功能的扩展,现在可以应用于,消息系统,日志收集,用户行为追踪,以及流式处理。
- Kakfa 特点:
高吞吐量,消息持久化(数据存于硬盘中,硬盘顺序IO速度高于内存的随机IO),高可靠性(分布式的服务器),高扩展性。
-kafka 术语:
1.Broker Zookeeper(kafka内置Zookeeper用于管理kafka集群)
2.Topic(点对点的模式:BlockingQueue每个数据只被一个消费者消费,不会重复。而kafka是基于发布订阅模式,生产者将消息放到某一个位置,可以有很多消费者订阅这个位置(topic),然后读取消息,这个消息可以被多个消费者同时得到)
Partition(主题topic位置的分区,每一个分区在队尾追加数据)
offset(消息在分区内存放的索引)
3.Leader Replica (主副本,对数据做备份,可以处理消费者想要获取分区数据的请求)
Follower Replica(从副本,只是备份,不负责作响应,如果主副本挂掉了,集群从众多的从副本中选举一个作为leader)
官网下载Kafka:
kafka_2.12-2.3.1.tgz
下载下来解压缩就可以,解压完成之后,需要修改一些配置,在config目录下修改zookeeper.properties的data-Dir,用与存放zookeeper运行时产生的数据
再修改server.properties中的log.dirs.
这样就安装好了
先启动zookeeper
进入bin目录,在bin录下以指定配置文件的方式启动zookeeper
./zookeeper-server-start.sh /Users/mac/IdeaProjects/kafka/config/zookeeper.properties
启动kafka
重新开一个终端,进入bin目录,以指定配置文件的方式启动kafka
./kafka-server-start.sh /Users/mac/IdeaProjects/kafka/config/server.properties
启动成功->
使用kafka
创建一个主题,kafka作为一个消息队列采用的是发布和订阅的模式,你要把消息发布到某一个主题下,首先你需要创建一个主题,主题代表了一个位置,也代表了一个消息的分类,比如这是点赞的消息,这是一个主题,这是关注的消息,又可以作为一个主题。
在bin目录下创建主题, --create,在localhost:9092这个服务器上创建主题,创建1个副本,创建1个分区,主题的名字是test
./kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic test
查看所有主题
./kafka-topics.sh --list --bootstrap-server localhost:9092
只有一个主题test
发送消息
调用kafka-console-producer发送消息,指定服务器localhost:9092,指定topic:test,发送消息
./kafka-console-producer.sh --broker-list localhost:9092 --topic test
接收消息
再启动一个终端,代表消费者,在bin路径下启动kafka-console-producer.sh,接收服务器localhost:9092 下的主题为test下的数据,from-beginning表示从头开始读。
./kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning
spring整合kafka
- 引入依赖
spring-kafka - 配置kafka
配置server consumer - 访问kafka
- 生产者
kafkaTemplate.send(topic,data);
-消费者
@KafkaListener(topics={"test})
public void handleMessage(ConsumerRecord record){
}
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
在application.properties配置
# KafkaProperties
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.group-id=community-consumer-group#消费者的分组id
spring.kafka.consumer.enable-auto-commit=true #是否自动提交 按偏移量读取的
spring.kafka.consumer.auto-commit-interval=3000 #自动提交的频率
consumer.properties中的配置
编写测试代码,生产者主要靠kafkaTemplate发送消息,指定消息的主题和内容,
消费者被动处理消息,需要用到@KafkaListener这个注解,里面的参数代表需要监听的主题,
一旦收到消息,会将消息封装成ConsumerRecord,交给加上这个注解的方法进行处理。
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommuntiyApplication.class)
public class KafakTests {
@Autowired
private KafkaProducer kafkaProducer;
@Test
public void testKafka(){
kafkaProducer.sendMessage("test","hello kafka");
kafkaProducer.sendMessage("test","在吗");
try{
Thread.sleep(1000*10);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
@Component
class KafkaProducer{
@Autowired
private KafkaTemplate kafkaTemplate;
public void sendMessage(String topic,String content){
kafkaTemplate.send(topic,content);
}
}
@Component
class KafkaConsumer{
@KafkaListener(topics = {"test"})
public void handleMessage(ConsumerRecord record){
System.out.println(record.value());
}
}