封装构建基于ES的Java API包

前言

前些日子,elastic公司成功上市了。旗下有一款优秀的开源搜索引擎ElasticSearch。

ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。

好了,扯远了,回归正题。今天我们使用Java对它的一些API进行封装,以使其API更具备灵活性。

完成对其简单的使用是比较简单的,我们今天把我们的一些调用代码封装,以便可以在各个项目中自由使用。这才是我们的根本目的。

今天,我们来构建一个工具包类。

环境准备

安装ElasticSearch,主要就是为了测试我们的代码,对于有现成环境的,如开发环境,测试环境上的ElasticSearch,这一步可以忽略。

  1. 首先请安装ElasticSearch,这里就不介绍了,我这里是安装的ElasticSearch最新版,6.4.0版本。https://www.elastic.co/downloads/elasticsearch 。安装成功后可以将其添加到环境变量中,然后启动。启动成功浏览器访问http://localhost:9200/ 会看到json信息。

  2. 安装Kibana,Kibana是ElasticSearch的可视化工具,可以方便的查看ElasticSearch及其运行状态。https://www.elastic.co/products/kibana 。我安装的也是最新版本,6.4.0. 安装成功后加入环境变量,然后启动,访问http://localhost:5601/ 可以看到Kibana界面。

  3. JDK版本为1.8

以上都为准备工作。

架构构建

org.elasticsearch.client.transport jar包已经有相关关于ES API的操作。

我们把它封装为专用jar包,建议使用Maven构建。如下:

Maven项目

首先,我们先建一个名叫 framework-es的Maven项目。如下。

建好各个package。

exception里面定义我们的异常。

factory里面用来生成esclient。

util里面放一些工具类。

vo里面可以放查询对象等。

property为配置文件。

test里面存放测试类。

各个package的名字可以自己定义,描述清目的即可。如下图。

upload successful

添加依赖

添加依赖,我的pom.xml依赖如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependencies>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>6.4.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
</dependencies>

思路

我的思路是构建一个单例的ESClient,用于与ES建立连接,以后增删改查等逻辑均使用此Client,保证资源的高效利用。这个也应当支持ES集群,有多个ES服务端也应当支持。而且地址应为可配置的。

话不多说,上代码,主要代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class ElasticSearchClientFactory {
private final static Logger logger = LoggerFactory.getLogger(ElasticSearchClientFactory.class);
private volatile TransportClient esClient;
//ES配置
private ElasticSearchConfiguration esConfig;
//校验多个ES地址的正则
private Pattern addRessPattern = Pattern.compile("^.+[:]\\d{1,5}\\s*(;.+[:]\\d{1,5}\\s*)*[;]?\\s*$");
public ElasticSearchClientFactory(final ElasticSearchConfiguration elasticSearchConfiguration){
this.esConfig=elasticSearchConfiguration;
}
/**
* 获取一个单例的ESClient
* @return
*/
public Client getEsClient(){
if(esClient==null){
synchronized (ElasticSearchClientFactory.class){
if(esClient==null){
logger.info("ElasticSearchClientFactory init start...");
try{
if(StringUtils.isNotBlank(esConfig.getLocalPropertiesPath())){
//获取ES配置信息
fillData();
}
logger.info("ESConfig is:{}",esConfig.toString());
//多个ES地址解析
List<HostAndPort> hostAndPortList = this.parseHostAndPortList(esConfig.getAddress());
TransportAddress [] transportAddress=new TransportAddress[hostAndPortList.size()];
for (int i = 0; i < hostAndPortList.size(); i++) {
transportAddress[i] = new TransportAddress(InetAddress.getByName(hostAndPortList.get(i).getIp()),hostAndPortList.get(i).getPort());
}
//节点名
String nodeName=esConfig.getNodeName()+ UUID.randomUUID();
String clusterName=esConfig.getClusterName();
Settings.Builder settingsBuilder = Settings.builder();
settingsBuilder.put("node.name", nodeName);
if(StringUtils.isNotBlank(clusterName)){
settingsBuilder.put("cluster.name", clusterName);
}
settingsBuilder.put("client.transport.sniff", true);
Settings settings = settingsBuilder.build();
TransportClient client = new PreBuiltTransportClient(settings);
//创建ESClient
esClient = client.addTransportAddresses(transportAddress);
logger.info("EalsticSearchClientFactory init is finished");
}catch(Exception e){
logger.error("EalsticSearchClientFactory create failed",e);
throw new ElasticSearchException("EalsticSearchClientFactory create faile",e);
}
}
}
}
return esClient;
}
//其他代码略
}

以上代码用于生成一个单例的ESClient类。

1
2
3
public class ElasticSearchConstants {
public final static String DEFAULT_PROPERTIES_PATH="property/es-config.properties";
}

同时默认配置文件为property/es-config.properties
可手动进行配置,且支持多个地址,地址写法为 127.0.0.1:9200;127.0.0.2:9300

