JAVA ElasticSearch 訪問控制最佳解決方案

java ElasticSearch訪問控制上存在如下多種接入實現方式:html

1)基於ES JAVA API實現;2)基於ES HTTP API實現;3)基於Spring Data ES實現;java

那麼問題來了,到底哪一種方式最好,靈活性、擴展性和完備性等更勝一籌呢?node

爲了找到最權威的答案,搜索了各大權威技術論壇,終於找到了想要的答案,特分享給你們,避免後續開發過程當中走彎路。git

畢竟站在巨人的肩膀上作事、消化吸取牛人的經驗、拒絕重複造輪子,是一個程序員必備的素質:)程序員

JAVA ES訪問一般存在如下三種方式,優缺點歸納總結以下,具體細節可參考如下英文內容哦~github

1)ES JAVA API:不推薦使用,該方式和ES版本強耦合,擴展性較差,ES升級後,會致使訪問控制端邏輯代碼須要同步修改;spring

2)ES HTTP API:推薦使用,但不須要開發人員手工實現底層HTTP層通信和JSON到JAVA對象的轉換,可直接藉助開源組件Jest實現;mongodb

3)Spring Data ES:不推薦使用,該方式和ES版本強耦合,擴展性較差,同時Spring對ES版本支持較低,不方便升級維護;apache

The Native Client

The obvious first choice is to look at the client Elasticsearch provides natively. Unlike other solutions there is no separate jar file that just contains the client API but you are integrating the whole application Elasticsearch. Partly this is caused by the way the client connects to Elasticsearch: It doesn’t use the REST API but connects to the cluster as a cluster node. This node normally doesn’t contain any data but is aware of the state of the cluster.json

The node client integrates with your Elasticsearch cluster

The node client integrates with your Elasticsearch cluster

On the right side we can see two normal nodes, each containing two shards. Each node of the cluster, including our application’s client node, has access to the cluster state as indicated by the cylinder icon. That way, when requesting a document that resides on one of the shards of Node 1 your client node already knows that it has to ask Node 1. This saves a potential hop that would occur when asking Node 2 for the document that would then route your request to Node 1 for you.

Creating a client node in code is easy. You can use the NodeBuilder to get access to the Client interface. This then has methods for all of the API functionality, e.g. for indexing and searching data.

