Kafka消费形式验证

前面的《Kafka笔记整理(一)》中有提到消费者的消费形式,说明如下:

、每个consumer属于一个consumer group,可以指定组id。group.id

、消费形式:
组内:组内的消费者消费同一份数据;同时只能有一个consumer消费一个Topic中的1个partition;
一个consumer可以消费多个partitions中的消息。所以,对于一个topic,同一个group中推荐不能有多于
partitions个数的consumer同时消费,否则将意味着某些consumer将无法得到消息。
组间:每个消费组消费相同的数据,互不影响。 、在一个consumer多个线程的情况下,一个线程相当于一个消费者。
例如:partition为3,一个consumer起了3个线程消费,另一个后来的consumer就无法消费

下面就来验证Kafka的消费形式,不过需要说明的是,在消费者的程序代码中,可以指定消费者的group.id(我们下面将会在配置文件中指定)。

而在使用kafka的shell命令时,其实也是可以指定配置文件来指定消费者的group.id的,如果不指定,那么kafka将会随机生成一个group.id(kafka-console-consumer.sh中的kafka.tools.ConsoleConsumer类,如果没有指定group.id,其策略是随机生成)。

在后面的程序代码中,会使用同一group.id开启4个消费的线程(因为我们创建的topic有3个partition),然后在终端中通过kafka shell来开启另外一个消费者,进而达到验证kafka消费形式的目的。

另外,在测试中使用的topic如下:

$ kafka-topics.sh --describe hadoop --zookeeper uplooking01:,uplooking02:,uplooking03:
Topic:hadoop PartitionCount: ReplicationFactor: Configs:
Topic: hadoop Partition: Leader: Replicas: ,, Isr: ,,
Topic: hadoop Partition: Leader: Replicas: ,, Isr: ,,
Topic: hadoop Partition: Leader: Replicas: ,, Isr: ,,

即partition为3,副本因为也为3.

程序代码

KafkaProducerOps.java
package com.uplooking.bigdata.kafka.producer;

import com.uplooking.bigdata.kafka.constants.Constants;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord; import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.Random; /**
* 通过这个KafkaProducerOps向Kafka topic中生产相关的数据
* <p>
* Producer
*/
public class KafkaProducerOps {
public static void main(String[] args) throws IOException {
/**
* 专门加载配置文件
* 配置文件的格式:
* key=value
*
* 在代码中要尽量减少硬编码
* 不要将代码写死,要可配置化
*/
Properties properties = new Properties();
InputStream in = KafkaProducerOps.class.getClassLoader().getResourceAsStream("producer.properties");
properties.load(in);
/**
* 两个泛型参数
* 第一个泛型参数:指的就是kafka中一条记录key的类型
* 第二个泛型参数:指的就是kafka中一条记录value的类型
*/
String[] girls = new String[]{"姚慧莹", "刘向前", "周 新", "杨柳"};
Producer<String, String> producer = new KafkaProducer<String, String>(properties);
Random random = new Random();
int start = ;
for (int i = start; i <= start + ; i++) {
String topic = properties.getProperty(Constants.KAFKA_PRODUCER_TOPIC);
String key = i + "";
String value = "今天的<--" + girls[random.nextInt(girls.length)] + "-->很美很美哦~";
ProducerRecord<String, String> producerRecord =
new ProducerRecord<String, String>(topic, key, value);
producer.send(producerRecord);
}
producer.close();
}
}
KafkaConsumerOps.java
package com.uplooking.bigdata.kafka.consumer;

