elasticsearch集群搭建及springboot集成使用

结合实际参与项目中对ELK日志管理的部署实施使用以及一些前辈的相关参考资料,本文对elasticsearch的相关介绍、部分原理、集群搭建以及集成到springboot中进行部分数据的维护使用等功能点进行总结记录,希望能够给后来者以参考借鉴。文中如有遗漏不足之处,希望读者给予评论指正,谢谢阅读!

1. ES介绍

Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎。
Elasticsearch是一个基于Apache Lucene的开源搜索引擎,无论在开源还是专有领域,
Lucene可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。
Elasticsearch是使用Java编写并使用Lucene来建立索引并实现搜索功能,
它的目的是通过简单连贯的RESTful API让全文搜索变得简单并隐藏Lucene的复杂性。

2. 原理

2.1 核心概念

(1) 接近实时(Near Realtime NRT) 
Elasticsearch是一个接近实时的搜索平台。

(2) 集群(cluster) 
一个集群由一个唯一的名字标识,默认为“elasticsearch”。
集群名称非常重要,具有相同集群名的节点才会组成一个集群。集群名称可以在配置文件中指定。

(3) 节点(node) 
存储集群的数据,参与集群的索引和搜索功能。
像集群有名字,节点也有自己的名称,默认在启动时会以一个随机的UUID的前七个字符作为节点的名字,
也可以为其指定任意的名字。通过集群名在网络中发现同伴组成集群。一个节点也可是集群。

(4) 索引(index) 
一个索引是一个文档的集合,每个索引有唯一的名字,通过名字来操作它。
一个集群中可以有任意多个索引。索引类似于关系型数据库中Database的概念。

(5) 类型(type) 
指在一个索引中,可以索引不同类型的文档。从6.0.0 版本起已废弃,一个索引中只存放一类数据。
类型类似于关系型数据库中Table的概念。

(6)文档(document) 
被索引的一条数据,索引的基本信息单元,以JSON格式来表示。
文档类似于关系型数据库中Record的概念。
实际上一个文档除了用户定义的数据外,还包括_index、_type和_id字段。

(7) 分片(shards)
在创建一个索引时可以指定分成多少个分片来存储。
每个分片本身也是一个功能完善且独立的“索引”,可以被放置在集群的任意节点上。
分片的好处:(1)允许我们水平切分/扩展容量,
(2)可在多个分片上进行分布式的、并行的操作,提高系统的性能和吞吐量。
注意:分片数创建索引时指定,创建后不可改了。备份数可以随时改。

