Kafka

一、Kafka概述

Kafka 是分布式发布-订阅消息系统。它最初由 LinkedIn 公司开发,使用 Scala语言编写,之后成为 Apache 项目的一部分。
	Kafka是一个分布式的、可分区的、可复制的消息系统。它提供了普通消息系统的功能,但具有自己独特的设计。

	它提供了类似于JMS的特性,但是在设计实现上完全不同,此外它并不是JMS规范的实现。kafka对消息保存时根据Topic进行归类,发送消息者称为Producer,消息接受者称为Consumer,此外kafka集群由多个kafka实例组成,每个实例(server)称为broker。无论是kafka集群,还是producer和consumer都依赖于zookeeper来保证系统可用性集群保存一些meta信息。

	
	
	kafka的特点:
		高吞吐量
			Kafka 每秒可以生产约 25 万消息(50 MB),每秒处理 55 万消息(110 MB)
		持久化数据存储
			可进行持久化操作。将消息持久化到磁盘,因此可用于批量消费,例如 ETL,以及实时应用程序。通过将数据持久化到硬盘以及 replication 防止数据丢失。
		分布式系统易于扩展
			所有的 producer、broker 和 consumer 都会有多个,均为分布式的。无需停机即可扩展机器。
		客户端状态维护
			消息被处理的状态是在 consumer 端维护,而不是由 server 端维护。当失败时能自动平衡。

二、Kafka中的基本概念

	Kafka将消息以topic为单位进行归纳。
	将向Kafka topic发布消息的程序称为producers.
	将预订topics并消费消息的程序称为consumer.
	Kafka以集群的方式运行,可以由一个或多个服务组成,每个服务叫做一个broker.
	producers通过网络将消息发送到Kafka集群,集群向消费者提供消息。 
	客户端和服务端通过TCP协议通信。Kafka提供了Java客户端,并且对多种语言都提供了支持。

三、Topics、Producers、Consumers

1.Topics
		一个topic是对一组消息的归纳。对每个topic,Kafka 对它的日志进行了分区(参考图:kafkaTopics)。
		每个分区都由一系列有序的、不可变的消息组成,这些消息被连续的追加到分区中。
		分区中的每个消息都有一个连续的序列号叫做offset,用来在分区中唯一的标识这个消息。
		在一个可配置的时间段内,Kafka集群保留所有发布的消息,不管这些消息有没有被消费。比如,如果消息的保存策略被设置为2天,那么在一个消息被发布的两天时间内,它都是可以被消费的。之后它将被丢弃以释放空间。Kafka的性能是和数据量无关的常量级的,所以保留太多的数据并不是问题。
		每个分区在Kafka集群的若干服务中都有副本,这样这些持有副本的服务可以共同处理数据和请求,副本数量是可以配置的。副本使Kafka具备了容错能力。
		每个分区都由一个服务器作为“leader”,零或若干服务器作为“followers”,leader负责处理消息的读和写,followers则去复制leader.如果leader down了,followers中的一台则会自动成为leader。集群中的每个服务都会同时扮演两个角色:作为它所持有的一部分分区的leader,同时作为其他分区的followers,这样集群就会据有较好的负载均衡。

		**实际上每个consumer唯一需要维护的数据是消息在日志中的位置,也就是offset.这个offset由consumer来维护:一般情况下随着consumer不断的读取消息,这offset的值不断增加,但其实consumer可以以任意的顺序读取消息,比如它可以将offset设置成为一个旧的值来重读之前的消息。
		**以上特点的结合,使Kafka consumers非常的轻量级:它们可以在不对集群和其他consumer造成影响的情况下读取消息。你可以使用命令行来"tail"消息而不会对其他正在消费消息的consumer造成影响。
		**将日志分区可以达到以下目的:首先这使得每个日志的数量不会太大,可以在单个服务上保存。另外每个分区可以单独发布和消费,为并发操作topic提供了一种可能。

	2.Producers
		Producer将消息发布到它指定的topic中,并负责决定发布到哪个分区。通常简单的由负载均衡机制随机选择分区,但也可以通过特定的分区函数选择分区。使用的更多的是第二种。

	3.Consumers
		发布消息通常有两种模式:队列模式(queuing)和发布-订阅模式(publish-subscribe)。

		(1)队列模式
			队列模式中,多个consumers可以同时从服务端读取消息,每个消息只被其中一个consumer读到;
		(2)发布订阅模式
			发布-订阅模式中消息被广播到所有的consumer中。		
		(3)Consumers可以加入一个consumer 组,共同竞争一个topic,topic中的消息将被分发到组中的一个成员中。同一组中的consumer可以在不同的程序中,也可以在不同的机器上。
			如果所有的consumer都在一个组中,这就成为了传统的队列模式,在各consumer中实现负载均衡。
			如果所有的consumer都不在不同的组中,这就成为了发布-订阅模式,所有的消息都被分发到所有的consumer中。
			更常见的是,每个topic都有若干数量的consumer组,每个组都是一个逻辑上的“订阅者”,为了容错和更好的稳定性,每个组由若干consumer组成。这其实就是一个发布-订阅模式,只不过订阅者是个组而不是单个consumer(参看图片:Consumer组.png)。
			
			--------------------------------------		
				相比传统的消息系统,Kafka可以很好的保证有序性。
				传统的队列在服务器上保存有序的消息,如果多个consumers同时从这个服务器消费消息,服务器就会以消息存储的顺序向consumer分发消息。虽然服务器按顺序发布消息,但是消息是被异步的分发到各consumer上,所以当消息到达时可能已经失去了原来的顺序,这意味着并发消费将导致顺序错乱。为了避免故障,这样的消息系统通常使用“专用consumer”的概念,其实就是只允许一个消费者消费消息,当然这就意味着失去了并发性。
				在这方面Kafka做的更好,通过分区的概念,Kafka可以在多个consumer组并发的情况下提供较好的有序性和负载均衡。将每个分区分只分发给一个consumer组,这样一个分区就只被这个组的一个consumer消费,就可以顺序的消费这个分区的消息。因为有多个分区,依然可以在多个consumer组之间进行负载均衡。注意consumer组的数量不能多于分区的数量,也就是有多少分区就允许多少并发消费。
				Kafka只能保证一个分区之内消息的有序性,在不同的分区之间是不可以的,这已经可以满足大部分应用的需求。如果需要topic中所有消息的有序性,那就只能让这个topic只有一个分区,当然也就只有一个consumer组消费它。
			--------------------------------------	