import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer; import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Properties;
import java.util.concurrent.*; /**
* 从kafka topic中消费数据
*/
public class KafkaConsumerOps {
public static void main(String[] args) throws IOException {
//线程池
ScheduledExecutorService service = Executors.newScheduledThreadPool();
System.out.println("外部开始时间:" + System.currentTimeMillis());
for (int i =; i < ; i++){
ScheduledFuture<?> schedule = service.schedule(
new ConsumerThread(),
5L,
TimeUnit.SECONDS);
}
}
} class ConsumerThread implements Runnable { public void run() {
System.out.println("线程ID:" + Thread.currentThread().getId() + "线程开始时间:" + System.currentTimeMillis());
/**
* 两个泛型参数
* 第一个泛型参数:指的就是kafka中一条记录key的类型
* 第二个泛型参数:指的就是kafka中一条记录value的类型
*/
Properties properties = new Properties();
try {
properties.load(KafkaConsumerOps.class.getClassLoader().getResourceAsStream("consumer.properties"));
} catch (IOException e) {
e.printStackTrace();
}
Consumer<String, String> consumer = new KafkaConsumer<String, String>(properties); Collection<String> topics = Arrays.asList("hadoop");
//消费者订阅topic
consumer.subscribe(topics);
ConsumerRecords<String, String> consumerRecords = null;
while (true) {
//接下来就要从topic中拉取数据
consumerRecords = consumer.poll();
//遍历每一条记录
for (ConsumerRecord consumerRecord : consumerRecords) {
long offset = consumerRecord.offset();
Object key = consumerRecord.key();
Object value = consumerRecord.value();
int partition = consumerRecord.partition();
System.out.println("CurrentThreadID: " + Thread.currentThread().getId() + "\toffset: " + offset + "\tpartition: " + partition + "\tkey: " + key + "\tvalue: " + value);
}
}
}
}
MyKafkaPartitioner.java
package com.uplooking.bigdata.kafka.partitioner;

import org.apache.kafka.clients.producer.Partitioner;
import org.apache.kafka.common.Cluster; import java.util.Map;
import java.util.Random; /**
* 创建自定义的分区,根据数据的key来进行划分
* <p>
* 可以根据key或者value的hashCode
* 还可以根据自己业务上的定义将数据分散在不同的分区中
* 需求:
* 根据用户输入的key的hashCode值和partition个数求模
*/
public class MyKafkaPartitioner implements Partitioner { public void configure(Map<String, ?> configs) { } /**
* 根据给定的数据设置相关的分区
*
* @param topic 主题名称
* @param key key
* @param keyBytes 序列化之后的key
* @param value value
* @param valueBytes 序列化之后的value
* @param cluster 当前集群的元数据信息
*/
public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
Integer partitionNums = cluster.partitionCountForTopic(topic);
int targetPartition = -;
if (key == null || keyBytes == null) {
targetPartition = new Random().nextInt() % partitionNums;
} else {
int hashCode = key.hashCode();
targetPartition = hashCode % partitionNums;
System.out.println("key: " + key + ", value: " + value + ", hashCode: " + hashCode + ", partition: " + targetPartition);
}
return targetPartition;
} public void close() {
}
}
Constants.java
package com.uplooking.bigdata.kafka.constants;

public interface Constants {
/**
* 生产的key对应的常量
*/
String KAFKA_PRODUCER_TOPIC = "producer.topic";
}
producer.properties
############################# Producer Basics #############################

# list of brokers used for bootstrapping knowledge about the rest of the cluster
# format: host1:port1,host2:port2 ...
bootstrap.servers=uplooking01:,uplooking02:,uplooking03: # specify the compression codec for all data generated: none, gzip, snappy, lz4
compression.type=none # name of the partitioner class for partitioning events; default partition spreads data randomly
partitioner.class=com.uplooking.bigdata.kafka.partitioner.MyKafkaPartitioner # the maximum amount of time the client will wait for the response of a request
#request.timeout.ms= # how long `KafkaProducer.send` and `KafkaProducer.partitionsFor` will block for
#max.block.ms= # the producer will wait for up to the given delay to allow other records to be sent so that the sends can be batched together
#linger.ms= # the maximum size of a request in bytes
#max.request.size= # the default batch size in bytes when batching multiple records sent to a partition
#batch.size= # the total bytes of memory the producer can use to buffer records waiting to be sent to the server
#buffer.memory= #####设置自定义的topic
producer.topic=hadoop key.serializer=org.apache.kafka.common.serialization.StringSerializer
value.serializer=org.apache.kafka.common.serialization.StringSerializer
consumer.properties
# Zookeeper connection string
# comma separated host:port pairs, each corresponding to a zk
# server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"
zookeeper.connect= uplooking01:,uplooking02:,uplooking03: bootstrap.servers=uplooking01:,uplooking02:,uplooking03: # timeout in ms for connecting to zookeeper
zookeeper.connection.timeout.ms= #consumer group id
group.id=test-consumer-group #consumer timeout
#consumer.timeout.ms= key.deserializer=org.apache.kafka.common.serialization.StringDeserializer
value.deserializer=org.apache.kafka.common.serialization.StringDeserializer
pom.xml

