spring mvc+ELK从头开始搭建日志平台

最近由于之前协助前公司做了点力所能及的事情,居然收到了一份贵重的端午礼物,是给我女儿的一个乐高积木,整个有7大包物件,我花了接近一天的时间一砖一瓦的组织起来,虽然很辛苦但是能够从过程中体验到乐趣。这次将分享从头搭建分布式日志系统,主要是在spring mvc上结合ELK套件实现(之前有些工作由于分工不同由不同的同事来完成,我只是在已经配置好的环境下做开发而已),包含如下这些技术点:

  •   spring mvc
  •   logback
  •   logstash
  •   elasticsearch
  •   kibana
  •   redis

来看下整体的架构图,这类架构非常容易解决当下分布式系统下的日志记录,查询以及分析困难的问题。


操作系统,IDE环境:

  •   eclipse
  •   windows

1:搭建spring mvc项目
eclipse自带创建的dynamic web project是个空结构,没有任何配置,我们要想跑起来一个hello world的项目,还需要做些配置,比如创建程序文件,比如view,controller等等。
spring tool suite可以帮助我们解决这个问题,它提供了spring mvc的项目模板,里面自带一个hello world的可启动的应用页面,在eclipse中可以很方便的以插件形式安装spring tool suit,安装好之后就可以创建了。
这里需要注意的是不同版本的spring tool suite在创建时的菜单会有不同,我目前的菜单位于:

首先要选中spring标签:

然后在File菜单下找:


创建好之后,我们就可以直接在tomcat下运行了,不需要任何的其它操作,相对创建的dynamic web project要方便的多,不过通过这种模板创建的项目也有缺点:如果你喜欢新的一些依赖包,那么你需要手工去pom文件中去更新版本号为你想要的,还有可能会引入部分你暂时可能用不上的一些第三方包。下图是稍加修改的项目完成图,是一个标准的maven结构的spring mvc。


2:redis安装
由于我的是windows环境,所以相应的需要下载windows版本的redis:
windows版:https://github.com/mythz/redis-windows
下载下来解压,然后选择一个版本:

配置文件我只修改了一个:bind,它是用来绑定一个固定IP的,为什么要显示的去绑定一个IP呢?后面会介绍我遇到的问题。
启动服务端:在redis/bin目录下执行:redis-server.exe redis.windows.conf即可启动

启动客户端:在redis/bin目录下执行:redis-cli.exe -h 127.0.0.1 -p 6379,在这个窗口可以通过一些redis命令再测试redis是否正常,比如get,set ,keys *等等。
3:ELK安装

在这个网站可以找到ELK最新版本:https://www.elastic.co/downloads,将elasticsearch,logstash,kibana这三个全部下载下来。

  •     配置elasticsearch

大部分的配置都使用默认的,只是为了好标识我修改了cluster.name以及node.name,详细的参数可研究文档。然后直接在bin目录下运行elasticsearch.bat就可以启动了。

打开http://127.0.0.1:9200/就可以,看到如下信息就说明启动正常了。


还有很多插件可以安装,用来帮助我们查看监控elasticsearch,这里先安装head,命令行进入elasticsearch的目录,然后执行plugin install mobz/elasticsearch-head即可安装。

安装成功后打开http://127.0.0.1:9200/_plugin/head/

  •     配置logstash

先看下logstash的架构设计以及与其它ELK的配合,本篇的data source就是redis,不涉及到filter,最终日志输出到elasticsearch中。

这里我们只配置input以及output,需要注意的是不同版本的logstash在配置上也会略有不同,大家有兴趣可以进一步做下对比。

复制代码
input {

    redis {
        data_type => "list"
        key => "logstash"
        host => "127.0.0.1"
        port => 6379
        threads => 5
        codec => "json"
    }
}
filter {

}
output {

    elasticsearch {
        hosts => ["127.0.0.1:9200"]
        index => "logstash-%{type}-%{+YYYY.MM.dd}"
        document_type => "%{type}"
        workers => 1
        flush_size => 20
        idle_flush_time => 1
        template_overwrite => true
    }
    stdout{}
}
复制代码

