← Kursa Dön
📄 Text · 35 min

Elasticsearch Java Client — Kurulum ve Konfigürasyon

Giriş — Şoför ve Araba

Bir arabanız var ama sürmek için ehliyetli bir şoföre ihtiyacınız var. Araba Elasticsearch cluster'ınız, şoför ise Java client'ınız. Şoför arabayı tanımalı: direksiyonu nerede, vitesi nasıl kullanılır, hız limitleri ne? Yanlış konfigüre edilmiş bir şoför ya kaza yapar (connection timeout) ya da çok yavaş gider (performans sorunu).

Java ile Elasticsearch'e bağlanmanın doğru yolu, modern Elasticsearch Java Client'ı kullanmaktır. Bu derste sıfırdan bir Java projesi oluşturacak, Elasticsearch'e bağlanacak, connection pooling yapılandıracak ve production-ready bir client konfigürasyonu oluşturacağız.


1. Java Client Tarihi ve Seçimi

Client Evrimi

ClientDurumElasticsearch Sürümü
Transport Client❌ Kaldırıldı (7.x)< 7.x
RestHighLevelClient (RHLC)⚠️ Deprecated (7.15+)7.x
Elasticsearch Java Client✅ Aktif, önerilen7.17+, 8.x

Bu derste Elasticsearch Java Client (co.elastic.clients) kullanacağız — modern, type-safe, builder pattern tabanlı API.

⚠️ Eğer eski kodda RestHighLevelClient görürseniz, migration yapmanız önerilir. Yeni projeler kesinlikle yeni client ile başlamalı.


2. Proje Kurulumu

2.1 Maven Dependency

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>elasticsearch-demo</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <elasticsearch.version>8.12.0</elasticsearch.version>
        <jackson.version>2.16.1</jackson.version>
    </properties>

    <dependencies>
        <!-- Elasticsearch Java Client -->
        <dependency>
            <groupId>co.elastic.clients</groupId>
            <artifactId>elasticsearch-java</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>

        <!-- Jackson JSON processor (zorunlu) -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <!-- Jakarta JSON Processing (zorunlu) -->
        <dependency>
            <groupId>jakarta.json</groupId>
            <artifactId>jakarta.json-api</artifactId>
            <version>2.1.3</version>
        </dependency>

        <!-- Jakarta JSON Processing Implementation -->
        <dependency>
            <groupId>org.eclipse.parsson</groupId>
            <artifactId>parsson</artifactId>
            <version>1.1.5</version>
        </dependency>

        <!-- SLF4J Logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.11</version>
        </dependency>
    </dependencies>
</project>

2.2 Gradle Dependency

build.gradle:

plugins {
    id 'java'
}

group = 'com.example'
version = '1.0.0'

java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'co.elastic.clients:elasticsearch-java:8.12.0'
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.16.1'
    implementation 'jakarta.json:jakarta.json-api:2.1.3'
    implementation 'org.eclipse.parsson:parsson:1.1.5'
    implementation 'org.slf4j:slf4j-simple:2.0.11'
}

3. Temel Bağlantı

3.1 En Basit Bağlantı

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;

public class BasicConnection {

    public static void main(String[] args) throws Exception {
        // 1. Low-level REST client oluştur
        RestClient restClient = RestClient.builder(
            new HttpHost("localhost", 9200, "http")
        ).build();

        // 2. Transport katmanı
        ElasticsearchTransport transport = new RestClientTransport(
            restClient, new JacksonJsonpMapper()
        );

        // 3. Elasticsearch client
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 4. Bağlantıyı test et
        var info = client.info();
        System.out.println("Cluster: " + info.clusterName());
        System.out.println("Version: " + info.version().number());

        // 5. Kapatma
        transport.close();
        restClient.close();
    }
}

Çıktı:

Cluster: elasticsearch
Version: 8.12.0

3.2 Katmanlı Mimari

ElasticsearchClient
        │
        ▼
ElasticsearchTransport (RestClientTransport)
        │
        ▼
RestClient (Apache HTTP Client)
        │
        ▼
HTTP/HTTPS → Elasticsearch Cluster
  • RestClient: HTTP bağlantı yönetimi, connection pooling

  • RestClientTransport: JSON serialization/deserialization

  • ElasticsearchClient: Type-safe API, builder pattern


4. Birden Fazla Node'a Bağlanma

Production ortamında Elasticsearch genellikle birden fazla node'dan oluşur:

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;

public class MultiNodeConnection {

    public static RestClient createRestClient() {
        return RestClient.builder(
            new HttpHost("es-node1.example.com", 9200, "https"),
            new HttpHost("es-node2.example.com", 9200, "https"),
            new HttpHost("es-node3.example.com", 9200, "https")
        ).build();
    }
}

