ELK = Elasticsearch + Logstash + Kibana。
- Elasticsearch :一个搜索和分析引擎
- Logstash:服务器端数据处理管道,能够同时从多个来源采集数据,转换数据,然后将数据发送到诸如 Elasticsearch 等“存储库”中
- Kibana :使用户在 Elasticsearch 中使用图形和图表对数据进行可视化。
一、 安装
1. ElasticSearch
很不错的安装教程
启动:
- ElasticSearch的bin目录下bat文件启动ES
- elasticsearch-head-master目录下,命令行输入grunt server启动head插件
- http://localhost:9100/ 在head下查看当前集群
2. Kibana
下载地址
这里我下载的是6.2.4版本,和ES版本一致
若本机已经安装并启动elasticsearch,则kibana会默认连接localhost:9200端口的elasticsearch
打开浏览器输入: http://localhost:5601/,就可以看到kibana的控制后台。
kibana配置修改:
config/kibana.yml
注意配置文件中对应的端口号(9200),连接哪个es配置哪个。.bat文件启动。
3. logstash
下载地址
同样是6.2.4版本
二、使用
1. logstash使用
ES可以通过JAVA API等方式将数据写入节点,但是使用logstash更快更方便。
入门大致格式参考:logstash从csv文件导入数据到elasticsearch
logstash启动:bin目录下logstash -f logstash.conf启动(logstash.conf为写好的配置文件)
这里在启动logstash时遇到一个问题:输入logstash -f logstash.conf后报找不到或无法加载主类。解决方法参考
模板使用
logstash向ES中写入数据时,如果对数据格式等有要求,要先定义模板。
例如我在开始时遇到的问题是,默认的模板对数据自动进行分词,而我本来不需要这种分词,那么就需要重新定义自己的模板。
这里推荐两篇文章,可以参考:
- 自定义/修改模板的使用:logstash采集规范与elasticsearch的template 、mapping 详细介绍
- 模板的定义(特别是mapping怎样写):logstash自定义模板输出到elasticsearch和增加输出字段
2. ElasticSearch使用
这里主要使用JAVA API来对ES操作,主要介绍JAVA API的方法,也可以使用Restful的方法。
(1)创建索引库
private TransportClient client;
@Before //表示在任意使用@Test注解标注的public void方法执行之前执行
//创建client对象
public void init() throws Exception{
//1.创建一个Settings对象,相当于是一个配置信息,主要配置集群的名称
Settings settings= Settings.builder().put("cluster.name","my-application").build();
//2.创建一个客户端client对象
client=new PreBuiltTransportClient(settings);
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9300));
}
/*
1. 创建索引库
*/
@Test
public void createIndex() throws Exception{
long startTime = System.currentTimeMillis();
init();
//3.使用client对象创建一个索引
client.admin().indices().prepareCreate("IndexName")
//执行操作
.get();
long createIndexTime = System.currentTimeMillis() - startTime;
System.out.println("创建索引的时间:"+createIndexTime);
//4.关闭client对象
client.close();
}
除了使用logstash定义mapping和上传数据的方法外,ES也可以自己通过JAVA API完成。(但是个人认为很麻烦,实际中没有使用)
/*
2.使用Java客户端设置Mappings
*/
@Test
public void setMappings() throws Exception{
init();
//3.创建一个mappings信息
XContentBuilder builder= XContentFactory.jsonBuilder()
.startObject() //开头的{
.startObject("article")
.startObject("properties")
.startObject("id")
.field("type","long")
.field("store",true)
.endObject()
.startObject("title")
.field("type","text")
.field("store",true)
.field("analyzer","ik_smart")
.endObject()
.startObject("content")
.field("type","text")
.field("store",true)
.field("analyzer","ik_smart")
.endObject()
.endObject()
.endObject()
.endObject();
//4.使用client把mapping信息设置到索引库中
client.admin().indices()
//设置要做映射的索引
.preparePutMapping("IndexName")
//设置要做映射的type
.setType("article")
//mapping信息,可以是XContextBuilder对象可以是json格式字符串
.setSource(builder)
//执行操作
.get();
//5.关闭连接
client.close();
}
/*
3.1. 添加文档(XContentBuilder)
*/
@Test
public void testAddDocument() throws Exception{
//3.创建一个文档对象
XContentBuilder builder = XContentFactory.jsonBuilder()
.startObject()
.field("id",1l)
.field("title","开开心心")
.field("content","今天也要加油鸭")
.endObject();
//4.把文档对象添加到索引库
//client.prepareIndex("IndexName","article","1");
client.prepareIndex()
//设置索引名称
.setIndex("IndexName")
//设置type
.setType("article")
//设置文档id,如果不设置会自动生成一个id
.setId("1")
//设置文档信息
.setSource(builder)
//执行操作
.get();
//5.关闭客户端
client.close();
}
/*
3.2. 添加文档(Jackson)
*/
@Test
public void testAddDocument2() throws Exception{
//创建article对象(创建一个pojo类)
Article article=new Article();
//设置对象属性
article.setId(3l);
article.setTitle("星期一");
article.setContent("又是没有好好减肥的一天");
//把article对象转换成json格式的字符串(使用工具类)
ObjectMapper objectMapper=new ObjectMapper();
String jsonDoc=objectMapper.writeValueAsString(article);
System.out.println(jsonDoc);
//使用client对象,把文档写入索引库
client.prepareIndex("IndexName","article","3")
.setSource(jsonDoc, XContentType.JSON)
.get();
//关闭客户端
client.close();
}
(2)查询
查询设置
private TransportClient client;
//查询设置
long startTime = System.currentTimeMillis();
SearchRequestBuilder srb=client.prepareSearch("healthapp_index");
QueryBuilder queryBuilder0= QueryBuilders.matchPhraseQuery("logKey",3);
QueryBuilder queryBuilder1=QueryBuilders.matchPhraseQuery("logKey",4);
执行查询
PUT _settings
{
"index": {
"max_result_window": "10000000"
}
}
SearchResponse sr = srb.setQuery(QueryBuilders.boolQuery()
.should(queryBuilder0)
.should(queryBuilder1)
)
.setFrom(0).setSize(260000)
.get();
查询中产生的问题及解决方法:
-
分页查询(from+size)
这里使用from和size进行分页查询:
from表示从第几行开始,size表示查询多少条文档。
from默认为0,size默认为10, 如果搜索size大于10000,需要设置index.max_result_window参数
注意:size的大小不能超过index.max_result_window这个参数的设置,默认为10,000。
这里设置成了260000,所以需要先修改该参数
可以在Kibina中修改
参考 -
TooManyClauses问题 如果bool查询的查询条件过多会导致TooManyClauses问题。
“caused_by”:{“type”:“too_many_clauses”,“reason”:“maxClauseCount is set to 1024”}
解决方式: 配置文件 Elasticsearch.yuml中配置 index.query.bool.max_clause_count:10240
5+版本配置: indices.query.bool.max_clause_count: 10240
设置最大限制bool查询的条数。过多会导致性能比较慢 -
分页查询之滚动查询
优点:当结果足够大的时候, scroll 性能更好。
缺点:但是不灵活和 scroll_id 难管理问题存在
方法参考:
Elasticsearch使用scroll进行分页查询
注意
1)第一次设置scrollId以后,要先执行一次才能获得现有的ID中滚动到的值。参考:[ElasticSearch]Java API 之 滚动搜索(Scroll API)
2)滚动时长的设置不要太短。
这里附上博主自己的实际使用:
//查询设置
long startTime = System.currentTimeMillis();
SearchRequestBuilder srb=client.prepareSearch("hdfs_es")
.setTypes("hdfs");
QueryBuilder queryBuilder0= QueryBuilders.termQuery("logKey",2);
QueryBuilder queryBuilder1=QueryBuilders.termQuery("logKey",3);
SearchResponse sr = srb.setQuery(queryBuilder0)
.setSize(50000).setScroll(new TimeValue(100000))
.get();
long totalCount=sr.getHits().getTotalHits();
int page=(int)totalCount/50000;
System.out.println("查询结果总记录数:" + totalCount);
SearchHits hits=sr.getHits();
for (SearchHit searchHit:hits) {
String[] outcsvline=new String[head1.length + head2.size()];
Map<String, Object> document = searchHit.getSource();
int logkey=Integer.parseInt(document.get("logKey").toString());
outcsvline[0] = document.get("logKey").toString();
outcsvline[1] = document.get("timeStamp").toString();
outcsvline[2] = document.get("Component").toString();
outcsvline[3] = document.get("Pid").toString();
outcsvline[4] = document.get("EventTemplate").toString();
outcsvline[5] = document.get("Level").toString();
int add = 0;
for (int kk = 0; kk < logkey; kk++) {
add += logkeycnt[kk];
}
for (int j = 0; j< logkeycnt[logkey]; j++) {
String tmp = (logkey + "") + "_ParameterList" + (j + "");
try {
outcsvline[6 + add + j] = document.get(tmp).toString();
} catch (Exception e) {
continue;
}
}
csvWriter.writeRecord(outcsvline);
}
for(int i=0;i<=page;i++){
sr = client.prepareSearchScroll(sr.getScrollId())
.setScroll(new TimeValue(100000))
.get();
hits=sr.getHits();
for (SearchHit searchHit:hits) {
String[] outcsvline=new String[head1.length + head2.size()];
Map<String, Object> document = searchHit.getSource();
int logkey=Integer.parseInt(document.get("logKey").toString());
outcsvline[0] = document.get("logKey").toString();
outcsvline[1] = document.get("timeStamp").toString();
outcsvline[2] = document.get("Component").toString();
outcsvline[3] = document.get("Pid").toString();
outcsvline[4] = document.get("EventTemplate").toString();
outcsvline[5] = document.get("Level").toString();
int add = 0;
for (int kk = 0; kk < logkey; kk++) {
add += logkeycnt[kk];
}
for (int j = 0; j< logkeycnt[logkey]; j++) {
String tmp = (logkey + "") + "_ParameterList" + (j + "");
try {
outcsvline[6 + add + j] = document.get(tmp).toString();
} catch (Exception e) {
continue;
}
}
csvWriter.writeRecord(outcsvline);
}
}
处理查询结果
//取查询结果
SearchHits searchHits = sr.getHits();
//取查询结果的总记录数
System.out.println("查询结果总记录数:" + searchHits.getTotalHits());
for (SearchHit searchHit:searchHits) {
String[] outcsvline=new String[head1.length + head2.size()];
Map<String, Object> document = searchHit.getSource();
//通过get方法取到ES结果中对应属性
int logkey=Integer.parseInt(document.get("logKey").toString());
outcsvline[0]=document.get("logKey").toString();
outcsvline[1]=document.get("timeStamp").toString();
outcsvline[2]=document.get("Component").toString();
outcsvline[3]=document.get("Pid").toString();
outcsvline[4]=document.get("EventTemplate").toString();
int add=0;
for(int kk=0;kk<logkey;kk++){
add+=logkeycnt[kk];
}
for(int i=0;i<logkeycnt[logkey];i++){
String tmp=(logkey+"")+"_ParameterList"+(i+"");
try{
outcsvline[5+add+i]=document.get(tmp).toString();
}catch (Exception e){
continue;
}
}
csvWriter.writeRecord(outcsvline);
}