Kafka

Kafka概述

官网:http://kafka.apache.org/

kafka是一套开源的消息系统,由scala写成。支持javaAPI的。kafka最初由LinkedIn公司开发,2011年开源。2012年从Apache毕业。是一个分布式消息队列,kafka读消息保存采用Topic进行归类;发送消息者成为Producer,消息接受者成为Consumer,此外kafka集群有多个kafka实例组成,每个实例(server)成为broker。无论是kafka集群,还是producer和consumer都依赖于zookeeper来保证系统可用性集群保存一些meta信息。

为什么要用消息队列

  • 解耦:为了避免出现问题
  • 拓展性:可增加处理过程
  • 灵活:面对访问量剧增,不会因为超负荷请求而完全瘫痪。
  • 可恢复:一部分组件失效,不会影响整个系统。可以进行恢复。
  • 缓冲:控制数据流经过系统的速度。
  • 顺序保证:对消息进行有序处理。
  • 异步通信:消息队列提供了异步处理的机制。允许用户把消息放到队列 , 不立刻处理。

kafka架构

一个Topic可以认为是一类消息,每个topic将被分成多个partition(区),每个partition在存储层面是append log文件。任何发布到此partition的消息都会被直接追加到log文件的尾部,每条消息在文件中的位置称为offset(偏移量),offset为一个long型数字,它是唯一标记一条消息。kafka并没有提供其他额外的索引机制来存储offset,因为在kafka中几乎不允许对消息进行“随机读写”。

分区:一个Topic的多个partitions,被分布在kafka集群中的多个server上;每个server(kafka实例)负责partitions中消息的读写操作;此外kafka还可以配置partitions需要备份的个数(replicas),每个partition将会被备份到多台机器上,以提高可用性;基于replicated方案,每个分区都有一个server为"leader";leader负责所有的读写操作,如果leader失效,那么将会有其他follower来接管(成为新的leader);follower只是单调的和leader跟进,同步消息即可。由此可见作为leader的server承载了全部的请求压力,因此从集群的整体考虑,有多少个partitions就意味着有多少个"leader",kafka会将"leader"均衡的分散在每个实例上,来确保整体的性能稳定。如果Topic的"replicationfactor"为N,那么允许N-1个kafka实例失效。

生产者:Producer将消息发布到指定的Topic中,同时Producer也能决定将此消息归属于哪个partition;Kafka生产者客户端发布消息到服务端的指定主题,会指定消息所属的分区。生产者发布消息时根据消息是否有键,采用不同的分区策略。消息没有键时,通过轮询方式进行客户端负载均衡;消息有键时,根据分区语义(例如hash)确保相同键的消息总是发送到同一分区。

消费者: 本质上kafka只支持Topic。每个consumer属于一个consumer group;反过来说,每个group中可以有多个consumer;发送到Topic的消息,只会被订阅此Topic的每个group中的一个consumer消费。但是一个consumer可以消费多个partitions中的消息。kafka只能保证一个partition中的消息被某个consumer消费时,消息是顺序的。

  • 如果所有的consumer都具有相同的group,这种情况和queue模式很像;消息将会在consumers之间负载均衡.
  • 如果所有的consumer都具有不同的group,那这就是"发布-订阅";消息将会广播给所有的消费者

日志文件的删除:kafka和JMS(Java Message Service)实现(activeMQ)不同的是:即使消息被消费,消息仍然不会被立即删除.日志文件将会根据broker中的配置要求,保留一定的时间之后删除;比如log文件保留2天,那么两天后,文件会被清除,无论其中的消息是否被消费.kafka通过这种简单的手段,来释放磁盘空间,以及减少消息消费之后对文件内容改动的磁盘IO开支

状态维护:kafka集群几乎不需要维护任何consumer和producer状态信息,这些信息有zookeeper保存;因此producer和consumer的客户端实现非常轻量级,它们可以随意离开,而不会对集群造成额外的影响。

消息传送机制