这样,中间应使用;分割。

我们对外提供一个工具类,供使用者进行对数据的操作。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class ElasticSearchUtil {
private final static Logger logger = LoggerFactory.getLogger(ElasticSearchUtil.class);
private static final SerializerFeature[] featuresWithNullValue={SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullBooleanAsFalse,
SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullNumberAsZero, SerializerFeature.WriteNullStringAsEmpty};
private ElasticSearchClientFactory elasticSearchClientFactory;
public void setElasticSearchClientFactory(ElasticSearchClientFactory elasticSearchClientFactory) {
this.elasticSearchClientFactory = elasticSearchClientFactory;
}

public ElasticSearchUtil(ElasticSearchClientFactory elasticSearchClientFactory){
this.elasticSearchClientFactory=elasticSearchClientFactory;
}
public ElasticSearchUtil(String localPropertiesPath){
ElasticSearchConfiguration esConfig=new ElasticSearchConfiguration();
esConfig.setLocalPropertiesPath(localPropertiesPath);
this.elasticSearchClientFactory=new ElasticSearchClientFactory(esConfig);
}
public ElasticSearchUtil() {
ElasticSearchConfiguration esConfig = new ElasticSearchConfiguration();
esConfig.setLocalPropertiesPath(ElasticSearchConstants.DEFAULT_PROPERTIES_PATH);
this.elasticSearchClientFactory = new ElasticSearchClientFactory(esConfig);
}
public ElasticSearchUtil(ElasticSearchConfiguration esConfig) {
this.elasticSearchClientFactory = new ElasticSearchClientFactory(esConfig);
}
public Client getEsClient() {
return elasticSearchClientFactory.getEsClient();
}

/**
* 创建索引
* @param index
* @param type
* @param id
* @param isOnlyCreate
* @param jsonString
* @return
*/
protected boolean createDocument(String index,String type,String id,boolean isOnlyCreate,String jsonString){
IndexResponse indexResponse;
if(StringUtils.isBlank(id)){
indexResponse=elasticSearchClientFactory.getEsClient().prepareIndex(index,type).setCreate(isOnlyCreate).setSource(jsonString, XContentType.JSON).get();
}else{
indexResponse=elasticSearchClientFactory.getEsClient().prepareIndex(index,type,id).setCreate(isOnlyCreate).setSource(jsonString,XContentType.JSON).get();
}

if(logger.isDebugEnabled()){
String _index=indexResponse.getIndex();
String _type=indexResponse.getType();
String _id=indexResponse.getId();
long _version = indexResponse.getVersion();
boolean created = RestStatus.CREATED.equals(indexResponse.status());
logger.debug(String.format("createDocument index:%s,type:%s,id:%s,version:%s,created:%s", _index, _type, _id, _version, created));
}

return RestStatus.CREATED.equals(indexResponse.status());
}
//其他代码略
}

创建好客户端后,我们可以用它执行增删改查,我们在封装一个ESUtil类,用于执行该系列操作,暴露一些特定增删改查接口。如上代码。

注:其他代码略。

这样,完成代码后,工程项目如下图所示。

upload successful

ElasticSearchException 为自定义异常类。

ElasticSearchClientFactory为主要方法,用来构建一个单例的ESClient

ElasticSearchConfiguration为ES配置类

ElasticSearchConstants为常量类,里面存放配置文件的路径

HostAndPort为地址和端口的一个辅助Bean

ElasticSearchUtil为主要方法,用于对外提供服务(CRUD)

DocumentVo为辅助Bean

es-config.properties为配置文件

当我们把项目打包成jar包时,配置文件可以不用打包,这样引入其他项目后,在其他项目里配置配置文件即可。

测试

若ES的安装路径D:\Program Files\elasticsearch-6.4.0\bin成功配置到环境变量后,在命令行输入elasticsearch便可启动服务。

若Kibana的安装路径D:\Program Files\kibana-6.4.0-windows-x86_64成功配置到环境变量后,在命令行输入kibana便可启动服务。

访问http://127.0.0.1:5601/ 看到可视化界面。

编写测试类。

1
2
3
4
5
6
7
8
9
10
public class ESTest {
private static ElasticSearchUtil esUtil=new ElasticSearchUtil();
private static String index="user_index";
private static String type="user_type";
public static void main(String[] args) {
JSONObject js=new JSONObject();
js.put("1","2");
esUtil.insertDocument(index,type,"123",js);
}
}

查看Kibana界面可以看到我们的数据被添加进来了。

upload successful

结论

关于ElasticSearch及Kibana的具体使用我们以后再讲。

我们平时开发中,也可以创建类似的工具包,提高代码使用率,实现软件的高内聚低耦合,同时也是提高自己。

部分未展示代码请见我的GitHub地址: https://github.com/javazwt/




-------------文章结束啦 ~\(≧▽≦)/~ 感谢您的阅读-------------

您的支持就是我创作的动力!

欢迎关注我的其它发布渠道