主要是kafka-clients的依赖:

<dependencies>
<!--kafka-->
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>0.10.0.1</version>
</dependency>
</dependencies>

测试

先在终端启动一个消费者,注意由于没有指定配置文件,所以其group.id是随机生成的:

$ kafka-console-consumer.sh --topic hadoop --zookeeper uplooking01:,uplooking02:,uplooking03:

接下来分别执行消费者的代码和生产者的代码,然后观察各个终端的输出。

生产者程序的终端输出如下:

key: , value: 今天的<--刘向前-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--刘向前-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--刘向前-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--杨柳-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--姚慧莹-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--姚慧莹-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--杨柳-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--刘向前-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--姚慧莹-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--杨柳-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--姚慧莹-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--周 新-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--姚慧莹-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--姚慧莹-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--刘向前-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--刘向前-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--杨柳-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--刘向前-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--杨柳-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--姚慧莹-->很美很美哦~, hashCode: , partition:
key: , value: 今天的<--杨柳-->很美很美哦~, hashCode: , partition:

消费者程序的终端输出如下:

外部开始时间:
线程ID:20线程开始时间:
线程ID:21线程开始时间:
线程ID:23线程开始时间:
线程ID:22线程开始时间:
CurrentThreadID: offset: partition: key: value: 今天的<--刘向前-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--杨柳-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--杨柳-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--杨柳-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--姚慧莹-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--刘向前-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--姚慧莹-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--姚慧莹-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--周 新-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--刘向前-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--刘向前-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--刘向前-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--杨柳-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--刘向前-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--杨柳-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--姚慧莹-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--刘向前-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--姚慧莹-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--姚慧莹-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--杨柳-->很美很美哦~
CurrentThreadID: offset: partition: key: value: 今天的<--姚慧莹-->很美很美哦~

消费者shell的终端输出如下:

$ kafka-console-consumer.sh --topic hadoop --zookeeper uplooking01:,uplooking02:,uplooking03:
今天的<--刘向前-->很美很美哦~
今天的<--姚慧莹-->很美很美哦~
今天的<--刘向前-->很美很美哦~
今天的<--姚慧莹-->很美很美哦~
今天的<--姚慧莹-->很美很美哦~
今天的<--杨柳-->很美很美哦~
今天的<--姚慧莹-->很美很美哦~
今天的<--刘向前-->很美很美哦~
今天的<--姚慧莹-->很美很美哦~
今天的<--姚慧莹-->很美很美哦~
今天的<--周 新-->很美很美哦~
今天的<--刘向前-->很美很美哦~
今天的<--刘向前-->很美很美哦~
今天的<--杨柳-->很美很美哦~
今天的<--刘向前-->很美很美哦~
今天的<--杨柳-->很美很美哦~
今天的<--杨柳-->很美很美哦~
今天的<--杨柳-->很美很美哦~
今天的<--姚慧莹-->很美很美哦~
今天的<--刘向前-->很美很美哦~
今天的<--杨柳-->很美很美哦~

分析

因为使用kafka shell的消费者的group.id是随机生成的,所以其肯定可以消费到topic下partition的消息,这是属于组间的消费。

而由于在消费者的程序代码中,4个线程都是使用同一个group.id的(都是使用consumer.properties这个配置文件),按照理论知识的理解,因为topic hadoop只有3个partition,所以只能有3个线程即3个consumer进行消息的消费,而观察输出,通过线程ID,发现确实只有三个线程消费了topic中的消息,这也验证了kafka组内消息的消费形式。

Kafka性能测试

参考文档:https://cwiki.apache.org/confluence/display/KAFKA/Performance+testing

生产能力测试

在kafka的安装目录的bin里有性能的评估工具bin/kafka-producer-perf-test.sh,主要输出4项指标,总共发送消息量(以MB为单位),每秒发送消息量(MB/second),发送消息总数,每秒发送消息数(records/second)。

测试如下:

