Kafka开源消息系统分布式集群搭建

发布 : 2016-02-12 分类 : 大数据 浏览 :

1.KafKa是什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
在流式计算中,KafKa一般用来缓存数据,Storm通过消费KafKa的数据进行计算

KafKa + Storm + Redis

a.Apache KafKa是一个开源消息系统,由Scala写成。
是由Apache软件基金会开发的一个开源消息系统项目

b.kafka最初是由LinkedIn开发,并与2011年初开源。2012年10月从Apache Incubator毕业。
该项目的目标是为了处理实时数据提供一个统一.高通量.低等待的平台。

c.KafKa是一个分布式消息队列:生产者.消费者的功能。它提供了类似于JMS的特性,但是在设计实现上完全不同,此外它并不是JMS规范的实现

d.KafKa对消息保存时根据Topic进行归类,发送消息者称为Producer,消息接受者称为Consumer,此外KafKa集群有多个KafKa实例组成,每个实例(server)称为broker。

e.无论是KafKa集群,还是producer和consumer都依赖于Zookeeper集群保存一些meta信息,来保证系统可用性。

2.JMS是什么

2.1.JMS的基础

1
2
3
JMS是什么:JMS是Java提供的一套技术规范
JMS干什么用:用来异构系统集成通信,缓解系统瓶颈,提高系统的伸缩性增强系统用户体验,使得系统模块化和组件化变得可行并更加灵活
通过什么方式:生产消费者模式(生产者.服务器.消费者)

Markdown

1
jdk.kafka.activemq......

2.2.JMS消息传输模型

1
2
3
4
5
6
7
点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除)
点对点模型通常是一个基于拉取或者轮循的消息传送模型,这种模型从队列中请求信息,而不是将消息推送到客户端。
这个模型的特点是发送到队列的消息被一个且只有一个接收者接收处理,即使有多个消息监听者也是如此

发布/订阅模式(一对多,数据生产后,推送给所有订阅者)
发布订阅模型则是一个基于推送的消息传送模型。发布订阅模型可以有多种不同的订阅者,临时订阅者只在监听主题时才接收消息,
而持久订阅者则监听主题的所有消息,即使当前订阅者不可用,处于离线状态

Markdown

1
2
queue.put(object)  数据生产
queue.take(object) 数据消费

2.3.JMS核心组件

1
2
3
4
Destination:消息发送的目的地,也就是前面说的Queue和Topic
Message:从字面上就可以看出是被发送的消息
Producer:消息的生产者,要发送一个消息,必须通过这个生产者来发送
MessageConsumer:与生产者相对应,这是消息的消费者或接收者,通过它来接收一个消息

Markdown

1
2
3
4
5
6
7
8
9
10
StreamMessage:Java 数据流消息,用标准流操作来顺序的填充和读取。
MapMessage:一个Map类型的消息;名称为 string 类型,而值为 Java 的基本类型。
TextMessage:普通字符串消息,包含一个String。
ObjectMessage:对象消息,包含一个可序列化的Java 对象
BytesMessage:二进制数组消息,包含一个byte[]。
XMLMessage: 一个XML类型的消息。
最常用的是TextMessage和ObjectMessage。

通过与ConnectionFactory可以获得一个connection
通过connection可以获得一个session会话

2.4常见的类JMS消息服务器

2.4.1.JMS消息服务器ActiveMQ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ActiveMQ是Apache出品,最流行的,能力强劲的开源消息总线。
ActiveMQ是一个完全支持JMS1.1和J2EE1.4规范的。

