1、消息队列(MQ:Message Quene)
概念:
存储消息(数据)的容器。容器具有队列的特点(按照顺序先进先出)FIFO。
2、消息队列中数据的处理模型:
2.2、传统数据的处理模型
2.3、消息队列中数据的处理模型(发邮件)
3、消息队列的核心组件及其运行模式:
3.1、核心组件:
producer :生产者,发送消息的程序(往队列中添加数据)
consumer:消费者,消费消息的程序(从队列中获取数据)
3.2、运行模式:
点对点(point to point):P2P模式包含三个角色:消息队列(Queue),发送者(Sender),接收者(Receiver)。每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。
特点:
生产者发送一条消息到queue,一个queue可以有很多消费者,但是一个消息只能被一个消费者接受,消息一旦被消费,消息就不再在消息队列中。queue实现了负载均衡
3.3、发布与订阅(Subscribe/Publish):
包含三个角色主题(Topic),发布者(Publisher),订阅者(Subscriber) 。多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。
特点:【常见于公众号订阅】
每个消息可以有多个消费者
发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息。为了消费消息,订阅者必须保持运行的状态。
4.1、降低系统间的耦合度
4.2、暂缓对数据的处理,在高并发情况下减小服务器的压力。
请求的异步处理(注册发送邮件),应用解耦(电商下订单创建物流信息,使用RPC通信),流量削锋(秒杀系统),日志处理(大数据flume+kafka)和消息通讯(聊天室)等场景
|
Kafka |
ActiveMQ |
RabbitMQ |
ZeroMQ【号称最快MQ】 |
Redis |
公司/社区 |
Apache/LinkedIn |
Apache |
MozillaPublic |
|
|
开发语言 |
Scala |
Java |
Erlang |
C、C++、Java、.NET、Python |
C++ |
支持的协议 |
仿AMQP |
OpenWire/STOMP |
AMQP |
|
|
事务 |
不支持 |
支持 |
不支持 |
|
|
集群 |
支持 |
支持 |
支持 |
|
|
负载均衡 |
支持 |
支持 |
支持 |
|
|
动态扩容 |
支持(Zookeeper) |
不支持 |
不支持 |
|
|
1、概念:
Kafka最初是由linked-in使用Scala编写的开源项目,后来交由apache管理的是一个分布式的基于publisher-subscribe的消息系统。Kafka中的Producer和consumer采用的是push-and-pull模式,即Producer只管向Broker push消息,consumer只管从Broker pull消息,两者对消息的生产和消费是异步的。
2、特点:
①、高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒。
②、顺序读写:kafka的消息是不断追加到文件中的,这个特性使kafka可以充分利用磁盘的顺序读写性能。顺序读写不需要硬盘磁头的寻道时间,只需很少的扇区旋转时间,所以速度远快于随机读写 (顺序读写的性能在某些情况下高于内存读写)
③、零拷贝:在Linux kernel2.2 之后出现了一种叫做"零拷贝(zero-copy)"系统调用机制,就是跳过“用户缓冲区”的拷贝,建立一个磁盘空间和内存的直接映射,数据不再复制到“用户态缓冲区”。
④、文件分段:kafka的队列topic被分为了多个区partition,每个partition又分为多个段segment,所以一个队列中的消息实际上是保存在N多个片段文件中。通过分段的方式,每次文件操作都是对一个小文件的操作,非常轻便,同时也增加了并行处理能力。
每一个分区里面都会包含***.index和***.log文件,***.index是针对***.log的文件索引,加快查询速度,会成对出现
⑤、批量发送:kafka允许进行批量发送消息,先将消息缓存在内存中,然后一次请求批量发送出去。大大减少服务端的I/O次数
⑥、数据压缩:Kafka还支持对消息集合进行压缩,Producer可以通过GZIP或Snappy格式对消息集合进行压缩。压缩的好处就是减少传输的数据量,减轻对网络传输的压力
⑦、可扩展性:kafka集群支持热扩展
⑧、持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失 activeMQ不支持持久化
⑨、容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败)
⑩、高并发:支持数千个客户端同时读写
3、kafka的使用场景:
日志收集:一个公司可以用Kafka可以收集各种服务的Log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、Hbase、Solr等。
用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。
运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。
流式处理:比如Spark Streaming和Storm 集成组件已经写好了!
4、kafka中的设计到的概念
1、Broker:每一个Broker就是kafka实例(服务器),多个Broker组成kafka集群。生产者会把消息发送到Broker,消费者会从Broker消费消息。Kafka集群会通过选举来选出一个Broker作为管理者(KafkaController),负责处理分区的Leader选举,以及分区的迁移。
2、Topic(主题):kafka对消息进行分类,每一个分类就是一个topic,一个topic可以分布在多台服务器上,在生产中通常一个业务对应一个topic。
3、Producer:生产者,发送消息的进程,将消息发送到topic中
4、Consumer:消费者,消费消息的进程,从topic中读取消息
5、ConsumerGroup:由多个消费者组成的组,每一个组中的所有消费者共同消费一个完整的Topic,彼此消费的消息不重复(消费方的集群)
6、Partition:由于消息是海量的,为了消息的读写性能,每一个topic都被分成很多部分(至少有一个分区),每一个部分就叫做一个分区(Partiton),在创建topic的时候可以指定分多少个区,每一个区就是一个文件目录,这个目录只负责存储该topic的一部分消息,所有的Partition中的消息加起来就组成一个完整的topic。每个topic的每个分区会被均匀的分布到kafka集群的各个broker机器上。一个分区可以看做是一个FIFO的队列。
7、Replication:副本,每一个分区都有多个副本
8、Leader和follower:每个topic的每一个分区的多个副本中,其中一个副本会被选为leader,其他的副本都是follower。Leader负责消息的读写,而所有的follower都会从leader复制消息
9、ISR:是Replicas的一个子集,表示目前活跃的并且同步速度能够跟得上leader的那些分区副本的集合。由于读写都是首先落到Leader上,所以一般来说通过同步机制从Leader上拉取数据的Replica都会和Leader有一些延迟,不管是在时间上跟不上进度,还是在数量上跟不上进度,都会被踢出ISR。每个Partition都有它自己独立的ISR。
10、Offset:每一条消息都有一个offset值作为唯一标识,就是一条消息在一个分区中的编号,编号的顺序不是全局的,所以不能跨分区。
11、Zookeeper:生产者、消费者、和Kafka集群都依赖于Zookeeper存储一些元数据信息
Kafka 的数据顺序读写
集群内部架构:1个Leader,两个Follower
一、启动Kafka的依赖组件,Zookeeper和Kafka服务Broker
1、启动zk:
./bin/zookeeper-server-start.sh config/zookeeper.properties
2、启动kafak的broker:
./bin/kafka-server-start.sh -daemon config/server.properties
3、在broker中创建一个topic:
./bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic topicName 这是一个Topic 的名字 这是创建一个Topic(一个业务对应一个Topic)
4、显示所有的topic:
./bin/kafka-topics.sh --list --zookeeper localhost:2181
5、显示某个特定topic的信息:
./bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic topicName
6、producer产生数据(往topic中push数据):
./bin/kafka-console-producer.sh --broker-list localhost:9092 --topic topicName
7、consumer消费topic中的数据(从topic中pull数据):
./bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic topicName --from-beginning
8、修改topic,添加分区数:4
./bin/kafka-topics.sh --zookeeper localhost:2181 --alter --topic topicName --partitions 4
9、删除topic:
./bin/kafka-topics.sh --zookeeper localhost:2181 --delete --topic topicName
此命令需要在server.properties文件中添加如下配置,才能使删除生效:delete.topic.enable=true
./bin/kafka-topics.sh --help 查看kafka 语法使用帮助说明
①、创建话题Topic
./bin/kafka-topics.sh --create --zookeeper 192.168.1.212:2181,192.168.1.214:2181,192.168.1.215:2181 --replication-factor 3 --partitions 3 --topic my-topic01
Created topic "my-topic01".--zookeeper 表示Kafka 连接的Zk集群,如果是单节点写一个即可。
--replication-factor 表示指定副本数,如果3台机器,可以设副本为 3
--partitions 3 表示当前Topic 的话题为 3
--topic my-topic01 表示当前的话题名称
②、显示某个特定topic的集群信息
./bin/kafka-topics.sh --describe --zookeeper 192.168.1.212:2181,192.168.1.214:2181,192.168.1.215:2181 --topic my-topic01
③、往topic中push数据
6667的端口来自 listeners
./bin/kafka-console-producer.sh --broker-list 192.168.1.212:6667,192.168.1.214:6667,192.168.1.215:6667 --topic my-topic01
④、从topic中pull数据
./bin/kafka-console-consumer.sh --zookeeper 192.168.1.212:6667,192.168.1.214:6667,192.168.1.215:6667 --topic my-topic01
切记 生产这生产数据,端口是配置文件listeners的端口 不是固定的6667
⑤、从头开始消费数据:
./bin/kafka-console-consumer.sh --zookeeper 192.168.1.212:2181,192.168.1.214:2181,192.168.1.215:2181 --from-beginning --topic my-topic01
消费者 连接端口为 2181 即zookeeper 的端口
--from-beginning 表是从当前话题最开始消费,如果不加即从当前消费者开始消费,从启动消费者后消费.......
pom
org.apache.kafka
kafka_2.10
0.8.0
log4j
log4j
1.2.15
jmxtools
com.sun.jdmk
jmxri
com.sun.jmx
jms
javax.jms
mail
javax.mail
创建配置文件:
package kafka.demo01;
public class Config {
public static final String TOPIC_NAME="mytopic-01";
public static final String BROKER_LIST = "192.168.1.212:6667,192.168.1.214:6667,192.168.1.215:6667";
public static final String ZK_LIST = "192.168.1.212:2181,192.168.1.215:2181,192.168.1.215:2181";
}
创建生产者代码:
package kafka.demo01;
import java.util.Properties;
import java.util.UUID;
import kafka.javaapi.producer.Producer;
import kafka.producer.KeyedMessage;
import kafka.producer.ProducerConfig;
import kafka.serializer.StringEncoder;
public class Producer1 {
public static void main(String[] args) {
try{
// 设置producer的配置信息
Properties properties = new Properties();
// 指定broke的地址列表,列表格式:host1:port1,host2:port2,host3:port3
properties.put("metadata.broker.list", Config.BROKER_LIST);
// 指定key 和value的序列化方式。默认是StringEncoder
properties.put("serializer.class", StringEncoder.class.getName());
ProducerConfig config =new ProducerConfig(properties);
Producer producer = new Producer(config);
int i = 0 ;
while(true){
String msgKey = "key"+i;
String msgContent = UUID.randomUUID().toString().replaceAll("_", "");
KeyedMessage msg = new KeyedMessage(Config.TOPIC_NAME , msgKey, msgContent);
producer.send(msg);
i++;
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
创建消费者代码:
package kafka.demo01;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import kafka.consumer.Consumer;
import kafka.consumer.ConsumerConfig;
import kafka.consumer.ConsumerIterator;
import kafka.consumer.KafkaStream;
import kafka.javaapi.consumer.ConsumerConnector;
import kafka.serializer.StringEncoder;
public class Consumer01 {
public static void main(String[] args) {
Properties prop = new Properties();
prop.put("zookeeper.connect", Config.ZK_LIST);
prop.put("serializer.class", StringEncoder.class.getName());
// prop.put("metadata.broker.list", MsgConfig.brokelist);
prop.put("group.id", "group1");
ConsumerConnector consumer = Consumer
.createJavaConsumerConnector(new ConsumerConfig(prop));
Map topicCountMap = new HashMap();
// 创建3个线程去消费topic中的内容,每一个线程一个stream流
topicCountMap.put(Config.TOPIC_NAME, 3);
Map>> messageStreams = consumer
.createMessageStreams(topicCountMap);
System.out.println(messageStreams);
KafkaStream kafkaStream = messageStreams.get(
Config.TOPIC_NAME).get(0);
ConsumerIterator iterator = kafkaStream.iterator();
while (iterator.hasNext()) {
String msg = new String(iterator.next().message());
System.out.println("收到消息:" + msg);
}
}
}
启动生产者:
启动消费者:
至此Java代码操作成功!
kafka.common.FailedToSendMessageException: Failed to send messages after 3 tries.
zookeeper.connect=localhost:2181改成zookeeper.connect=192.168.1.116 (自己的服务器IP地址):2181
修改Windows的hosts 配置文件
即可解决问题