然后在logstash目录下执行logstash -f etc/logstash.d/即可启动

  •     配置kinbana
    • elasticesearch.url指向之前配置好的elasticsearch地址。
    • kinbna.index,这个是用来存储kibana自身的一些信息的。

  •     集成logback

需要有一个记录日志的入口,将logback-classic引入进来,为了将日志传递给redis,需要配置一个logback-redis-appender,依赖如下:

复制代码
<!-- Logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${org.slf4j-version}</version>
        </dependency>
         <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>


        <!--logstash begin -->
        <dependency>
            <groupId>com.cwbase</groupId>
            <artifactId>logback-redis-appender</artifactId>
            <version>1.1.3</version>
            <exclusions>
                <exclusion>
                    <groupId>redis.clients</groupId>
                    <artifactId>jedis</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
复制代码

 

配置logback.xml,key需要与logstash配置文件中配置的key相匹配。

复制代码
<appender name="LOGSTASH" class="com.cwbase.logback.RedisAppender">
        <source>logstashdemo</source>
        <type>dev</type>
        <host>127.0.0.1</host>
        <key>logstash</key>
        <tags>dev</tags>
        <mdc>true</mdc>
        <location>true</location>
        <callerStackIndex>0</callerStackIndex>
    </appender>
复制代码

在homecontroller中记录日志,slf4j由于完成了与logback完美集成,所以我们也不需要做任何转换类的配置即可实现日志记录。


前文中曾经提到在配置redis时,设置了bind属性,让其指定到一个固定的IP。如果不指定,在logstash连接redis会有问题,其中的原因有待后续进一步确认。

4:运行网站,查看日志

当redis,elasticsearch,logstash服务运行正常后,启动spring mvc,通过logger记录的日志就可以在kibana中方便的查看了。

测试logback是否已经将日志发送到redis,可以通过redis的命令来查看是否包含了配置的logstash这个key,还可以通过llen来判断日志是否在正常的递增。

如果上面都正常,再找开kibana的页面,第一次打开会提示创建索引规则,创建好之后就可以看到日志已经被采集到elasticsearch中了。

 

经过接近两天的研究,终于从0开始搭建成功了spring mvc+ELK的分布式日志管理平台,java平台的优势就是开源的产品多,可利用优秀插件也多,擅于去发倔还是可以很省事的做些比较优秀的项目的。虽然本篇只是一个练手入门文章,但有了开始就会有收获。

 

本文参考:

  • http://os.51cto.com/art/201403/431103.htm
  • http://kibana.logstash.es
  • http://blog.csdn.net/kmtong/article/details/38920327
  • http://www.cnblogs.com/xing901022/p/4802822.html
  • http://blog.csdn.net/july_2/article/details/24481935
  • https://www.elastic.co/guide/en/kibana/current/getting-started.html

ElasticSearch性能优化策略

ElasticSearch性能优化主要分为4个方面的优化。

一、服务器部署

1、增加1-2台服务器,用于负载均衡节点

elasticSearch的配置文件中有2个参数:node.masternode.data。这两个参 数搭配使用时,能够帮助提供服务器性能。

1.1> node.master: false    node.data: true

        node服务器只作为一个数据节点,只用于存储索引数据。使该node服务器功能 单一,只用于数据存储和数据查询,降低其资源消耗率。

    1.2> node.master: true    node.data: false

        node服务器只作为一个主节点,但不存储任何索引数据。该node服务器将使用 自身空闲的资源,来协调各种创建索引请求或者查询请求,讲这些请求合理分发到相关 的node服务器上。

    1.3> node.master: false    node.data: false

node服务器即不会被选作主节点,也不会存储任何索引数据。该服务器主要用 于查询负载均衡。在查询的时候,通常会涉及到从多个node服务器上查询数据,并请 求分发到多个指定的node服务器,并对各个node服务器返回的结果进行一个汇总处理, 最终返回给客户端。

2、关闭data节点服务器中的http功能

针对ElasticSearch集群中的所有数据节点,不用开启http服务。将其中的配置 参数这样设置:http.enabled: false,同时也不要安装head, bigdesk, marvel等监控 插件,这样保证data节点服务器只需处理创建/更新/删除/查询索引数据等操作。