主要特点:
多种语言和协议编写客户端。语言:Java.C.C++.C#.Ruby.Perl.Python.PHP。
应用协议:OpenWire,Stomp REST,WS Notification,XMPP,AMQP
完全支持JMS1.1和J2EE1.4规范(持久化,XA消息,事务)
对Spring的支持,ActiveMQ可以很容易内嵌到使用Spring的系统里面去,而且也支持Spring2.0的特性
通过了常见J2EE服务器(如Geronimo,JBoss 4,GlassFish,WebLogic)的测试,其中通过JCA1.5 resource adaptors的配置,
可以让ActiveMQ可以自动的部署到任何兼容J2EE 1.4商业服务器上
支持多种传送协议:in=VM,TCP,SSL,NIO,UDP,JGroups,JXTA
支持通过JDBC和journal提供高速的消息持久化
从设计上保证了高性能的集群,客户端-服务器,点对点
支持Ajax
支持与Axis的整合
可以很容易得调用内嵌JMS provider,进行测试

2.4.2.分布式消息中间件 Metamorphosis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Metamorphosis(MetaQ)是一个高性能.高可用.可拓展的分布式消息中间件,
类似于LinkedIn的Kafka,具有消息存储顺序写.吞吐量大和支持本地和XA事务等特性,
适用于大吞吐量.顺序消息.广播和日志数据传输场景,在淘宝和支付宝有着广泛的应用,
现已开源。

主要特点:
1.生产者.服务器和消费者都可分布
2.消息存储顺序写
3.性能极高,吞吐量大
4.支持消息顺序
5.支持本地和XA事务
6.客户端pull,随机读,利用sendfile系统调用,zero-copy,批量拉数据
7.支持消费端事务
8.支持消息广播模式
9.支持异步发送消息
10.支持http协议
11.支持消息重试和recover
12.数据迁移.扩容对用户透明
13.消费状态保存在客户端
14.支持同步和异步复制两种HA
15.支持group commit

2.4.3.分布式消息中间件 RocketMQ

1
2
3
4
5
6
7
RockeMQ是一款分布式.队列模型的消息中间件,具有以下特点:
能够保证严格的消息顺序
提供丰富的消息拉取模式
高效的订阅者水平拓展能力
实时的消息订阅机制
亿级消息堆积能力
Metaq3.0版本改名,产品名称改为RocketMQ

2.4.4.其他MQ

1
2
3
4
5
.NET消息中间件DotNetMQ
基于Hbase的消息队列 HQueue
Go的MQ框架 KiteQ
AMQP消息服务器 RabbitMQ
MemcacheQ是一个基于MemcacheDB的消息队列服务器

3.为什么需要消息队列(重要)

1
2
消息系统的核心作用就是三点:解耦,异步和并行
以用户注册的案例来说明消息系统的作用

3.1.用户注册的一般流程

Markdown

1
问题:随着后端流程越来越多,每步流程都需要额外的耗费很多时间,从而会导致用户更长的等待延迟

3.2.用户注册的并行执行

Markdown

1
2
问题:系统并行的发起了4个请求,4个请求中,如果某一个环节执行1分钟,其他环节再快,用户也需要等待1分钟。
如果其中一个环节异常之后,整个服务挂掉了。

Markdown

3.3.用户注册的最终一致

Markdown

1
2
1.保证主流程的正常执行.执行成功之后,发送MQ消息出去
2.需要这个destincation的其他系统通过消费数据再执行,最终一致

Markdown

4.Kafka核心组件

1
2
3
4
5
Topic:消息根据Topic进行分类
Producer:发送消息者
Consumer:消息接受者
broker:每个Kafka实例(server)
Zookeeper:依赖集群保存meta信息

Markdown

5.Kafka集群搭建

5.1.下载解压

1
2
从官网下载Kafka,下载地址http://kafka.apache.org/downloads.html
注意这里最好下载scala2.10版本的kafka,因为scala2.10版本的兼容性比较好和2.11版本差别太大

Markdown

1
2
通过命令解压
[root@node02 software]# tar -zxvf kafka_2.10-0.9.0.1.tgz -C /opt/modules

5.2.重命名

1
[root@node02 modules]# mv kafka_2.10-0.9.0.1 kafka

5.3.配置启动

5.4.1.配置config/server.properties

