Introduction into the Java HTTP REST client for Elasticsearch

A presentation at Workshop in February 2020 in Munich, Germany by Alexander Reelsen

Slide 1

Slide 1

Introduction into the Java HTTP REST client for Elasticsearch Alexander Reelsen Community Advocate alex@elastic.co | @spinscale

Slide 2

Slide 2

Agenda History & Background Getting up and running Executing operations

Slide 3

Slide 3

Elastic Stack

Slide 4

Slide 4

Elasticsearch in 10 seconds Search Engine (FTS, Analytics, Geo), near real-time Distributed, scalable, highly available, resilient Interface: HTTP & JSON Heart of the Elastic Stack (Kibana, Logstash, Beats)

Slide 5

Slide 5

History TransportClient has existed since earliest versions Uses port 9300 just like the node to node communication Uses custom serialization (BWC guarantees) All the query builders available, all POJOs usable

Slide 6

Slide 6

Implementation Depends on the Elasticsearch core project Based on Apache HTTP Client (works on java 8), might want to consider shading Supports synchronous calls & cancellable async calls Threadsafe RestLowLevelClient RestHighLevelClient

Slide 7

Slide 7

Architecture

Slide 8

Slide 8

LowLevelRestClient TLS setup Basic Auth Sniffing Node Selectors

Slide 9

Slide 9

HighLevelRestClient Requests classes for all endpoints Builders for queries/aggregations

Slide 10

Slide 10

Compatibility Requires java 8 forward compatible, 7.0 with 7.x, 7.1 with 7.1 - 7.x Also upgrade the REST client when upgrading ES cluster (bugfixes, REST API breaking changes over majors) Upgrade the client last

Slide 11

Slide 11

Instantiating a client RestHighLevelClient client = new RestHighLevelClient( RestClient.builder(new HttpHost(“localhost”, 9200))); ClusterHealthResponse response = client.cluster().health(new ClusterHealthRequest(), RequestOptions.DEFAULT); ActionListener<ClusterHealthResponse> listener = ActionListener.<ClusterHealthResponse>wrap( r -> System.out.println(r.getStatus()), Throwable::printStackTrace); client.cluster() .healthAsync(new ClusterHealthRequest(), RequestOptions.DEFAULT, listener);

Slide 12

Slide 12

Basic Auth final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(“user”, “password”)); RestClientBuilder builder = RestClient.builder( new HttpHost(“localhost”, 9200)) .setHttpClientConfigCallback(new HttpClientConfigCallback() { @Override public HttpAsyncClientBuilder customizeHttpClient( HttpAsyncClientBuilder httpClientBuilder) { return httpClientBuilder .setDefaultCredentialsProvider(credentialsProvider); } });

Slide 13

Slide 13

TLS KeyStore truststore = KeyStore.getInstance(“jks”); try (InputStream is = Files.newInputStream(keyStorePath)) { truststore.load(is, keyStorePass.toCharArray()); } SSLContextBuilder sslBuilder = SSLContexts.custom() .loadTrustMaterial(truststore, null); final SSLContext sslContext = sslBuilder.build(); RestClientBuilder builder = RestClient.builder( new HttpHost(“localhost”, 9200, “https”)) .setHttpClientConfigCallback(new HttpClientConfigCallback() { @Override public HttpAsyncClientBuilder customizeHttpClient( HttpAsyncClientBuilder httpClientBuilder) { return httpClientBuilder.setSSLContext(sslContext); } });

Slide 14

Slide 14

Indexing final byte[] bytes = …; final IndexRequest request = new IndexRequest(index); // optional request.id(“my_id”); request.source(bytes, XContentType.JSON); final IndexResponse response = client.index(request, RequestOptions.DEFAULT);

Slide 15

Slide 15

Bulk indexing BulkRequest request = new BulkRequest(); request.add(new IndexRequest(“my_index”).id(“1”) .source(XContentType.JSON,”name”, “My first product”)); request.add(new IndexRequest(“my_index”).id(“2”) .source(XContentType.JSON,”name”, “My second product”)); request.add(new IndexRequest(“my_index”).id(“3”) .source(XContentType.JSON,”name”, “My third product”)); client.bulk(request, RequestOptions.DEFAULT);

Slide 16

Slide 16

Bulk Processor Bulk requests should be limited by size/number of documents Requires manual checking for constant indexing Use the built-in BulkProcessor for that.

Slide 17

Slide 17

Bulk Processor BulkProcessor bulkProcessor = BulkProcessor.builder( client, new BulkProcessor.Listener() { @Override public void beforeBulk(long executionId, BulkRequest request) { … } @Override public void afterBulk(long executionId, BulkRequest request, BulkResponse response) { … } @Override public void afterBulk(long executionId, BulkRequest request, Throwable failure) { … } }) .setBulkActions(10000) .setBulkSize(new ByteSizeValue(5, ByteSizeUnit.MB)) .setFlushInterval(TimeValue.timeValueSeconds(5)) .setConcurrentRequests(1) .setBackoffPolicy( BackoffPolicy.exponentialBackoff(TimeValue.timeValueMillis(100), 3)) .build();

