elasticsearch环境搭建与工具选择

1. 项目背景

    工程文件中的价格数据需要保存归档,可在新的工程文件中参考历史价格。为了实现快速搜索,选择用elasticSearch保存和查询数据。

2. 版本1.0 httpClient封装

    初次接触es,经了解es可以通过RESTful API with JSON over HTTP方式交互,项目需求相对简单,通过自行拼装api方式实现对es的操作,采用这种方式,可以快速实现需求。
    该方式优点在于自由度高,学习成本较低,可以快速实现简单需求。缺点在于需要进行大量的json字符请求拼装,复杂操作封装难度较高,扩展性差,没有发挥java语言优势。

3. 版本2.0 springboot + jpa

    1.0版本自行封装请求,进行了较多字符串拼接操作,实现难度较高,扩展困难,开发周期相对较长,因此考虑借用已有框架操作es。项目本身基于springboot,springboot支持对es的操作,所以2.0版本采用springboot+jpa的方式实现。
在项目中添加相应的Maven依赖,开始基本使用。

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
 </dependency>

实体类

@Data
@NoArgsConstructor
public class BQItemDocument {

    String code;
    String description;
    Double price;
    String unit;

}

repository类

public interface CustomerRepository extends ElasticsearchRepository<BQItemDocument, String> {

    public List<BQItemDocument> findByCode(String code);

    public List<BQItemDocument> findByDescription(String description);

}

    采用springboot+jpa方式实现简单,从编码层面该实现方式封装了es特性,采用jpa操作es和采用jpa操作MySQL、mongodb区别不大,对于熟悉jpa的人来说比较友好。持续使用一段时间后,发现该使用方式存在一定的问题。
- 版本问题

Spring Boot Version (x)Spring Data Elasticsearch Version (y)Elasticsearch Version (z)
x <= 1.3.5y <= 1.3.4z <= 1.7.2*
x >= 1.4.x2.0.0 <=y < 5.0.0**2.0.0 <= z < 5.0.0**

    根据版本信息表,项目基于springboot 1.5.9版本,对应的es版本不能高于5,新版本es已更新到6.2+,不能使用新版的es的特性,例如deleteByQuery等,功能不全面,使用不方便
- entity设计和mapping映射问题
    采用该方式需要进行实体类设计,需要针对文件提取部分重要字段形成实体记录,项目文件以json文件保存,无法枚举所有字段,提取实体类后源文件会有部分字段丢失,丢失字段当前需求不影响,但是当需求变更时,可能会存在数据缺失问题。项目对于es的定位为数据仓库,希望能保存所有数据已被不时之需。另外,采用注解方式定义字段类型时出现不生效情况,无法解决。
- 个性化查询问题
    采用jpa方式进行查询时,进行简单查询相对容易,需要进行个性化的复杂查询时,实现比较复杂。
    基于上述考量,2.0版本放弃。

4. 版本3.0 TransportClient

    es基于java实现,使用java连接具有天生优势,java可以通过transportClient连接9300端口操作es,transportClient 提供了丰富的Java API,因此版本3.0出现。
在pom文件中引入相关依赖,依赖版本和使用的es对应即可。

<!-- elasticsearch -->
<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>${elasticsearch.version}</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>transport</artifactId>
    <version>${elasticsearch.version}</version>
</dependency>

ES连接配置
- application.properties文件


# ElasticSearch配置
elasticsearch.host=127.0.0.1
elasticsearch.port=9300
elasticsearch.cluster-name=elasticsearch
  • TransportClient配置类
@Configuration
public class ESConfig {

    @Value("${elasticsearch.host}")
    private String host;
    @Value("${elasticsearch.port}")
    private Integer port;
    @Value("${elasticsearch.cluster-name}")
    private String clusterName;

    @Bean
    public TransportClient transportClient() throws UnknownHostException {
        Settings settings = Settings.builder().put("cluster.name", clusterName).build();
        TransportClient client = new PreBuiltTransportClient(settings);
        String[] hosts = Strings.splitStringByCommaToArray(host);
        for (int i = 0; i < hosts.length; i++) {
            TransportAddress hostNode = new TransportAddress(InetAddress.getByName(hosts[i]), port);
            client.addTransportAddress(hostNode);
        }
        return client;
    }

}

mapping配置方式
    2.0版本中的mapping设置问题,在3.0版本中采用index template方式在进行定义,在创建新的index时定义好index中字段映射。