http功能可以在非数据节点服务器上开启,上述相关的监控插件也安装到这些服 务器上,用于监控ElasticSearch集群状态等数据信息。

这样做一来出于数据安全考虑,二来出于服务性能考虑。

3、一台服务器上最好只部署一个Node

一台物理服务器上可以启动多个Node服务器节点(通过设置不同的启动port), 但一台服务器上的CPU,内存,硬盘等资源毕竟有限,从服务器性能考虑,不建议一台 服务器上启动多个node节点。

二、服务器配置

1、配置索引线程池的大小

ElastiSearch服务器有多个线程池大小配置。主要有:indexsearchsuggest getbulkpercolatesnapshotsnapshot_datawarmerrefresh

在此主要针对indexsearch进行一个配置调整。index操作包含:创 建/更新/删除索引数据。search操作主要针对用户的各种搜索操作。

具体配置如下:

threadpool:

    index:

        type: fixed

        size: 100

    search:

        type: fixed

        size: 1000

2、创建/查找索引设置相同的分词解析器

索引服务器用到了ik中文分词插件,对于添加到该搜索服务器中的数据都使用该 中文分词(例如orgglobal对象中的orgName就使用了ik中文分词)。当执行搜索请求 时,搜索关键词也需要用到相关的中文分词器,如果不指定设置的话,则会使用服务器 默认的中文分词standard,而使用standard作为中文分词器进行查询时,性能不好。 通过将ik中分词设置为默认的分词器时,则查询效率是standard2-3倍。

该配置具体如下:

index:

     analysis:

         analyzer:

        ik:

          alias: [news_analyzer_ik,ik_analyzer]

          type: org.elasticsearch.index.analysis.IkAnalyzerProvider

index.analysis.analyzer.default.type: ik

    3、确定分片(shard)的数量和副本(replica)的数量

ElasticSearch在创建索引数据时,最好指定相关的shards数量和replicas

    否则会使用服务器中的默认配置参数shards=5replicas=1

因为这两个属性的设置直接影响集群中索引和搜索操作的执行。假设你有足够的   

    机器来持有碎片和副本,那么可以按如下规则设置这两个值:
1)
拥有更多的碎片可以提升索引执行能力,并允许通过机器分发一个大型的索引;
2)
拥有更多的副本能够提升搜索执行能力以及集群能力。
对于一个索引来说,number_of_shards只能设置一次,而number_of_replicas可以使用索引更新设置API在任何时候被增加或者减少。

这两个配置参数在配置文件的配置如下:

index.number_of_shards: 5

index.number_of_shards: 1

    4、查询速度慢的日志配置

在进行实际应用中,会记录下查询速度慢或者添加索引速度慢的操作记录,为后

    续性能优化提供依据。其具体配置如下:

index.search.slowlog.threshold.query.warn: 10s

index.search.slowlog.threshold.query.info: 5s

index.search.slowlog.threshold.query.debug: 2s

index.search.slowlog.threshold.query.trace: 500ms

index.search.slowlog.threshold.fetch.warn: 1s

index.search.slowlog.threshold.fetch.info: 800ms

index.search.slowlog.threshold.fetch.debug: 500ms

index.search.slowlog.threshold.fetch.trace: 200ms

index.indexing.slowlog.threshold.index.warn: 10s

index.indexing.slowlog.threshold.index.info: 5s

index.indexing.slowlog.threshold.index.debug: 2s

index.indexing.slowlog.threshold.index.trace: 500ms

三、数据结构优化

1、尽量减少不需要的字段

ElasticSearch中存储的数据是用于搜索服务,因此其他一些不需要用于搜索的字段最好不存到ES中,这样即节省空间,同时在相同的数据量下,也能提高搜索性能。

2routing值的设置

通常情况下,往ElasticSearch服务器添加索引数据时,是无需指定routing值。ElasticSearch会根据索引Id,将该条数据存储到ElasticSearch集群中的一个shard中。而当指定了routing值为accountId(用户Id),则ElasticSearch会将相同accountId的多个数据都存放到同一个shard中,后续查询的时候,在指定routing值后,ElasticSearch只需要查询一个shard就能得到所有需要的数据,而不用再去查询所有的shard,从而大大提供了搜索性能。