(8)副本(replicas) 
一个分片可以有多个副本。副本的好处:(1)高可用,一个主分片挂了,副本分片就顶上去;
(2)扩展搜索的并发能力、吞吐量。
搜索可以在所有的副本上并行运行。-高并发下副本也可搜索
ESRDBS
索引(index)数据库(database
类型(type)(ES6.0之后被废弃,es7中完全删除)表(table)
映射(mapping)表结构(schema)
文档(document)行(row)
字段(field)列(column)
倒排索引索引
查询DSLSQL
GET http://…SELECT * FROM table
PUT http://…UPDATE table SET
DELETE http://…DELETE

2.2 索引功能

Elasticsearch最关键的就是提供强大的索引能力,
Elasticsearch索引的精髓:一切设计都是为了提高搜索的性能。
Elasticsearch的索引思路:
将磁盘里的东西尽量搬进内存,减少磁盘随机读取次数(同时也利用磁盘顺序读特性),
结合各种奇技淫巧的压缩算法,用及其苛刻的态度使用内存。

倒排索引操作步骤:
(1)先将文档中包含的关键字全部提取出来
(2)然后再将关键字与文档的对应关系保存起来
(3)最后对关键字本身做索引排序

倒排索引形象理解可参考下列文章:
终于有人把Elasticsearch原理讲透了!.
Elasticsearch-基础介绍及索引原理分析.

2.3 ES特性

速度快、易扩展、弹性、灵活、操作简单、多语言客户端、X-Pack、hadoop/spark、开箱即用。
分布式:横向扩展非常灵活
全文检索:基于lucene的强大的全文检索能力;
近实时搜索和分析:数据进入ES,可达到近实时搜索,还可进行聚合分析
高可用:容错机制,自动发现新的或失败的节点,重组和重新平衡数据
模式自由:ES的动态mapping机制可以自动检测数据的结构和类型,创建索引并使数据可搜索。
RESTful API:JSON + HTTP

3. 分词器(analyzer)

搜索引擎的核心是倒排索引,而倒排索引的基础就是分词。
所谓分词可以简单理解为将一个完整的句子切割为一个个单词的过程。在 es 中单词对应英文为 term 。

分词器使用的两个情形:  
(1)Index time analysis.  创建或者更新文档时,会对文档进行分词
(2)Search time analysis.  查询时,对查询语句分词

查询时分词发生在用户查询时,ES 会即时地对用户输入的关键词进行分词,分词结果只存在内存中,
当查询结束时,分词结果也会随即消失。
而创建更新时分词发生在文档写入时,ES 会对文档进行分词后,将结果存入倒排索引,
该部分最终会以文件的形式存储于磁盘上,不会因查询结束或者 ES 重启而丢失。

ES 中处理分词的部分被称作分词器,英文是Analyzer,它决定了分词的规则。
ES 自带了很多默认的分词器,比如Standard、Keyword、Whitespace等等,默认是Standard。
当我们在读时或者写时分词时可以指定要使用的分词器。

(0)插入数据

POST indexName/type
{
   "name":"stono",
   "country":"China",
   "age":111,
   "date":"1999-11-11",
   "desc":"Bringing you face to face with heart-pounding animal action, mind-blowing ideas and connecting you to over sixty years of astounding natural history content"
}

(1)查询时分词实例

GET indexName/_search
{
	"query":{
		"match":{
			"desc":{
				"query":"China",
				 "analyzer": "standard"
			}
		}
	}
}

(2)创建时分词实例

PUT indexName
{
  "mappings": {
    "doc": {
      "properties": {
        "title":{
          "type": "text",
          "analyzer": "whitespace",
          "search_analyzer": "standard"
        }
      }
    }
  }
}

注意:

明确字段是否需要分词,不需要分词的字段将type设置为keyword,可以节省空间和提高写性能。

(3)查看分词结果

GET indexName/_analyze
{
  "analyzer": "standard",
  "text": "this is a test"
}

分词结果

{
  "tokens": [
    {
      "token": "this",
      "start_offset": 0,
      "end_offset": 4,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "is",
      "start_offset": 5,
      "end_offset": 7,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "a",
      "start_offset": 8,
      "end_offset": 9,
      "type": "<ALPHANUM>",
      "position": 2
    },
    {
      "token": "test",
      "start_offset": 10,
      "end_offset": 14,
      "type": "<ALPHANUM>",
      "position": 3
    }
  ]
}

(3)中文分词器

中文分词器

下载地址
	https://github.com/medcl/elasticsearch-analysis-ik/releases
    下载编译后的文件zip,解压后 复制到es安装包 plugins文件夹下,重启es即可

4. 集群搭建

4.1 下载文件

https://www.elastic.co/cn/downloads/elasticsearch

可下载windows版本和Linux X86_64版本,分别部署于windows server系统和Linux系统环境;

4.2 开启安全认证

1.生成证书

为Elasticsearch集群创建一个证书颁发机构。

./elasticsearch-certutil ca

全部回车就可以,不再对证书设置密码,生成证书文件elastic-stack-ca.p12

2.为集群中的每个节点生成证书和私钥

./elasticsearch-certutil cert --ca elastic-stack-ca.p12

一路直接回车,会生成第二个证书文件 elastic-certificates.p12

3.将证书拷贝到elasticsearch的每个节点下面config目录下

elastic-certificates.p12

4.配置elasticsearch.yml文件

xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate

xpack.security.transport.ssl.keystore.path: elastic-certificates.p12
xpack.security.transport.ssl.truststore.path: elastic-certificates.p12

5.如果在创建证书的过程中加了密码,需要将你的密码加入到你的Elasticsearch keystore中去。每个节点都需要

bin/elasticsearch-keystore add xpack.security.transport.ssl.keystore.secure_password
bin/elasticsearch-keystore add xpack.security.transport.ssl.truststore.secure_password

6.给认证的集群创建用户密码

	bin/elasticsearch-setup-passwords interactive

可以给ES中许多内置的用户创建密码

elastic 账号:拥有 superuser 角色,是内置的超级用户。
kibana 账号:拥有 kibana_system 角色,用户 kibana 用来连接 elasticsearch 并与之通信。
				Kibana 服务器以该用户身份提交请求以访问集群监视 API 和 .kibana 索引。不能访问 index。
logstash_system 账号:拥有 logstash_system 角色。用户 Logstash 在 Elasticsearch 中存储监控信息时使用。
beats_system账号:拥有 beats_system 角色。用户 Beats 在 Elasticsearch 中存储监控信息时使用。
elastic是超级用户,它可以做任何事情

4.2 调整配置

系统配置

vi /etc/security/limits.conf 
添加如下内容:
* soft nofile 65536
* hard nofile 65536
* soft nproc 4096
* hard nproc 4096
需重启服务生效

vi /etc/sysctl.conf 
添加下面配置: 
vm.max_map_count=655360 
执行命令,使配置起效
sysctl -p

ES配置,找到config路径下elasticsearch.yml配置

# 集群名称
cluster.name: es-cluster

#节点名称
node.name: node-1
node.attr.rack: r1

#数据存放路径以及日志存放路径
path.data: /home/u-0/es/data01/data
path.logs: /home/u-0/es/data01/logs

#节点对外暴露IP访问
network.host: 0.0.0.0

#Http访问端口号以及es集群通讯端口号
http.port: 9201
transport.tcp.port: 9301

# 集群节点
discovery.seed_hosts: ["192.168.65.146:9301", "192.168.65.148:9302","192.168.65.149:9303"]

# 主节点资格供选节点
cluster.initial_master_nodes: ["node-1", "node-2","node-3"]

# ssl相关配置
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate

xpack.security.transport.ssl.keystore.path: elastic-certificates.p12
xpack.security.transport.ssl.truststore.path: elastic-certificates.p12

4.3 启动服务

执行命令

./elasticsearch -d(-d后台启动)

4.4 服务以及集群状态验证

Restful Api接口访问节点

http://192.168.65.146:9201/_cluster/health?pretty

输入账号密码

{
  "cluster_name" : "es-cluster",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 3,
  "number_of_data_nodes" : 3,
  "active_primary_shards" : 1,
  "active_shards" : 2,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 100.0
}

集群状态正常运行

5. springboot集成客户端使用

5.1 引入jar包

客户端使用 RestHighLevelClient

    compile 'org.elasticsearch:elasticsearch:7.12.0'
    compile 'org.elasticsearch.client:elasticsearch-rest-high-level-client:7.12.0'
    compile 'org.elasticsearch.client:elasticsearch-rest-client:7.12.0'

5.2 客户端配置

配置文件增加配置项
elasticSearch.hosts=192.168.65.146:9200,192.168.65.148:9200,192.168.65.149:9200
elasticSearch.userName=elastic
elasticSearch.password=password

@Configuration
public class EsConfiguration {

    @Value("${elasticSearch.hosts}")
    String hosts;

    @Value("${elasticSearch.userName}")
    private String userName;

    @Value("${elasticSearch.password}")
    private String password;

    @Bean(name = "highLevelClient")
    public RestHighLevelClient highLevelClient(@Autowired RestClientBuilder restClientBuilder) {
        String[] hosts = this.hosts.split(",");
        HttpHost[] httpHosts = new HttpHost[hosts.length];
        for(int i=0;i<hosts.length;i++) {
            String host = hosts[i].split(":")[0];
            int port = Integer.parseInt(hosts[i].split(":")[1]);
            httpHosts[i] = new HttpHost(host, port, "http");
        }

        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));

        RestClientBuilder builder = RestClient.builder(httpHosts).setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
            @Override
            public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {
                requestConfigBuilder.setConnectTimeout(-1);
                requestConfigBuilder.setSocketTimeout(-1);
                requestConfigBuilder.setConnectionRequestTimeout(-1);
                return requestConfigBuilder;
            }
        }).setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
            @Override
            public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
                httpClientBuilder.disableAuthCaching();
                return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
            }
        });
        RestHighLevelClient client = new RestHighLevelClient(builder);
        return client;
    }

}

