Kafka
Kafka是Apache下的分布式消息中间件,需要zookeeper的分布式管理才能使用
架构如下
Partition
kafka的存储,就不得不提到分区,即partitions,创建一个topic时,同时可以指定分区数目,分区数越多,其吞吐量也越大,但是需要的资源也越多,同时也会导致更高的不可用性,kafka在接收到生产者发送的消息之后,会根据均衡策略将消息存储到不同的分区中
每个partition可以被认为是一个无限长度的数组,新数据 顺序追加进这个数组。物理上,每个partition对应于一个文件夹。一个broker上可以存放多个 partition。这样,producer可以将数据发送给多个broker上的多个partition,consumer也可以并 行从多个broker上的不同paritition上读数据,实现了水平扩展
offset
同一个partition上顺序存储顺序读写
offset是kafka消费者在对应分区上已经消费的消息数
Kafka环境搭建
在台式机上做了三个虚拟机,组成一个虚拟机集群,安装zookeeper后,完成分布式结构
安装kafka,运行
运行生产者和消费者
java代码进行生产与消费
package com.kafka.sample;
import kafka.producer.KeyedMessage;
import kafka.javaapi.producer.Producer;
import kafka.serializer.StringEncoder;
import java.util.Properties;
public class Hello01Producer extends Thread{
private Producer<String,String> producer;
public Hello01Producer(String pName){
super.setName(pName);
Properties properties=new Properties();
properties.put("metadata.broker.list","192.168.76.200:9092,192.168.76.201:9092,192.168.76.202:9092");
//设定写出数据的格式
properties.put("serializer.class", StringEncoder.class.getName());
//设定应答方式
properties.put("ack",1);
//批量
properties.put("batch.size",16384);
//通过api创建生产者对象
producer= new Producer<String,String>(new kafka.producer.ProducerConfig(properties));
}
public void run(){
//初始化计数器
int count=0;
System.out.println("Hello01Producer开始发送数据");
while(count<10000){
String key =String.valueOf(++count);
String value=Thread.currentThread().getName()+"----"+count;
KeyedMessage<String,String> message=new KeyedMessage<>("bdplog",key,value);
producer.send(message);
System.out.println("生产者Producer生产----"+key+"----"+value);
try{
//生产速度
Thread.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String[] args){
Hello01Producer producer1=new Hello01Producer("Robert");
producer1.start();
}
}
package com.kafka.sample;
import kafka.consumer.Consumer;
import kafka.consumer.ConsumerConfig;
import kafka.consumer.ConsumerIterator;
import kafka.consumer.KafkaStream;
import kafka.javaapi.consumer.ConsumerConnector;
import kafka.message.MessageAndMetadata;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
public class Hello01Consumer extends Thread {
private ConsumerConnector consumer;
public Hello01Consumer(String cName) {
super.setName(cName);
//配置文件
Properties properties = new Properties();
//zk地址
properties.put("zookeeper.connect", "192.168.76.200:2181,192.168.76.201:2181,192.168.76.202:2181");
//消费者所在组名
properties.put("group.id", "SHSXT-BigData");
//zk超时时间
properties.put("zookeeper.session.timeout.ms", "400");
//第一次消费时,从最低偏移量开始
properties.put("auto.offset.reset", "smallest");
//自动提交偏移量
properties.put("auto.commit.enable", "true");
//提交偏移量时间间隔
properties.put("auto.commit.interval.ms", "1000");
//通过api创建对象
consumer = Consumer.createJavaConsumerConnector(new ConsumerConfig(properties));
}
@Override
public void run() {
//描述读取哪个topic,需要几个线程
Map<String,Integer> topicCountMap=new HashMap<>();
topicCountMap.put("bdplog",1);
//消费者根据配置信息开始读取
Map<String, List<KafkaStream<byte[],byte[]>>> consumeMap=consumer.createMessageStreams(topicCountMap);
//每个线程对应一个KafkaStream
List<KafkaStream<byte[],byte[]>> list=consumeMap.get("bdplog");
//获取KafkaStream
KafkaStream stream0=list.get(0);
ConsumerIterator<byte[],byte[]> iterator=stream0.iterator();
//开始迭代并获取数据
while(iterator.hasNext()){
//获取一条消息
MessageAndMetadata<byte[],byte[]> value=iterator.next();
int partition=value.partition();
long offset=value.offset();
String data=new String(value.message());
System.out.println("----消费者Consumer消费----"+data+"----partition:"+partition+"----offset:"+offset);
}
}
public static void main(String[] args){
Hello01Consumer consumer1=new Hello01Consumer("ConsumerName");
consumer1.start();
}
}
ack
kafka提供了三种消息确认策略方式
ack=0,1,-1
0
意味着producer不等待broker同步完成的确认,继续发送下一条(批)信息
提供了最低的延迟。但是最弱的持久性,当服务器发生故障时,就很可能发生数据丢失。例如leader已经死亡,producer不知情,还会继续发送消息broker接收不到数据就会数据丢失
1
意味着producer要等待leader成功收到数据并得到确认,才发送下一条message。此选项提供了较好的持久性较低的延迟性。
Partition的Leader死亡,follwer尚未复制,数据就会丢失
-1
意味着producer得到follwer确认,才发送下一条数据
每个partition不只有一个,而是有一个leader(红色)和多个replica(蓝色),生产者根据消息的topic和key值,确定了消息要发往哪个partition之后(假设是p1),会找到partition对应的leader(也就是broker2里的p1),然后将消息发给leader,leader负责消息的写入,并与其余的replica进行同步
参考:
常见问题
Kafka中的ISR、AR又代表什么?ISR的伸缩又指什么
分区中的所有副本统称为AR(Assigned Repllicas)。所有与leader副本保持一定程度同步的副本(包括Leader)组成ISR(In-Sync Replicas),ISR集合是AR集合中的一个子集。消息会先发送到leader副本,然后follower副本才能从leader副本中拉取消息进行同步,同步期间内follower副本相对于leader副本而言会有一定程度的滞后。前面所说的“一定程度”是指可以忍受的滞后范围,这个范围可以通过参数进行配置。与leader副本同步滞后过多的副本(不包括leader)副本,组成OSR(Out-Sync Relipcas),由此可见:AR=ISR+OSR。在正常情况下,所有的follower副本都应该与leader副本保持一定程度的同步,即AR=ISR,OSR集合为空
任意一个超过阈值都会把follower剔除出ISR, 存入OSR(Outof-Sync Replicas)列表,新加入的follower也会先存放在OSR中(策略剔除、策略加入)
Kafka中的broker 是干什么的
broker 是消息的代理,Producers往Brokers里面的指定Topic中写消息,Consumers从Brokers里面拉取指定Topic的消息,然后进行业务处理,broker在中间起到一个代理保存消息的中转站
Kafka follower如何与leader同步数据
Kafka的复制机制既不是完全的同步复制,也不是单纯的异步复制
日志复制算法(log replication algorithm)必须提供的基本保证是,如果它告诉客户端消息已被提交,而当前 leader 出现故障,新选出的 leader 也必须具有该消息。在出现故障时,Kafka 会从挂掉 leader 的 ISR 里面选择一个 follower 作为这个分区新的 leader ;换句话说,是因为这个 follower 是跟上 leader 写进度的。
每个分区的 leader 会维护一个 in-sync replica(同步副本列表,又称 ISR)。当 producer 往 broker 发送消息,消息先写入到对应 leader 分区上,然后复制到这个分区的所有副本中。只有将消息成功复制到所有同步副本(ISR)后,这条消息才算被提交。由于消息复制延迟受到最慢同步副本的限制,因此快速检测慢副本并将其从 ISR 中删除非常重要
Kafka中是怎么体现消息顺序性的?
kafka每个partition中的消息在写入时都是有序的,消费时,每个partition只能被每一个group中的一个消费者消费,保证了消费时也是有序的。
整个topic不保证有序。如果为了保证topic整个有序,那么将partition调整为1.