Slide 18

Slide 18

Searching final String input = “my nice query”; final SearchRequest searchRequest = new SearchRequest(); final SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.multiMatchQuery(input, “name”, “description”)); searchRequest.source(searchSourceBuilder); final SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); // no hits if (response.getHits().getTotalHits().value == 0) { return; } for (SearchHit hit : response.getHits().getHits()) { // per hit processing }

Slide 19

Slide 19

Use builders for Queries QueryBuilders.* for queries AggregationBuilders.* for aggs PipelineAggregatorBuilders.* for pipeline aggs

Slide 20

Slide 20

Pagination using from / offset private SearchRequest createSearchRequest(String input, int from, int size) { final SearchRequest searchRequest = new SearchRequest(); final SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.from(from); searchSourceBuilder.size(size); searchSourceBuilder.query(QueryBuilders.multiMatchQuery(input, “name”, “description”)); searchRequest.source(searchSourceBuilder); } return searchRequest;

Slide 21

Slide 21

Pagination using search_after SearchRequest searchRequest = new SearchRequest(INDEX); searchRequest.source().query(QueryBuilders.matchQuery(“name”, “Name”)); searchRequest.source().sort(SortBuilders.fieldSort(“price”).order(SortOrder.DESC)); final SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); SearchRequest searchAfterRequest = new SearchRequest(INDEX); searchAfterRequest.source().query(QueryBuilders.matchQuery(“name”, “Name”)); searchAfterRequest.source().sort(SortBuilders.fieldSort(“price”).order(SortOrder.DESC)); SearchHit lastHit = response.getHits().getHits()[response.getHits().getHits().length-1]; searchAfterRequest.source().searchAfter(lastHit.getSortValues()); final SearchResponse searchAfterResponse = client.search(searchAfterRequest, RequestOptions.DEFAULT);

Slide 22

Slide 22

Node Selectors (Low Level REST client) final NodeSelector INGEST_NODE_SELECTOR = nodes -> { final Iterator<Node> iterator = nodes.iterator(); while (iterator.hasNext()) { Node node = iterator.next(); // roles may be null if we don’t know, thus we keep the node in then… if (node.getRoles() != null && node.getRoles().isIngest() == false) { iterator.remove(); } } }; HttpHost host = new HttpHost(“localhost”, 9200, “http”); final RestClientBuilder builder = RestClient.builder(host); builder.setNodeSelector(INGEST_NODE_SELECTOR);

Slide 23

Slide 23

Sniffers (Low Level REST client) Fetches list of current nodes Uses a background thread (requires proper shutdown) Configurable interval Optional: Sniffing on failure Own dependency

Slide 24

Slide 24

Sniffers (Low Level REST client) RestClient restClient = RestClient.builder( new HttpHost(“localhost”, 9200, “http”)) .build(); Sniffer sniffer = Sniffer.builder(restClient).build(); // don’t forget to close both restClient.close(); sniffer.close();

Slide 25

Slide 25

Remember, remember… Consider shading of the client Modular, requires dependencies for certain features Dependencies

Slide 26

Slide 26

Deprecation warnings Error on deprecation warnings (useful when testing upgrades) final RestClientBuilder builder = RestClient.builder(host); builder.setStrictDeprecationMode(true);

Slide 27

Slide 27

Using against Elastic Cloud String cloudId = “optionalHumanReadableName:” + “dXMtZWFzdC0xLmF3cy5mb3VuZC5pbyRlbGFzdGljc2VhcmNoJGtpYmFuYQ==”; final RestClientBuilder builder = RestClient.builder(cloudId);

Slide 28

Slide 28

Debugging & Logging Apache HTTPClient uses JCL Set org.elasticsearch.client=DEBUG for more logging Set org.apache.http.wire=TRACE for whole HTTP request ^^ Privacy!

Slide 29

Slide 29

Demo Uses testcontainers Demo code available on github

Slide 30

Slide 30

Summary

Slide 31

Slide 31

Summary Think async! Sniffing is your friend! Documentation With a bit of code change, you can either start a docker container or run against another cluster!

Slide 32

Slide 32

Elastic Cloud

Slide 33

Slide 33

Elastic Support Subscriptions

Slide 34

Slide 34

Getting more help

Slide 35

Slide 35

Discuss Forum https://discuss.elastic.co

Slide 36

Slide 36

Community & Meetups https://community.elastic.co

Slide 37

Slide 37

Official Elastic Training https://training.elastic.co

Slide 38

Slide 38

Thanks for listening Q&A Alexander Reelsen Community Advocate alex@elastic.co | @spinscale