四、运行期优化

1optimize

随着时间的推移,ElasicSearch中每个shard的数据也会越来越多,索引越来越大,而生成的segment(在每个shard中,每个索引文件实际是由多个sgment文件组成)也会越来越多。而segment越多的话,则查询的性能越差,所以通过调用optimize命令,将多个segment合并成更少数量的segment(最少为一个),从而来提高查询性能。

在调用该命令时,可以设置几个参数,这些参数的具体含义如下:

1.1> max_num_segments

段数优化。要全面优化索引,将其设置为1。默认设置只需检查是否需要执行一个合并,如果需要,则执行它。【经过测试,该值越小,查询速度越快】

1.2> only_expunge_deletes

该优化操作是否只清空打有删除标签的索引记录。在Lucence中,在执行删除操作时,不会直接删除segment中的记录,而是对该记录打上delete标签。当多个segment进行合并操作时,就会生成一个新的segment,而该新的segment中不再包含删除的记录。这个参数允许只对哪些包含删除记录的segment进行优化操作。

1.3>flush

在执行完优化操作之后,再执行刷新操作。默认值为true

1.4>wait_for_merge

当该参数设置为true时,表示其他请求操作要等到合并segment操作结束之后,再进行响应。值得注意的是,由于这个优化操作是一个非常耗时,耗资源的事情,用户提交的请求操作是不能容忍等待这么久,所以这个参数最好设置为false.

具体调用命令如下:

http://localhost:9200/indexName/_optimize?only_expunge_deletes=true&wait_for_merge=false

2warmers

ElasticSearch服务器启动之后,业务系统中要使用的索引数据暂时没有导入到内存中,因此当用户进行第一次数据搜索时,会因为数据导入耗时很久,而严重影响用户的使用体验。为了解决该问题,可以使用warmer工具。通过ElastiSearch提供的工具,可以register/delete/get特定名称的warmer。通常情况下,warmer包含的请求需要载入大量的索引数据(例如在数据搜索中需要针对特定字段的排序操作,或者用到一些聚合sum,min,max函数的查询等),这样才能达到预热的效果。

具体调用示例如下(下面的warmer是针对索引名为testwarmer,warmer定义的名字为warmer_1):

curl -XPUT localhost:9200/test/_warmer/warmer_1 -d ‘{
“query” : {
“match_all” : {}
},
“aggs” : {
“aggs_1” : {
“terms” : {
“field” : “field”
}
}
}
}’

ElasticSearch的部署、同步与调优

ElasticSearch是一个强大的搜索服务器,基于Apache Lucene的全文搜索引擎开发,具有高性能、分布式和零配置的优点。在当前的项目中,我们希望ES能承担亿级文档的搜索,而ES也证明了即便面对这样的数据规模,也能实现十分迅速的搜索响应。

概念

  • 节点(Node):节点是一个ES的实例,一般一台主机上部署一个节点-
  • 集群(Cluster):集群由若干节点组成,和任意节点的通信等价于和集群的通信
  • 分片(Shard):一个索引会分成多个分片存储,分片数量在索引建立后不可更改
  • 副本(Replica):副本是分片的一个拷贝,目的在于提高系统的容错性和搜索的效率
  • 索引(Index):类似数据库的库
  • 类型(Type):类似数据库的表
  • 文档(Document):类似数据库的行,包含一个或多个Field
  • 字段(Field):搜索的最小单元,可通过Mapping定义不同的属性(比如可否被搜索)

部署

ElasticSearch 1.5.0版本为例

ES的使用很简单,从官网下载压缩包后,解压后输入如下指令:

./bin/elasticsearch -d --cluster.name [your_cluster_name] --node.name [your_node_name]

一旦在多台主机上启动拥有同一个cluster.name的ES实例,它们会自动组成一个集群。

elasticsearch-head

elasticsearch-head是一个必装的插件,它提供了一个web界面,显示集群和索引的状态,同时具备浏览和搜索文档的功能。只需要通过ES的plugin指令安装就OK了:

