Kafka系列之-自定义Producer
前面已经讲到了,在Kafka中,Message是由Producer产生的,Producer产生的Message会发送到Topic的指定Partition中。Producer可以有多种形式,也可以由用户通过Java,C以及Python语言来自定义。
Kafka中Producer的主要作用和地位如下图所示,Producer通过获取某个Topic指定Partition的Leader节点连接到Kafka集群中,
一、Java Producer API
用户可以基于Kafka提供的API自定义Producer,在这些API中有几个主要的类:
1. kafka.javaapi.producer.Producer
类定义:
class Producer[ K,V ](private val underlying: kafka.producer.Producer[K ,V])
UML图:
2. kafka.producer.ProducerConfig
类定义:
class ProducerConfig private (val props: VerifiableProperties)
extends AsyncProducerConfig with SyncProducerConfigShared
UML图:
3. kafka.producer.KeyedMessage
类定义:
case class KeyedMessage[ K, V ](val topic: String, val key: K, val partKey: Any , val message: V)
二、自定义简单的Producer
接下来根据上面的三个类,使用Java代码实现一个简单的Producer向Broker发送Message。这个Producer会为特定的Topic生成Message并发送到默认的Partition中。
具体代码和过程在代码和注释中。
1、Java代码
package ckm.kafka.producer;
import kafka.javaapi.producer.Producer;
import kafka.producer.KeyedMessage;
import kafka.producer.ProducerConfig;
import java.util.Date;
import java.util.Properties;
/**
* 一个简单的Kafka Producer类,传入两个参数:
* topic num
* 设置主题和message条数
*
* 执行过程:
* 1、创建一个topic
* kafka-topic.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 3 --topic xxxx
* 2、运行本类中的代码
* 3、查看message
* kafka-console-consumer.sh --zookeeper localhost:2181 --from-beginning --topic xxxx
* kafka
*/
public class SimpleKafkaProducer {
/**
* Producer的两个泛型,第一个指定Key的类型,第二个指定value的类型
*/
private static Producer<String, String> producer;
public SimpleKafkaProducer() {
Properties props = new Properties();
/**
* 指定producer连接的broker列表
*/
props.put("metadata.broker.list", "m000:9092, m001:9092, m002:9092");
/**
* 指定message的序列化方法,用户可以通过实现kafka.serializer.Encoder接口自定义该类
* 默认情况下message的key和value都用相同的序列化,但是可以使用"key.serializer.class"指定key的序列化
*/
props.put("serializer.class", "kafka.serializer.StringEncoder");
/**
* 这个参数用于通知broker接收到message后是否向producer发送确认信号
* 0 - 表示producer不用等待任何确认信号,会一直发送消息,
* 否则producer进入等待状态
* -1 - 表示leader状态的replica需要等待所有in-sync状态的replica都接收到消息后才会向producer发送确认信号,
* 再次之前producer一直处于等待状态
*/
props.put("request.required.acks", "1");
ProducerConfig config = new ProducerConfig(props);
producer = new Producer<String, String>(config);
}
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("Please Input Topic and Message Numbers");
}
String topic = (String) args[0];
int count = Integer.parseInt((String) args[1]);
System.out.println("Topic = " + topic);
System.out.println("Message Nums = " + count);
SimpleKafkaProducer simpleProducer = new SimpleKafkaProducer();
simpleProducer.publishMessage(topic, count);
}
/**
* 根据topic和消息条数发送消息
* @param topic
* @param count
*/
private void publishMessage(String topic, int count) {
for (int i = 0; i < count; i ++) {
String runtime = new Date().toString();
String msg = "Message published time - " + runtime;
System.out.println("msg = " + msg);
/**
* 第一个泛型指定用于分区的key的类型,第二个泛型指message的类型
* topic只能为String类型
*/
KeyedMessage<String, String> data = new KeyedMessage<String, String>(topic, msg);
producer.send(data);
}
producer.close();
}
}
2、运行
(1)启动ZooKeeper
$ZK_HOME/bin/zkServer.sh start
(2)启动Kafka集群
cd $KAFKA_HOME
nohup bin/kafka-server-start.sh config/server.properties &
(3)创建测试Topic
$KAFKA_HOME/bin/kafka-topics.sh --create --zookeeper m000:2181 --replication-factor 1 --partition 3 --topic simple-kafka-producer
(4)运行SimpleKafkaProducer 代码
运行该代码,向simple-kafka-producer Topic发送10条Message
java -cp KafkaTestProgram.jar ckm.kafka.producer.SimpleKafkaProducer simple-kafka-producer 10
(5)查看simple-kafka-producer中的Message
bin/kafka-console-consumer.sh --zookeeper m000:2181 --from-beginning --topic simple-kafka-producer
三、自定义Partition的Producer
这一节中除了实现Producer之外,还自定义了Message的Partition划分过程。
在这里,将会模拟一个网页访问日志生成的过程,每条随机生成的日志Message中包含三个部分的信息:
- 页面访问时间戳
- 页面名称
- 访问页面的IP地址
1、Java代码
(1)Producer
package ckm.kafka.producer;
import kafka.javaapi.producer.Producer;
import kafka.producer.KeyedMessage;
import kafka.producer.ProducerConfig;
import java.util.Date;
import java.util.Properties;
import java.util.Random;
/**
* 一个自定义分区的Kafka Producer类,传入两个参数:
* topic num
* 设置主题和message条数
*
* 模拟用户点击日志,日志格式为:“时间,网址,IP地址"格式
*
* 自定义分区,通过IP地址最后一位与分区数求余,message分散到0~partition - 1这些分区中
*
* 执行过程:
* 1、创建一个topic
* kafka-topic.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 3 --topic xxxx
* 2、运行本类中的代码
* 3、查看message
* kafka-console-consumer.sh --zookeeper localhost:2181 --from-beginning --topic xxxx
* kafka
*/
public class KafkaProducerWithPartition {
/**
* Producer的两个泛型,第一个指定Key的类型,第二个指定value的类型
*/
private static Producer<String, String> producer;
public KafkaProducerWithPartition() {
Properties props = new Properties();
/**
* 指定producer连接的broker列表
*/
props.put("metadata.broker.list", "m000:9092, m001:9092, m002:9092");
/**
* 指定message的序列化方法,用户可以通过实现kafka.serializer.Encoder接口自定义该类
* 默认情况下message的key和value都用相同的序列化,但是可以使用"key.serializer.class"指定key的序列化
*/
props.put("serializer.class", "kafka.serializer.StringEncoder");
/**
* 这个参数用于通知broker接收到message后是否向producer发送确认信号
* 0 - 表示producer不用等待任何确认信号,会一直发送消息
* 1 - 表示leader状态的replica在接收到message后需要向producer发送一个确认信号,否则producer进入等待状态
* -1 - 表示leader状态的replica需要等待所有in-sync状态的replica都接收到消息后才会向producer发送确认信号,再次之前producer一直处于等待状态
*/
props.put("request.required.acks", "1");
/**
* 指定partition类,自定义的分区类,继承自kafka.producer.Partitioner接口
*/
props.put("partitioner.class", "ckm.kafka.producer.SimplePartitioner");
ProducerConfig config = new ProducerConfig(props);
producer = new Producer<String, String>(config);
}
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("Please Input Topic and Message Numbers");
}
String topic = (String) args[0];
int count = Integer.parseInt((String) args[1]);
System.out.println("Topic = " + topic);
System.out.println("Message Nums = " + count);
KafkaProducerWithPartition simpleProducer = new KafkaProducerWithPartition();
simpleProducer.publishMessage(topic, count);
}
/**
* 根据topic和消息条数发送消息
* @param topic
* @param count
*/
private void publishMessage(String topic, int count) {
Random random = new Random();
for (int i = 0; i < count; i ++) {
String runtime = new Date().toString();
// 访问的IP地址
String clientIP = "192.168.1." + random.nextInt(255);
String msg = runtime + ",kafka.apache.org," + clientIP;
System.out.println("msg = " + msg);
/**
* 第一个泛型指定用于分区的key的类型,第二个泛型指message的类型
* topic只能为String类型
* 和上一个Producer相比,多了一个用于分区的key
*/
KeyedMessage<String, String> data = new KeyedMessage<String, String>(topic, clientIP, msg);
producer.send(data);
}
producer.close();
}
}
(2)Partitioner
package ckm.kafka.producer;
import kafka.producer.Partitioner;
import kafka.utils.VerifiableProperties;
/**
* Created by ckm on 2016/8/3.
*/
public class SimplePartitioner implements Partitioner {
/**
* 不写这个方法,会报错
* Exception in thread "main" java.lang.NoSuchMethodException: ckm.kafka.producer.SimplePartitioner.<init>(kafka.utils.VerifiableProperties)
* at java.lang.Class.getConstructor0(Class.java:2892)
* at java.lang.Class.getConstructor(Class.java:1723)
* at kafka.utils.Utils$.createObject(Utils.scala:436)
* at kafka.producer.Producer.<init>(Producer.scala:61)
* at kafka.javaapi.producer.Producer.<init>(Producer.scala:26)
* at ckm.kafka.producer.KafkaProducerWithPartition.<init>(KafkaProducerWithPartition.java:58)
* at ckm.kafka.producer.KafkaProducerWithPartition.main(KafkaProducerWithPartition.java:70)
* @param verifiableProperties
*/
public SimplePartitioner(VerifiableProperties verifiableProperties) {
}
public int partition(Object key, int numPartitions) {
int partition = 0;
String partitionKey = (String) key;
int offset = partitionKey.lastIndexOf('.');
if (offset > 0) {
partition = Integer.parseInt(partitionKey.substring(offset + 1)) % numPartitions;
}
return partition;
}
}
2、运行
由于前面已经启动了ZooKeeper以及Kafka,这里直接从创建Topic开始
(1)创建Topic
创建一个partition为3,replication为3的topic。
$KAFKA_HOME/bin/kafka-topics.sh --create --zookeeper m000:2181 --replication-factor 3 --partitions 3 --topic partition-kafka-producer
如何使用list命令查看该Topic,可以参考前面的示例
(2)运行Java代码
java -cp KafkaTestProgram.jar ckm.kafka.producer.KafkaProducerWithPartition partition-kafka-producer 100
往partition-kafka-producer Topic中写入100条随机生成的Message。
(3)查看这些Message
$KAFKA_HOME/bin/kafka-console-consumer.sh --zookeeper m000:2181 --from-beginning --topic partition-kafka-producer
四、自定义Producer的封装
上面两种自定义的Producer中,其实有很多代码是重复性的。接下来对Kafka自定义Producer进行一定的封装,使其使用和配置更加简便。
经过封装后,producer有关的参数都写在properties文件中。
第二步中的Producer的调用方法为:
KafkaProducerTool kafkaProducerTool = new KafkaProducerToolImpl();
kafkaProducerTool.publishMessage("test message");
两行代码就可以将该message发送到配置的Kafka集群指定的topic中。
第三步中的自定义Partitioner的Producer的调用方法为:
KafkaProducerTool kafkaProducerTool = new KafkaProducerToolImpl();
Properties producerProperties = kafkaProducerTool.getProducerProperties();
// 如果properties配置文件中没有配置该参数的话,手动设置
producerProperties.put("partitioner.class", "SimplePartitioner");
kafkaProducerTool.publishPartitionedMessage("partition-key", "test messate");
具体代码可以参考KafkaProducerTool。
欢迎提出宝贵意见。
Kafka系列之-自定义Producer的更多相关文章
- Kafka系列之-Kafka Protocol实例分析
本文基于A Guide To The Kafka Protocol文档,以及Spark Streaming中实现的org.apache.spark.streaming.kafka.KafkaClust ...
- Kafka系列之-Kafka监控工具KafkaOffsetMonitor配置及使用
KafkaOffsetMonitor是一个可以用于监控Kafka的Topic及Consumer消费状况的工具,其配置和使用特别的方便.源项目Github地址为:https://github.com/q ...
- apache kafka系列之Producer处理逻辑
最近研究producer的负载均衡策略,,,,我在librdkafka里边用代码实现了partition 值的轮询方法,,,但是在现场验证时,他的负载均衡不起作用,,,所以来找找原因: 下文是一篇描 ...
- Kafka系列2:深入理解Kafka消费者
Kafka系列2:深入理解Kafka消费者 上篇聊了Kafka概况,包含了Kafka的基本概念.设计原理,以及设计核心.本篇单独聊聊Kafka的消费者,包括如下内容: 生产者是如何生产消息 如何创建生 ...
- Kafka系列一之架构介绍和安装
Kafka架构介绍和安装 写在前面 还是那句话,当你学习一个新的东西之前,你总得知道这个东西是什么?这个东西可以用来做什么?然后你才会去学习它,使用它.简单来说,kafka既是一个消息队列,如今,它也 ...
- Kafka系列1:Kafka概况
Kafka系列1:Kafka概况 Kafka是当前分布式系统中最流行的消息中间件之一,凭借着其高吞吐量的设计,在日志收集系统和消息系统的应用场景中深得开发者喜爱.本篇就聊聊Kafka相关的一些知识点. ...
- kafka C客户端librdkafka producer源码分析
from:http://www.cnblogs.com/xhcqwl/p/3905412.html kafka C客户端librdkafka producer源码分析 简介 kafka网站上提供了C语 ...
- Kafka系列2-producer和consumer报错
1. 使用127.0.0.1启动生产和消费进程: 1)启动生产者进程: bin/kafka-console-producer.sh --broker-list 127.0.0.1:9092 --top ...
- kafka系列四、kafka架构原理、高可靠性存储分析及配置优化
一.概述 Kakfa起初是由LinkedIn公司开发的一个分布式的消息系统,后成为Apache的一部分,它使用Scala编写,以可水平扩展和高吞吐率而被广泛使用.目前越来越多的开源分布式处理系统如Cl ...
随机推荐
- SpringCloud异常(Euruka):Application run failed java.lang.NoSuchMethodError: org.springframework.boot.builder.SpringApplicationBuilder
在测试Euruka作为服务注册中心的时候碰到了这个问题,错误提示如下: "C:\Program Files\Java\jdk1.8.0_161\bin\java" -XX:Tier ...
- 【转】JAVA异常报错大全
算术异常类:ArithmeticExecption 空指针异常类:NullPointerException 类型强制转换异常:ClassCastException 数组负下标异常:NegativeAr ...
- [HEOI2016]排序
题目描述 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他.这个难题是这样子 的:给出一个1到n的全排列,现在对这个全排列序列进 ...
- [USACO13DEC]假期计划(黄金)Vacation Planning (gold)
题目翻译不好,这里给出一份 题目背景 Awson是某国际学校信竞组的一只大佬.由于他太大佬了,于是干脆放弃了考前最后的集训,开车(他可是老司机)去度假.离开学校前,他打开地图,打算做些规划. 题目描述 ...
- ●线段树题之wows
●模拟考试的一道似乎是学长出的题,还不错,挺考代码能力的.以此记录. ●题目以被上传(改了改说法),6128 Lence的方块们 ● (像手纸一样长的贴图……) ●题目大意: 有横向排布的n个点,每个 ...
- 【USACO】 洞穴奶牛
题目描述 贝西喜欢去洞穴探险.这次她去的地方由 N 个洞穴组成,编号分别是 1 到 N,1 号洞穴是出发 的起点. 洞穴之间由 M 条隧道相连,双向通行,第 i 条隧道连接 A i 和 B i .每条 ...
- hdu4729 树链剖分+二分
An Easy Problem for Elfness Time Limit: 5000/2500 MS (Java/Others) Memory Limit: 65535/65535 K (J ...
- [bzoj5016][Snoi2017]一个简单的询问
来自FallDream的博客,未经允许,请勿转载,谢谢. 给你一个长度为N的序列ai,1≤i≤N和q组询问,每组询问读入l1,r1,l2,r2,需输出 get(l,r,x)表示计算区间[l,r]中 ...
- Linux命令-关机命令详解
关机命令:1.halt 立刻关机 2.poweroff 立刻关机 3.shutdown -h now 立刻关机(root用户使用) 4.shutdown -h 10 10分钟后自动关机 如果是通过sh ...
- mysql中binlog与存储引擎的2PC
mysql内部的2PC mysql开启binlog后实际上可以认为其数据有两份,binlog中一份,引擎中一份(这里先把存储引擎中数据看成整体的单独一份,另外也可以把binlog看成是一个引擎).既然 ...