对于JMS实现,消息传输担保非常直接:有且只有一次(exactly once)。在kafka中稍有不同:

  •    at most once: 最多一次,这个和JMS中"非持久化"消息类似.发送一次,无论成败,将不会重发.
  •   at least once: 消息至少发送一次,如果消息未能接受成功,可能会重发,直到接收成功.
  • exactly once: 消息只会发送一次.

    at most once: 消费者fetch消息,然后保存offset,然后处理消息;当client保存offset之后,但是在消息处理过程中出现了异常,导致部分消息未能继续处理.那么此后"未处理"的消息将不能被fetch到,这就是"at most once".

    at least once: 消费者fetch消息,然后处理消息,然后保存offset.如果消息处理成功之后,但是在保存offset阶段zookeeper异常导致保存操作未能执行成功,这就导致接下来再次fetch时可能获得上次已经处理过的消息,这就是"at least once",原因offset没有及时的提交给zookeeper,zookeeper恢复正常还是之前offset状态.

    exactly once: kafka中并没有严格的去实现(基于2阶段提交,事务),我们认为这种策略在kafka中是没有必要的.

    通常情况下"at-least-once"是我们搜选.(相比at most once而言,重复接收数据总比丢失数据要好).

kafka集群安装部署

1 上传解压
2 在kafka目录下创建logs文件夹
3 修改配置文件  config/server.properties
    #broker的全局唯一编号,不能重复
    broker.id=0
    #是否允许删除topic
    delete.topic.enable=true
    #处理网络请求的线程数量
    num.network.threads=3
    #用来处理磁盘IO的线程数量
    num.io.threads=8
    #发送套接字的缓冲区大小
    socket.send.buffer.bytes=102400
    #接收套接字的缓冲区大小
    socket.receive.buffer.bytes=102400
    #请求套接字的最大缓冲区大小
    socket.request.max.bytes=104857600
    #kafka运行日志存放的路径
    log.dirs=/opt/module/kafka/logs
    #topic在当前broker上的分区个数
    num.partitions=1
    #用来恢复和清理data下数据的线程数量
    num.recovery.threads.per.data.dir=1
    #segment文件保留的最长时间,超时将被删除
    log.retention.hours=168
    #配置连接Zookeeper集群地址
    zookeeper.connect=bigdata111:2181,bigdata112:2181,bigdata113:2181
4 启动集群
    bin/kafka-server-start.sh config/server.properties &
5 关闭
    bin/kafka-server-stop.sh

 Kafka命令行

1 查看当前集群中已存在的主题topic
    bin/kafka-topics.sh --zookeeper bigdata111:2181 --list
2 创建topic
    bin/kafka-topics.sh --zookeeper bigdata111:2181 --create --replication-facto
r 3 --partitions 1 --topic guanxuan
    --zookeeper 连接zk集群
    --create 创建
    --replication-factor 副本
    --partitions 分区
    --topic 主题名
3 删除主题
bin/kafka-topics.sh --zookeeper bigdata111:2181 --delete --topic guanxuan
4 发送消息
生产者启动:
    bin/kafka-console-producer.sh --broker-list bigdata111:9092 --topic guanxuan
消费者启动:
    bin/kafka-console-consumer.sh --bootstrap-server bigdata111:9092 --topic guanxuan --from-beginning
5 查看主题详细信息
    bin/kafka-topics.sh --zookeeper bigdata111:2181 --describe --topic guanxuan

Java代码操作

高级API

1)高级API优点

高级API 写起来简单

不需要自行去管理offset,系统通过zookeeper自行管理。

不需要管理分区,副本等情况,.系统自动管理。

消费者断线会自动根据上一次记录在zookeeper中的offset去接着获取数据(默认设置1分钟更新一下zookeeper中存的offset)

可以使用group来区分对同一个topic 的不同程序访问分离开来(不同的group记录不同的offset,这样不同程序读取同一个topic才不会因为offset互相影响)

2)高级API缺点

不能自行控制offset(对于某些特殊需求来说)

不能细化控制如分区、副本、zk等

低级API

1)低级 API 优点

能够让开发者自己控制offset,想从哪里读取就从哪里读取。

