{"id":17132206,"url":"https://github.com/bitsofinfo/hazelcast-etcd-discovery-spi","last_synced_at":"2025-07-23T17:34:05.709Z","repository":{"id":55115720,"uuid":"46809569","full_name":"bitsofinfo/hazelcast-etcd-discovery-spi","owner":"bitsofinfo","description":"Etcd based discovery strategy SPI for Hazelcast enabled applications","archived":false,"fork":false,"pushed_at":"2021-02-09T19:19:06.000Z","size":485,"stargazers_count":12,"open_issues_count":2,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-14T21:05:38.077Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://bitsofinfo.wordpress.com/2015/11/25/hazelcast-discovery-with-etcd","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bitsofinfo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-11-24T18:08:09.000Z","updated_at":"2022-08-15T06:55:08.000Z","dependencies_parsed_at":"2022-08-14T12:30:42.770Z","dependency_job_id":null,"html_url":"https://github.com/bitsofinfo/hazelcast-etcd-discovery-spi","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/bitsofinfo/hazelcast-etcd-discovery-spi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitsofinfo%2Fhazelcast-etcd-discovery-spi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitsofinfo%2Fhazelcast-etcd-discovery-spi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitsofinfo%2Fhazelcast-etcd-discovery-spi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitsofinfo%2Fhazelcast-etcd-discovery-spi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bitsofinfo","download_url":"https://codeload.github.com/bitsofinfo/hazelcast-etcd-discovery-spi/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitsofinfo%2Fhazelcast-etcd-discovery-spi/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266720180,"owners_count":23973910,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T19:26:22.086Z","updated_at":"2025-07-23T17:34:05.668Z","avatar_url":"https://github.com/bitsofinfo.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# hazelcast-etcd-discovery-spi\n\n**IMPORTANT**: This README is relevant for the current *branch*/*tag* you have selected above.\n\nProvides a Etcd based discovery strategy for Hazlecast 3.6+ enabled applications.\nThis is an easy to configure plug-and-play Hazlecast DiscoveryStrategy that will optionally register each of your Hazelcast instances with Etcd and enable Hazelcast nodes to dynamically discover one another via Etcd.\n\n* [Status](#status)\n* [Releases](#releases)\n* [Requirements](#requirements)\n* [Maven/Gradle install](#mavengradle)\n* [Features](#features)\n* [Usage](#usage)\n* [Build from source](#building)\n* [Unit tests](#tests)\n* [Related Info](#related)\n* [Todo](#todo)\n* [Notes](#notes)\n* [Docker info](#docker)\n\n![Diagram of hazelcast etcd discovery strategy](/docs/diag.png \"Diagram1\")\n\n## \u003ca id=\"status\"\u003e\u003c/a\u003eStatus\n\nThis is beta code, tested against Hazelcast 3.6-EA+ through 3.6 stable\n\n**IMPORTANT:**: Do not rely on JCenter/Bintray anymore! Update your gradle/maven dependencies to use Maven Central: https://search.maven.org/search?q=g:org.bitsofinfo\n\n## \u003ca id=\"releases\"\u003e\u003c/a\u003eReleases\n\n* **1.0-RC4-20210205**: Same as `1.0-RC4` but made compliant for Maven Central due to JCenter/Bintray closure. Includes explicit dependency of HZ `[3.6,3.12]`\n  \n* [1.0-RC4](https://github.com/bitsofinfo/hazelcast-etcd-discovery-spi/releases/tag/1.0-RC4): Java 1.7+. SSL cert file improvements [pull/5](https://github.com/bitsofinfo/hazelcast-etcd-discovery-spi/pull/5)\n\n* [1.0-RC3](https://github.com/bitsofinfo/hazelcast-etcd-discovery-spi/releases/tag/1.0-RC3): Tested against Hazelcast 3.6-EA through 3.6 stable, add support for username and password for [issue #1](https://github.com/bitsofinfo/hazelcast-etcd-discovery-spi/issues/1)\n\n* [1.0-RC2](https://github.com/bitsofinfo/hazelcast-etcd-discovery-spi/releases/tag/1.0-RC2): Tested against Hazelcast 3.6-EA through 3.6 stable\n\n* [1.0-RC1](https://github.com/bitsofinfo/hazelcast-etcd-discovery-spi/releases/tag/1.0-RC1): Tested against Hazelcast 3.6-EA and 3.6-RC1\n\n## \u003ca id=\"requirements\"\u003e\u003c/a\u003eRequirements\n\n* Java 7+\n* [Hazelcast 3.6+](https://hazelcast.org/)\n* [Etcd](https://github.com/coreos/etcd)\n\n## \u003ca id=\"mavengradle\"\u003e\u003c/a\u003eMaven/Gradle\n\nTo use this discovery strategy in your Maven or Gradle project use the dependency samples below.\n\n### Gradle:\n\n```\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n\tcompile 'org.bitsofinfo:hazelcast-etcd-discovery-spi:1.0-RC4-20210205'\n}\n```\n\n### Maven:\n\n```\n\u003cdependencies\u003e\n    \u003cdependency\u003e\n        \u003cgroupId\u003eorg.bitsofinfo\u003c/groupId\u003e\n        \u003cartifactId\u003ehazelcast-etcd-discovery-spi\u003c/artifactId\u003e\n        \u003cversion\u003e1.0-RC4-20210205\u003c/version\u003e\n    \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n\n## \u003ca id=\"features\"\u003e\u003c/a\u003eFeatures\n\n\n* Supports two modes of operation:\n\t* **Read-write**: peer discovery and registration of a hazelcast instance (self registration)\n\t* **Read-only**: peer discovery only with an manual Etcd key-path setup (no registration by the strategy itself)\n\n* If you don't want to use the built in Etcd registration, just specify the `DoNothingRegistrator` (see below) in your hazelcast discovery-strategy XML config. This will require you to manually create node key-paths against Etcd that defines the hazelcast service; in the format:\n\t* `/[etcd-service-name]/[hz-instance-id] = {\"ip\":\"xx.xx.xx.xx\", \"hostname\":\"my.host\", \"port\":5701}` where `hz-instance-id` can be anything but must be unique.\n\t* `etcd-service-name` must match the value of the `\u003cetcd-service-name\u003e` in your hazelcast XML config for this discovery strategy.\n\n* If using self-registration, either `LocalDiscoveryNodeRegistrator` or `ExplicitIpPortRegistrator` which additionally support:\n    * Automatic registration of the hazelcast instance with Etcd (under `\u003cetcd-service-name\u003e` key)\n    * Control which IP/PORT is published for the hazelcast node in Etcd\n    * Configurable discovery delay\n    * Automatic Etcd de-registration of instance via ShutdownHook\n\n## \u003ca id=\"usage\"\u003e\u003c/a\u003eUsage\n\n* Ensure your project has the `hazelcast-etcd-discovery-spi` artifact dependency declared in your maven pom or gradle build file as described above. Or build the jar yourself and ensure the jar is in your project's classpath.\n\n* Have Etcd running and available somewhere on your network, start it such as:\n```\n./etcd\n```\n\n* Configure your hazelcast.xml configuration file to use the `EtcdDiscoveryStrategy` (similar to the below): [See hazelcast-etcd-discovery-spi-example.xml](src/main/resources/hazelcast-etcd-discovery-spi-example.xml) for a full example with documentation of options.\n\n* Launch your hazelcast instances, configured with the Etcd discovery-strategy similar to the below: [see ManualRunner.java](src/test/java/org/bitsofinfo/hazelcast/discovery/etcd/ManualRunner.java) example.\n\n\n## \u003ca id=\"Secured Communication\"\u003e\u003c/a\u003eSecured Communication\n\nBy default, when the HTTPS protocol is defined in order to communicate with ETCD the standard SSLContext is used (usually using 'cacerts' system keystore, if not set otherwise). If you need to define your own\nSSLContext with custom certificates there are three optional parameters for defining input for a custom SSLContext in order to establish secure communication to ETCD.\n\nThe optional security properties provide locations of certificates (and keys) for secure communication. You can configure trusted root certificates that are needed for communication with ETCD, when secure communication is enabled for your ETCD instance. You can also provide a client certificate and a private key through 'etcd-client-cert-location' and 'etcd-client-key-location' in case your ETCD has client-authentication activated and requests a client certificate.\n\nIn case your root certificates and client certificate are chained in one file it is OK to define this file in 'etcd-client-cert-location' and omit the trusted-cert property. The implementation will extract the first certificate as your client certificate and the rest as the trusted root/intermediate certificates.\n\nCertificates should be X509 and provided in a PEM encoded file. Keys can be provided as PKCS#8 or PKCS#1 in a PEM encoded file.\n\n\n```\n\u003cnetwork\u003e\n  \u003cport auto-increment=\"true\"\u003e5701\u003c/port\u003e\n\n  \u003cjoin\u003e\n    \u003cmulticast enabled=\"false\"/\u003e\n    \u003caws enabled=\"false\"/\u003e\n    \u003ctcp-ip enabled=\"false\" /\u003e\n\n     \u003cdiscovery-strategies\u003e\n       \u003cdiscovery-strategy enabled=\"true\"\n           class=\"org.bitsofinfo.hazelcast.discovery.etcd.EtcdDiscoveryStrategy\"\u003e\n\n         \u003cproperties\u003e\n              \u003cproperty name=\"etcd-uris\"\u003ehttp://localhost:4001\u003c/property\u003e\n              \u003c!-- Optional Username for etcd --\u003e\n              \u003cproperty name=\"etcd-username\"\u003eroot\u003c/property\u003e\n              \u003c!-- Optional Password for etcd --\u003e\n              \u003cproperty name=\"etcd-password\"\u003epassword\u003c/property\u003e\n\t\t       \u003cproperty name=\"etcd-service-name\"\u003ehz-discovery-test-cluster\u003c/property\u003e\n              \u003cproperty name=\"etcd-registrator\"\u003eorg.bitsofinfo.hazelcast.discovery.etcd.LocalDiscoveryNodeRegistrator\u003c/property\u003e\n              \u003c!-- Optional cert/key files for SSL connection to etcd, in case etcd is configured for secure communication --\u003e\n              \u003cproperty name=\"etcd-client-cert-location\"\u003e/path/to/tls.crt\u003c/property\u003e\n              \u003cproperty name=\"etcd-client-key-location\"\u003e/path/to/tls.key\u003c/property\u003e\n              \u003cproperty name=\"etcd-trusted-cert-location\"\u003e/path/to/trusted.crt\u003c/property\u003e\n\t\t       \u003cproperty name=\"etcd-registrator-config\"\u003e\u003c![CDATA[\n\t\t\t\t\t{\n\t\t\t\t\t  \"preferPublicAddress\":false\n\t\t\t\t\t}\n              ]]\u003e\u003c/property\u003e\n        \u003c/properties\u003e\n      \u003c/discovery-strategy\u003e\n    \u003c/discovery-strategies\u003e\n\n  \u003c/join\u003e\n\u003c/network\u003e\n```\n\n* Once nodes are joined you can query Etcd to see the auto-registration of hazelcast instances works, the service-id's generated etc\n\n```\n$ ./etcdctl ls hz-discovery-test-cluster\n/hz-discovery-test-cluster/hz-discovery-test-cluster-192.168.0.208-192.168.0.208-5701\n\n$ ./etcdctl get /hz-discovery-test-cluster/hz-discovery-test-cluster-192.168.0.208-192.168.0.208-5701\n{\"ip\":\"192.168.0.208\",\"port\":5702,\"hostname\":\"192.168.0.208\",\"registeredAt\":\"2015.11.25 14:25:59.026 +0000\"}\n```\n\n\n## \u003ca id=\"building\"\u003e\u003c/a\u003eBuild from source\n\n* From the root of this project, build a Jar : `./gradlew assemble`\n\n* Include the built jar artifact located at `build/libs/hazelcast-etcd-discovery-spi-[VERSION].jar` in your hazelcast project\n\n* If not already present in your hazelcast application's Maven (pom.xml) or Gradle (build.gradle) dependencies section; ensure that these dependencies are present (versions may vary as appropriate):\n\n```\ncompile group: 'org.mousio', name: 'etcd4j', version:'2.9.0'\ncompile group: 'com.google.code.gson', name: 'gson', version:'2.4'\n```\n\n\n## \u003ca id=\"tests\"\u003e\u003c/a\u003eUnit-tests\n\nIt may also help you to understand the functionality by checking out and running the unit-tests\nlocated at [src/test/java](src/test/java). **BE SURE TO READ** the comments as some of the tests require\nyou to setup your local Etcd and edit certain files.\n\nFrom the command line you can run `TestExplicitIpPortRegistrator` and `TestLocalDiscoveryNodeRegistrator` unit-tests by invoking the `runTests` task using `gradlew` that runs both tests and displays the result on the console.\n\n```\n$ ./gradlew runTests\n```\n\nThe task above will display output indicating the test has started and whether the test has passed or failed.\n\n###### Sample output for passing test:\n```\norg.bitsofinfo.hazelcast.discovery.etcd.TestExplicitIpPortRegistrator \u003e testExplicitIpPortRegistrator STARTED\n\norg.bitsofinfo.hazelcast.discovery.etcd.TestExplicitIpPortRegistrator \u003e testExplicitIpPortRegistrator PASSED\n```\n\n###### Sample output for failing test:\n```\norg.bitsofinfo.hazelcast.discovery.etcd.TestDoNothingRegistrator \u003e testDoNothingRegistrator STARTED\n\norg.bitsofinfo.hazelcast.discovery.etcd.TestDoNothingRegistrator \u003e testDoNothingRegistrator FAILED\n    java.lang.AssertionError at TestDoNothingRegistrator.java:85\n```\n\nTo run individual unit-test, use the `test.single` argument to provide the unit-test you would like to run. The command below runs the unit test for `TestDoNothingRegistrator`\n\n```\n$ ./gradlew test -Dtest.single=TestDoNothingRegistrator\n```\n\n##### Note on running `TestDoNothingRegistrator` unit-test\nThe `TestDoNothingRegistrator` unit-test should be run separately using the `test.single` argument as demonstrated above as it requires you to register a service with your local etcd with 5 nodes/instances. Please **CAREFULLY READ** the comments in `TestDoNothingRegistrator.java` to see how this test should be run.\n\n\n## \u003ca id=\"related\"\u003e\u003c/a\u003eRelated info\n\n* https://github.com/coreos/etcd\n* https://github.com/jurmous/etcd4j\n* http://docs.hazelcast.org/docs/3.6/manual/html-single/index.html#discovery-spi\n* **Consul** version of this: https://github.com/bitsofinfo/hazelcast-consul-discovery-spi\n\n## \u003ca id=\"notes\"\u003e\u003c/a\u003e Notes\n\n### \u003ca id=\"docker\"\u003e\u003c/a\u003eContainerization (Docker) notes\n\nThis library may also be helpful to you: [docker-discovery-registrator-consul](https://github.com/bitsofinfo/docker-discovery-registrator-consul)\n\nOne of the main drivers for coding this module was for Hazelcast applications that were deployed as Docker containers\nthat would need to automatically register themselves with Etcd for higher level cluster orchestration of the cluster.\n\nIf you are deploying your Hazelcast application as a Docker container, one helpful tip is that you will want to avoid hardwired\nconfiguration in the hazelcast XML config, but rather have your Docker container take startup arguments that would be translated\nto `-D` system properties on startup. Convienently Hazelcast can consume these JVM system properties and replace variable placeholders in the XML config. See this documentation for examples: [http://docs.hazelcast.org/docs/3.6/manual/html-single/index.html#using-variables](http://docs.hazelcast.org/docs/3.6/manual/html-single/index.html#using-variables)\n\nSpecifically when using this discovery strategy and Docker, it may be useful for you to use the [ExplicitIpPortRegistrator](src/main/java/org/bitsofinfo/hazelcast/discovery/etcd/ExplicitIpPortRegistrator.java) `EtcdRegistrator` **instead** of the *LocalDiscoveryNodeRegistrator* as the latter relies on hazelcast to determine its IP/PORT and this may end up being the local container IP, and not the Docker host IP, leading to a situation where a unreachable IP/PORT combination is published to Etcd.\n\n**Example:** excerpt from [explicitIpPortRegistrator-example.xml](src/main/resources/explicitIpPortRegistrator-example.xml)\n\nStart your hazelcast app such as with the below, this would assume that hazelcast is actually reachable via this configuration\nvia your Docker host and the port mappings that were specified on `docker run`. (i.e. the IP below would be your docker host/port that is mapped to the actual hazelcast app container and port it exposes for hazelcast).\n\nThis library may also be helpful to you: [docker-discovery-registrator-consul](https://github.com/bitsofinfo/docker-discovery-registrator-consul)\n\nSee this [Docker issue for related info](https://github.com/docker/docker/issues/3778) on detecting mapped ports/ip from **within** a container\n\n`java -jar myHzApp.jar -DregisterWithIpAddress=\u003cdockerHostIp\u003e -DregisterWithPort=\u003cmappedContainerPortOnDockerHost\u003e .... `\n\n```\n\u003cproperty name=\"etcd-registrator-config\"\u003e\u003c![CDATA[\n      {\n        \"registerWithIpAddress\":\"${registerWithIpAddress}\",\n        \"registerWithPort\":${registerWithPort}\n      }\n  ]]\u003e\u003c/property\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitsofinfo%2Fhazelcast-etcd-discovery-spi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbitsofinfo%2Fhazelcast-etcd-discovery-spi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitsofinfo%2Fhazelcast-etcd-discovery-spi/lists"}