RestClient otomatik olarak node'lar arasında round-robin load balancing yapar. Bir node'a ulaşılamazsa otomatik olarak diğerine geçer.

Node Discovery (Sniffing)

import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.sniff.Sniffer;
import org.elasticsearch.client.sniff.SniffOnFailureListener;

public class SniffingConnection {

    public static void main(String[] args) throws Exception {
        // Failure listener
        SniffOnFailureListener failureListener = new SniffOnFailureListener();

        RestClient restClient = RestClient.builder(
            new HttpHost("es-node1.example.com", 9200, "https")
        )
        .setFailureListener(failureListener)
        .build();

        // Sniffer — cluster'daki diğer node'ları otomatik keşfeder
        Sniffer sniffer = Sniffer.builder(restClient)
            .setSniffIntervalMillis(60000)      // 60 saniyede bir keşfet
            .setSniffAfterFailureDelayMillis(30000) // Hata sonrası 30s bekle
            .build();

        failureListener.setSniffer(sniffer);

        // Kullanım...

        // Kapatma sırası önemli!
        sniffer.close();
        restClient.close();
    }
}

Sniffer, Elasticsearch cluster'daki tüm node'ları _nodes/http API'si ile keşfeder ve RestClient'ın node listesini otomatik günceller.

⚠️ Sniffer dependency:

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-client-sniffer</artifactId>
    <version>8.12.0</version>
</dependency>

5. Güvenli Bağlantı (HTTPS + Authentication)

5.1 Basic Authentication

import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;

public class SecureConnection {

    public static RestClient createSecureClient() {
        BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(
            AuthScope.ANY,
            new UsernamePasswordCredentials("elastic", "changeme")
        );

        return RestClient.builder(
            new HttpHost("localhost", 9200, "https")
        )
        .setHttpClientConfigCallback(httpClientBuilder ->
            httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
        )
        .build();
    }
}

5.2 API Key Authentication

import org.apache.http.Header;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.client.RestClient;

public class ApiKeyConnection {

    public static RestClient createApiKeyClient() {
        String apiKey = "base64_encoded_api_key";

        return RestClient.builder(
            new HttpHost("localhost", 9200, "https")
        )
        .setDefaultHeaders(new Header[]{
            new BasicHeader("Authorization", "ApiKey " + apiKey)
        })
        .build();
    }
}

5.3 SSL/TLS ile Self-Signed Certificate

import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import javax.net.ssl.SSLContext;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;

public class SslConnection {

    public static RestClient createSslClient() throws Exception {
        // CA sertifikasını yükle
        Path caCertPath = Path.of("/path/to/http_ca.crt");
        CertificateFactory factory = CertificateFactory.getInstance("X.509");

        Certificate trustedCa;
        try (InputStream is = Files.newInputStream(caCertPath)) {
            trustedCa = factory.generateCertificate(is);
        }

        // Truststore oluştur
        KeyStore trustStore = KeyStore.getInstance("pkcs12");
        trustStore.load(null, null);
        trustStore.setCertificateEntry("ca", trustedCa);

        // SSL context
        SSLContext sslContext = SSLContexts.custom()
            .loadTrustMaterial(trustStore, null)
            .build();

        // Credentials
        BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(
            AuthScope.ANY,
            new UsernamePasswordCredentials("elastic", "changeme")
        );

        return RestClient.builder(
            new HttpHost("localhost", 9200, "https")
        )
        .setHttpClientConfigCallback(httpClientBuilder ->
            httpClientBuilder
                .setSSLContext(sslContext)
                .setDefaultCredentialsProvider(credentialsProvider)
        )
        .build();
    }
}

6. Timeout ve Connection Pool Konfigürasyonu

6.1 Request Timeout'lar

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;

public class TimeoutConfiguration {

    public static RestClient createConfiguredClient() {
        return RestClient.builder(
            new HttpHost("localhost", 9200, "http")
        )
        .setRequestConfigCallback(requestConfigBuilder ->
            requestConfigBuilder
                .setConnectTimeout(5000)        // Bağlantı kurma: 5 saniye
                .setSocketTimeout(60000)         // Veri okuma: 60 saniye
                .setConnectionRequestTimeout(1000) // Pool'dan bağlantı alma: 1 saniye
        )
        .setHttpClientConfigCallback(httpClientBuilder ->
            httpClientBuilder
                .setMaxConnTotal(100)           // Toplam maksimum bağlantı
                .setMaxConnPerRoute(50)         // Node başına maksimum bağlantı
        )
        .build();
    }
}