自行控制连接分区,对分区自定义进行负载均衡

对zookeeper的依赖性降低(如:offset不一定非要靠zk存储,自行存储offset即可,比如存在文件或者内存中)

2)低级API缺点

太过复杂,需要自行控制offset,连接哪个分区,找到分区leader 等。

/**
 * @author Admin
 * @date
 * @version 1.0
 */
public class Consumer1 {
	public static void main(String[] args) {
		//1.配置消费者属性
		Properties prop = new Properties();
		//配置属性
		//服务器地址指定
		prop.put("bootstrap.servers", "192.168.50.184:9092");
		//配置消费者组
		prop.put("group.id", "g1");
		//配置是否自动确认offset
		prop.put("enable.auto.commit", "true");
		//序列化
		    prop.put("key.deserializ er","org.apache.kafka.common.serialization.StringDeserializer");
		prop.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
		//2.实例消费者
		final KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(prop);
		
		//4.释放资源 线程安全
		Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
			public void run() {
				if(consumer != null) {
					consumer.close();
				}
			}
		}));
		
		//订阅消息主题
		consumer.subscribe(Arrays.asList("shengdan"));
		//3.拉消息 推push 拉poll
		while(true) {
			ConsumerRecords<String,String> records = consumer.poll(1000);
			//遍历消息
			for(ConsumerRecord<String,String> record:records) {
				System.out.println(record.topic() + "------" + record.value());
			}
			
		}
	}
}
public class Producer2 {
	public static void main(String[] args) {
		// 1.配置生产者属性(指定多个参数)
		Properties prop = new Properties();
		// 参数配置
		// kafka节点的地址
		prop.put("bootstrap.servers", "192.168.50.183:9092");
		// 发送消息是否等待应答
		prop.put("acks", "all");
		// 配置发送消息失败重试
		prop.put("retries", "0");
		// 配置批量处理消息大小
		prop.put("batch.size", "10241");
		// 配置批量处理数据延迟
		prop.put("linger.ms", "5");
		// 配置内存缓冲大小
		prop.put("buffer.memory", "12341235");
		// 消息在发送前必须序列化
		prop.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
		prop.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
		prop.put("partitioner.class", "com.itstar.kafka.kafka_producer.Patition1");
        //拦截器
		//ArrayList<String> inList = new ArrayList<String>();
		//inList.add("com.itstare.kafka.interceptor.TimeInterceptor");
		//prop.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, inList);
		//2.实例化producer
		KafkaProducer<String, String> producer = new KafkaProducer<String, String>(prop);
		//3.发送消息
		for(int i = 0;i<99;i++) {
			producer.send(new ProducerRecord<String, String>("yuandan", "hunterhenshuai" + i), new Callback() {
				
				public void onCompletion(RecordMetadata metadata, Exception exception) {
					//如果metadata不为null 拿到当前的数据偏移量与分区
					if(metadata != null) {
						System.out.println(metadata.topic() + "----" + metadata.offset() + "----" + metadata.partition());
					}
				}
			});
		}
		//4.关闭资源
		producer.close();
	}
}
/**
 *拦截器添加时间戳
 */
public class TimeInterceptor implements ProducerInterceptor<String, String>{

	//配置信息
	public void configure(Map<String, ?> configs) {
	}
	//业务逻辑
	public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
		
		return new ProducerRecord<String, String>(
				record.topic(), 
				record.partition(),
				record.key(),
				System.currentTimeMillis() + "-" + record.value());
	}
	//发送失败调用
	public void onAcknowledgement(RecordMetadata metadata, Exception exception) {
	}
	//关闭资源
	public void close() {
	}
}

Kafka Stream

当前已经有非常多的流式处理系统,最知名且应用最多的开源流式处理系统有Spark Streaming和Apache Storm。Apache Storm发展多年,应用广泛,提供记录级别的处理能力,当前也支持SQL on Stream。而Spark Streaming基于Apache Spark,可以非常方便与图计算,SQL处理等集成,功能强大,对于熟悉其它Spark应用开发的用户而言使用门槛低。另外,目前主流的Hadoop发行版,如Cloudera和Hortonworks,都集成了Apache Storm和Apache Spark,使得部署更容易。