相关配置参数可根据实际服务环境进行调整

5.3 工具类

@Service
public class EsUtil {

    private static final Logger logger = LoggerFactory.getLogger(EsUtil.class);

    @Autowired
    private RestHighLevelClient highLevelClient;

   // 查询
    public Map<String,Object> query(String indexName, String module, Integer pageIndex, Integer pageSize,
                                           Date startTime, Date endTIme String account) {
        Map<String,Object> ret = new HashMap<>();
        List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
        SearchRequest searchRequest = new SearchRequest(indexName);
        queryBuilder(pageIndex, pageSize, module, searchRequest, startTime, endTIme, account);
        try {
            SearchResponse response = highLevelClient.search(searchRequest,RequestOptions.DEFAULT);
            for(SearchHit hit: response.getHits().getHits()){
                Map<String, Object> map = hit.getSourceAsMap();
                result.add(map);
            }
            long total = response.getHits().getTotalHits().value;
            ret.put("total",total);
            ret.put("result",result);
            return ret;
        } catch (IOException e) {
            logger.info(e.getMessage(),e);
        }
        return new HashMap<String,Object>();
    }

    private void queryBuilder(Integer pageIndex, Integer pageSize, String module,
                               SearchRequest searchRequest, Date beginTime, Date endTime,String name) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if(!StringUtils.isEmpty(module) ){
            MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("module",module);
            boolQueryBuilder.must(queryBuilder);
        }
        if(!StringUtils.isEmpty(account)){
            MatchQueryBuilder queryBuilder2 = QueryBuilders.matchQuery("userInfo.account",name);
            boolQueryBuilder.must(queryBuilder2);
         }
         // 分页
        if (pageIndex != 0 && pageSize != 0) {
            searchSourceBuilder.size(pageSize);
            if (pageIndex <= 0) {
                pageIndex = 0;
            }
            searchSourceBuilder.from((pageIndex - 1) * pageSize);
        }
        // 时间区间
        if(beginTime != null || endTime != null){
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("@timestamp");
            if(beginTime != null){
                rangeQueryBuilder.from(beginTime).timeZone("GMT+8");
            }
            if(endTime != null){
                rangeQueryBuilder.to(endTime).timeZone("GMT+8");
            }
            boolQueryBuilder.must(rangeQueryBuilder);
        }
       // 排序
        searchSourceBuilder.query(boolQueryBuilder).sort("@timestamp", SortOrder.DESC);
        searchRequest.source(searchSourceBuilder);
    }

    //创建索引
    public void testCreateIndex(String indexName) throws IOException {
        CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName);
        CreateIndexResponse response = highLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
        logger.info(response);
    }

    /**
     * 测试索引是否存在
     */
    public void testExistIndex(String indexName) throws IOException {
        GetIndexRequest request = new GetIndexRequest(indexName);
        boolean exists = highLevelClient.indices().exists(request, RequestOptions.DEFAULT);
        logger.info(exists);
    }

    /**
     * 删除索引
     */
    public void deleteIndex(String indexName) throws IOException {
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indexName);
        AcknowledgedResponse delete = highLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
        logger.info(delete.isAcknowledged());
    }

    /**
     * 测试添加文档
     */
    public void createDocument(String indexStr, Map map) throws IOException {
        IndexRequest request = new IndexRequest(indexStr);
        request.id("1");
        request.timeout(TimeValue.timeValueSeconds(1));
        request.timeout("1s");
        //将我们的数据放入请求,json
        request.source(map, XContentType.JSON);
        //客服端发送请求
        IndexResponse index = highLevelClient.index(request, RequestOptions.DEFAULT);
        logger.info((index.toString());
        //对应我们的命令返回状态
        logger.info(index.status());
    }

    //获取文档信息
    public void testGetDocument(String indexName, String id) throws IOException {
        GetRequest getRequest = new GetRequest(indexName, id);
        GetResponse response = highLevelClient.get(getRequest, RequestOptions.DEFAULT);
        //打印文档信息
        logger.info(response.getSourceAsString());
        logger.info(response);
    }
}

