Flink实时处理并将结果写入ElasticSearch实战
参考原博客: https://blog.csdn.net/weixin_44516305/article/details/90258883
1 需求分析
使用Flink对实时数据流进行实时处理,并将处理后的结果保存到Elasticsearch中,在Elasticsearch中使用IK Analyzer中文分词器对指定字段进行分词。
为了模拟获取流式数据,自定义一个流式并行数据源,每隔10ms生成一个Customer类型的数据对象并返回给Flink进行处理。
Flink处理后的结果保存在Elasticsearch中的index_customer索引的type_customer类型中,并且对description字段的数据使用IK Analyzer中文分词器进行分词。
2 Flink实时处理
2.1 版本说明
- Flink:1.8.0
- Elasticsearch:6.5.4
- JDK:1.8
使用IDEA创建一个名称为FlinkElasticsearchDemo的Maven工程,目录结构如下图所示:
2.3 程序代码
- 在pom.xml中引入flink以及flink连接elasticsearch相关的依赖,代码如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.flink</groupId>
<artifactId>flink-elasticsearch-demo</artifactId>
<version>1.0-SNAPSHOT</version> <properties>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-core</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients_2.11</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-java</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java_2.11</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-elasticsearch6_2.11</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>
2. 创建两个具有依赖关系的实体类Customer和Address,用于封装实时数据,代码如下所示:
package com.flink.domain; import java.util.Date; /**
* 客户实体类
*/
public class Customer {
private Long id;
private String name;
private Boolean gender;
private Date birth;
private Address address;
private String description; public Long getId() {
return id;
} public void setId(Long id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Boolean getGender() {
return gender;
} public void setGender(Boolean gender) {
this.gender = gender;
} public Date getBirth() {
return birth;
} public void setBirth(Date birth) {
this.birth = birth;
} public Address getAddress() {
return address;
} public void setAddress(Address address) {
this.address = address;
} public String getDescription() {
return description;
} public void setDescription(String description) {
this.description = description;
}
}
package com.flink.domain; /**
* 地址实体类
*/
public class Address {
private Integer id;
private String province;
private String city; public Address(Integer id, String province, String city) {
this.id = id;
this.province = province;
this.city = city;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getProvince() {
return province;
} public void setProvince(String province) {
this.province = province;
} public String getCity() {
return city;
} public void setCity(String city) {
this.city = city;
}
}
3. 自定义一个获取流式实时数据的Flink数据源,如下所示:
package com.flink.source; import com.flink.domain.Address;
import com.flink.domain.Customer;
import org.apache.flink.streaming.api.functions.source.ParallelSourceFunction;
import java.util.Date;
import java.util.Random; /**
* 自定义的流式并行数据源
*/
public class StreamParallelSource implements ParallelSourceFunction<Customer> { private boolean isRunning = true;
private String[] names = new String[5];
private Address[] addresses = new Address[5];
private Random random = new Random();
private Long id = 1L; public void init() {
names[0] = "刘备";
names[1] = "关羽";
names[2] = "张飞";
names[3] = "曹操";
names[4] = "诸葛亮"; addresses[0]= new Address(1, "湖北省", "武汉市");
addresses[1]= new Address(2, "湖北省", "黄冈市");
addresses[2]= new Address(3, "广东省", "广州市");
addresses[3]= new Address(4, "广东省", "深圳市");
addresses[4]= new Address(5, "浙江省", "杭州市");
} /**
* 每隔10ms生成一个Customer数据对象(模拟获取实时数据)
*/
@Override
public void run(SourceContext sourceContext) throws Exception {
init();
while(isRunning) {
int nameIndex = random.nextInt(5);
int addressIndex = random.nextInt(5); Customer customer = new Customer();
customer.setId(id++);
customer.setName(names[nameIndex]);
customer.setGender(random.nextBoolean());
customer.setBirth(new Date());
customer.setAddress(addresses[addressIndex]);
customer.setDescription("" + names[nameIndex] + "来自" + addresses[addressIndex].getProvince() + addresses[addressIndex].getCity());
/**
* 把创建的数据返回给Flink进行处理
*/
sourceContext.collect(customer);
Thread.sleep(10);
}
} @Override
public void cancel() {
this.isRunning = false;
}
}
4. 编写一个Flink实时处理流式数据的主程序,代码如下所示:
package com.flink.main; import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.flink.domain.Customer;
import com.flink.source.StreamParallelSource;
import org.apache.flink.api.common.functions.FilterFunction;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.RuntimeContext;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.elasticsearch.ElasticsearchSinkFunction;
import org.apache.flink.streaming.connectors.elasticsearch.RequestIndexer;
import org.apache.flink.streaming.connectors.elasticsearch6.ElasticsearchSink;
import org.apache.http.HttpHost;
import org.elasticsearch.client.Requests;
import java.util.ArrayList;
import java.util.List; /**
* Flink实时处理并将结果写入到ElasticSearch主程序
*/
public class FlinkToElasticSearchApp { public static void main(String[] args) throws Exception {
/**
* 获取流处理环境并设置并行度
*/
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(4); /**
* 指定数据源为自定义的流式并行数据源
*/
DataStream<Customer> source = env.addSource(new StreamParallelSource()); /**
* 对数据进行过滤
*/
DataStream<Customer> filterSource = source.filter(new FilterFunction<Customer>() {
@Override
public boolean filter(Customer customer) throws Exception {
if (customer.getName().equals("曹操") && customer.getAddress().getProvince().equals("湖北省")) {
return false;
}
return true;
}
}); /**
* 对过滤后的数据进行转换
*/
DataStream<JSONObject> transSource = filterSource.map(new MapFunction<Customer, JSONObject>() {
@Override
public JSONObject map(Customer customer) throws Exception {
String jsonString = JSONObject.toJSONString(customer, SerializerFeature.WriteDateUseDateFormat);
System.out.println("当前正在处理:" + jsonString);
JSONObject jsonObject = JSONObject.parseObject(jsonString);
return jsonObject;
}
}); /**
* 创建一个ElasticSearchSink对象
*/
List<HttpHost> httpHosts = new ArrayList<>();
httpHosts.add(new HttpHost("localhost", 9200, "http"));
ElasticsearchSink.Builder<JSONObject> esSinkBuilder = new ElasticsearchSink.Builder<JSONObject>(
httpHosts,
new ElasticsearchSinkFunction<JSONObject>() {
@Override
public void process(JSONObject customer, RuntimeContext ctx, RequestIndexer indexer) {
// 数据保存在Elasticsearch中名称为index_customer的索引中,保存的类型名称为type_customer
indexer.add(Requests.indexRequest().index("index_customer").type("type_customer").id(String.valueOf(customer.getLong("id"))).source(customer));
}
}
);
// 设置批量写数据的缓冲区大小
esSinkBuilder.setBulkFlushMaxActions(50); /**
* 把转换后的数据写入到ElasticSearch中
*/
transSource.addSink(esSinkBuilder.build()); /**
* 执行
*/
env.execute("execute FlinkElasticsearchDemo");
} }
至此,使用Flink对流式数据进行实时处理并将处理结果保存到Elasticsearch中的程序已经全部完成。
说明:Flink把数据保存到Elasticsearch时,如果Elasticsearch中没有提前创建对应名称的索引,则会自动创建对应名称的索引。
如果不需要在Elasticsearch中对指定字段使用IK Analyzer中文分词器进行分词,则不需要阅读第3节内容,直接阅读第4节即可。
3 Elasticsearch准备
如果希望对Elasticsearch中指定索引中的数据的指定字段使用中文分词器进行分词,则需要先在Elasticsearch中创建索引并指定分词器,所以需要先确保Elasticsearch中已经安装了分词器插件。
说明:本文使用Elasticsearch可视化插件操作Elasticsearch。
3.1 安装IK Analyzer中文分词器
本文中使用的是IK Analyzer中文分词器,并且基于Window 10操作系统,具体的安装过程如下图所示:
1 打开CMD命令窗口并切换到Elasticsearch安装目录下的bin目录中。
2 运行以下命令下载elasticsearch 6.5.4版本对应的IK Analyzer中文分词器:
elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.5.4/elasticsearch-analysis-ik-6.5.4.zip
3 下载完成后提示是否安装,直接输入y进行安装,完整的过程如下图所示:
4 安装完成后,在Elasticsearch的安装目录的plugins目录下会有一个analysis-ik目录,则表示安装完成,如下所示:
5 重启elasticsearch,并通过elasticsearch-head插件来检验IK Analyzer中文分词器是否已安装成功,在复合查询页面输入如下图所示内容,然后提交请求,如果出现如右图所示的分词结果就表明IK Analyzer中文分词器安装成功:
3.2 在Elasticsearch中创建索引
本文是要把过滤后符合条件的Customer类型的数据保存到ElasticSearch中,并能够对Customer中的description字段进行中文分词,
所以需要在Elasticsearch中创建一个索引,通过elasticsearch-head插件创建索引如下图所示,提交请求后如果如下图右边所示则创建成功:
创建索引index_customer的具体json体如下所示:
{
"settings": {
"index": {
"number_of_shards": "5",
"number_of_replicas": "1"
},
"analysis":{
"analyzer":{
"ik":{
"tokenizer": "ik_max_word"
}
}
}
},
"mappings": {
"type_customer": {
"properties": {
"id": {
"type": "long"
},
"name": {
"type": "text"
},
"gender": {
"type": "boolean"
},
"birth": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
"address": {
"properties": {
"id": {
"type": "integer"
},
"province": {
"type": "keyword"
},
"city": {
"type": "keyword"
}
}
},
"description": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
}
}
}
}
}
创建成功后在概览页面可以查看到如下信息:
4 测试Flink实时处理
启动Elasticsearch并成功创建索引后,直接运行程序中的FlinkToElasticSearchApp程序,在IDEA的控制台就可以看到如下输出信息,则表示Flink程序正在运行并进行实时处理:
此时,在Elasticsearch-head插件中可以查看到index_customer索引中的数据如下图所示,则表示Flink程序实时处理的结果已经正常保存到了Elasticsearch中:
由于本文在创建index_customer索引时,指定了对description字段使用IK Analyzer中文分词器,所以,在左侧的description字段索引框中输入查询内容之后,右边就会快速查询出description字段中包含了查询内容的所有的数据.
Flink写入数据到ElasticSearch (ElasticSearch详细使用指南及采坑记录)
https://blog.csdn.net/lisongjia123/article/details/81121994
Flink 写入数据到 ElasticSearch
https://blog.csdn.net/weixin_44876457/article/details/89398743
Flink实时处理并将结果写入ElasticSearch实战的更多相关文章
- ElasticSearch实战系列二: ElasticSearch的DSL语句使用教程---图文详解
前言 在上一篇中介绍了ElasticSearch集群和kinaba的安装教程,本篇文章就来讲解下 ElasticSearch的DSL语句使用. ElasticSearch DSL 介绍 Elastic ...
- ElasticSearch实战系列四: ElasticSearch理论知识介绍
前言 在前几篇关于ElasticSearch的文章中,简单的讲了下有关ElasticSearch的一些使用,这篇文章讲一下有关 ElasticSearch的一些理论知识以及自己的一些见解. 虽然本人是 ...
- ElasticSearch实战系列七: Logstash实战使用-图文讲解
前言 在上一篇中我们介绍了Logstash快速入门,本文主要介绍的是ELK日志系统中的Logstash的实战使用.实战使用我打算从以下的几个场景来进行讲解. 时区问题解决方案 在我们使用logstas ...
- ElasticSearch实战系列八: Filebeat快速入门和使用---图文详解
前言 本文主要介绍的是ELK日志系统中的Filebeat快速入门教程. ELK介绍 ELK是三个开源软件的缩写,分别表示:Elasticsearch , Logstash, Kibana , 它们都是 ...
- ElasticSearch实战系列九: ELK日志系统介绍和安装
前言 本文主要介绍的是ELK日志系统入门和使用教程. ELK介绍 ELK是三个开源软件的缩写,分别表示:Elasticsearch , Logstash, Kibana , 它们都是开源软件.新增了一 ...
- ElasticSearch实战系列十: ElasticSearch冷热分离架构
前言 本文主要介绍ElasticSearch冷热分离架构以及实现. 冷热分离架构介绍 冷热分离是目前ES非常火的一个架构,它充分的利用的集群机器的优劣来实现资源的调度分配.ES集群的索引写入及查询速度 ...
- ElasticSearch实战系列十一: ElasticSearch错误问题解决方案
前言 本文主要介绍ElasticSearch在使用过程中出现的各种问题解决思路和办法. ElasticSearch环境安装问题 1,max virtual memory areas vm.max_ma ...
- ElasticSearch实战-入门
http://www.cnblogs.com/smartloli/ 1.概述 今天接着<ElasticSearch实战-日志监控平台>一文来给大家分享后续的学习,在<ElasticS ...
- ElasticSearch实战-日志监控平台
1.概述 在项目业务倍增的情况下,查询效率受到影响,这里我们经过讨论,引进了分布式搜索套件——ElasticSearch,通过分布式搜索来解决当下业务上存在的问题.下面给大家列出今天分析的目录: El ...
随机推荐
- python之变量的数据类型(3)dict 及解构简单介绍
一.变量的数据类型(3) 1. dict 字典dict 用{}来表示 键值对数据 {key:value} 唯一性 键 都必须是可哈希的 不可变的数据类型就可以当做字典中的键 值 没有任何限制 2.增删 ...
- [转] 雷电三和typec傻傻分不清
原文:https://club.lenovo.com.cn/thread-4921715-1-1.html 因为形状完全一致,所以很多人都误以为Type-C=雷电3. 实际上,雷电3只是采用了Type ...
- 【转】TCP之深入浅出send和recv
本篇我们用一个测试机上的阻塞socket实例来说明主题.文章中所有图都是在测试系统上现截取的. 需要理解的3个概念 1. TCP socket的buffer 每个TCP socket在内核中都有一个发 ...
- P2577 [ZJOI2005]午餐[DP]
题目描述 上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂.这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭.由于每个人的口味(以及胃口)不同,所以他们要吃的菜各 ...
- 在动态sql的使用where时,if标签判断中,如果实体类中的某一个属性是String类型,那么就可以这样来判断连接语句:
在动态sql的使用where时,if标签判断中,如果实体类中的某一个属性是String类型,那么就可以这样来判断连接语句: 如果是String类型的字符串进行判空的时候: <if test=&q ...
- 题解 洛谷P4779 【【模板】单源最短路径(标准版)】
正权图,貌似看来是一道裸的 \(dijkstra\) \(dijkstra\)的主要步骤: 首先,在\(dijkstra\)中,源点表示一开始的出发点,蓝点表示还未确定的点,白点则表示已经确定的点. ...
- String 堆内存和栈内存
java把内存划分为两种:一种是栈(stack)内存,一种是堆(heap)内存 在函数中定义的一些基本类型的变量和对象的引用变量都在栈内存中分配,当在一段代码块定义一个变量时,java就在栈中为这个变 ...
- python字符转数字
print(float("260.01478420521632365364543423")) 260.0147842052163
- CSP2019 爆炸记
前言 第一次去参加\(csp\),被吊打,很慌. 之前\(NOIp\)普及组勉强一等,很慌. 考的也不是很好吧,很慌. 反正菜就对了. day -? 初赛,旁边坐着本校高三爷. 初赛比之前的模拟题简单 ...
- python爬取动态网页数据,详解
原理:动态网页,即用js代码实现动态加载数据,就是可以根据用户的行为,自动访问服务器请求数据,重点就是:请求数据,那么怎么用python获取这个数据了? 浏览器请求数据方式:浏览器向服务器的api(例 ...