[uplooking@uplooking01 ~]$ kafka-producer-perf-test.sh --topic flume-kafka --num-records  --producer-props bootstrap.servers=uplooking01:,uplooking02:,uplooking03: --throughput  --record-size
records sent, 9994.4 records/sec (0.95 MB/sec), 3.1 ms avg latency, 258.0 max latency.
records sent, 10040.0 records/sec (0.96 MB/sec), 2.4 ms avg latency, 141.0 max latency.
records sent, 10004.0 records/sec (0.95 MB/sec), 1.2 ms avg latency, 19.0 max latency.
records sent, 10000.0 records/sec (0.95 MB/sec), 2.3 ms avg latency, 127.0 max latency.
records sent, 10002.0 records/sec (0.95 MB/sec), 1.3 ms avg latency, 24.0 max latency.
records sent, 10004.0 records/sec (0.95 MB/sec), 2.4 ms avg latency, 186.0 max latency.
records sent, 10002.0 records/sec (0.95 MB/sec), 15.1 ms avg latency, 466.0 max latency.
records sent, 10002.0 records/sec (0.95 MB/sec), 11.1 ms avg latency, 405.0 max latency.
records sent, 10000.0 records/sec (0.95 MB/sec), 1.2 ms avg latency, 19.0 max latency.
records sent, 10004.0 records/sec (0.95 MB/sec), 1.2 ms avg latency, 20.0 max latency.
records sent, 10000.0 records/sec (0.95 MB/sec), 1.2 ms avg latency, 30.0 max latency.
records sent, 10002.0 records/sec (0.95 MB/sec), 1.3 ms avg latency, 19.0 max latency.
records sent, 9998.0 records/sec (0.95 MB/sec), 1.4 ms avg latency, 49.0 max latency.
records sent, 10006.6 records/sec (0.95 MB/sec), 37.9 ms avg latency, 617.0 max latency.
records sent, 10002.0 records/sec (0.95 MB/sec), 1.5 ms avg latency, 74.0 max latency.
records sent, 10001.4 records/sec (0.95 MB/sec), 1.3 ms avg latency, 19.0 max latency.
records sent, 10000.0 records/sec (0.95 MB/sec), 1.8 ms avg latency, 132.0 max latency.
records sent, 10002.0 records/sec (0.95 MB/sec), 1.2 ms avg latency, 15.0 max latency.
records sent, 10000.0 records/sec (0.95 MB/sec), 1.9 ms avg latency, 121.0 max latency.
records sent, 9999.200064 records/sec (0.95 MB/sec), 4.96 ms avg latency, 617.00 ms max latency, ms 50th, ms 95th, ms 99th, ms .9th.

参数说明如下:

--num-records    总共生产的消息数量
--throughput 每秒需要生产的消息数量
--record-size 每条消息的大小,单位为字节

消费能力测试:

[uplooking@uplooking01 ~]$ kafka-consumer-perf-test.sh --topic flume-kafka --messages  --broker-list uplooking01:,uplooking02:,uplooking03: --threads  --zookeeper uplooking01:,uplooking02:,uplooking03:
start.time, end.time, data.consumed.in.MB, MB.sec, data.consumed.in.nMsg, nMsg.sec
-- :::, -- :::, 97.3055, 76.4380, , 801512.1760

上面的测试为需要消费一百万条消息,输出的参数说明如下:

开始时间     结束时间     消费消息总大小   每秒消费大小    消费消息总条数    每秒消费条数
start.time, end.time, data.consumed.in.MB, MB.sec, data.consumed.in.nMsg, nMsg.sec
-- :::, -- :::, 97.3055, 76.4380, , 801512.1760