6. 参考

[1]. https://zhuanlan.zhihu.com/p/91821035
[2]. https://blog.csdn.net/star1210644725/article/details/107121850
[3]. https://developer.51cto.com/art/201904/594615.htm?pc
[4]. https://www.cnblogs.com/dreamroute/p/8484457.html

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我来回答一下关于如何在Spring Boot集成Elasticsearch的问题。 首先,你需要确保你的Spring Boot项目中已经引入了Spring Data Elasticsearch依赖。可以在项目的pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> ``` 接下来,你需要配置Elasticsearch客户端连接信息。在application.properties(或者application.yml)文件中添加以下配置: ```properties spring.data.elasticsearch.cluster-nodes=<elasticsearch服务器地址>:<端口号> ``` 配置完毕后,你可以在Spring Boot应用中使用Elasticsearch的Repository接口来访问Elasticsearch。 例如,创建一个Book实体类: ```java @Document(indexName = "book") public class Book { @Id private String id; private String name; private String author; private String description; // 省略getter和setter方法 } ``` 然后创建一个继承自ElasticsearchRepository的BookRepository接口: ```java public interface BookRepository extends ElasticsearchRepository<Book, String> { List<Book> findByName(String name); } ``` 在这个接口中,我们定义了一个根据书名查询书籍的方法。在使用时,可以直接注入这个接口,并调用其中的方法: ```java @Service public class BookService { @Autowired private BookRepository bookRepository; public List<Book> searchBooks(String name) { return bookRepository.findByName(name); } } ``` 以上就是在Spring Boot集成Elasticsearch的基本步骤。希望可以对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值