
# 通过SpringBoot接入集群
在使用Elasticsearch进行数据查询和管理时，如果直接与Elasticsearch的底层API进行交互，会增加开发复杂度，还可能导致代码维护困难。CSS服务的Elasticsearch集群支持通过Spring Data Elasticsearch（Spring Boot生态的Elasticsearch集成组件）进行数据查询和管理。该组件基于Elasticsearch官方Java API封装，开发者可通过Spring的Repository接口或原生查询DSL高效访问集群，无需直接处理底层API。Spring Boot的详细使用指导请参见[Spring Boot](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/)。
#### 前提条件
- Elasticsearch集群处于可用状态。
- 运行Java代码的服务器与Elasticsearch集群之间网络互通。
- 根据集群的网络配置方式，获取集群的访问地址，操作指导请参见[获取集群访问地址](https://support.huaweicloud.com/usermanual-css/css_01_0190.html#ZH-CN_TOPIC_0000001975823337__section855085010198)。
- 确认服务器已安装Java，要求JDK版本为1.8及以上，JDK1.8官网下载地址：[Java Downloads](https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html)。
- 确认Elasticsearch Rest High Level Client版本：为确保更好的兼容性，建议使用与Elasticsearch集群同版本的Java客户端连接Elasticsearch集群。 本文以2.5.5版本Spring Boot为例，对应的Spring Data Elasticsearch版本是4.2.x，连接的是7.10.2版本的Elasticsearch集群。
  
 
#### 准备工作
1. 确认Spring Boot版本是否满足兼容性要求，可以参考[官方兼容性列表](https://docs.spring.io/spring-data/elasticsearch/reference/elasticsearch/versions.html)。
   本文以2.5.5版本Spring Boot为例，对应的Spring Data Elasticsearch版本是4.2.x，访问是Elasticsearch 7.10.2。
   
2. 创建SpringBoot项目。
3. 引入Java依赖。通过Maven方式引入apache版本。
   ```
   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.5.5</version>
   </parent>
   <dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
       </dependency>
       <dependency>
           <groupId>org.elasticsearch.client</groupId>
           <artifactId>elasticsearch-rest-high-level-client</artifactId>
           <version>7.10.2</version>
       </dependency>
   </dependencies>
   ```
   
 
#### 接入集群
当使用Spring Boot访问不同安全类型的Elasticsearch集群时，示例代码会有差异，请根据实际业务场景选择参考文档。
表1接入集群的场景介绍 
| Elasticsearch集群安全类型                                              | 是否加载安全证书 | 参考文档                                                                                                              |
|:---|:---|:---|
| 非安全模式 安全模式+HTTP协议 | -        | [Spring Boot连接HTTP协议的集群]         |
| 安全模式+HTTPS协议                                                     | 否        | [Spring Boot连接HTTPS协议的集群（无证书）]  |
| 安全模式+HTTPS协议                                                     | 是        | [Spring Boot连接HTTPS协议的集群（加载证书）] |
   
 #### Spring Boot连接HTTP协议的集群
使用Spring Boot连接非安全模式的Elasticsearch集群或安全模式且未启用HTTPS协议的Elasticsearch集群。
1. 配置文件"application.properties"。
   ```
   elasticsearch.url=host1:9200,host2:9200
   //非安全集群不用配置如下两行。
   elasticsearch.username=username
   elasticsearch.password=password
   ```
   表2配置参数说明 
   | 参数       | 描述        |
   |:---|:---|
   | host     | 集群的访问地址。  |
   | username | 访问集群的用户名。 |
   | password | 用户名对应的密码。 |
      
   
2. 配置客户端代码。
   ```
   package com.xxx.configuration;
   import org.elasticsearch.client.RestHighLevelClient;
   import org.springframework.beans.factory.annotation.Value;
   import org.springframework.context.annotation.Bean;
   import org.springframework.context.annotation.ComponentScan;
   import org.springframework.context.annotation.Configuration;
   import org.springframework.data.elasticsearch.client.ClientConfiguration;
   import org.springframework.data.elasticsearch.client.RestClients;
   import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
   import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
   @Configuration
   // com.xxx.repository为仓库目录，通过extends org.springframework.data.elasticsearch.repository.ElasticsearchRepository进行具体定义
   @EnableElasticsearchRepositories(basePackages = "com.xxx.repository")
   // com.xxx为项目目录，例如com.company.project
   @ComponentScan(basePackages = "com.xxx")
   public class Config extends AbstractElasticsearchConfiguration {
       @Value("${elasticsearch.url}")
       public String elasticsearchUrl;
       // 非安全集群不用配置如下两个参数
       @Value("${elasticsearch.username}")
       public String elasticsearchUsername;
       @Value("${elasticsearch.password}")
       public String elasticsearchPassword;
       @Override
       @Bean
       public RestHighLevelClient elasticsearchClient() {
           final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
               .connectedTo(StringHostParse(elasticsearchUrl))
               // 非安全集群无需配置withBasicAuth
               .withBasicAuth(elasticsearchUsername, elasticsearchPassword)
               .build();
           return RestClients.create(clientConfiguration).rest();
       }
       private String[] StringHostParse(String hostAndPorts) {
           return hostAndPorts.split(",");
       }
   }
   ```
   
3. 当SpringBoot应用正常启动，则表示集群连接成功。
 
 #### Spring Boot连接HTTPS协议的集群（无证书）
使用Spring Boot在不加载安全证书的情况下连接安全模式+HTTPS协议的Elasticsearch集群。
1. 配置文件"application.properties"。
   ```
   elasticsearch.url=host1:9200,host2:9200
   elasticsearch.username=username
   elasticsearch.password=password
   ```
   表3配置参数说明 
   | 参数       | 描述        |
   |:---|:---|
   | host     | 集群的访问地址。  |
   | username | 访问集群的用户名。 |
   | password | 用户名对应的密码。 |
      
   
2. 配置客户端代码。
   ```
   package com.xxx.configuration;
   import org.elasticsearch.client.RestHighLevelClient;
   import org.springframework.beans.factory.annotation.Value;
   import org.springframework.context.annotation.Bean;
   import org.springframework.context.annotation.ComponentScan;
   import org.springframework.context.annotation.Configuration;
   import org.springframework.data.elasticsearch.client.ClientConfiguration;
   import org.springframework.data.elasticsearch.client.RestClients;
   import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
   import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
   import java.security.KeyManagementException;
   import java.security.NoSuchAlgorithmException;
   import java.security.SecureRandom;
   import java.security.cert.CertificateException;
   import java.security.cert.X509Certificate;
   import javax.net.ssl.HostnameVerifier;
   import javax.net.ssl.SSLContext;
   import javax.net.ssl.SSLSession;
   import javax.net.ssl.TrustManager;
   import javax.net.ssl.X509TrustManager;
   @Configuration
   // com.xxx.repository为仓库目录，通过extends org.springframework.data.elasticsearch.repository.ElasticsearchRepository进行具体定义
   @EnableElasticsearchRepositories(basePackages = "com.xxx.repository")
   // com.xxx为项目目录，例如com.company.project
   @ComponentScan(basePackages = "com.xxx")
   public class Config extends AbstractElasticsearchConfiguration {
       @Value("${elasticsearch.url}")
       public String elasticsearchUrl;
       @Value("${elasticsearch.username}")
       public String elasticsearchUsername;
       @Value("${elasticsearch.password}")
       public String elasticsearchPassword;
       @Override
       @Bean
       public RestHighLevelClient elasticsearchClient() {
           SSLContext sc = null;
           try {
               sc = SSLContext.getInstance("SSL");
               sc.init(null, trustAllCerts, new SecureRandom());
           } catch (KeyManagementException | NoSuchAlgorithmException e) {
               e.printStackTrace();
           }
           final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
               .connectedTo(StringHostParse(elasticsearchUrl))
               .usingSsl(sc, new NullHostNameVerifier())
               .withBasicAuth(elasticsearchUsername, elasticsearchPassword)
               .build();
           return RestClients.create(clientConfiguration).rest();
       }
       private String[] StringHostParse(String hostAndPorts) {
           return hostAndPorts.split(",");
       }
       public static TrustManager[] trustAllCerts = new TrustManager[] {
           new X509TrustManager() {
               @Override
               public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
               }
               @Override
               public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
               }
               @Override
               public X509Certificate[] getAcceptedIssuers() {
                   return null;
               }
           }
       };
       public static class NullHostNameVerifier implements HostnameVerifier {
           @Override
           public boolean verify(String arg0, SSLSession arg1) {
               return true;
           }
       }
   }
   ```
   
3. 当SpringBoot应用正常启动，则表示集群连接成功。
 
 #### Spring Boot连接HTTPS协议的集群（加载证书）
使用Spring Boot在加载安全证书的情况下连接安全模式+HTTPS协议的Elasticsearch集群。
1. [获取并上传安全证书]。
2. 配置文件"application.properties"。
   ```
   elasticsearch.url=host1:9200,host2:9200
   elasticsearch.username=username
   elasticsearch.password=password
   ```
   表4配置参数说明 
   | 参数       | 描述        |
   |:---|:---|
   | host     | 集群的访问地址。  |
   | username | 访问集群的用户名。 |
   | password | 用户名对应的密码。 |
      
   
3. 配置客户端代码。
   ![](https://support.huaweicloud.com/usermanual-css/public_sys-resources/note_3.0-zh-cn.png)
   - com.xxx为项目目录，例如com.company.project。
   
   - com.xxx.repository为仓库目录，通过extends org.springframework.data.elasticsearch.repository.ElasticsearchRepository进行具体定义。
    
   ```
   package com.xxx.configuration;
   import org.elasticsearch.client.RestHighLevelClient;
   import org.springframework.beans.factory.annotation.Value;
   import org.springframework.context.annotation.Bean;
   import org.springframework.context.annotation.ComponentScan;
   import org.springframework.context.annotation.Configuration;
   import org.springframework.data.elasticsearch.client.ClientConfiguration;
   import org.springframework.data.elasticsearch.client.RestClients;
   import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
   import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
   import java.io.File;
   import java.io.FileInputStream;
   import java.io.InputStream;
   import java.security.KeyStore;
   import java.security.SecureRandom;
   import java.security.cert.CertificateException;
   import java.security.cert.X509Certificate;
   import javax.net.ssl.HostnameVerifier;
   import javax.net.ssl.SSLContext;
   import javax.net.ssl.SSLSession;
   import javax.net.ssl.TrustManager;
   import javax.net.ssl.TrustManagerFactory;
   import javax.net.ssl.X509TrustManager;
   @Configuration
   // com.xxx.repository为仓库目录，通过extends org.springframework.data.elasticsearch.repository.ElasticsearchRepository进行具体定义
   @EnableElasticsearchRepositories(basePackages = "com.xxx.repository")
   // com.xxx为项目目录，例如com.company.project
   @ComponentScan(basePackages = "com.xxx")
   public class Config extends AbstractElasticsearchConfiguration {
       @Value("${elasticsearch.url}")
       public String elasticsearchUrl;
       @Value("${elasticsearch.username}")
       public String elasticsearchUsername;
       @Value("${elasticsearch.password}")
       public String elasticsearchPassword;
       @Override
       @Bean
       public RestHighLevelClient elasticsearchClient() {
           SSLContext sc = null;
           try {
               // certFilePath和certPassword是安全证书的存放路径及其密码
               TrustManager[] tm = {new MyX509TrustManager(certFilePath, certPassword)};
               sc = SSLContext.getInstance("SSL", "SunJSSE");
               sc.init(null, tm, new SecureRandom());
           } catch (Exception e) {
               e.printStackTrace();
           }
           final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
               .connectedTo(StringHostParse(elasticsearchUrl))
               .usingSsl(sc, new NullHostNameVerifier())
               .withBasicAuth(elasticsearchUsername, elasticsearchPassword)
               .build();
           return RestClients.create(clientConfiguration).rest();
       }
       private String[] StringHostParse(String hostAndPorts) {
           return hostAndPorts.split(",");
       }
       public static class MyX509TrustManager implements X509TrustManager {
           X509TrustManager sunJSSEX509TrustManager;
           MyX509TrustManager(String certFilePath, String certPassword) throws Exception {
               File file = new File(certFilePath);
               if (!file.isFile()) {
                   throw new Exception("Wrong Certification Path");
               }
               System.out.println("Loading KeyStore " + file + "...");
               InputStream in = new FileInputStream(file);
               KeyStore ks = KeyStore.getInstance("JKS");
               ks.load(in, certPassword.toCharArray());
               TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509", "SunJSSE");
               tmf.init(ks);
               TrustManager[] tms = tmf.getTrustManagers();
               for (TrustManager tm : tms) {
                   if (tm instanceof X509TrustManager) {
                       sunJSSEX509TrustManager = (X509TrustManager) tm;
                       return;
                   }
               }
               throw new Exception("Couldn't initialize");
           }
           @Override
           public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
           }
           @Override
           public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
           }
           @Override
           public X509Certificate[] getAcceptedIssuers() {
               return new X509Certificate[0];
           }
       }
       public static class NullHostNameVerifier implements HostnameVerifier {
           @Override
           public boolean verify(String arg0, SSLSession arg1) {
               return true;
           }
       }
   }
   ```
   
4. 当SpringBoot应用正常启动，则表示集群连接成功。
 
 #### 获取并上传安全证书
当接入安全模式+HTTPS协议的Elasticsearch集群时，如需加载安全证书则可以参考如下步骤获取安全证书，并上传至客户端。
1. 获取安全证书（CloudSearchService.cer）。
   1. 登录[云搜索服务管理控制台](https://console.huaweicloud.com/elasticsearch/)。
   
   2. 在左侧导航栏，选择"集群管理 \> Elasticsearch"。
   
   3. 在集群列表，单击目标集群名称，进入集群详情页。
   
   4. 选择"概览"页签，在"网络信息"下方，单击"HTTPS访问"的"下载证书"获取安全证书。
      图1下载安全证书   
      ![](https://support.huaweicloud.com/usermanual-css/images/zh-cn_image_0000002412660693.png) 
    
2. 转换安全证书（CloudSearchService.cer）。将下载的安全证书上传到客户端机器上，使用keytool工具将".cer"证书转换成Java可以读取的".jks"证书格式。
   - 在Linux系统中，执行如下命令转换证书。
     ```
     keytool -import -alias newname -keystore ./truststore.jks -file ./CloudSearchService.cer
     ```
     
   
   - 在Windows系统中，执行如下命令转换证书。
     ```
     keytool -import -alias newname -keystore .\truststore.jks -file .\CloudSearchService.cer
     ```
     
   
   
   其中，*newname*是由用户自定义的证书名称。
   该命令执行后，会提示设置证书密码，并确认密码。请保存该密码，后续接入集群会使用。
   
 