1
2
[root@node02 ~]# cd /opt/modules/kafka/
[root@node02 kafka]# vi config/server.properties

5.4.2.配置broker.id从0开始,后面其他节点配置1,2,3,4等等

1
2
broker.id=0
host.name=node02

Markdown

5.4.3.事先启动zookeeper集群,这里配置zookeeper集群的地址

1
zookeeper.connect=node02:2181,node03:2181,node04:2181

Markdown

5.4.4.将集群分发到其他机器,比如

1
2
[root@node02 ~]# scp -r /opt/modules/kafka/ root@node03:/opt/modules/kakfa
[root@node02 ~]# scp -r /opt/modules/kafka/ root@node04:/opt/modules/kakfa

5.4.5.这里注意修改broker.id.host.name

1
2
3
4
[root@node03 ~]# cd /opt/modules/kafka/
[root@node03 kafka]# vi config/server.properties
broker.id=1
host.name=node03

Markdown

1
2
3
4
[root@node04 ~]# cd /opt/modules/kafka
[root@node04 kafka]# vi config/server.properties
broker.id=2
host.name=node04

Markdown

5.4.6.修改完配置以后在每个节点启动kafka的broker

1
2
3
[root@node02 kafka]# ./bin/kafka-server-start.sh config/server.properties
[root@node03 kafka]# ./bin/kafka-server-start.sh config/server.properties
[root@node04 kafka]# ./bin/kafka-server-start.sh config/server.properties

Markdown

5.4.7.测试

5.4.7.1.新建一个topic
1
2
[root@node02 kafka]# ./bin/kafka-topics.sh -zookeeper node02:2181,node03:2181,node04:2181 -topic test -replication-factor 2 -partitions 5 -create
Created topic "test".

Markdown

5.4.7.2.查看当前的topic
1
2
[root@node02 kafka]# ./bin/kafka-topics.sh -zookeeper node02:2181,node03:2181,node04:2181 -list
test

Markdown

5.4.7.3.查看一个topic的分区及副本状态信息
1
[root@node03 kafka]# ./bin/kafka-topics.sh --describe --zookeeper node02:2181,node03:2181,node04:2181 --topic test

Markdown

5.4.7.4.在一个节点创建一个provider
1
在provider输入信息,在consumer能接收到信息
1
[root@node03 kafka]# ./bin/kafka-console-producer.sh --broker-list node02:9092,node03:9092,node04:9092 --topic test

Markdown

5.4.7.5.在另一个节点创建一个consumer
1
[root@node04 kafka]# ./bin/kafka-console-consumer.sh --zookeeper node02:2181,node03:2181,node04:2181 --from-beginning -topic test

Markdown

5.5.Kafka生产者Java API

Markdown

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.matrix.kafka;

import kafka.javaapi.producer.Producer;
import kafka.producer.KeyedMessage;
import kafka.producer.ProducerConfig;

import java.util.Properties;
import java.util.UUID;