既然Apache Spark与Apache Storm拥用如此多的优势,那为何还需要Kafka Stream呢?主要有如下原因。

第一,Spark和Storm都是流式处理框架,而Kafka Stream提供的是一个基于Kafka的流式处理类库。框架要求开发者按照特定的方式去开发逻辑部分,供框架调用。开发者很难了解框架的具体运行方式,从而使得调试成本高,并且使用受限。而Kafka Stream作为流式处理类库,直接提供具体的类给开发者调用,整个应用的运行方式主要由开发者控制,方便使用和调试。

第二,虽然Cloudera与Hortonworks方便了Storm和Spark的部署,但是这些框架的部署仍然相对复杂。而Kafka Stream作为类库,可以非常方便的嵌入应用程序中,它对应用的打包和部署基本没有任何要求。

第三,就流式处理系统而言,基本都支持Kafka作为数据源。例如Storm具有专门的kafka-spout,而Spark也提供专门的spark-streaming-kafka模块。事实上,Kafka基本上是主流的流式处理系统的标准数据源。换言之,大部分流式系统中都已部署了Kafka,此时使用Kafka Stream的成本非常低。

第四,使用StormSpark Streaming时,需要为框架本身的进程预留资源,如Storm的supervisor和Spark on YARN的node manager。即使对于应用实例而言,框架本身也会占用部分资源,如Spark Streaming需要为shuffle和storage预留内存。但是Kafka作为类库不占用系统资源。

第五,由于Kafka本身提供数据持久化,因此Kafka Stream提供滚动部署和滚动升级以及重新计算的能力。

第六,由于Kafka Consumer Rebalance机制,Kafka Stream可以在线动态调整并行度

案例:

       实时处理单词带有”>>>”前缀的内容。例如输入”trainning>>>xq,最终处理成“xq”

public class Application {

	public static void main(String[] args) {
		// 定义输入的topic
        String from = "first";
        // 定义输出的topic
        String to = "second";
        // 设置参数
        Properties settings = new Properties();
        settings.put(StreamsConfig.APPLICATION_ID_CONFIG, "logFilter");
        settings.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "bigdata11:9092");
        StreamsConfig config = new StreamsConfig(settings);
        // 构建拓扑
        TopologyBuilder builder = new TopologyBuilder();
        builder.addSource("SOURCE", from)
               .addProcessor("PROCESS", new ProcessorSupplier<byte[], byte[]>() {
					@Override
					public Processor<byte[], byte[]> get() {
						// 具体分析处理
						return new LogProcessor();
					}
				}, "SOURCE")
                .addSink("SINK", to, "PROCESS");
        // 创建kafka stream
        KafkaStreams streams = new KafkaStreams(builder, config);
        streams.start();
	}
}
public class LogProcessor implements Processor<byte[], byte[]> {
	private ProcessorContext context;
	@Override
	public void init(ProcessorContext context) {
		this.context = context;
	}
	@Override
	public void process(byte[] key, byte[] value) {
		String input = new String(value);
		// 如果包含“>>>”则只保留该标记后面的内容
		if (input.contains(">>>")) {
			input = input.split(">>>")[1].trim();
			// 输出到下一个topic
			context.forward("logProcessor".getBytes(), input.getBytes());
		}else{
			context.forward("logProcessor".getBytes(), input.getBytes());
		}
	}
	@Override
	public void punctuate(long timestamp) {
	}
	@Override
	public void close() {
		
	}
}

bigdata113上启动生产者  

bin/kafka-console-producer.sh --broker-list bigdata11:9092 --topic first

>hello>>>world

>h>>>itstar

>hahaha

在bigdata112上启动消费者

bin/kafka-console-consumer.sh --zookeeper bigdata11:2181 --from-beginning --topic second

world

itstar

hahaha

 

博客参考了:https://www.cnblogs.com/likehua/p/3999538.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值