6.2 Timeout Değerleri Rehberi

ParametreVarsayılanÖnerilenAçıklama
connectTimeout1000ms5000msTCP bağlantısı kurma süresi
socketTimeout30000ms60000msYanıt bekleme süresi
connectionRequestTimeout-1 (sınırsız)1000msConnection pool'dan bağlantı alma
maxConnTotal30100-200Toplam bağlantı sayısı
maxConnPerRoute1050-100Node başına bağlantı sayısı

⚠️ `socketTimeout`: Büyük bulk işlemleri veya yoğun aggregation'lar 30 saniyeden fazla sürebilir. Production'da 60-120 saniye önerilir.

6.3 Keep-Alive ve Connection Reuse

import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;

.setHttpClientConfigCallback(httpClientBuilder ->
    httpClientBuilder
        .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy())
        .setMaxConnTotal(100)
        .setMaxConnPerRoute(50)
)

7. Production-Ready Client Factory

Tüm konfigürasyonları birleştiren kapsamlı bir factory sınıfı:

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;

import java.io.Closeable;
import java.io.IOException;

public class ElasticsearchClientFactory implements Closeable {

    private final RestClient restClient;
    private final ElasticsearchTransport transport;
    private final ElasticsearchClient client;

    public ElasticsearchClientFactory(ElasticsearchConfig config) {
        // 1. HTTP Host'ları oluştur
        HttpHost[] hosts = config.getHosts().stream()
            .map(host -> new HttpHost(host.getHostname(), host.getPort(), host.getScheme()))
            .toArray(HttpHost[]::new);

        // 2. RestClient Builder
        RestClientBuilder builder = RestClient.builder(hosts);

        // 3. Timeout konfigürasyonu
        builder.setRequestConfigCallback(requestConfig ->
            requestConfig
                .setConnectTimeout(config.getConnectTimeoutMs())
                .setSocketTimeout(config.getSocketTimeoutMs())
                .setConnectionRequestTimeout(config.getConnectionRequestTimeoutMs())
        );

        // 4. HTTP Client konfigürasyonu (auth + connection pool)
        builder.setHttpClientConfigCallback(httpClient -> {
            // Authentication
            if (config.getUsername() != null) {
                BasicCredentialsProvider credProvider = new BasicCredentialsProvider();
                credProvider.setCredentials(
                    AuthScope.ANY,
                    new UsernamePasswordCredentials(
                        config.getUsername(), config.getPassword()
                    )
                );
                httpClient.setDefaultCredentialsProvider(credProvider);
            }

            // Connection pool
            httpClient.setMaxConnTotal(config.getMaxConnections());
            httpClient.setMaxConnPerRoute(config.getMaxConnectionsPerRoute());

            return httpClient;
        });

        this.restClient = builder.build();

        // 5. Jackson ObjectMapper
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        // 6. Transport ve Client
        this.transport = new RestClientTransport(
            restClient, new JacksonJsonpMapper(objectMapper)
        );
        this.client = new ElasticsearchClient(transport);
    }

    public ElasticsearchClient getClient() {
        return client;
    }

