Elasticsearch 与 Kafka 整合剖析
1.概述
目前,随着大数据的浪潮,Kafka 被越来越多的企业所认可,如今的Kafka已发展到0.10.x,其优秀的特性也带给我们解决实际业务的方案。对于数据分流来说,既可以分流到离线存储平台(HDFS),离线计算平台(Hive仓库),也可以分流实时流水计算(Storm,Spark)等,同样也可以分流到海量数据查询(HBase),或是及时查询(ElasticSearch)。而今天笔者给大家分享的就是Kafka 分流数据到 ElasticSearch。
2.内容
我们知道,ElasticSearch是有其自己的套件的,简称ELK,即ElasticSearch,Logstash以及Kibana。ElasticSearch负责存储,Logstash负责收集数据来源,Kibana负责可视化数据,分工明确。想要分流Kafka中的消息数据,可以使用Logstash的插件直接消费,但是需要我们编写复杂的过滤条件,和特殊的映射处理,比如系统保留的`_uid`字段等需要我们额外的转化。今天我们使用另外一种方式来处理数据,使用Kafka的消费API和ES的存储API来处理分流数据。通过编写Kafka消费者,消费对应的业务数据,将消费的数据通过ES存储API,通过创建对应的索引的,存储到ES中。其流程如下图所示:

上图可知,消费收集的数据,通过ES提供的存储接口进行存储。存储的数据,这里我们可以规划,做定时调度。最后,我们可以通过Kibana来可视化ES中的数据,对外提供业务调用接口,进行数据共享。
3.实现
下面,我们开始进行实现细节处理,这里给大家提供实现的核心代码部分,实现代码如下所示:
3.1 定义ES格式
我们以插件的形式进行消费,从Kafka到ES的数据流向,只需要定义插件格式,如下所示:
{
"job": {
"content": {
"reader": {
"name": "kafka",
"parameter": {
"topic": "kafka_es_client_error",
"groupid": "es2",
"bootstrapServers": "k1:9094,k2:9094,k3:9094"
},
"threads": 6
},
"writer": {
"name": "es",
"parameter": {
"host": [
"es1:9300,es2:9300,es3:9300"
],
"index": "client_error_%s",
"type": "client_error"
}
}
}
}
}
这里处理消费存储的方式,将读和写的源分开,配置各自属性即可。
3.2 数据存储
这里,我们通过每天建立索引进行存储,便于业务查询,实现细节如下所示:
public class EsProducer {
private final static Logger LOG = LoggerFactory.getLogger(EsProducer.class);
private final KafkaConsumer<String, String> consumer;
private ExecutorService executorService;
private Configuration conf = null;
private static int counter = 0;
public EsProducer() {
String root = System.getProperty("user.dir") + "/conf/";
String path = SystemConfigUtils.getProperty("kafka.x.plugins.exec.path");
conf = Configuration.from(new File(root + path));
Properties props = new Properties();
props.put("bootstrap.servers", conf.getString("job.content.reader.parameter.bootstrapServers"));
props.put("group.id", conf.getString("job.content.reader.parameter.groupid"));
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.StringDeserializer");
consumer = new KafkaConsumer<String, String>(props);
consumer.subscribe(Arrays.asList(conf.getString("job.content.reader.parameter.topic")));
}
public void execute() {
executorService = Executors.newFixedThreadPool(conf.getInt("job.content.reader.threads"));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(100);
if (null != records) {
executorService.submit(new KafkaConsumerThread(records, consumer));
}
}
}
public void shutdown() {
try {
if (consumer != null) {
consumer.close();
}
if (executorService != null) {
executorService.shutdown();
}
if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
LOG.error("Shutdown kafka consumer thread timeout.");
}
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
}
class KafkaConsumerThread implements Runnable {
private ConsumerRecords<String, String> records;
public KafkaConsumerThread(ConsumerRecords<String, String> records, KafkaConsumer<String, String> consumer) {
this.records = records;
}
@Override
public void run() {
String index = conf.getString("job.content.writer.parameter.index");
String type = conf.getString("job.content.writer.parameter.type");
for (TopicPartition partition : records.partitions()) {
List<ConsumerRecord<String, String>> partitionRecords = records.records(partition);
for (ConsumerRecord<String, String> record : partitionRecords) {
JSONObject json = JSON.parseObject(record.value());
List<Map<String, Object>> list = new ArrayList<>();
Map<String, Object> map = new HashMap<>();
index = String.format(index, CalendarUtils.timeSpan2EsDay(json.getLongValue("_tm") * 1000L));
if (counter < 10) {
LOG.info("Index : " + index);
counter++;
}
for (String key : json.keySet()) {
if ("_uid".equals(key)) {
map.put("uid", json.get(key));
} else {
map.put(key, json.get(key));
}
list.add(map);
}
EsUtils.write2Es(index, type, list);
}
}
}
}
}
这里消费的数据源就处理好了,接下来,开始ES的存储,实现代码如下所示:
public class EsUtils {
private static TransportClient client = null;
static {
if (client == null) {
client = new PreBuiltTransportClient(Settings.EMPTY);
}
String root = System.getProperty("user.dir") + "/conf/";
String path = SystemConfigUtils.getProperty("kafka.x.plugins.exec.path");
Configuration conf = Configuration.from(new File(root + path));
List<Object> hosts = conf.getList("job.content.writer.parameter.host");
for (Object object : hosts) {
try {
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(object.toString().split(":")[0]), Integer.parseInt(object.toString().split(":")[1])));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void write2Es(String index, String type, List<Map<String, Object>> dataSets) {
BulkRequestBuilder bulkRequest = client.prepareBulk();
for (Map<String, Object> dataSet : dataSets) {
bulkRequest.add(client.prepareIndex(index, type).setSource(dataSet));
}
bulkRequest.execute().actionGet();
// if (client != null) {
// client.close();
// }
}
public static void close() {
if (client != null) {
client.close();
}
}
}
这里,我们利用BulkRequestBuilder进行批量写入,减少频繁写入率。
4.调度
存储在ES中的数据,如果不需要长期存储,比如:我们只需要存储及时查询数据一个月,对于一个月以前的数据需要清除掉。这里,我们可以编写脚本直接使用Crontab来进行简单调用即可,脚本如下所示:
#!/bin/sh
# <Usage>: ./delete_es_by_day.sh kafka_error_client logsdate 30 </Usage>
echo "<Usage>: ./delete_es_by_day.sh kafka_error_client logsdate 30 </Usage>"
index_name=$
daycolumn=$
savedays=$
format_day=$ if [ ! -n "$savedays" ]; then
echo "Oops. The args is not right,please input again...."
exit
fi if [ ! -n "$format_day" ]; then
format_day='%Y%m%d'
fi sevendayago=`date -d "-${savedays} day " +${format_day}` curl -XDELETE "es1:9200/${index_name}/_query?pretty" -d "
{
"query": {
"filtered": {
"filter": {
"bool": {
"must": {
"range": {
"${daycolumn}": {
"from": null,
"to": ${sevendayago},
"include_lower": true,
"include_upper": true
}
}
}
}
}
}
}
}" echo "Finished."
然后,在Crontab中进行定时调度即可。
5.总结
这里,我们在进行数据写入ES的时候,需要注意,有些字段是ES保留字段,比如`_uid`,这里我们需要转化,不然写到ES的时候,会引发冲突导致异常,最终写入失败。
6.结束语
这篇博客就和大家分享到这里,如果大家在研究学习的过程当中有什么问题,可以加群进行讨论或发送邮件给我,我会尽我所能为您解答,与君共勉
Elasticsearch 与 Kafka 整合剖析的更多相关文章
- flume与kafka整合
flume与kafka整合 前提: flume安装和测试通过,可参考:http://www.cnblogs.com/rwxwsblog/p/5800300.html kafka安装和测试通过,可参考: ...
- 5 kafka整合storm
本博文的主要内容有 .kafka整合storm .storm-kafka工程 .storm + kafka的具体应用场景有哪些? 要想kafka整合storm,则必须要把这个storm-kafk ...
- 【转】Spark Streaming和Kafka整合开发指南
基于Receivers的方法 这个方法使用了Receivers来接收数据.Receivers的实现使用到Kafka高层次的消费者API.对于所有的Receivers,接收到的数据将会保存在Spark ...
- SparkStreaming+Kafka整合
SparkStreaming+Kafka整合 1.需求 使用SparkStreaming,并且结合Kafka,获取实时道路交通拥堵情况信息. 2.目的 对监控点平均车速进行监控,可以实时获取交通拥堵情 ...
- Spring Kafka整合Spring Boot创建生产者客户端案例
每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 创建一个kafka-producer-master的maven工程.整个项目结构如下: ...
- 【SpringBoot】搜索框架ElasticSearch介绍和整合SpringBoot
========================12章 搜索框架ElasticSearch介绍和整合SpringBoot ============================= 加入小D课堂技术交 ...
- 当Elasticsearch遇见Kafka
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由michelmu发表于云+社区专栏 Elasticsearch作为当前主流的全文检索引擎,除了强大的全文检索能力和高扩展性之外,对多种 ...
- ambari下的flume和kafka整合
1.配置flume #扫描指定文件配置 agent.sources = s1 agent.channels = c1 agent.sinks = k1 agent.sources.s1.type=ex ...
- Flume+Kafka整合
脚本生产数据---->flume采集数据----->kafka消费数据------->storm集群处理数据 日志文件使用log4j生成,滚动生成! 当前正在写入的文件在满足一定的数 ...
随机推荐
- MAC Mysql 重置密码
使用mac电脑,当mysql登录密码忘记时,需要重置密码.步骤如下: 1. 关闭当前正在运行的mysql进程. A.进入"偏好设置",选择mysql, 再选"stop m ...
- std::thread使用
本文将从以下三个部分介绍C++11标准中的thread类,本文主要内容为: 启动新线程 等待线程与分离线程 线程唯一标识符 1.启动线程 线程再std::threada对象创建时启动.最简单的情况下, ...
- phpcms 制作简单企业站的常用标签
标题 title 关键字 keywords 描述 description 来源 copyfrom 允许访问 allow_visitor==1 thumb 缩略图 {template "con ...
- java实现对服务器的自动巡检邮件通知
1.需求 之前一直是手动的巡检,然后贴图,最近服务器数量大增,有点忙不过来了.因为一直用的java,对shell脚本不是特别了解,所以这次用java写了个小项目,实现对多服务器,多任务的巡检,巡检结果 ...
- Java常用API解析——序列化API
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6797659.html 工作中发现,自己对Java的了解还很片面,没有深入的研究,有很多的J ...
- 亲测可用!!!golang如何在idea中保存时自动进行代码格式化
亲测可用,golang在idea中的代码自动格式化 1.ctrl+alt+s打开设置界面,选择[Plugins] -> [Install JetBrains plugin...] -> 搜 ...
- redis持久化快速回忆手册
Redis提供的持久化机制: 1). RDB持久化:该机制是指在指定的时间间隔内将内存中的数据集快照写入磁盘. 2). AOF持久化:该机制将以日志的形式记录服务器所处理的每一个写操作,在Redis服 ...
- Linux-vim学习入门
1.前言 vi/vim是linux中很重要的文本编辑器.我第一次使用这个编辑器时,很不习惯,甚至都不知道如何移动光标和插入字符.慢慢地经过学习,才知道如何使用vi/vim. vi/vi ...
- springmvc缓存和mybatis缓存
首先要有一个搭建好的ssm框架,笔者使用的是基于maven搭建的ssm框架. 加入springmvc缓存: 导入相关依赖包: <dependency> <groupId>org ...
- webpack的多文件打包问题
1.第三方库如vue,vue-router可以利用webpack中的entry指定vendor:['vue','vue-router']来打包在一个文件中 2.将这些文件单独提取出来,在页面中使用&l ...