/**
* 这是一个简单的Kafka producer代码 包含两个功能: 1.数据发送 2.数据按照自定义的partition策略进行发送
*
* KafkaSpout的类
*/
public class KafakProducerSimple {

/**
* 1.指定当前kafka producer生产的数据的目的地 创建topic可以输入以下命令,在kafka集群的任一节点进行创建。
* bin/kafka-topics.sh --create --zookeeper zk01:2181 --replication-factor 1
* --partitions 1 --topic test
*/
private final static String TOPIC = "test";

public static void main(String[] args) throws Exception {
/**
* 2.读取配置文件
*/
Properties props = new Properties();
/*
* key.serializer.class默认为serializer.class
*/
props.put("serializer.class", "kafka.serializer.StringEncoder");
/*
* kafka broker对应的主机,格式为host1:port1,host2:port2
*/
props.put("zk.connect", "node02:2181,node03:2181,node04:2181");
props.put("metadata.broker.list", "node02:9092,node03:9092,node04:9092");
/*
* request.required.acks,设置发送数据是否需要服务端的反馈,有三个值0,1,-1
* 0,意味着producer永远不会等待一个来自broker的ack,这就是0.7版本的行为。
* 这个选项提供了最低的延迟,但是持久化的保证是最弱的,当server挂掉的时候会丢失一些数据。 1,意味着在leader
* replica已经接收到数据后,producer会得到一个ack。
* 这个选项提供了更好的持久性,因为在server确认请求成功处理后,client才会返回。
* 如果刚写到leader上,还没来得及复制leader就挂了,那么消息才可能会丢失。
* -1,意味着在所有的ISR都接收到数据后,producer才得到一个ack。
* 这个选项提供了最好的持久性,只要还有一个replica存活,那么数据就不会丢失
*/
props.put("request.required.acks", "1");
/*
* 可选配置,如果不配置,则使用默认的partitioner partitioner.class
* 默认值:kafka.producer.DefaultPartitioner
* 用来把消息分到各个partition中,默认行为是对key进行hash。
*/
// props.put("partitioner.class", "com.matrix.kafka.MyLogPartitioner");
/**
* 3.通过配置文件,创建生产者
*/
Producer<String, String> producer = new Producer<String, String>(new ProducerConfig(props));
// 发送数据
for (int messageNo = 1; messageNo < 100000; messageNo++) {
/**
* 5.调用producer的send方法发送数据 注意:这里需要指定
* partitionKey,用来配合自定义的MyLogPartitioner进行数据分发
*/
producer.send(
new KeyedMessage<String, String>(TOPIC, messageNo + "", "appid" + UUID.randomUUID() + "matrix"));
}
}
}

Markdown

5.5.1.用一个comsumer从某一个topic中读取信息

1
[root@node04 kafka]# ./bin/kafka-console-consumer.sh --zookeeper node02:2181,node03:2181,node04:2181 --from-beginning -topic test

Markdown

5.6.Kafka消费者Java API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.matrix.kafka;

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.KafkaStream;
import kafka.javaapi.consumer.ConsumerConnector;
import kafka.message.MessageAndMetadata;

public class KafkaConsumerSimple {

private static final String topic = "test";
private static final Integer threads = 1;

public static void main(String[] args) {
Properties props = new Properties();
props.put("zookeeper.connect", "node02:2181,node03:2181,node04:2181");
props.put("group.id", "1111");
props.put("auto.offset.reset", "smallest");

ConsumerConfig config = new ConsumerConfig(props);
ConsumerConnector consumer = Consumer.createJavaConsumerConnector(config);
Map<String, Integer> topicCountMap = new HashMap<String, Integer>();
topicCountMap.put(topic, 1);
// topicCountMap.put("mygirls", 1);
// topicCountMap.put("myboys", 1);
Map<String, List<KafkaStream<byte[], byte[]>>> consumerMap = consumer.createMessageStreams(topicCountMap);
List<KafkaStream<byte[], byte[]>> streams = consumerMap.get(topic);

for (final KafkaStream<byte[], byte[]> kafkaStream : streams) {
new Thread(new Runnable() {
@Override
public void run() {
for (MessageAndMetadata<byte[], byte[]> mm : kafkaStream) {
String msg = new String(mm.message());
System.out.println(msg);
}
}

}).start();

}
}
}

Markdown

5.7.关闭Kafka服务

1
2
3
[root@node02 kafka]# ./bin/kafka-server-stop.sh
[root@node03 kafka]# ./bin/kafka-server-stop.sh
[root@node04 kafka]# ./bin/kafka-server-stop.sh
本文作者 : Matrix
原文链接 : https://matrixsparse.github.io/2016/02/12/Kafka开源消息系统分布式集群搭建/
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

知识 & 情怀 | 二者兼得

微信扫一扫, 向我投食

微信扫一扫, 向我投食

支付宝扫一扫, 向我投食

支付宝扫一扫, 向我投食

留下足迹