    public boolean isHealthy() {
        try {
            var health = client.cluster().health();
            return !"red".equals(health.status().jsonValue());
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public void close() throws IOException {
        transport.close();
        restClient.close();
    }
}

Config Sınıfı

import java.util.List;

public class ElasticsearchConfig {

    private List<HostConfig> hosts;
    private String username;
    private String password;
    private int connectTimeoutMs = 5000;
    private int socketTimeoutMs = 60000;
    private int connectionRequestTimeoutMs = 1000;
    private int maxConnections = 100;
    private int maxConnectionsPerRoute = 50;

    // Getters, setters, builder...

    public static ElasticsearchConfig local() {
        ElasticsearchConfig config = new ElasticsearchConfig();
        config.hosts = List.of(new HostConfig("localhost", 9200, "http"));
        return config;
    }

    public static ElasticsearchConfig production() {
        ElasticsearchConfig config = new ElasticsearchConfig();
        config.hosts = List.of(
            new HostConfig("es-node1.example.com", 9200, "https"),
            new HostConfig("es-node2.example.com", 9200, "https"),
            new HostConfig("es-node3.example.com", 9200, "https")
        );
        config.username = "elastic";
        config.password = System.getenv("ES_PASSWORD");
        config.connectTimeoutMs = 5000;
        config.socketTimeoutMs = 120000;
        config.maxConnections = 200;
        config.maxConnectionsPerRoute = 100;
        return config;
    }

    // Getter methods
    public List<HostConfig> getHosts() { return hosts; }
    public String getUsername() { return username; }
    public String getPassword() { return password; }
    public int getConnectTimeoutMs() { return connectTimeoutMs; }
    public int getSocketTimeoutMs() { return socketTimeoutMs; }
    public int getConnectionRequestTimeoutMs() { return connectionRequestTimeoutMs; }
    public int getMaxConnections() { return maxConnections; }
    public int getMaxConnectionsPerRoute() { return maxConnectionsPerRoute; }

    public static class HostConfig {
        private final String hostname;
        private final int port;
        private final String scheme;

        public HostConfig(String hostname, int port, String scheme) {
            this.hostname = hostname;
            this.port = port;
            this.scheme = scheme;
        }

        public String getHostname() { return hostname; }
        public int getPort() { return port; }
        public String getScheme() { return scheme; }
    }
}

Kullanım

public class Application {

    public static void main(String[] args) throws Exception {
        // Lokal geliştirme
        try (var factory = new ElasticsearchClientFactory(ElasticsearchConfig.local())) {
            ElasticsearchClient client = factory.getClient();

            // Sağlık kontrolü
            System.out.println("Healthy: " + factory.isHealthy());

            // Cluster bilgisi
            var info = client.info();
            System.out.println("Cluster: " + info.clusterName());
            System.out.println("Version: " + info.version().number());

            // Cluster health
            var health = client.cluster().health();
            System.out.println("Status: " + health.status());
            System.out.println("Nodes: " + health.numberOfNodes());
            System.out.println("Shards: " + health.activeShards());
        }
    }
}

8. Asenkron Client

Asenkron işlemler için ElasticsearchAsyncClient:

import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;

public class AsyncClientDemo {

    public static void main(String[] args) throws Exception {
        RestClient restClient = RestClient.builder(
            new HttpHost("localhost", 9200, "http")
        ).build();

        ElasticsearchTransport transport = new RestClientTransport(
            restClient, new JacksonJsonpMapper()
        );

        // Async client
        ElasticsearchAsyncClient asyncClient = new ElasticsearchAsyncClient(transport);

        // Asenkron çağrı
        asyncClient.info().thenAccept(info -> {
            System.out.println("Cluster: " + info.clusterName());
            System.out.println("Version: " + info.version().number());
        }).exceptionally(ex -> {
            System.err.println("Hata: " + ex.getMessage());
            return null;
        }).get(); // Bekleme (demo için)

        // Birden fazla asenkron işlem
        var indexFuture = asyncClient.indices().exists(e -> e.index("my_index"));
        var healthFuture = asyncClient.cluster().health();

        // Paralel çalışır
        boolean exists = indexFuture.get().value();
        var health = healthFuture.get();

        System.out.println("Index exists: " + exists);
        System.out.println("Cluster status: " + health.status());

        transport.close();
        restClient.close();
    }
}

9. Index Yönetimi

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.indices.*;

public class IndexManagement {

    public static void createIndex(ElasticsearchClient client) throws Exception {
        // Index oluştur
        CreateIndexResponse response = client.indices().create(c -> c
            .index("products")
            .settings(s -> s
                .numberOfShards("3")
                .numberOfReplicas("1")
                .refreshInterval(t -> t.time("5s"))
            )
            .mappings(m -> m
                .properties("name", p -> p
                    .text(t -> t.analyzer("turkish"))
                )
                .properties("category", p -> p
                    .keyword(k -> k)
                )
                .properties("price", p -> p
                    .float_(f -> f)
                )
                .properties("created_at", p -> p
                    .date(d -> d.format("yyyy-MM-dd'T'HH:mm:ss"))
                )
            )
        );

        System.out.println("Index oluşturuldu: " + response.acknowledged());
    }

    public static void checkIndex(ElasticsearchClient client) throws Exception {
        // Index var mı?
        boolean exists = client.indices().exists(e -> e
            .index("products")
        ).value();
        System.out.println("Index var mı: " + exists);

        // Mapping bilgisi
        GetMappingResponse mapping = client.indices().getMapping(g -> g
            .index("products")
        );
        System.out.println("Mapping: " + mapping);
    }

    public static void deleteIndex(ElasticsearchClient client) throws Exception {
        DeleteIndexResponse response = client.indices().delete(d -> d
            .index("products")
        );
        System.out.println("Index silindi: " + response.acknowledged());
    }

    public static void aliasManagement(ElasticsearchClient client) throws Exception {
        // Alias ekle
        client.indices().putAlias(a -> a
            .index("products_v2")
            .name("products")
        );

        // Alias listele
        GetAliasResponse aliases = client.indices().getAlias(a -> a
            .name("products")
        );
        System.out.println("Aliases: " + aliases.result());
    }
}

10. Best Practices

✅ Yapın

UygulamaNeden
Client'ı singleton olarak kullanınThread-safe, connection pool'u paylaşır
try-with-resources ile kapatınKaynak sızıntısını önler
Timeout değerlerini ayarlayınVarsayılanlar production için yetersiz olabilir
Birden fazla node belirtinYüksek erişilebilirlik
Environment variable ile credentialsHardcoded şifre = güvenlik açığı

❌ Yapmayın

UygulamaNeden
Her istekte yeni client oluşturmayınConnection overhead, kaynak israfı
Şifreleri kod içine yazmayınGüvenlik riski
socketTimeout'u çok kısa tutmayınBüyük sorgular timeout alır
maxConnPerRoute'u çok düşük tutmayınYoğun trafikte darboğaz
Transport Client kullanmayınTamamen kaldırıldı

11. Yaygın Hatalar

Hata 1: Connection Refused

// ❌ Elasticsearch çalışmıyor veya yanlış port
// java.net.ConnectException: Connection refused

// ✅ Kontrol edin:
// 1. Elasticsearch çalışıyor mu? → curl localhost:9200
// 2. Port doğru mu? (9200 varsayılan)
// 3. Host doğru mu? (Docker'da farklı olabilir)

Hata 2: SSL Handshake Failure

// ❌ HTTPS bağlantısında sertifika hatası
// javax.net.ssl.SSLHandshakeException

// ✅ Elasticsearch 8.x varsayılan olarak HTTPS kullanır
// 1. http_ca.crt sertifikasını yükleyin
// 2. Veya development'ta SSL'i kapatın:
//    elasticsearch.yml: xpack.security.http.ssl.enabled: false

Hata 3: Jackson Modül Eksikliği

// ❌ Java 8 Date/Time tiplerini serialize edemez
// com.fasterxml.jackson.databind.exc.InvalidDefinitionException

// ✅ JavaTimeModule ekleyin:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
// + jackson-datatype-jsr310 dependency

Hata 4: Client Kapatmayı Unutmak

// ❌ Kaynak sızıntısı
ElasticsearchClient client = ...;
client.search(...); // client asla kapatılmaz

// ✅ try-with-resources kullanın
try (var factory = new ElasticsearchClientFactory(config)) {
    var client = factory.getClient();
    // ... işlemler
} // Otomatik kapatılır

12. Sağlık Kontrolü ve Monitoring

public class HealthCheck {

    public static void checkClusterHealth(ElasticsearchClient client) throws Exception {
        // Cluster health
        var health = client.cluster().health();

        System.out.println("=== Cluster Health ===");
        System.out.println("Cluster: " + health.clusterName());
        System.out.println("Status: " + health.status());
        System.out.println("Nodes: " + health.numberOfNodes());
        System.out.println("Data Nodes: " + health.numberOfDataNodes());
        System.out.println("Active Shards: " + health.activeShards());
        System.out.println("Relocating: " + health.relocatingShards());
        System.out.println("Unassigned: " + health.unassignedShards());

        // Durum kontrolü
        String status = health.status().jsonValue();
        switch (status) {
            case "green":
                System.out.println("✅ Cluster sağlıklı");
                break;
            case "yellow":
                System.out.println("⚠️ Replica shard'lar atanmamış");
                break;
            case "red":
                System.out.println("❌ Primary shard'lar eksik!");
                break;
        }
    }

    // Ping — basit bağlantı testi
    public static boolean ping(ElasticsearchClient client) {
        try {
            client.ping();
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

Özet

  • Elasticsearch Java Client (co.elastic.clients) modern ve önerilen client'tır — RestHighLevelClient deprecated

  • Client katmanlı mimariye sahiptir: RestClient (HTTP) → Transport (JSON) → ElasticsearchClient (API)

  • Maven/Gradle dependency'leri: elasticsearch-java, jackson-databind, jakarta.json-api, parsson

  • Birden fazla node belirtilerek yüksek erişilebilirlik sağlanır — round-robin load balancing otomatik

  • Authentication: Basic Auth, API Key veya SSL/TLS ile güvenli bağlantı

  • Timeout ve connection pool ayarları production performansı için kritik — varsayılanları artırın

  • Client singleton olarak kullanılmalı ve işiniz bitince kapatılmalı (try-with-resources)

  • Asenkron client (ElasticsearchAsyncClient) paralel işlemler için CompletableFuture döndürür

  • Cluster health kontrolü ile bağlantı durumunu izleyin — green/yellow/red