Kafka笔记整理(三):消费形式验证与性能测试的更多相关文章

  1. Kafka笔记整理(一)

    Kafka简介 消息队列(Message Queue) 消息 Message 网络中的两台计算机或者两个通讯设备之间传递的数据.例如说:文本.音乐.视频等内容. 队列 Queue 一种特殊的线性表(数 ...

  2. Python学习笔记整理(三)Python中的动态类型简介

    Python中只有一个赋值模型 一.缺少类型声明语句的情况 在Python中,类型是在运行过程中自动决定的,而不是通过代码声明.这意味着没有必要事声明变量.只要记住,这个概念实质上对变量,对象和它们之 ...

  3. Struts2学习笔记整理(三)

    Struts2的输入校验 之前对请求参数的输入校验一般分为两部分:1.客户端校验,也就是我们写js代码去对客户的误操作进行过滤  2.服务端校验, 这是整个应用组织非法数据的最后防线. Struts2 ...

  4. Kafka笔记整理(二):Kafka Java API使用

    下面的测试代码使用的都是下面的topic: $ kafka-topics.sh --describe hadoop --zookeeper uplooking01:,uplooking02:,uplo ...

  5. Flex 笔记整理 三

    1. Panel, TitleWindow PopUpManager 透明 用一个类,这个类里引用一个组件, P如 Panel, TitleWindow等, 利用PopUpManager来弹出显示.  ...

  6. Deep Learning(深度学习)学习笔记整理系列之(三)

    Deep Learning(深度学习)学习笔记整理系列 zouxy09@qq.com http://blog.csdn.net/zouxy09 作者:Zouxy version 1.0 2013-04 ...

  7. Kafka学习笔记(三)——架构深入

    之前搭建好了Kafka的学习环境,了解了具体的配置文件内容,并且测试了生产者.消费者的控制台使用方式,也学习了基本的API.那么下一步,应该学习一下具体的内部流程~ 1.Kafka的工作流程 大致的工 ...

  8. struts2学习笔记(三)—— 在用户注冊程序中使用验证框架

    实现目标:       1.使用验证框架对用户注冊信息进行验证       2.验证username.password.邮箱不能为空       3.验证username.password长度     ...

  9. java笔记整理

    Java 笔记整理 包含内容     Unix Java 基础, 数据库(Oracle jdbc Hibernate pl/sql), web, JSP, Struts, Ajax Spring, E ...

随机推荐

  1. 关于如何自定义handler

    1.在web.config 中的webserver 下添加 <handlers> <add verb="*" path="*.html" ty ...

  2. [oracle] update语句卡住问题

    执行update语句的时候发现执行半天不成功 update main_order set order_source = '2', order_status = '2' 查询哪些对象被锁 select ...

  3. RAID的简单说明

    RAID,为 Redundant Arrays of Independent Disks 的简称,中文为廉价※冗余磁盘阵列 . 磁盘阵列 其实也分为软阵列 (Software Raid) 和硬阵列 ( ...

  4. 转载 HTTPS 之fiddler抓包、jmeter请求

    转载自 http://suixiang0923.github.io/2016/01/12/%E6%B5%85%E8%B0%88HTTPS%E4%BB%A5%E5%8F%8AFiddler%E6%8A% ...

  5. Spring_day02--log4j介绍_Spring整合web项目演示

    log4j介绍 1 通过log4j可以看到程序运行过程中更详细的信息 (1)经常使用log4j查看日志 2 使用 (1)导入log4j的jar包 (2)复制log4j的配置文件,复制到src下面 3 ...

  6. Leetcode: Merge/Insert Interval

    题目 Given a collection of intervals, merge all overlapping intervals. For example,Given [1,3],[2,6],[ ...

  7. 内存泄露,GC相关

    内存泄露就是对象不在GC的掌控之内 下面对象会发生内存泄露现象: 1.没有引用的对象 2.虚,软,弱 引用对象 GC引用的对象指的是 1.JavaStack中引用的对象 2.方法区中静态引用指向的对象 ...

  8. Git 安装与使用

    http://blog.csdn.net/lishuo_os_ds/article/details/8078475#sec-1.8.2 http://blog.csdn.net/showhilllee ...

  9. Layui前后台交互数据获取java

    Layui简介 Layui是一款适用于后台程序员的UI框架,学习成本低.Json数据格式交互前后台,并且也相当适用单页面开发.有兴趣的朋友可以看看layui官网. Layui前后台数据交互 layui ...

  10. 【BZOJ4262】Sum 单调栈+线段树

    [BZOJ4262]Sum Description Input 第一行一个数 t,表示询问组数. 第一行一个数 t,表示询问组数. 接下来 t 行,每行四个数 l_1, r_1, l_2, r_2. ...