kafka 简介
先不论用什么来实现,我们先评估业务。
1、你用消息,你要明白,你这个消息,消费的时候需要不需要回复已已消费?
2、你能不能重复消费消息?
3、如果消息丢失了怎么办?
4、另外,这些消息是怎么一个消费规律?一次性会发送多少?频率是怎样?
5、服务器架构需要如何设计?是否需要集群?是否需要分布式?
kafka笔记
1/kafka是一个分布式的消息缓存系统
2/kafka集群中的服务器都叫做broker
3/kafka有两类客户端,一类叫producer(消息生产者),一类叫做consumer(消息消费者),客户端和broker服务器之间采用tcp协议连接
4/kafka中不同业务系统的消息可以通过topic进行区分,而且每一个消息topic都会被分区,以分担消息读写的负载
5/每一个分区都可以有多个副本,以防止数据的丢失
6/某一个分区中的数据如果需要更新,都必须通过该分区所有副本中的leader来更新
7/消费者可以分组,比如有两个消费者组A和B,共同消费一个topic:order_info,A和B所消费的消息不会重复
比如 order_info 中有100个消息,每个消息有一个id,编号从0-99,那么,如果A组消费0-49号,B组就消费50-99号
8/消费者在具体消费某个topic中的消息时,可以指定起始偏移量
数据采集分享过程
kafka简介 producer consumer cg(consumer group)
1)Producer :消息生产者,就是向 kafka broker 发消息的客户端。
2)Consumer :消息消费者,向 kafka broker 取消息的客户端
3)Topic :可以理解为一个队列。
4)Consumer Group (CG):这是kafka用来实现一个topic消息的广播(发给所有的consumer)
和单播(发给任意一个 consumer)的手段。一个 topic 可以有多个 CG。topic 的消息会复制-
给 consumer。如果需要实现广播,只要每个 consumer 有一个独立的 CG 就可以了。要实现
单播只要所有的 consumer 在同一个 CG。用 CG 还可以将 consumer 进行自由的分组而不需
要多次发送消息到不同的 topic。
5)Broker :一台 kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker
可以容纳多个 topic。
6)Partition:为了实现扩展性,一个非常大的 topic 可以分布到多个 broker(即服务器)上,
一个 topic 可以分为多个 partition,每个 partition 是一个有序的队列。partition 中的每条消息
都会被分配一个有序的 id(offset)。kafka 只保证按一个 partition 中的顺序将消息发给
consumer,不保证一个 topic 的整体(多个 partition 间)的顺序。
7)Offset:kafka 的存储文件都是按照 offset.kafka 来命名,用 offset 做名字的好处是方便查
找。例如你想找位于 2049 的位置,只要找到 2048.kafka 的文件即可。当然 the first offset 就
是 00000000000.kafka
消息分组及leader示意
kafka消息分组机制
message
kafka 原理
核心组成 kafka cluster broker topicA patition0 leader follower
kafka集群安装
server.properties
broker.id=1
zookeeper.connect=master:2182,master:2183,master:2184
listeners=PLAINTEXT://:9092
advertised.listeners=PLAINTEXT://master:9092
log.dirs=/home/hadoop/app/kafka_2.12-2.6.2/logs
server2.properties
broker.id=2
zookeeper.connect=master:2182,master:2183,master:2184
listeners=PLAINTEXT://:9093
advertised.listeners=PLAINTEXT://master:9093
log.dirs=/home/hadoop/app/kafka_2.12-2.6.2/logs-2
server3.properties
broker.id=3
zookeeper.connect=master:2182,master:2183,master:2184
listeners=PLAINTEXT://:9094
advertised.listeners=PLAINTEXT://master:9094
log.dirs=/home/hadoop/app/kafka_2.12-2.6.2/logs-3
1、解压
2、修改server.properties
broker.id=1
zookeeper.connect=weekend05:2181,weekend06:2181,weekend07:2181
3、将zookeeper集群启动
4、在每一台节点上启动broker
bin/kafka-server-start.sh config/server.properties
./kafka-server-start.sh …/config/server.properties 1>/dev/null 2>&1 &
5、在kafka集群中创建一个topic
bin/kafka-topics.sh --create --zookeeper weekend05:2181 --replication-factor 3 --partitions 1 --topic order
6、用一个producer向某一个topic中写入消息
bin/kafka-console-producer.sh --broker-list weekend:9092 --topic order
7、用一个comsumer从某一个topic中读取信息
bin/kafka-console-consumer.sh --zookeeper weekend05:2181 --from-beginning --topic order
8、查看一个topic的分区及副本状态信息
bin/kafka-topics.sh --describe --zookeeper weekend05:2181 --topic order
jps
./kafka-server-start.sh ../config/server.properties 1>/dev/null 2>&1 &
./kafka-server-start.sh ../config/server-1.properties 1>/dev/null 2>&1 &
./kafka-server-start.sh ../config/server-2.properties 1>/dev/null 2>&1 &
kafka shell
./kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 1 --topic my-replicated-topic
./kafka-topics.sh --describe --zookeeper localhost:2181 --topic my-replicated-topic
//所有分区的摘要
Topic:my-replicated-topic PartitionCount:1 ReplicationFactor:3 Configs:
//提供一个分区信息,因为我们只有一个分区,所以只有一行。
Topic: my-replicated-topic Partition: 0 Leader: 1 Replicas: 1,2,0 Isr: 1,2,0
./kafka-topics.sh --describe --zookeeper localhost:2181 --topic test
Topic:test PartitionCount:1 ReplicationFactor:1 Configs:
Topic: test Partition: 0 Leader: 0 Replicas: 0 Isr: 0
bin/zookeeper-server-start.sh -daemon config/zookeeper.properties
bin/kafka-server-start.sh -daemon config/server.properties
./kafka-console-producer.sh --broker-list master:9092 --topic myorder
./kafka-console-consumer.sh --bootstrap-server master:9093 --from-beginning --topic myorder
java api 生产者 消费者
java api 生产者 SynProducer 同步发送消息
Future metadataFuture = producer.send(record);
RecordMetadata recordMetadata = metadataFuture.get();
System.out.println(“offset:”+recordMetadata.offset());
一条一条发送,慢
package com.example.demo.test;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
//1 同步发送消息
public class SynProducer {
private static Properties getProps(){
Properties props = new Properties();
props.put("bootstrap.servers", "master:9092");
props.put("acks", "all"); // 发送所有ISR
props.put("retries", 2); // 重试次数
props.put("batch.size", 16384); // 批量发送大小
props.put("buffer.memory", 33554432); // 缓存大小,根据本机内存大小配置
props.put("linger.ms", 1000); // 发送频率,满足任务一个条件发送
props.put("client.id", "producer-syn-1"); // 发送端id,便于统计
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
return props;
}
public static void main(String[] args) {
KafkaProducer<String, String> producer = new KafkaProducer<>(getProps());
for(int i=0; i< 10; i++){
// 三个参数,topic,key:用户分配partition,value:发送的值
ProducerRecord<String, String> record = new ProducerRecord<>("test-syn", "topic_"+i,"test-syn-"+i);
Future<RecordMetadata> metadataFuture = producer.send(record);
RecordMetadata recordMetadata = null;
try {
recordMetadata = metadataFuture.get();
System.out.println("发送成功!");
System.out.println("topic:"+recordMetadata.topic());
System.out.println("partition:"+recordMetadata.partition());
System.out.println("offset:"+recordMetadata.offset());
} catch (InterruptedException|ExecutionException e) {
System.out.println("发送失败!");
e.printStackTrace();
}
}
producer.flush();
producer.close();
}
}
java api 生产者 ASynProducer 异步发送消息
lambada表达式 (a,b)->a+b
// 相比同步发送,异步发送需要传入callback,发送结果回来回调callback方法
producer.send(record, (recordMetadata, e) -> {
if(e != null){
System.out.println(“发送失败!”);
e.printStackTrace();
}else {
System.out.println(“发送成功!”);
System.out.println(“offset:”+recordMetadata.offset());
}
});
package com.example.demo.test;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import java.util.Properties;
//2 异步发送消息
public class ASynProducer {
private static Properties getProps(){
Properties props = new Properties();
props.put("bootstrap.servers", "master:9092");
props.put("acks", "all"); // 发送所有ISR
props.put("retries", 2); // 重试次数
props.put("batch.size", 16384); // 批量发送大小
props.put("buffer.memory", 33554432); // 缓存大小,根据本机内存大小配置
props.put("linger.ms", 1000); // 发送频率,满足任务一个条件发送
props.put("client.id", "producer-asyn-1"); // 发送端id,便于统计
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
return props;
}
public static void main(String[] args) {
KafkaProducer<String, String> producer = new KafkaProducer<>(getProps());
for(int i=0; i< 10; i++){
ProducerRecord<String, String> record = new ProducerRecord<>("test-asyn", "topic_"+i,"test-asyn-"+i);
// 相比同步发送,异步发送需要传入callback,发送结果回来回调callback方法
producer.send(record, (recordMetadata, e) -> {
if(e != null){
System.out.println("发送失败!");
e.printStackTrace();
}else {
System.out.println("发送成功!");
System.out.println("topic:"+recordMetadata.topic());
System.out.println("partition:"+recordMetadata.partition());
System.out.println("offset:"+recordMetadata.offset());
}
});
}
producer.flush();
producer.close();
}
}
java api 生产者 FireProducer 及时发送消息
// 不关心发送结果
producer.send(record);
java api 消费者 CommitConsumer同步提交
props.put(“enable.auto.commit”, “false”); // 开启自动提交
// 当前批次offset
consumer.commitSync();
package com.example.demo.consumer;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
//五、同步提交
//同步提交,提交后broke会阻塞等结果返回,在成功提交或碰到无怯恢复的错误之前,commitSync()会一直重试
public class CommitConsumer {
private static Properties getProps(){
Properties props = new Properties();
props.put("bootstrap.servers", "master:9092");
props.put("group.id", "test_3");
props.put("session.timeout.ms", 30000); // 如果其超时,将会可能触发rebalance并认为已经死去,重新选举Leader
props.put("enable.auto.commit", "false"); // 开启自动提交
props.put("auto.commit.interval.ms", "1000"); // 自动提交时间
props.put("auto.offset.reset","earliest"); // 从最早的offset开始拉取,latest:从最近的offset开始消费
props.put("client.id", "consumer-2"); // 发送端id,便于统计
props.put("max.poll.records","1000"); // 每次批量拉取条数
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
return props;
}
public static void main(String[] args) {
try (KafkaConsumer<String, String> consumer = new KafkaConsumer<>(getProps())) {
List<String> topics = new ArrayList<>();
topics.add("producer-syn");
consumer.subscribe(topics);
for(;;){
// 拉取任务超时时间
ConsumerRecords<String,String> records = consumer.poll(1000);
for(ConsumerRecord consumerRecord : records){
System.out.println("partition:"+consumerRecord.partition());
System.out.println("offset:"+consumerRecord.offset());
System.out.println("key:"+consumerRecord.key());
System.out.println("value:"+consumerRecord.value());
}
// 当前批次offset
consumer.commitSync();
}
}
}
// 异步提交
// commitAsync()方法提交最后一个偏移量。
// 在成功提交或碰到无怯恢复的错误之前,commitSync()会一直重试,
// 但是commitAsync()不会,这也是commitAsync()不好的一个地方。
// 它之所以不进行重试,是因为在它收到服务器响应的时候,
// 可能有一个更大的偏移量已经提交成功。假设我们发出一个请求用于提交偏移量2000,
// 这个时候发生了短暂的通信问题,服务器收不到请求,自然也不会作出任何响应。
// 与此同时,我们处理了另外一批消息,并成功提交了偏移量3000。
// 如果commitAsync()重新尝试提交偏移量2000 ,它有可能在偏移量3000之后提交成功。
// 系统会记录最后提交的偏移量,这个时候如果发生再均衡,就会出现重复消息,会从2000开始。
public static void main2(String[] args) {
try (KafkaConsumer<String, String> consumer = new KafkaConsumer<>(getProps())) {
List<String> topics = new ArrayList<>();
topics.add("producer-syn");
consumer.subscribe(topics);
for(;;){
// 拉取任务超时时间
ConsumerRecords<String,String> records = consumer.poll(1000);
for(ConsumerRecord consumerRecord : records){
System.out.println("partition:"+consumerRecord.partition());
System.out.println("offset:"+consumerRecord.offset());
System.out.println("key:"+consumerRecord.key());
System.out.println("value:"+consumerRecord.value());
}
// 当前批次offset
consumer.commitAsync();
}
}
}
}
java api 消费者 AutoCommitConsumer 自动提交offset
props.put(“enable.auto.commit”, “true”); // 开启自动提交
props.put(“auto.commit.interval.ms”, “1000”); // 自动提交时间
package com.example.demo.consumer;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
//4 自动提交offset
public class AutoCommitConsumer {
private static Properties getProps(){
Properties props = new Properties();
props.put("bootstrap.servers", "master:9092");
props.put("group.id", "test_3");
props.put("session.timeout.ms", 30000); // 如果其超时,将会可能触发rebalance并认为已经死去,重新选举Leader
props.put("enable.auto.commit", "true"); // 开启自动提交
props.put("auto.commit.interval.ms", "1000"); // 自动提交时间
props.put("auto.offset.reset","earliest"); // 从最早的offset开始拉取,latest:从最近的offset开始消费
props.put("client.id", "producer-syn-1"); // 发送端id,便于统计
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
return props;
}
public static void main(String[] args) {
try (KafkaConsumer<String, String> consumer = new KafkaConsumer<>(getProps())) {
List<String> topics = new ArrayList<>();
topics.add("producer-syn");
consumer.subscribe(topics);
// 拉取任务超时时间
for(;;){
ConsumerRecords<String,String> records = consumer.poll(1000);
for(ConsumerRecord consumerRecord : records){
System.out.println("partition:"+consumerRecord.partition());
System.out.println("offset:"+consumerRecord.offset());
System.out.println("key:"+consumerRecord.key());
System.out.println("value:"+consumerRecord.value());
}
}
}
}
}
java api 消费者 CommitAsynCallbackConsumer 同步和异步组合提交
props.put(“enable.auto.commit”, “false”); // 开启自动提交
ConsumerRecords<String,String> records = consumer.poll(1000);
// 当前批次offset
consumer.commitAsync((map, e) -> {
if(e != null){`在这里插入代码片`
System.out.println("提交失败:"+map.get(""));
}
});
java api 消费者 提交到特定的partition、偏移量
// 为提高消费吞吐量,可使用线程处理,消费者只负责接收消息,由线程池处理。
ConsumerRecords<String,String> records = consumer.poll(1000);
Map<TopicPartition, OffsetAndMetadata> offsets = new HashMap<>();
// 指定topic 、partition
TopicPartition topicPartition = new TopicPartition("producer-syn",0);
// 指定offset
OffsetAndMetadata offsetAndMetadata = new OffsetAndMetadata(100);
// 可以提交多个topic
offsets.put(topicPartition, offsetAndMetadata);
// 提交offset
consumer.commitSync(offsets);
java api 消费者 从特定偏移量处开始消费
//前面都是consumer.poll()之后读取该批次的消息,kafka还提供了从分区的开始或者末尾读消息的功能:
TopicPartition topicPartition = new TopicPartition("producer-syn",0);
// 只有先pull一次,seek才会生效,启动后第一次拉取不返回数据
consumer.seek(topicPartition,100L);
// new ArrayList<>(topicPartition)
List list = new ArrayList<>();
list.add(topicPartition);
// consumer.seekToEnd(list);
// consumer.seekToBeginning(list);
consumer.commitSync();
java api 消费者 监听rebalance提交
//前面我们说过当发生consumer退出或者新增,
// partition新增的时候会触发再均衡。
// 那么发生再均衡的时候如果某个consumer正在消费的任务没有消费完该如何提交当前消费到的offset呢?
// kafka提供了再均衡监听器,在发生再均衡之前监听到,
// 当前consumer可以在失去分区所有权之前处理offset关闭句柄等。
// 指定offset
Map<TopicPartition, OffsetAndMetadata> currentOffset = new HashMap<>();
//consumer.subscribe(topics);
consumer.subscribe(topics, new ConsumerRebalanceListener() {
@Override
public void onPartitionsRevoked(Collection<TopicPartition> collection) {
System.out.println("发生rebalance!提交:"+currentOffset);
consumer.commitAsync(currentOffset,null);
}
@Override
public void onPartitionsAssigned(Collection<TopicPartition> collection) {
}
});
// 拉取任务超时时间
ConsumerRecords<String,String> records = consumer.poll(1000);
TopicPartition topicPartition = new TopicPartition("test-syn",0);
OffsetAndMetadata offsetAndMetadata = new OffsetAndMetadata(offset);
currentOffset.put(topicPartition, offsetAndMetadata);
consumer.commitSync();
java api 消费者 PartitionConsumer 主动分配分区消费
到目前为止我们讨论的都是消费者群组,分区被自动分配给群组的消费者,群组的消费者有变动会触发再均衡。
那么是不是可以回归到别的消息队列的方式:不需要群组消费者也可以自己订阅主题?
kafka也提供了这样的案例,因为kafka的主题有分区的概念,
那么如果没有群组就意味着你的自己订阅到特定的一个分区才能消费内容。
如果是这样的话,就不需要订阅主题,而是为自己分配分区。
一个消费者可以订阅主题(并加入消费者群组),或者为自己分配分区,但不能同时做这两件事情。
注:group.id配置不能与其他consumer重复,否则会报错,直接去掉也行。
public static void main(String[] args) {
try (KafkaConsumer<String, String> consumer = new KafkaConsumer<>(getProps())) {
List<PartitionInfo> partitionInfoList = consumer.partitionsFor("test-syn");
List<TopicPartition> topicPartitionList = new ArrayList<>();
if(partitionInfoList != null){
for(PartitionInfo partitionInfo : partitionInfoList){
topicPartitionList.add(new TopicPartition(partitionInfo.topic(),partitionInfo.partition()));
consumer.assign(topicPartitionList);
}
}
Map map = consumer.metrics();
System.out.println(map);
for(;;){
// 拉取任务超时时间
ConsumerRecords<String,String> records = consumer.poll(1000);
for(ConsumerRecord consumerRecord : records){
System.out.println("partition:"+consumerRecord.partition());
System.out.println("offset:"+consumerRecord.offset());
System.out.println("key:"+consumerRecord.key());
System.out.println("value:"+consumerRecord.value());
}
consumer.commitSync();
}
}
}
kafka -storm 整合机制
storm-kafka\storm-kafka-client-1.2.3.jar
StormKafkaMainTest.class
MyboltO->BaseRichBolt
prepare()
execute(Tuple input){
//这里把消息大一出来,在对应的woker下面的日志可以找到打印的内容
String out = input.getString(0);
System.out.println(out);
}
declareOutputFields()
main(){
TopologyBuilder builder
ByTopicRecordTranslator brt
brt.forTopic(“test7”, ® -> new Values(r.value(),r.topic()), new Fields(“values”,“test7”));
KafkaSpoutConfig ksc
builder.setSpout(“kafkaspout”, new KafkaSpout<>(ksc), 2);
builder.setBolt(“mybolt1”, new MyboltO(), 4).shuffleGrouping(“kafkaspout”);
Config config
//LocalCluster cu = new LocalCluster();
//cu.submitTopology(“test”, config, builder.createTopology());
StormSubmitter.submitTopology(“storm-kafka-clients”, config, builder.createTopology());
}
本地执行结果 LocalCluster.submitTopology()
9614 [Thread-21-kafkaspout-executor[1 1]] INFO o.a.s.k.s.KafkaSpout - Partitions revoked. [consumer-group=skc-test, consumer=org.apache.kafka.clients.consumer.KafkaConsumer@32091b56, topic-partitions=[]]
9614 [Thread-21-kafkaspout-executor[1 1]] INFO
o.a.s.k.s.KafkaSpout - Partitions reassignment. [task-ID=1, consumer-group=skc-test, consumer=org.apache.kafka.clients.consumer.KafkaConsumer@32091b56, topic-partitions=[test7-0]]
package cn.itcast.storm.topology;
import java.util.Map;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.storm.Config;
import org.apache.storm.LocalCluster;
import org.apache.storm.StormSubmitter;
import org.apache.storm.kafka.spout.ByTopicRecordTranslator;
import org.apache.storm.kafka.spout.KafkaSpout;
import org.apache.storm.kafka.spout.KafkaSpoutConfig;
import org.apache.storm.kafka.spout.KafkaSpoutConfig.FirstPollOffsetStrategy;
import org.apache.storm.task.OutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.TopologyBuilder;
import org.apache.storm.topology.base.BaseRichBolt;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Tuple;
import org.apache.storm.tuple.Values;
//参考如下
//https://community.hortonworks.com/articles/87597/how-to-write-topology-with-the-new-kafka-spout-cli.html
//https://github.com/apache/storm/blob/master/examples/storm-kafka-client-examples/src/main/java/org/apache/storm/kafka/spout/KafkaSpoutTopologyMainNamedTopics.java#L52
public class StormKafkaMainTest {
public static void main(String[] args) {
TopologyBuilder builder = new TopologyBuilder();
//该类将传入的kafka记录转换为storm的tuple
ByTopicRecordTranslator<String,String> brt =
new ByTopicRecordTranslator<>( (r) -> new Values(r.value(),r.topic()),new Fields("values","test7"));
//设置要消费的topic即test7
brt.forTopic("test7", (r) -> new Values(r.value(),r.topic()), new Fields("values","test7"));
//类似之前的SpoutConfig
KafkaSpoutConfig<String,String> ksc = KafkaSpoutConfig
//bootstrapServers 以及topic(test7)
.builder("master:9092,master:9093,master:9094", "test7")
//设置group.id
.setProp(ConsumerConfig.GROUP_ID_CONFIG, "skc-test")
//设置开始消费的气势位置
.setFirstPollOffsetStrategy(FirstPollOffsetStrategy.LATEST)
//设置提交消费边界的时长间隔
.setOffsetCommitPeriodMs(10_000)
//Translator
.setRecordTranslator(brt)
.build();
builder.setSpout("kafkaspout", new KafkaSpout<>(ksc), 2);
builder.setBolt("mybolt1", new MyboltO(), 4).shuffleGrouping("kafkaspout");
//builder.setSpout("kafkaspout", new KafkaSpout<>(ksc),2);
//builder.setBolt("word-spilter", new WordSpliter(), 4).shuffleGrouping("test7");
//builder.setBolt("writer", new WriterBolt(), 4).fieldsGrouping("word-spilter", new Fields("word"));
Config config = new Config();
config.setNumWorkers(2);
config.setNumAckers(0);
try {
//LocalCluster cu = new LocalCluster();
//cu.submitTopology("test", config, builder.createTopology());
StormSubmitter.submitTopology("storm-kafka-clients", config, builder.createTopology());
} catch (Exception e) {
e.printStackTrace();
}
}
}
class MyboltO extends BaseRichBolt{
private static final long serialVersionUID = 1L;
OutputCollector collector = null;
public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
this.collector = collector;
}
public void execute(Tuple input) {
//这里把消息大一出来,在对应的woker下面的日志可以找到打印的内容
String out = input.getString(0);
System.out.println(out);
//collector.ack(input);
}
public void declareOutputFields(OutputFieldsDeclarer declarer) {
}
}