很久没写过文章了,最近也没做太多的技术分析,所以没什么干货,这两天学习了解了下kafka,因为自己也不是很熟悉,所以当做学习笔记吧,大家也纯当我写的笔记来看,不要吹毛求疵的挑毛病了,等我再多研究研究给出一些技术性的文章来。
流式计算是目前大数据领域很火的一种术语,stream的技术有很多,很早的storm,然后是spark stream 、flink,以及我将要学习了解的kafka stream。
其实我对流式计算了解不深,得到的资料也是百度到的,我知道的是它是实时数据的一种计算方式。实时数据与历史数据不一样的是,历史数据我们可以通过将历史保存下来的数据进行加工处理得到我们想要的,而实时数据就不能这与处理了,除非你不断的去做这个操作,那显然这样对于系统的压力会很大。
别的流技术我不太懂,有时间再看并做出对比,我就弄了下kafka stream的,我的初步理解是,kafka stream的计算过程是这样的,从kafka订阅数据,然后进行计算,最后将计算结果发送到其他储存的地方,比如hbase或者写回到kafka。
网上的例子都是统计发送的语句的单个单词出现的次数,我这里也给一个这样的例子:
public class SendNum {
public static void main(String[] args) {
Properties props = new Properties();
props.put("bootstrap.servers", "192.168.64.103:9092");
props.put("acks", "all");
props.put("retries", 0);
props.put("batch.size", 16384);
props.put("linger.ms", 1);
props.put("buffer.memory", 33554432);
props.put("key.serializer", "org.apache.kafka.common.serialization.IntegerSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
Producer producer = new KafkaProducer<>(props);
Scanner scanner = new Scanner(System.in);
int times = 0;
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (line == null || line.equals("Bye!")) {
break;
}
times += 1;
producer.send(new ProducerRecord<>("SendWords",times,line));
}
producer.close();
}
}
public class ShowNumber {
public static void main(String[] args) {
Properties props = new Properties();
props.put("bootstrap.servers", "192.168.64.103:9092");
props.put("group.id", "test");
props.put("enable.auto.commit", "true");
props.put("auto.commit.interval.ms", "1000");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.LongDeserializer");
final KafkaConsumer consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("CountSendWordsNum"),new ConsumerRebalanceListener() {
public void onPartitionsRevoked(Collection collection) {
}
public void onPartitionsAssigned(Collection collection) {
//将偏移设置到最开始
consumer.seekToBeginning(collection);
}
});
while (true) {
ConsumerRecords records = consumer.poll(100);
for (ConsumerRecord record : records) {
System.out.println(""+record.offset() +" "+ record.key()+" " + record.value() +"\n");
}
}
}
}
public class StreamMain {
public static void main(String[] args) {
Properties config = new Properties();
config.put(StreamsConfig.APPLICATION_ID_CONFIG, "wordcount-application2");
config.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.64.103:9092");
config.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
config.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());
StreamsBuilder builder = new StreamsBuilder();
KStream textLines = builder.stream("SendWords");
KTable wordCounts = textLines
.flatMapValues(textLine -> {
return Arrays.asList(textLine.toLowerCase().split("\\W+"));
})
.groupBy((key, word) -> {
try {
System.out.println("key is =>"+Serdes.Integer().deserializer().deserialize("SendWords",key.getBytes()));
}catch (Exception e) {
}
System.out.println("value is =>" +word);
return word;
})
.count(Materialized.>as("counts-store"));
wordCounts.toStream().to("CountSendWordsNum", Produced.with(Serdes.String(), Serdes.Long()));
KafkaStreams streams = new KafkaStreams(builder.build(), config);
streams.start();
}
}
以上三个类,sendNum就是给kafka发送语句的,showNumber则是显示每个单词数量,而StreamMain则是作为流计算的部分,计算每个单词的数量,kafka stream使用了函数式语言的一些术语和操作,用到了函数式monad操作(比如flatMapValues,groupBy等),我也不解释,涉及函数式编程的思想,大家有兴趣可以去找相关的文章,或者学习下scala这种函数式语言,kafka其本身是scala实现了。
这个字数统计是很简单的,这里也是将实时统计的数据写回到kafka中。
注意,应该在使用之前先创建好这两个topic:SendWords、CountSendWordsNum。
有点晚了,本想再多写一个例子,下期再补充吧。