elasticsearch用java实现分布式服务

本文介绍了一种改进的Elasticsearch与Spring Data集成方案,通过重构ElasticsearchTemplate,实现更灵活的数据检索功能。文章详细阐述了两种部署方案,并提供了一个用于监听并处理消息队列中数据变化的实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于上一遍博客中elasticsearch5.x在linux上分布式安装(多节点) 装的elasticsearch服务比较新,所以将elasticsearch单独做成一个服务运行,需要用的时候用路由转发请求这个服务。

因为elasticsearch版本比较新,所以用到了spring data的里程版本,具体的pom.xml配置和当前这个项目 可以去我github主页下载(https://github.com/lh2420124680/ElasticServer)。

elasticsearch底层用的是lucene,它的搜索速度是非常快的。

在实际的应用中我们有两种部署方案,分别应对不同的业务场景:

①实时保存数据入索引库

       当用户保存数据的时候,保存一份到数据库中,当数据库保存成功后,将保存的数据发送到消息队里中(本项目中的消息中间件用的是activeMQ),然后当消息队列监听到有消息发过过来的时候,接受到消息,也就是接受到保存的数据,然后消息监听器执行elasticsearch方法将保存到数据库中的数据同时也保存到对应的索引库中。这种方法对原来的代码侵入少,因为elasticsearch的服务是单独的一个项目。

        流程:前端将要插入的数据发送给后台 ->  插入数据库成功 -> 前端执行发送消息队列的方法 -> 发送的数据进入消息队列 -> 消息监听器接收到消息后将数据插入到对应的索引库中。

        本方法适用查询实时性并且结构简单的数据。

②每天定时从数据库中将数据同步到索引库中。

       这种方法需要用到elastic旗下的一些插件,可以参考本博客中的 elasticsearch与oracle数据库数据同步

        本方法适用查询非实时性但结构复杂的数据。


本篇博客主要用的是第一个方法,对spring data中的ElasticsearchTemplate进行了重构,因为ElasticsearchTemplate对于返回值类型依赖实体,并且实体需要用@document注解才能使用,所以本项目中对ElasticsearchTemplate源码进行了重构,将发送参数改为json对象的字符串,返回结果为List<Map>这样的类型,这样就不用每次做新业务时要用到elasticsearch的时候去新增当前业务实体从而去修改代码,重构后只用一次部署,后面就可以适用多个业务场景。


下面会列出一些需要注意的点,需要深入研究的朋友可以去我的githib上下载该项目(https://github.com/lh2420124680/ElasticServer)。

elasticsearch主要的配置文件:com-zlb-elastic.xml


这里我没有用ElasticsearchTemplate,而是用的自定义的elasticObjectTemplate类,让这个类继承ElasticsearchTemplate类,然后重写父类。


然后修改了ElasticsearchTemplate类中的queryForList方法。(父类中的只有查询对象和实体对象两个参数)


本项目v1.0只是对单个插入到索引库的方法进行了重构,后面会慢慢重构ElasticsearchTemplate所有的主要方法。


这里我们需要将传过来的json对象转为object数组,因为父类中会用到


另外我们还需要重构一个类ResultsMapper,用于索引的返回结果对象。

ResultsMapper是用于ElasticsearchTemplate类中返回结果对象的处理类,他返回的List为传输过来的实体泛型,例如当我查询的时候传输后来为Man类,那么他返回就是List<Man>,因为本次重构去掉了实体,用Map来代替实体模型,所以我们也许需要重写ResultsMapper类,但是ResultsMapper是一个接口,我们找到DefaultResultMapper类实现了这个ResultsMapper接口,所有我们重写一个DefaultResultObjectMapper类去继承DefaultResultMapper这个类,然后主要重写mapEntity方法。



以上就是对源码的重写的一些要点,虽然说的不是很清楚,但是看过几遍ElasticsearchTemplate的源码就会理解。


消息监听器,监听到数据就调用自定义的ElasticObjectTemplate方法去插入到索引库中。

package com.zjy.jms;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zjy.biz.ElasticBiz;
import com.zjy.helper.ElasticObjectTemplate;
import com.zjy.helper.EscapeHelper;
import com.zjy.helper.StringUtils;

import java.io.UnsupportedEncodingException;
import java.util.Map;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

@Component
public class ElasticReceiver implements MessageListener {

	private ElasticObjectTemplate elasticObjectTemplate;

	public ElasticObjectTemplate getElasticObjectTemplate() {
		return elasticObjectTemplate;
	}

	public void setElasticObjectTemplate(ElasticObjectTemplate elasticObjectTemplate) {
		this.elasticObjectTemplate = elasticObjectTemplate;
	}

	public void onMessage(Message message) {
		try {
			String msg = ((TextMessage) message).getText();
			String data = java.net.URLDecoder.decode(msg, "UTF-8");
			
			String unescape = EscapeHelper.unescape(data);
			
			JSONObject parseObject = JSON.parseObject(unescape);
			String dataId = StringUtils.GetGUID();
			String indexName = parseObject.getString("indexName");
			String typeName = parseObject.getString("typeName");
			String entity = parseObject.getString("entity");
			
			IndexQuery indexQuery = new IndexQueryBuilder().withId(dataId).withIndexName(indexName).withType(typeName).withObject(entity).build();  
			String index = elasticObjectTemplate.index(indexQuery);
			System.out.println("indexName:"+indexName + index);
		} catch (JMSException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}

}

elasticsearch查询,删除的服务接口。

package com.zjy.service;

import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSON;
import com.zjy.helper.DataConvertHelper;
import com.zjy.helper.ElasticObjectTemplate;
import com.zjy.helper.EscapeHelper;
import com.zjy.helper.ListResult;
import com.zjy.helper.StringUtils;

@RestController
@RequestMapping(value = "/ElasticService")
public class ElasticService {

	@Autowired
	private ElasticObjectTemplate elasticObjectTemplate;
	
	/**
	 * @param request
	 * @return
	 */
	@RequestMapping(value = "/queryResource.ashx", method = RequestMethod.GET)
	public ListResult<Map<String, Object>> queryResource(HttpServletRequest request) {
		Map<String, Object> where = DataConvertHelper.getRequestParams(request);
		String indexName = where.get("indexName").toString();
		String typeName = where.get("typeName").toString();
		
		// 创建搜索条件对象(条件   参数名 + "_jz" 为精准匹配, 参数名 + "_mh" 为模糊匹配)
		BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
		String key = "";
		String value = "";
		String mode = "";
		String reallyParam = "";
		for(Map.Entry<String, Object> map : where.entrySet()) {
			key = map.getKey();
			value = map.getValue().toString();
			mode = key.substring(key.length()-3);
			reallyParam = key.substring(0, key.length()-3);
			if ("_jz".equals(mode)) { // 该条件为精准查询
				boolQuery.must(QueryBuilders.termQuery(reallyParam, value));
			} else if ("_mh".equals(mode)) { //  该条件为模糊查询
				boolQuery.must(QueryBuilders.matchQuery(reallyParam, value));
			}
		}
		
		// 创建排序对象 (0是倒叙,1是正序)
		Object sort = where.get("sort");
		Object sortType = where.get("sortType");
		SortBuilder sortBuilder = null;
		if (!StringUtils.IsEmptyOrNull(sort) && !StringUtils.IsEmptyOrNull(sortType)) {
			if ("0".equals(sortType)) { // 倒叙
				sortBuilder = SortBuilders.fieldSort(sort.toString()).order(SortOrder.DESC);
			} else { // 正序
				sortBuilder = SortBuilders.fieldSort(sort.toString()).order(SortOrder.ASC);
			}
		}
		
		// 分页数据
		int pageIndex = StringUtils.IsEmptyOrNull(where.get("pageIndex")) ? 1 : Integer.valueOf(where.get("pageIndex").toString());
		int pageSize = StringUtils.IsEmptyOrNull(where.get("pageSize")) ? 10 : Integer.valueOf(where.get("pageSize").toString());
		
		// 创建SearchQuery查询对象 判断排序对象是否空,是空就不创建排序对象
		SearchQuery searchQuery = null;
		if (sortBuilder == null) {
			searchQuery = new NativeSearchQueryBuilder()
					.withQuery(boolQuery)
					.withPageable(PageRequest.of((pageIndex-1)*pageSize, pageSize))
					.build();
		} else {
			searchQuery = new NativeSearchQueryBuilder()
					.withQuery(boolQuery)
					.withSort(sortBuilder)
					.withPageable(PageRequest.of(pageIndex, pageSize))
					.build();
		}
		List<Map<String, Object>> list = elasticObjectTemplate.queryForList(searchQuery, indexName, typeName);
		
		searchQuery = new NativeSearchQueryBuilder()
				.withQuery(boolQuery)
				.withIndices(indexName)
				.withTypes(typeName)
				.build();
		long count = elasticObjectTemplate.count(searchQuery);
		
		ListResult<Map<String, Object>> result = new ListResult<Map<String, Object>>();
		result.setRows(list);
		result.setTotal((int)count);
		result.setPageindex(pageIndex);
		result.setPages(pageSize);
		return result;
	}
	@RequestMapping(value = "/delete.ashx", method = RequestMethod.POST)
	public boolean insert(HttpServletRequest request) {
		Map<String, Object> where = DataConvertHelper.getRequestParams(request);
		String indexName = where.get("indexName").toString();
		String typeName = where.get("typeName").toString();
		String gid = where.get("gid").toString();
		elasticObjectTemplate.delete(indexName, typeName, gid);
		return false;
	}
	
}

对于前端传输的参数的接口规则可以参考我写的elasticsearch服务api接口文档。(文档地址见图下面的链接)


elasticsearch服务api接口文档下载地址:链接:https://pan.baidu.com/s/1bap8LsjH-GkoT2xJuWZcsw 密码:6tn3

代码地址:https://github.com/lh2420124680/ElasticServer





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

翅膀君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值