Client client = NodeBuilder.nodeBuilder() .client(true) .node() .client(); boolean indexExists = client.admin().indices().prepareExists(INDEX).execute().actionGet().isExists(); if (indexExists) { client.admin().indices().prepareDelete(INDEX).execute().actionGet(); } client.admin().indices().prepareCreate(INDEX).execute().actionGet(); SearchResponse allHits = client.prepareSearch(Indexer.INDEX) .addFields("title", "category") .setQuery(QueryBuilders.matchAllQueryRead More

You can see that after having the client interface we can issue index and search calls to Elasticsearch. The fluent API makes the code very readable. Note that the final actionGet() call on the operations is caused by the asynchronous nature of Elasticsearch and is not related to the HTTP operation. Each operation returns a Futurethat provides access to the result once it is available.

Most of the operations are available using dedicated builders and methods but you can also use the generic jsonBuilder() that can construct arbitrary JSON objects for you.

An alternative to the node client we have seen above is the TransportClient. It doesn’t join but connects to an existing cluster using the transport module (the layer that is also used for inter-node communication). This can be useful if maintaining the cluster state in your application can be problematic, e.g. when you are having tight constraints regarding your memory consumption or you are restarting your application a lot.

The TransportClient can be created by passing in one or more urls to nodes of your cluster:

Client client = new TransportClient() .addTransportAddress(new InetSocketTransportAddress("localhost", 9300)) .addTransportAddress(new InetSocketTransportAddress("localhost", 9301));

Using the property client.transport.sniff the TransportClient will also retrieve all the URLs for the other nodes of the cluster for you and use those in a round robin fashion.

The native Java client is the perfect solution if you need to have all of the features of Elasticsearch available. New functionality is automatically available with every release. You can either use the node client that will save you some hops or the TransportClient that communicates with an existing cluster.

If you’d like to learn more about the two kinds of clients you can have a look at this article on using Elasticsearch from Java or this post on networking on the Found blog.

Note: Elasticsearch service providers that have built a highly secure platform and service, e.g. implementing security measures such as ACLs and encryption, do not support unmodified clients. For more details regarding Found’s requirements, see Found Elasticsearch Transport Module.

Jest

For when you need a lightweight client in your application (regarding jar size or memory consumption) there is a nice alternative. Jest provides an implementation of the Elasticsearch REST API using the Apache HttpComponents project.

The API of Jest is very similar to the Elasticsearch API. It uses a fluent API with lots of specialized builders. All of the interaction happens using the JestClient that can be created using a factory:

JestClientFactory factory = new JestClientFactory(); factory.setHttpClientConfig(new HttpClientConfig.Builder("http://localhost:9200") .multiThreaded(true) .build()); JestClient client = factory.getObject();

When it comes to communicating with Elasticsearch you have two options: You can either create strings in the JSON-API of Elasticsearch or you can reuse the builder classes of Elasticsearch. If it’s not a problem to have the Elasticsearch dependency on your classpath this can lead to cleaner code. This is how you conditionally create an index using Jest:

boolean indexExists = client.execute(new IndicesExists.Builder("jug").build()).isSucceeded(); if (indexExists) { client.execute(new DeleteIndex.Builder("jug").build()); } client.execute(new CreateIndex.Builder("jug").build());

And this is how a search query can be executed.

String query = "{\n" + " \"query\": {\n" + " \"filtered\" : {\n" + " \"query\" : {\n" + " \"query_string\" : {\n" + " \"query\" : \"java\"\n" + " }\n" + " }" + " }\n" + " }\n" + "}"; Search.Builder searchBuilder = new Search.Builder(query).addIndex("jug").addType("talk"); SearchResult result = client.execute(searchBuilder.build());

You can see that concatenating the query can become complex so if you have the option to use the Elasticsearch builders you should try it.

The really great thing about Jest is that you can use Java Beans directly for indexing and searching. Suppose we have a bean Talk with several properties we can index instances of those in bulk in the following way:

Builder bulkIndexBuilder = new Bulk.Builder(); for (Talk talk : talks) { bulkIndexBuilder.addAction(new Index.Builder(talk).index("jug").type("talk").build()); } client.execute(bulkIndexBuilder.build());

Given the SearchResult we have seen above we can then also retrieve our talk instances directly from the Elasticsearch results:

List<Hit<Talk, Void>> hits = result.getHits(Talk.class); for (Hit<Talk, Void> hit: hits) { Talk talk = hit.source; log.info(talk.getTitle()); }

Besides the execute method we have used so far there is also an async variant that returns a Future.

The structure of the JEST API is really nice, you will find your way around it immediately. The possibility to index and retrieve Java Beans in your application makes it a good alternative to the native client. But there is also one thing I absolutely don’t like: It throws too many checked Exceptions, e.g. a plain Exception on the central execute method of the JestClient. Also, there might be cases where the Jest client doesn’t offer all of the functionality of newer Elasticsearch versions immediately. Nevertheless, it offers a really nice way to access your Elasticsearch instance using the REST API.

For more information on Jest you can consult the project documentation on GitHub. There is also a nice article on Elasticsearch at IBM developerWorks that demonstrates some of the features using Jest.

Spring Data Elasticsearch

The Spring Data project is a set of APIs that provide access to multiple data stores using a similar feeling. It doesn’t try to use one API for everything, so the characteristics of a certain data store can still be available. The project supports many stores, Spring Data JPA and Spring Data MongoDB being among the more popular. Starting with the latest GA release the implementation of Spring Data Elasticsearch is also officially part of the Spring Data release.

Spring Data Elasticsearch goes even one step further than the Jest client when it comes to indexing Java Beans. With Spring Data Elasticsearch you annotate your data objects with a @Document annotation that you can also use to determine index settings like name, numbers of shards or number of replicas. One of the attributes of the class needs to be an id, either by annotating it with @Id or using one of the automatically found names id or documentId. The other properties of your document can either come with or without annotations: without an annotation it will automatically be mapped by Elasticsearch, using the @Field annotation you can provide a custom mapping. The following class uses the standard mapping for speaker but a custom one for the title.

@Document(indexName="talks") public class Talk { @Id private String path; @Field(type=FieldType.String, index=FieldIndex.analyzed, indexAnalyzer="german", searchAnalyzer="german") private String title; private List<String> speakers; @Field(type= FieldType.Date) private Date date; // getters and setters ommitted }

There are two ways to use the annotated data objects: Either using a repository or the more flexible template support. The ElasticsearchTemplate uses the Elasticsearch Client and provides a custom layer for manipulating data in Elasticsearch, similar to the popular JDBCTemplate or RESTTemplate. The following code indexes a document and uses a GET request to retrieve it again.

IndexQuery query = new IndexQueryBuilder().withIndexName("talks").withId("/tmp").withObject(talk).build(); String id = esTemplate.index(query); GetQuery getQuery = new GetQuery(); getQuery.setId(id); Talk queriedObject = esTemplate.queryForObject(getQuery, Talk.class);

Note that none of the classes used in this example are part of the Elasticsearch API. Spring Data Elasticsearch implements a completely new abstraction layer on top of the Elasticsearch Java client.

The second way to use Spring Data Elasticsearch is by using a Repository, an interface you can extend. There is a general interface CrudRepository available for all Spring Data projects that provides methods like findAll(), count(), delete(...) and exists(...). PagingAndSortingRepository provides additional support for, what a surprise, paging and sorting.

For adding specialized queries to your application you can extend the ElasticsearchCrudRepository and declare custom methods in it. What might come as a surprise at first: You don’t have to implement a concrete instance of this interface, Spring Data automatically creates a proxy for you that contains the implementation. What kind of query is executed is determined by the name of the method, which can be something like findByTitleAndSpeakers(String title, String speaker). Besides the naming convention you can also annotate the methods with an @Query annotation that contains the native JSON query or you can even implement the method yourself.

Spring Data Elasticsearch provides lot of functionality and can be a good choice if you are already using Spring or even Spring Data. Some of the functionality of Elasticsearch might not be available at first or is more difficult to use because of the custom abstraction layer.

相關文章
相關標籤/搜索