三、Kafka伪分布式安装搭建

资源: kafka:i0fp
伪分布式
伪分布式安装:
		下载Kafka安装包
		解压:
			tar -zxvf kafka_2.9.2-0.8.1.1.tgz
		修改server.properties文件
			log.dirs=/tmp/kafka-logs
		修改zookeeper.properties
			dataDir=/tmp/zookeeper
			
	启动kafka:
		启动zookeeper:
			zkServer.sh start
		启动kafka:
			bin/kafka-server-start.sh config/server.properties
        创建topic:
		bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test

	查看topic:
		bin/kafka-topics.sh --list --zookeeper localhost:2181

	使用命令行producer从文件中或者从标准输入中读取消息并发送到服务端:
		bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test 

	启动命令行consumer读取消息并输出到标准输出:
		bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginning

完全分布式

下载Kafka安装包
	解压:
		tar -zxvf kafka_2.9.2-0.8.1.1.tgz
	配置:
		在config目录下,修改server.properties,在文件中修改如下参数:
			broker.id=0 #当前server编号
			port=9093 #使用的端口(文件中没有这个配置,不需要修改,默认为9093)
			log.dirs=/tmp/kafka-logs-1 #日志存储目录
			zookeeper.connect=hadoop01:2181,hadoop02:2181
	启动:
		启动zookeeper:zkServer.sh start
		在各个机器上执行:bin/kafka-server-start.sh config/server.properties
	测试:
		创建一个拥有3个副本的topic:
			bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 1 --topic my-replicated-topic

		查看每个节点的信息:
			bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic my-replicated-topic
		
		向topic发送消息:
			bin/kafka-console-producer.sh --broker-list localhost:9092 --topic my-replicated-topic

		消费消息:
			bin/kafka-console-consumer.sh --zookeeper localhost:2181 --from-beginning --topic my-replicated-topic
		
		实验 - 容错性:
			kill -9 7564
			bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic my-replicated-topic
			bin/kafka-console-consumer.sh --zookeeper localhost:2181 --from-beginning --topic my-replicated-topic

四、Kafka的javaApi操作

创建java工程,导入kafka相关包: 资源
package com.liming;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.junit.Test;

import kafka.consumer.Consumer;
import kafka.consumer.ConsumerConfig;
import kafka.consumer.ConsumerConnector;
import kafka.consumer.ConsumerIterator;
import kafka.consumer.KafkaStream;
import kafka.javaapi.producer.Producer;
import kafka.message.MessageAndMetadata;
import kafka.producer.KeyedMessage;
import kafka.producer.ProducerConfig;

public class Demo01 {
	@SuppressWarnings("deprecation")
	@Test
	public void producer(){
		Properties prop = new Properties();
		prop.put("serializer.class", "kafka.serializer.StringEncoder");
        prop.put("metadata.broker.list", "192.168.239.129:9092");
		Producer<Integer, String> producer = new Producer<Integer,String>(new ProducerConfig(prop ));
		producer.send(new KeyedMessage<Integer, String>("topic1", "java_api发送"));
		
	}
	@Test
	public void consumer(){
		Properties properties = new Properties();  
		properties.put("zookeeper.connect", "192.168.239.129:2181,192.168.239.130:2181,192.168.239.131:2181");//声明zk  
		properties.put("group.id", "group1");
		properties.put("auto.offset.reset", "smallest");
		
		kafka.javaapi.consumer.ConsumerConnector consumer = Consumer.createJavaConsumerConnector(new ConsumerConfig(properties));
		Map<String, Integer> topicCountMap = new HashMap<String, Integer>();  
		topicCountMap.put("topic1", 1); // 一次从主题中获取一个数据  
		Map<String, List<KafkaStream<byte[], byte[]>>> streams = consumer.createMessageStreams(topicCountMap);
		KafkaStream<byte[], byte[]> kafkaStream = streams.get("topic1").get(0);
		ConsumerIterator<byte[], byte[]> iterator = kafkaStream.iterator();
		while(iterator.hasNext()){
			MessageAndMetadata<byte[], byte[]> next = iterator.next();
			byte[] message = next.message();
			System.out.println(new String(message));
		}
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值