在项目resource文件夹下新建bqitem.json文件,设置关键字段映射类型,在项目启动时,检查index是否存在,不存在则依据temlate创建。

  • temlate定义
{
  "template":"bqitem",
  "index_patterns":["bqitem_*"],
  "mappings": {
    "bqitem": {
      "dynamic": true,
      "properties": {
        "code": {
          "type": "keyword" },
        "description": {
          "type": "text" },
        "descriptionOld": {
          "type": "text" },
        "spec": {
          "type": "text" },
        "ord": {
          "type": "double" },
        "date": {
          "type": "date",
          "format": "yyyy-MM-dd HH:mm:ss" }
      }
    }
  }
}
  • index初始化
@Component
public class InitESIndexCLRunner implements CommandLineRunner {

    @Autowired
    private TransportClient client;
    @Value("${company.name}")
    private String companyName;

    @Override
    public void run(String... args) throws Exception {
        HashMap<String, String> indexConfigMap = buildIndexConfigMap();

        CountDownLatch countDownLatch = new CountDownLatch(indexConfigMap.size());
        for (HashMap.Entry<String, String> entry : indexConfigMap.entrySet()) {
            new Thread(() -> {
                checkAndCreateIndex(client.admin(), entry.getKey(), entry.getValue());
                countDownLatch.countDown();
            }).start();
        }
        try {
            countDownLatch.await();
            System.out.println("index mapping config inited");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private HashMap<String, String> buildIndexConfigMap() {
        HashMap<String, String> map = Maps.newHashMap();
        map.put(getIndexName(ArchiveConsts.ITEM_INDEX_NAME), ArchiveConsts.ITEM_CONFIG_PATH);
        map.put(getIndexName(ArchiveConsts.DB_ITEM_INDEX_NAME), ArchiveConsts.DB_ITEM_CONFIG_PATH);
        return map;
    }

    private void checkAndCreateIndex(AdminClient adminClient, String index, String configPath) {
        if (!existIndex(adminClient, index)) {
            CreateIndexRequest createRequest = Requests.createIndexRequest(index);
            try {
                putTemplate(adminClient, index, configPath);
                CreateIndexResponse createResponse = adminClient.indices().create(createRequest).get();
                System.out.println(new StringBuilder().append(index).append(" mapping inited").toString());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private boolean existIndex(AdminClient adminClient, String index) {
        IndicesExistsRequest existsRequest = Requests.indicesExistsRequest(index);
        IndicesExistsResponse existsResponse = adminClient.indices().exists(existsRequest).actionGet();
        return existsResponse.isExists();
    }

    private void putTemplate(AdminClient adminClient, String index, String path) throws IOException {
        Resource res = new ClassPathResource(path);
        if (!res.exists()) {
            return;
        }

        InputStream inputStream = res.getInputStream();
        String content = IOUtils.toString(inputStream, Charsets.UTF_8);
        JSONObject jsonObject = JSONObject.parseObject(content);
        PutIndexTemplateRequest putTplRequest = adminClient.indices().preparePutTemplate(index).request().source(jsonObject.toString(), XContentType.JSON);
        PutIndexTemplateResponse response = adminClient.indices().putTemplate(putTplRequest).actionGet();
    }

    private String getIndexName(String index) {
        if (!Strings.isNullOrEmpty(companyName) && !companyName.equals("company.name")) {
            return index + "_" + companyName;
        }
        return index;
    }
}

简单使用
service层方法:获取全部文档,进行业务逻辑处理

@Service
public class QueryServiceImpl {

    @Autowired
    private TransportClient esClient;

    public String getAll(){
        SearchRequestBuilder searchRequestBuilder = esClient.prepareSearch(getIndexName());
        searchRequestBuilder.setScroll(new TimeValue(ESUtils.QueryScrollAliveTime)).setSize(ESUtils.QueryHitsSize);
        SearchResponse response = searchRequestBuilder.get();

        do {
            for (SearchHit hit : response.getHits().getHits()) {
                Map<String, Object> sourceMap = hit.getSourceAsMap();
                //处理业务逻辑  

            }
            response = esClient.prepareSearchScroll(response.getScrollId()).setScroll(new TimeValue(ESUtils.QueryScrollAliveTime)).execute().actionGet();
        } while (response.getHits().getHits().length != 0);

        return null;
    }

}

至此,3.0版本完成基本的基础设置、mapping映射、demo验证,可以进行业务逻辑的实现了。

5. 总结

从确定需求到选型es后,在不断学习es文档,编码实现功能过程中,面对全新的技术,确实走过了比较多的弯路,踩不不少坑,得到一定的经验和教训。
- 快速试错,发现问题后快速验证,及时调整方向;
- 选择比努力重要,特别是基础工具的选择,对后续业务逻辑的实现具有重大影响;
- 对于新接触的技术,尽量使用新的版本,多看官方文档。
- 路漫漫其修远兮。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值