./bin/plugin -install mobz/elasticsearch-head

同步

通常线上系统都不会使用ES作为主存储,从主存储创建索引的效率是我们关心的。ES的bulk API能支持批量操作,大大提升了创建索引的效率。以下是使用pyelasticsearch(非官方的一个Python客户端)批量创建索引的范例:

from pyelasticsearch import ElasticSearch
from pyelasticsearch import bulk_chunks

es = ElasticSearch()

def documents():
    for _doc in docs:
        yield es.index_op(doc=_doc, id=doc['id'])

for chunk in bulk_chunks(documents(), docs_per_chunk=500, bytes_per_chunk=10000):
    es.bulk(chunk, index='index-test', doc_type='doc')

单机索引200万条记录的耗时约10分钟。

中文

ES支持中文的前提是安装正确的分词组件,比如elasticsearch-analysis-ik。但貌似该组件的最新版本(1.2.9)不支持plugin指令直接安装,只能通过Maven重新编译了:

git clone https://github.com/medcl/elasticsearch-analysis-ik.git --depth 1
cd elasticsearch-analysis-ik/
# 真心希望你的网络棒棒嗒
mvn package
unzip ./target/releases/elasticsearch-analysis-ik-1.2.9.zip

zip解压得到5个jar包:

  • elasticsearch-analysis-ik-1.2.9.jar
  • httpclient-4.3.5.jar
  • httpcore-4.3.2.jar
  • commons-logging-1.1.3.jar
  • commons-codec-1.6.jar

返回ES目录,新建路径./plugins/analysis-ik并把上述jar包全部移进去。
第二步,把elasticsearch-analysis-ik/config/ik文件夹(IK自带的词典)复制到ES目录的./config路径下。
第三步,在./config/elasticsearch.yml文件的最后加上:

index:
  analysis:
    analyzer:
      ik:
          alias: [news_analyzer_ik,ik_analyzer]
          type: org.elasticsearch.index.analysis.IkAnalyzerProvider

index.analysis.analyzer.default.type : "ik"

至此大功告成。注意配置分词组件必须在创建索引之前,否则是无效的。

调优

ES的调优分两个层面,一是Java层面的调优,包括加大JVM的可用内存及单线程内存。

对Unix系统,可修改./bin/elasticsearch.in.sh文件:

# 一般分配主机1/4-1/2的内存
if [ "x$ES_MIN_MEM" = "x" ]; then
    ES_MIN_MEM=12g
fi
if [ "x$ES_MAX_MEM" = "x" ]; then
    ES_MAX_MEM=12g
fi

JAVA_OPTS="$JAVA_OPTS -Xms${ES_MIN_MEM}"
JAVA_OPTS="$JAVA_OPTS -Xmx${ES_MAX_MEM}"
# 线程大小, ES单线程承载的数据量比较大
JAVA_OPTS="$JAVA_OPTS -Xss128m"

调优的第二个层面是ES本身的调优,修改./config/elasticsearch.yml文件,关键的项目如下所示:

# 分片数量,推荐分片数*副本数=集群数量
# 分片会带来额外的分割和合并的损耗,理论上分片数越少,搜索的效率越高
index.number_of_shards: 20
# 锁定内存,不让JVM写入swapping,避免降低ES的性能
bootstrap.mlockall: true
# 缓存类型设置为Soft Reference,只有当内存不够时才会进行回收
index.cache.field.max_size: 50000
index.cache.field.expire: 10m
index.cache.field.type: soft

来自:建造者说

elasticsearch三个重要的优化

1、内存优化
在bin/elasticsearch.in.sh中进行配置
修改配置项为尽量大的内存:
ES_MIN_MEM=8g
ES_MAX_MEM=8g
两者最好改成一样的,否则容易引发长时间GC(stop-the-world)

elasticsearch默认使用的GC是CMS GC
如果你的内存大小超过6G,CMS是不给力的,容易出现stop-the-world
建议使用G1 GC
注释掉:
JAVA_OPTS=”$JAVA_OPTS -XX:+UseParNewGC”
JAVA_OPTS=”$JAVA_OPTS -XX:+UseConcMarkSweepGC”

JAVA_OPTS=”$JAVA_OPTS -XX:CMSInitiatingOccupancyFraction=75″
JAVA_OPTS=”$JAVA_OPTS -XX:+UseCMSInitiatingOccupancyOnly”
修改为:
JAVA_OPTS=”$JAVA_OPTS -XX:+UseG1GC”
JAVA_OPTS=”$JAVA_OPTS -XX:MaxGCPauseMillis=200″

如果G1 GC优点是减少stop-the-world在几率,但是CPU占有率高。
需要更优化的性能,你可以参考
http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/G1GettingStarted/index.html

2、合理配置主节点和数据节点
配置文件:conf/elasticsearch.yaml
node.master: true
node.data: true

1) 当master为false,而data为true时,会对该节点产生严重负荷;
2) 当master为true,而data为false时,该节点作为一个协调者;
3) 当master为false,data也为false时,该节点就变成了一个负载均衡器。

3、设置合理的刷新时间
建立的索引,不会立马查到,这是为什么elasticsearch为near-real-time的原因
需要配置index.refresh_interval参数,默认是1s。
你可以像
http://zhaoyanblog.com/archives/299.html
文件中一样,调用接口配置
也可以直接写到conf/elasticsearch.yaml文件中
index.refresh_interval:1s
这样所有新建的索引都使用这个刷新频率。

ElasticSearch优化的一些方法

1. 多线程程序插入,可以根据服务器情况开启多个线程index
速度可以提高n倍, n>=2

2. 如果有多台机器,可以以每台设置n个shards的方式,根据业务情况,可以考虑取消replias
curl -XPUT ‘http://10.1.*.*:9200/dw-search/’ -d ‘{
“settings” : {
“number_of_shards” : 20,
“number_of_replicas” : 0
}
}’
这里设置20个shards, 复制为0,如果需要replicas,可以完成index后再修改为replicas>=1
原文:http://www.elasticsearch.org/guide/reference/api/admin-indices-create-index.html

3. 提高ES占用内存
内存适当调大,初始是256M, 最大1G,
调大后,最小和最大一样,避免GC, 并根据机器情况,设置内存大小,
$ bin/elasticsearch -f -Xmx4g -Xms4g -Des.index.storage.type=memory
原文:http://www.elasticsearch.org/guide/reference/setup/installation.html

4. 减少shard刷新间隔
curl -XPUT ‘http://10.1.*.*:9200/dw-search/_settings’ -d ‘{
“index” : {
“refresh_interval” : “-1”
}
}’

完成bulk插入后再修改为初始值
curl -XPUT ‘http://10.1.*.*:9200/dw-search/_settings’ -d ‘{
“index” : {
“refresh_interval” : “1s”
}
}’

5. 设置一个shard的段segment最大数
可以减少段文件数,提高查询速度
curl -XPOST ‘http://10.1.*.*:9200/dw-search/_optimize?max_num_segments=5’
注意:有时候可能需要多次执行
原文:http://www.elasticsearch.org/guide/reference/api/admin-indices-update-settings.html
原文:http://www.elasticsearch.org/guide/reference/index-modules/merge.html

6. 去掉mapping中_all域
Index中默认会有_all的域,这个会给查询带来方便,但是会增加索引时间和索引尺寸
“_all” : {“enabled” : false}
原文:http://www.elasticsearch.org/guide/reference/mapping/all-field.html
curl -XPOST ‘http://10.1.*.*:9200/dw-search/pt_normal/_mapping’ –data-binary @pt_normal_properties.mapping

7. 设置source为压缩模式或者disable
compress=true这个能大大减少index的尺寸
disable将直接没有_source域

8. 增加merge.policy.merge_factor数
设置merge.policy.merge_factor到30,初始是10
增加这个数需要更多的内存,bulk index可以调大这个值.
如果是即时索引,应该调小这个值
原文:http://www.elasticsearch.org/guide/reference/index-modules/merge.html

9. 修改Client获得方式为
Node node = nodeBuilder().client(true).node();
Client client = node.client()
相比transport client更快
测试效果,速度提高不明朗,且报错。去除