├── .editorconfig ├── .github ├── maven-cd-settings.xml ├── maven-ci-settings.xml └── workflows │ ├── ci-4.x.yml │ ├── ci-5.x-stable.yml │ ├── ci-5.x.yml │ ├── ci-matrix-5.x.yml │ ├── ci.yml │ └── deploy.yml ├── .gitignore ├── .travis.deploy.artifacts.sh ├── .travis.yml ├── LICENSE.txt ├── README.adoc ├── pom.xml └── src ├── main ├── asciidoc │ └── index.adoc ├── java │ ├── example │ │ ├── Examples.java │ │ └── package-info.java │ └── io │ │ └── vertx │ │ └── spi │ │ └── cluster │ │ └── zookeeper │ │ ├── ZookeeperClusterManager.java │ │ ├── impl │ │ ├── ConfigUtil.java │ │ ├── RetryPolicyHelper.java │ │ ├── SubsMapHelper.java │ │ ├── ZKAsyncMap.java │ │ ├── ZKCounter.java │ │ ├── ZKLock.java │ │ ├── ZKMap.java │ │ └── ZKSyncMap.java │ │ └── package-info.java └── resources │ ├── META-INF │ └── services │ │ └── io.vertx.core.spi.VertxServiceProvider │ └── default-zookeeper.json └── test ├── java └── io │ └── vertx │ ├── core │ ├── ProgrammaticZKClusterManagerTest.java │ ├── ZKClusteredComplexHATest.java │ ├── ZKClusteredHATest.java │ ├── eventbus │ │ ├── ZKClusteredEventbusTest.java │ │ ├── ZKFaultToleranceTest.java │ │ └── ZKNodeInfoTest.java │ └── shareddata │ │ ├── ZKClusteredAsyncMapTest.java │ │ ├── ZKClusteredAsynchronousLockTest.java │ │ └── ZKClusteredSharedCounterTest.java │ ├── ext │ └── web │ │ └── sstore │ │ └── ZKClusteredSessionHandlerTest.java │ ├── servicediscovery │ └── impl │ │ └── ZKDiscoveryImplClusteredTest.java │ └── spi │ └── cluster │ └── zookeeper │ ├── ConsumerRoundRobinTest.java │ ├── MockZKCluster.java │ ├── RetryPolicyTest.java │ └── ZKSyncMapTest.java └── resources ├── log4j.properties └── zookeeper.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | trim_trailing_whitespace = true 8 | end_of_line = lf 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.github/maven-cd-settings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | false 20 | 21 | 22 | 23 | vertx-snapshots-repository 24 | ${env.VERTX_NEXUS_USERNAME} 25 | ${env.VERTX_NEXUS_PASSWORD} 26 | 27 | 28 | 29 | 30 | 31 | google-mirror 32 | 33 | true 34 | 35 | 36 | 37 | google-maven-central 38 | GCS Maven Central mirror EU 39 | https://maven-central.storage-download.googleapis.com/maven2/ 40 | 41 | true 42 | 43 | 44 | false 45 | 46 | 47 | 48 | 49 | 50 | google-maven-central 51 | GCS Maven Central mirror 52 | https://maven-central.storage-download.googleapis.com/maven2/ 53 | 54 | true 55 | 56 | 57 | false 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /.github/maven-ci-settings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | false 20 | 21 | 22 | 23 | google-mirror 24 | 25 | true 26 | 27 | 28 | 29 | google-maven-central 30 | GCS Maven Central mirror EU 31 | https://maven-central.storage-download.googleapis.com/maven2/ 32 | 33 | true 34 | 35 | 36 | false 37 | 38 | 39 | 40 | 41 | 42 | google-maven-central 43 | GCS Maven Central mirror 44 | https://maven-central.storage-download.googleapis.com/maven2/ 45 | 46 | true 47 | 48 | 49 | false 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /.github/workflows/ci-4.x.yml: -------------------------------------------------------------------------------- 1 | name: vertx-zookeeper (4.x) 2 | on: 3 | schedule: 4 | - cron: '0 4 * * *' 5 | jobs: 6 | CI: 7 | strategy: 8 | matrix: 9 | include: 10 | - os: ubuntu-latest 11 | jdk: 8 12 | uses: ./.github/workflows/ci.yml 13 | with: 14 | branch: 4.x 15 | jdk: ${{ matrix.jdk }} 16 | os: ${{ matrix.os }} 17 | secrets: inherit 18 | Deploy: 19 | if: ${{ github.repository_owner == 'vert-x3' && (github.event_name == 'push' || github.event_name == 'schedule') }} 20 | needs: CI 21 | uses: ./.github/workflows/deploy.yml 22 | with: 23 | branch: 4.x 24 | jdk: 8 25 | secrets: inherit 26 | -------------------------------------------------------------------------------- /.github/workflows/ci-5.x-stable.yml: -------------------------------------------------------------------------------- 1 | name: vertx-zookeeper (5.x-stable) 2 | on: 3 | push: 4 | branches: 5 | - '5.[0-9]+' 6 | pull_request: 7 | branches: 8 | - '5.[0-9]+' 9 | schedule: 10 | - cron: '0 6 * * *' 11 | jobs: 12 | CI-CD: 13 | uses: ./.github/workflows/ci-matrix-5.x.yml 14 | secrets: inherit 15 | with: 16 | branch: ${{ github.event_name == 'schedule' && vars.VERTX_5_STABLE_BRANCH || github.event.pull_request.head.sha || github.ref_name }} 17 | -------------------------------------------------------------------------------- /.github/workflows/ci-5.x.yml: -------------------------------------------------------------------------------- 1 | name: vertx-zookeeper (5.x) 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | schedule: 10 | - cron: '0 5 * * *' 11 | jobs: 12 | CI-CD: 13 | uses: ./.github/workflows/ci-matrix-5.x.yml 14 | secrets: inherit 15 | with: 16 | branch: ${{ github.event.pull_request.head.sha || github.ref_name }} 17 | -------------------------------------------------------------------------------- /.github/workflows/ci-matrix-5.x.yml: -------------------------------------------------------------------------------- 1 | name: CI matrix (5.x) 2 | on: 3 | workflow_call: 4 | inputs: 5 | branch: 6 | required: true 7 | type: string 8 | jobs: 9 | CI: 10 | strategy: 11 | matrix: 12 | include: 13 | - os: ubuntu-latest 14 | jdk: 11 15 | uses: ./.github/workflows/ci.yml 16 | with: 17 | branch: ${{ inputs.branch }} 18 | jdk: ${{ matrix.jdk }} 19 | os: ${{ matrix.os }} 20 | secrets: inherit 21 | Deploy: 22 | if: ${{ github.repository_owner == 'vert-x3' && (github.event_name == 'push' || github.event_name == 'schedule') }} 23 | needs: CI 24 | uses: ./.github/workflows/deploy.yml 25 | with: 26 | branch: ${{ inputs.branch }} 27 | jdk: 11 28 | secrets: inherit 29 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | workflow_call: 4 | inputs: 5 | branch: 6 | required: true 7 | type: string 8 | jdk: 9 | default: 8 10 | type: string 11 | os: 12 | default: ubuntu-latest 13 | type: string 14 | jobs: 15 | Test: 16 | name: Run tests 17 | runs-on: ${{ inputs.os }} 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | with: 22 | ref: ${{ inputs.branch }} 23 | - name: Install JDK 24 | uses: actions/setup-java@v2 25 | with: 26 | java-version: ${{ inputs.jdk }} 27 | distribution: temurin 28 | - name: Run tests 29 | run: mvn -s .github/maven-ci-settings.xml -q clean verify -B 30 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | workflow_call: 4 | inputs: 5 | branch: 6 | required: true 7 | type: string 8 | jdk: 9 | default: 8 10 | type: string 11 | jobs: 12 | Deploy: 13 | name: Deploy to OSSRH 14 | runs-on: ubuntu-latest 15 | env: 16 | VERTX_NEXUS_USERNAME: ${{ secrets.VERTX_NEXUS_USERNAME }} 17 | VERTX_NEXUS_PASSWORD: ${{ secrets.VERTX_NEXUS_PASSWORD }} 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | with: 22 | ref: ${{ inputs.branch }} 23 | - name: Install JDK 24 | uses: actions/setup-java@v2 25 | with: 26 | java-version: ${{ inputs.jdk }} 27 | distribution: temurin 28 | - name: Get project version 29 | run: echo "PROJECT_VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:evaluate -Dexpression=project.version -q -DforceStdout | grep -v '\[')" >> $GITHUB_ENV 30 | - name: Maven deploy 31 | if: ${{ endsWith(env.PROJECT_VERSION, '-SNAPSHOT') }} 32 | run: mvn deploy -s .github/maven-cd-settings.xml -DskipTests -B 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .gradle 3 | .idea 4 | .vertx 5 | .classpath 6 | .project 7 | .settings 8 | .yardoc 9 | .yardopts 10 | build 11 | target 12 | out 13 | *.iml 14 | *.ipr 15 | *.iws 16 | test-output 17 | Scratch.java 18 | ScratchTest.java 19 | test-results 20 | test-tmp 21 | *.class 22 | -------------------------------------------------------------------------------- /.travis.deploy.artifacts.sh: -------------------------------------------------------------------------------- 1 | PROJECT_VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:evaluate -Dexpression=project.version -B | grep -v '\[') 2 | if [[ "$PROJECT_VERSION" =~ .*SNAPSHOT ]] && [[ "${TRAVIS_BRANCH}" =~ ^master$|^[0-9]+\.[0-9]+$ ]] && [[ "${TRAVIS_PULL_REQUEST}" = "false" ]]; 3 | then 4 | mvn deploy -s .travis.maven.settings.xml -DskipTests -B; 5 | fi 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | install: true 3 | branches: 4 | only: 5 | - master 6 | - /^\d+\.\d+$/ 7 | cache: 8 | directories: 9 | - $HOME/.m2 10 | before_cache: 11 | - rm -rf $HOME/.m2/repository/io/vertx/ 12 | jobs: 13 | include: 14 | - stage: test 15 | name: "Unit tests - OracleJDK 8" 16 | script: mvn -B -DtestLogLevel=OFF test 17 | jdk: oraclejdk8 18 | - name: "Unit tests - OpenJDK 11" 19 | if: type != pull_request 20 | script: mvn -B -DtestLogLevel=OFF test 21 | jdk: openjdk11 22 | - stage: deploy 23 | name: "Deploy to Sonatype's snapshots repository" 24 | jdk: openjdk8 25 | if: type != pull_request 26 | script: bash .travis.deploy.artifacts.sh 27 | notifications: 28 | email: 29 | recipients: 30 | - stream1984@me.com 31 | on_success: always 32 | on_failure: always 33 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013 The original author or authors 3 | * ------------------------------------------------------ 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Zookeeper Cluster Manager 2 | 3 | image:https://github.com/vert-x3/vertx-zookeeper/workflows/CI/badge.svg?branch=master["Build Status", link="https://github.com/vert-x3/vertx-zookeeper/actions?query=workflow%3ACI"] 4 | 5 | This is a cluster manager implementation for Vert.x that uses http://zookeeper.apache.org/[Zookeeper]. 6 | 7 | Please see the main documentation on the web-site for a full description: 8 | 9 | * https://vertx.io/docs/vertx-zookeeper/java/[Web-site documentation] 10 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | io.vertx 7 | vertx5-parent 8 | 12 9 | 10 | 11 | vertx-zookeeper 12 | 5.1.0-SNAPSHOT 13 | Vert.x Zookeeper Cluster Manager 14 | 15 | 16 | 5.4.0 17 | 3.7.2 18 | 4.13.1 19 | ${project.basedir}/src/main/asciidoc 20 | 21 | 22 | 23 | 24 | 25 | io.vertx 26 | vertx-dependencies 27 | ${project.version} 28 | pom 29 | import 30 | 31 | 32 | 33 | 34 | 35 | scm:git:git@github.com:vert-x3/vertx-zookeeper.git 36 | scm:git:git@github.com:vert-x3/vertx-zookeeper.git 37 | git@github.com:vert-x3/vertx-zookeeper.git 38 | 39 | 40 | 41 | 42 | io.vertx 43 | vertx-codegen-api 44 | true 45 | 46 | 47 | io.vertx 48 | vertx-codegen-json 49 | true 50 | 51 | 52 | io.vertx 53 | vertx-docgen-api 54 | true 55 | 56 | 57 | io.vertx 58 | vertx-core 59 | provided 60 | 61 | 62 | io.vertx 63 | vertx-web 64 | true 65 | 66 | 67 | 68 | org.apache.curator 69 | curator-framework 70 | ${curator.version} 71 | 72 | 73 | 74 | 75 | 76 | 77 | org.slf4j 78 | slf4j-api 79 | 80 | 81 | jline 82 | jline 83 | 84 | 85 | log4j 86 | log4j 87 | 88 | 89 | 90 | com.google.guava 91 | guava 92 | 93 | 94 | 95 | 96 | org.apache.zookeeper 97 | zookeeper 98 | ${zookeeper.version} 99 | 100 | 101 | io.netty 102 | netty-handler 103 | 104 | 105 | io.netty 106 | netty-tranasport-native-epoll 107 | 108 | 109 | org.slf4j 110 | slf4j-api 111 | 112 | 113 | org.slf4j 114 | slf4j-log4j12 115 | 116 | 117 | log4j 118 | log4j 119 | 120 | 121 | com.github.spotbugs 122 | spotbugs-annotations 123 | 124 | 125 | 126 | 127 | org.apache.curator 128 | curator-recipes 129 | ${curator.version} 130 | 131 | 132 | 133 | 134 | com.google.guava 135 | guava 136 | 137 | 138 | 139 | 140 | org.slf4j 141 | slf4j-api 142 | true 143 | 144 | 145 | org.apache.logging.log4j 146 | log4j-api 147 | true 148 | 149 | 150 | org.apache.logging.log4j 151 | log4j-core 152 | true 153 | 154 | 155 | 156 | junit 157 | junit 158 | ${junit.version} 159 | test 160 | 161 | 162 | org.apache.curator 163 | curator-test 164 | ${curator.version} 165 | test 166 | 167 | 168 | com.google.guava 169 | guava 170 | 171 | 172 | org.junit.jupiter 173 | junit-jupiter-api 174 | 175 | 176 | 177 | 178 | io.vertx 179 | vertx-core 180 | test-jar 181 | test 182 | 183 | 184 | io.vertx 185 | vertx-web 186 | test-jar 187 | test 188 | 189 | 190 | io.vertx 191 | vertx-service-discovery 192 | test 193 | 194 | 195 | io.vertx 196 | vertx-service-proxy 197 | test 198 | 199 | 200 | io.vertx 201 | vertx-service-discovery 202 | test-jar 203 | test 204 | 205 | 206 | org.assertj 207 | assertj-core 208 | 3.3.0 209 | test 210 | 211 | 212 | com.jayway.awaitility 213 | awaitility 214 | 1.7.0 215 | test 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | maven-compiler-plugin 224 | 225 | 226 | default-compile 227 | 228 | 229 | 230 | io.vertx 231 | vertx-codegen 232 | processor 233 | 234 | 235 | io.vertx 236 | vertx-docgen-processor 237 | processor 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | org.apache.maven.plugins 246 | maven-surefire-plugin 247 | 248 | false 249 | 250 | PARANOID 251 | ${project.build.directory} 252 | ${project.version} 253 | true 254 | 255 | 256 | -Xmx1200M 257 | 1 258 | true 259 | 260 | 261 | 262 | 263 | 264 | 265 | maven-assembly-plugin 266 | 267 | 268 | package-docs 269 | 270 | single 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | coverage 282 | 283 | 284 | 285 | 286 | org.apache.maven.plugins 287 | maven-surefire-plugin 288 | 289 | false 290 | 291 | PARANOID 292 | ${project.build.directory} 293 | ${project.version} 294 | true 295 | 296 | 297 | 298 | -Xmx1200M 299 | 1 300 | true 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | -------------------------------------------------------------------------------- /src/main/asciidoc/index.adoc: -------------------------------------------------------------------------------- 1 | = Zookeeper Cluster Manager 2 | 3 | This is a cluster manager implementation for Vert.x that uses http://zookeeper.apache.org/[Zookeeper]. 4 | 5 | It implements interfaces of vert.x cluster totally. So you can using it to instead of vertx-hazelcast if you want. 6 | This implementation is packaged inside: 7 | 8 | [source,xml,subs="+attributes"] 9 | ---- 10 | 11 | io.vertx 12 | vertx-zookeeper 13 | ${maven.version} 14 | 15 | ---- 16 | 17 | In Vert.x a cluster manager is used for various functions including: 18 | 19 | * Discovery and group membership of Vert.x nodes in a cluster 20 | * Maintaining cluster wide topic subscriber lists (so we know which nodes are interested in which event busaddresses) 21 | * Distributed Map support 22 | * Distributed Locks 23 | * Distributed Counters 24 | 25 | Cluster managersdo not* handle the event bus inter-node transport, this is done directly by Vert.x with TCP connections. 26 | 27 | == How to work 28 | We are using http://curator.apache.org/[Apache Curator] framework rather than zookeeper client directly, so 29 | we have a dependency for libraries used in Curator such as `guava`, `slf4j` and of course `zookeeper`. 30 | 31 | Since ZK using tree dictionary to store data, we can take root path as namespace default root path is `io.vertx` which in default-zookeeper.json. 32 | and there are another 5 sub path to record other information for functions in vert.x cluster manager, all you can change the path is `root path`. 33 | 34 | you can find all the vert.x node information in path of `/io.vertx/cluster/nodes/`, 35 | `/io.vertx/asyncMap/$name/` record all the `AsyncMap` you created with `io.vertx.core.shareddata.AsyncMap` interface. 36 | `/io.vertx/asyncMultiMap/$name/` record all the `AsyncMultiMap` you created with `io.vertx.core.spi.cluster.AsyncMultiMap` interface. 37 | `/io.vertx/locks/` record distributed Locks information. 38 | `/io.vertx/counters/` record distributed Count information. 39 | 40 | == Using this cluster manager 41 | 42 | If you are using Vert.x from the command line, the jar corresponding to this cluster manager (it will be named `vertx-zookeeper-${maven.version}`.jar` 43 | should be in the `lib` directory of the Vert.x installation. 44 | 45 | If you want clustering with this cluster manager in your Vert.x Maven or Gradle project then just add a dependency to 46 | the artifact: `io.vertx:vertx-zookeeper:${version}` in your project. 47 | 48 | If the jar is on your classpath as above then Vert.x will automatically detect this and use it as the cluster manager. 49 | Please make sure you don't have any other cluster managers on your classpath or Vert.x might 50 | choose the wrong one. 51 | 52 | You can also specify the cluster manager programmatically if you are embedding Vert.x by specifying it on the options 53 | when you are creating your Vert.x instance, for example: 54 | 55 | [source, $lang] 56 | ---- 57 | {@link example.Examples#example1()} 58 | ---- 59 | 60 | == Configuring this cluster manager 61 | 62 | Usually the cluster manager is configured by a file 63 | https://github.com/vert-x3/vertx-zookeeper/blob/master/src/main/resources/default-zookeeper.json[`default-zookeeper.json`] 64 | which is packaged inside the jar. 65 | 66 | If you want to override this configuration you can provide a file called `zookeeper.json` on your classpath and this 67 | will be used instead. If you want to embed the `zookeeper.json` file in a fat jar, it must be located at the root of the 68 | fat jar. If it's an external file, the*directory** containing the file must be added to the classpath. For 69 | example, if you are using the _launcher_ class from Vert.x, the classpath enhancement can be done as follows: 70 | 71 | [source,shell] 72 | ---- 73 | # If the zookeeper.json is in the current directory: 74 | java -jar ... -cp . -cluster 75 | vertx run MyVerticle -cp . -cluster 76 | 77 | # If the zookeeper.json is in the conf directory 78 | java -jar ... -cp conf -cluster 79 | ---- 80 | 81 | Another way to override the configuration is by providing the system property `vertx.zookeeper.conf` with a 82 | location: 83 | 84 | [source,shell] 85 | ---- 86 | # Use a cluster configuration located in an external file 87 | java -Dvertx.zookeeper.config=./config/my-zookeeper-conf.json -jar ... -cluster 88 | 89 | # Or use a custom configuration from the classpath 90 | java -Dvertx.zookeeper.config=classpath:my/package/config/my-cluster-config.json -jar ... -cluster 91 | ---- 92 | 93 | The `vertx.zookeeper.config` system property, when present, overrides any `zookeeper.json` from the classpath, but if 94 | loading 95 | from this system property fails, then loading falls back to either `zookeeper.json` or the Zookeeper default configuration. 96 | 97 | The configuration file is described in detail in `default-zookeeper.json`'s comment. 98 | 99 | You can also specify configuration programmatically if embedding: 100 | 101 | [source,java] 102 | ---- 103 | {@link example.Examples#example2()} 104 | ---- 105 | 106 | The retry policy can be specified in the json configuration as following: 107 | 108 | [source,json] 109 | ---- 110 | { 111 | "retry":{ 112 | "policy": "exponential_backoff" 113 | } 114 | } 115 | ---- 116 | 117 | The possible value for the policy are: 118 | 119 | * `exponential_backoff` (default) 120 | * `bounded_exponential_backoff` 121 | * `one_time` 122 | * `n_times` 123 | * `forever` 124 | * `until_elapsed` 125 | 126 | 127 | IMPORTANT: You can also configure the zookeeper hosts using the `vertx.zookeeper.hosts` system property. 128 | 129 | === Enabling logging 130 | 131 | When trouble-shooting clustering issues with Zookeeper it's often useful to get some logging output from Zookeeper 132 | to see if it's forming a cluster properly. You can do this (when using the default JUL logging) by adding a file 133 | called `vertx-default-jul-logging.properties` on your classpath. This is a standard java.util.logging (JUL) 134 | configuration file. Inside it set: 135 | 136 | [source,properties] 137 | ---- 138 | org.apache.zookeeper.level=INFO 139 | ---- 140 | 141 | and also 142 | 143 | [source,properties] 144 | ---- 145 | java.util.logging.ConsoleHandler.level=INFO 146 | java.util.logging.FileHandler.level=INFO 147 | ---- 148 | -------------------------------------------------------------------------------- /src/main/java/example/Examples.java: -------------------------------------------------------------------------------- 1 | package example; 2 | 3 | import io.vertx.core.Vertx; 4 | import io.vertx.core.VertxOptions; 5 | import io.vertx.core.json.JsonObject; 6 | import io.vertx.core.spi.cluster.ClusterManager; 7 | import io.vertx.spi.cluster.zookeeper.ZookeeperClusterManager; 8 | import org.apache.curator.framework.CuratorFramework; 9 | 10 | /** 11 | * Created by stream. 12 | */ 13 | public class Examples { 14 | 15 | public void example1() { 16 | ClusterManager mgr = new ZookeeperClusterManager(); 17 | Vertx.builder() 18 | .withClusterManager(mgr) 19 | .buildClustered().onComplete(res -> { 20 | if (res.succeeded()) { 21 | Vertx vertx = res.result(); 22 | } else { 23 | // failed! 24 | } 25 | }); 26 | } 27 | 28 | public void example2() { 29 | JsonObject zkConfig = new JsonObject(); 30 | zkConfig.put("zookeeperHosts", "127.0.0.1"); 31 | zkConfig.put("rootPath", "io.vertx"); 32 | zkConfig.put("retry", new JsonObject() 33 | .put("initialSleepTime", 3000) 34 | .put("maxTimes", 3)); 35 | 36 | 37 | ClusterManager mgr = new ZookeeperClusterManager(zkConfig); 38 | 39 | Vertx.builder() 40 | .withClusterManager(mgr) 41 | .buildClustered().onComplete(res -> { 42 | if (res.succeeded()) { 43 | Vertx vertx = res.result(); 44 | } else { 45 | // failed! 46 | } 47 | }); 48 | } 49 | 50 | public void example3(CuratorFramework curator) { 51 | ClusterManager mgr = new ZookeeperClusterManager(curator); 52 | Vertx.builder().withClusterManager(mgr).buildClustered().onComplete(res -> { 53 | if (res.succeeded()) { 54 | Vertx vertx = res.result(); 55 | } else { 56 | // failed! 57 | } 58 | }); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/example/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013 The original author or authors 3 | * ------------------------------------------------------ 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | @Source 17 | package example; 18 | 19 | import io.vertx.docgen.Source; -------------------------------------------------------------------------------- /src/main/java/io/vertx/spi/cluster/zookeeper/ZookeeperClusterManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2020 The original author or authors 3 | * ------------------------------------------------------ 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | 17 | package io.vertx.spi.cluster.zookeeper; 18 | 19 | import io.vertx.core.*; 20 | import io.vertx.core.buffer.Buffer; 21 | import io.vertx.core.internal.VertxInternal; 22 | import io.vertx.core.internal.logging.Logger; 23 | import io.vertx.core.internal.logging.LoggerFactory; 24 | import io.vertx.core.json.JsonObject; 25 | import io.vertx.core.shareddata.AsyncMap; 26 | import io.vertx.core.shareddata.Counter; 27 | import io.vertx.core.shareddata.Lock; 28 | import io.vertx.core.spi.cluster.*; 29 | import io.vertx.spi.cluster.zookeeper.impl.*; 30 | import org.apache.curator.RetryPolicy; 31 | import org.apache.curator.framework.CuratorFramework; 32 | import org.apache.curator.framework.CuratorFrameworkFactory; 33 | import org.apache.curator.framework.api.CuratorEventType; 34 | import org.apache.curator.framework.imps.CuratorFrameworkState; 35 | import org.apache.curator.framework.recipes.cache.PathChildrenCache; 36 | import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; 37 | import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; 38 | import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex; 39 | import org.apache.zookeeper.CreateMode; 40 | import org.apache.zookeeper.KeeperException; 41 | 42 | import java.util.ArrayList; 43 | import java.util.List; 44 | import java.util.Map; 45 | import java.util.Objects; 46 | import java.util.Optional; 47 | import java.util.UUID; 48 | import java.util.concurrent.ConcurrentHashMap; 49 | import java.util.concurrent.ExecutorService; 50 | import java.util.concurrent.Executors; 51 | import java.util.concurrent.TimeUnit; 52 | import java.util.function.Function; 53 | import java.util.stream.Collectors; 54 | 55 | /** 56 | * A cluster manager that uses Zookeeper 57 | * 58 | * @author Stream.Liu 59 | */ 60 | public class ZookeeperClusterManager implements ClusterManager, PathChildrenCacheListener { 61 | 62 | private static final Logger log = LoggerFactory.getLogger(ZookeeperClusterManager.class); 63 | 64 | private VertxInternal vertx; 65 | 66 | private NodeListener nodeListener; 67 | private RegistrationListener registrationListener; 68 | private PathChildrenCache clusterNodes; 69 | private volatile boolean active; 70 | private volatile boolean joined; 71 | 72 | private String nodeId; 73 | private NodeInfo nodeInfo; 74 | private CuratorFramework curator; 75 | private boolean customCuratorCluster; 76 | private RetryPolicy retryPolicy; 77 | private SubsMapHelper subsMapHelper; 78 | private final Map localNodeInfo = new ConcurrentHashMap<>(); 79 | private final Map locks = new ConcurrentHashMap<>(); 80 | private final Map> asyncMapCache = new ConcurrentHashMap<>(); 81 | 82 | private JsonObject conf = new JsonObject(); 83 | 84 | private static final String ZK_PATH_LOCKS = "/locks/"; 85 | private static final String ZK_PATH_CLUSTER_NODE = "/cluster/nodes/"; 86 | private static final String ZK_PATH_CLUSTER_NODE_WITHOUT_SLASH = "/cluster/nodes"; 87 | 88 | private ExecutorService lockReleaseExec; 89 | 90 | private Function resolveNodeId = path -> { 91 | String[] pathArr = path.split("\\/"); 92 | return pathArr[pathArr.length - 1]; 93 | }; 94 | 95 | public ZookeeperClusterManager() { 96 | conf = ConfigUtil.loadConfig(null); 97 | } 98 | 99 | public ZookeeperClusterManager(CuratorFramework curator) { 100 | this(curator, UUID.randomUUID().toString()); 101 | } 102 | 103 | public ZookeeperClusterManager(String resourceLocation) { 104 | conf = ConfigUtil.loadConfig(resourceLocation); 105 | } 106 | 107 | public ZookeeperClusterManager(CuratorFramework curator, String nodeId) { 108 | Objects.requireNonNull(curator, "The Curator instance cannot be null."); 109 | Objects.requireNonNull(nodeId, "The nodeId cannot be null."); 110 | this.curator = curator; 111 | this.nodeId = nodeId; 112 | this.customCuratorCluster = true; 113 | } 114 | 115 | public ZookeeperClusterManager(JsonObject config) { 116 | this.conf = config; 117 | } 118 | 119 | public void setConfig(JsonObject conf) { 120 | this.conf = conf; 121 | } 122 | 123 | public JsonObject getConfig() { 124 | return conf; 125 | } 126 | 127 | public CuratorFramework getCuratorFramework() { 128 | return this.curator; 129 | } 130 | 131 | @Override 132 | public void init(Vertx vertx) { 133 | this.vertx = (VertxInternal) vertx; 134 | } 135 | 136 | @Override 137 | public void getAsyncMap(String name, Completable> promise) { 138 | vertx.>executeBlocking(() -> { 139 | @SuppressWarnings("unchecked") 140 | AsyncMap zkAsyncMap = (AsyncMap) asyncMapCache.computeIfAbsent(name, key -> new ZKAsyncMap<>(vertx, curator, name)); 141 | return zkAsyncMap; 142 | }).onComplete(promise); 143 | } 144 | 145 | @Override 146 | public Map getSyncMap(String name) { 147 | return new ZKSyncMap<>(curator, name); 148 | } 149 | 150 | @Override 151 | public void getLockWithTimeout(String name, long timeout, Completable promise) { 152 | vertx.executeBlocking(() -> { 153 | ZKLock lock = locks.get(name); 154 | if (lock == null) { 155 | InterProcessSemaphoreMutex mutexLock = new InterProcessSemaphoreMutex(curator, ZK_PATH_LOCKS + name); 156 | lock = new ZKLock(mutexLock, lockReleaseExec); 157 | } 158 | try { 159 | if (lock.getLock().acquire(timeout, TimeUnit.MILLISECONDS)) { 160 | locks.putIfAbsent(name, lock); 161 | return lock; 162 | } else { 163 | throw new VertxException("Timed out waiting to get lock " + name); 164 | } 165 | } catch (Exception e) { 166 | throw new VertxException("get lock exception", e); 167 | } 168 | }, false).onComplete(promise); 169 | } 170 | 171 | @Override 172 | public void getCounter(String name, Completable promise) { 173 | vertx.executeBlocking(() -> { 174 | try { 175 | Objects.requireNonNull(name); 176 | return new ZKCounter(vertx, curator, name, retryPolicy); 177 | } catch (Exception e) { 178 | throw new VertxException(e, true); 179 | } 180 | }).onComplete(promise); 181 | } 182 | 183 | @Override 184 | public String getNodeId() { 185 | return nodeId; 186 | } 187 | 188 | @Override 189 | public List getNodes() { 190 | return clusterNodes.getCurrentData().stream() 191 | .map(childData -> resolveNodeId.apply(childData.getPath())) 192 | .collect(Collectors.toList()); 193 | } 194 | 195 | @Override 196 | public void registrationListener(RegistrationListener registrationListener) { 197 | this.registrationListener = registrationListener; 198 | } 199 | 200 | @Override 201 | public void nodeListener(NodeListener listener) { 202 | this.nodeListener = listener; 203 | } 204 | 205 | @Override 206 | public void setNodeInfo(NodeInfo nodeInfo, Completable promise) { 207 | synchronized (this) { 208 | this.nodeInfo = nodeInfo; 209 | } 210 | try { 211 | Buffer buffer = Buffer.buffer(); 212 | nodeInfo.writeToBuffer(buffer); 213 | curator.create().orSetData().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).inBackground((c, e) -> { 214 | if (e.getType() == CuratorEventType.SET_DATA || e.getType() == CuratorEventType.CREATE) { 215 | vertx.runOnContext(Avoid -> { 216 | localNodeInfo.put(nodeId, nodeInfo); 217 | promise.succeed(); 218 | }); 219 | } 220 | }).withUnhandledErrorListener(log::error).forPath(ZK_PATH_CLUSTER_NODE + nodeId, buffer.getBytes()); 221 | } catch (Exception e) { 222 | log.error("create node failed.", e); 223 | } 224 | } 225 | 226 | @Override 227 | public synchronized NodeInfo getNodeInfo() { 228 | return nodeInfo; 229 | } 230 | 231 | @Override 232 | public void getNodeInfo(String nodeId, Completable promise) { 233 | vertx.executeBlocking(() -> { 234 | return Optional.ofNullable(clusterNodes.getCurrentData(ZK_PATH_CLUSTER_NODE + nodeId)) 235 | .map(childData -> { 236 | Buffer buffer = Buffer.buffer(childData.getData()); 237 | NodeInfo nodeInfo = new NodeInfo(); 238 | nodeInfo.readFromBuffer(0, buffer); 239 | return nodeInfo; 240 | }).orElseThrow(() -> new VertxException("Not a member of the cluster", true)); 241 | }, false).onComplete(promise); 242 | } 243 | 244 | private void addLocalNodeId() throws VertxException { 245 | clusterNodes = new PathChildrenCache(curator, ZK_PATH_CLUSTER_NODE_WITHOUT_SLASH, true); 246 | clusterNodes.getListenable().addListener(this); 247 | try { 248 | clusterNodes.start(PathChildrenCache.StartMode.NORMAL); 249 | //Join to the cluster 250 | createThisNode(); 251 | joined = true; 252 | subsMapHelper = new SubsMapHelper(curator, vertx, registrationListener, nodeId); 253 | } catch (Exception e) { 254 | throw new VertxException(e); 255 | } 256 | } 257 | 258 | private void createThisNode() throws Exception { 259 | try { 260 | curator.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(ZK_PATH_CLUSTER_NODE + nodeId, nodeId.getBytes()); 261 | } catch (KeeperException.NodeExistsException e) { 262 | //idempotent 263 | log.info("node:" + nodeId + " have created successful."); 264 | } 265 | } 266 | 267 | @Override 268 | public void join(Completable promise) { 269 | vertx.executeBlocking(() -> { 270 | if (!active) { 271 | active = true; 272 | lockReleaseExec = Executors.newCachedThreadPool(r -> new Thread(r, "vertx-zookeeper-service-release-lock-thread")); 273 | 274 | //The curator instance has been passed using the constructor. 275 | if (customCuratorCluster) { 276 | addLocalNodeId(); 277 | return null; 278 | } 279 | 280 | if (curator == null) { 281 | retryPolicy = RetryPolicyHelper.createRetryPolicy(conf.getJsonObject("retry", new JsonObject())); 282 | 283 | // Read the zookeeper hosts from a system variable 284 | String hosts = System.getProperty("vertx.zookeeper.hosts"); 285 | if (hosts == null) { 286 | hosts = conf.getString("zookeeperHosts", "127.0.0.1"); 287 | } 288 | log.info("Zookeeper hosts set to " + hosts); 289 | 290 | curator = CuratorFrameworkFactory.builder() 291 | .connectString(hosts) 292 | .namespace(conf.getString("rootPath", "io.vertx")) 293 | .sessionTimeoutMs(conf.getInteger("sessionTimeout", 20000)) 294 | .connectionTimeoutMs(conf.getInteger("connectTimeout", 3000)) 295 | .retryPolicy(retryPolicy).build(); 296 | } 297 | curator.start(); 298 | while (curator.getState() != CuratorFrameworkState.STARTED) { 299 | try { 300 | Thread.sleep(100); 301 | } catch (InterruptedException e) { 302 | if (curator.getState() != CuratorFrameworkState.STARTED) { 303 | throw new VertxException("zookeeper client being interrupted while starting.", true); 304 | } 305 | } 306 | } 307 | nodeId = UUID.randomUUID().toString(); 308 | addLocalNodeId(); 309 | return null; 310 | } else { 311 | return null; 312 | } 313 | }).onComplete(promise); 314 | } 315 | 316 | @Override 317 | public void leave(Completable promise) { 318 | vertx.executeBlocking(() -> { 319 | synchronized (ZookeeperClusterManager.this) { 320 | if (active) { 321 | active = false; 322 | joined = false; 323 | lockReleaseExec.shutdown(); 324 | try { 325 | clusterNodes.close(); 326 | subsMapHelper.close(); 327 | curator.close(); 328 | } catch (Exception e) { 329 | log.warn("zookeeper close exception.", e); 330 | } 331 | } 332 | return null; 333 | } 334 | }).onComplete(promise); 335 | } 336 | 337 | @Override 338 | public boolean isActive() { 339 | return active; 340 | } 341 | 342 | @Override 343 | public void addRegistration(String address, RegistrationInfo registrationInfo, Completable promise) { 344 | System.out.println("ADD REGISTRATION " + address + " " + registrationInfo); 345 | subsMapHelper.put(address, registrationInfo, (res, err) -> { 346 | System.out.println("ADD REG COMPLETE " + err); 347 | promise.complete(null, err); 348 | }); 349 | } 350 | 351 | @Override 352 | public void removeRegistration(String address, RegistrationInfo registrationInfo, Completable promise) { 353 | subsMapHelper.remove(address, registrationInfo, promise); 354 | } 355 | 356 | @Override 357 | public void getRegistrations(String address, Completable> promise) { 358 | vertx.executeBlocking(() -> subsMapHelper.get(address), false).onComplete(ar -> { 359 | System.out.println("GET REG " + ar.result()); 360 | promise.complete(ar.result(), ar.cause()); 361 | }); 362 | } 363 | 364 | @Override 365 | public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { 366 | if (!active) return; 367 | switch (event.getType()) { 368 | case CHILD_ADDED: 369 | try { 370 | if (nodeListener != null && client.getState() != CuratorFrameworkState.STOPPED) { 371 | nodeListener.nodeAdded(resolveNodeId.apply(event.getData().getPath())); 372 | } 373 | } catch (Throwable t) { 374 | log.error("Failed to handle memberAdded", t); 375 | } 376 | break; 377 | case CHILD_REMOVED: 378 | try { 379 | if (nodeListener != null && client.getState() != CuratorFrameworkState.STOPPED) { 380 | nodeListener.nodeLeft(resolveNodeId.apply(event.getData().getPath())); 381 | } 382 | } catch (Throwable t) { 383 | log.warn("Failed to handle memberRemoved", t); 384 | } 385 | break; 386 | case CHILD_UPDATED: 387 | //log.warn("Weird event that update cluster node. path:" + event.getData().getPath()); 388 | break; 389 | case CONNECTION_RECONNECTED: 390 | if (joined) { 391 | createThisNode(); 392 | List> futures = new ArrayList<>(); 393 | for(Map.Entry entry : localNodeInfo.entrySet()) { 394 | Promise promise = Promise.promise(); 395 | setNodeInfo(entry.getValue(), promise); 396 | futures.add(promise.future()); 397 | } 398 | Future.all(futures).onComplete(ar -> { 399 | if (ar.failed()) { 400 | log.error("recover node info failed.", ar.cause()); 401 | } 402 | }); 403 | } 404 | break; 405 | case CONNECTION_SUSPENDED: 406 | //just release locks on this node. 407 | locks.values().forEach(ZKLock::release); 408 | break; 409 | case CONNECTION_LOST: 410 | //release locks and clean locks 411 | joined = false; 412 | locks.values().forEach(ZKLock::release); 413 | locks.clear(); 414 | break; 415 | } 416 | } 417 | } 418 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/spi/cluster/zookeeper/impl/ConfigUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2023 The original author or authors 3 | * ------------------------------------------------------ 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | 17 | package io.vertx.spi.cluster.zookeeper.impl; 18 | 19 | import io.vertx.core.internal.logging.Logger; 20 | import io.vertx.core.internal.logging.LoggerFactory; 21 | import io.vertx.core.json.JsonObject; 22 | 23 | import java.io.BufferedInputStream; 24 | import java.io.BufferedReader; 25 | import java.io.File; 26 | import java.io.FileInputStream; 27 | import java.io.FileNotFoundException; 28 | import java.io.IOException; 29 | import java.io.InputStream; 30 | import java.io.InputStreamReader; 31 | 32 | public class ConfigUtil { 33 | 34 | private static final Logger log = LoggerFactory.getLogger(ConfigUtil.class); 35 | 36 | private static final String DEFAULT_CONFIG_FILE = "default-zookeeper.json"; 37 | private static final String CONFIG_FILE = "zookeeper.json"; 38 | private static final String ZK_SYS_CONFIG_KEY = "vertx.zookeeper.config"; 39 | 40 | public static JsonObject loadConfig(String resourceLocation) { 41 | JsonObject conf = null; 42 | try ( 43 | InputStream is = getConfigStream(resourceLocation != null ? resourceLocation : System.getProperty(ZK_SYS_CONFIG_KEY)); 44 | BufferedReader reader = new BufferedReader(new InputStreamReader(new BufferedInputStream(is))) 45 | ) { 46 | String line; 47 | StringBuilder sb = new StringBuilder(); 48 | while ((line = reader.readLine()) != null) { 49 | sb.append(line); 50 | } 51 | conf = new JsonObject(sb.toString()); 52 | } catch (IOException ex) { 53 | log.error("Failed to read config", ex); 54 | } 55 | return conf; 56 | } 57 | 58 | private static InputStream getConfigStream(String resourceLocation) { 59 | InputStream is = getConfigStreamFor(resourceLocation); 60 | if (is == null) { 61 | is = getConfigStreamFromClasspath(CONFIG_FILE, DEFAULT_CONFIG_FILE); 62 | } 63 | return is; 64 | } 65 | 66 | private static InputStream getConfigStreamFor(String resourceLocation) { 67 | InputStream is = null; 68 | if (resourceLocation != null) { 69 | if (resourceLocation.startsWith("classpath:")) { 70 | return getConfigStreamFromClasspath(resourceLocation.substring("classpath:".length()), CONFIG_FILE); 71 | } 72 | File cfgFile = new File(resourceLocation); 73 | if (cfgFile.exists()) { 74 | try { 75 | is = new FileInputStream(cfgFile); 76 | } catch (FileNotFoundException ex) { 77 | log.warn(String.format("Failed to open file '%s' defined in '%s'. Continuing classpath search for %s", resourceLocation, ZK_SYS_CONFIG_KEY, CONFIG_FILE)); 78 | } 79 | } 80 | } 81 | return is; 82 | } 83 | 84 | private static InputStream getConfigStreamFromClasspath(String configFile, String defaultConfig) { 85 | InputStream is = null; 86 | ClassLoader ctxClsLoader = Thread.currentThread().getContextClassLoader(); 87 | if (ctxClsLoader != null) { 88 | is = ctxClsLoader.getResourceAsStream(configFile); 89 | } 90 | if (is == null) { 91 | is = ConfigUtil.class.getClassLoader().getResourceAsStream(configFile); 92 | if (is == null) { 93 | is = ConfigUtil.class.getClassLoader().getResourceAsStream(defaultConfig); 94 | } 95 | } 96 | return is; 97 | } 98 | 99 | private ConfigUtil() { 100 | // Utility class 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/spi/cluster/zookeeper/impl/RetryPolicyHelper.java: -------------------------------------------------------------------------------- 1 | package io.vertx.spi.cluster.zookeeper.impl; 2 | 3 | import io.vertx.core.internal.logging.Logger; 4 | import io.vertx.core.internal.logging.LoggerFactory; 5 | import io.vertx.core.json.JsonObject; 6 | import org.apache.curator.RetryPolicy; 7 | import org.apache.curator.retry.*; 8 | 9 | public class RetryPolicyHelper { 10 | private static final String DEFAULT_RETRY_POLICY = "exponential_backoff"; 11 | private static final Logger log = LoggerFactory.getLogger(RetryPolicyHelper.class); 12 | 13 | 14 | /** 15 | * creates a {@link RetryPolicy} based on a JsonObject configuration. 16 | * It falls back to {@link ExponentialBackoffRetry} if no valid policy is specified. 17 | * 18 | * @param conf the configuration object 19 | * @return RetryPolicy instance 20 | */ 21 | public static RetryPolicy createRetryPolicy(JsonObject conf){ 22 | String policy = conf.getString("policy", DEFAULT_RETRY_POLICY); 23 | int initialSleepTime = conf.getInteger("initialSleepTime", 1000); 24 | int maxTimes = conf.getInteger("maxTimes", 5); 25 | int intervalTimes = conf.getInteger("intervalTimes",10000); 26 | 27 | switch (policy){ 28 | case "bounded_exponential_backoff": return new BoundedExponentialBackoffRetry(initialSleepTime,maxTimes,intervalTimes); 29 | case "one_time": return new RetryOneTime(intervalTimes); 30 | case "n_times": return new RetryNTimes(maxTimes, intervalTimes); 31 | case "forever": return new RetryForever(intervalTimes); 32 | case "until_elapsed": return new RetryUntilElapsed(maxTimes,intervalTimes); 33 | case DEFAULT_RETRY_POLICY: return new ExponentialBackoffRetry(initialSleepTime, maxTimes, intervalTimes); 34 | default: { 35 | log.warn(String.format("%s is not a valid policy, falling back to %s",policy, DEFAULT_RETRY_POLICY)); 36 | return new ExponentialBackoffRetry(initialSleepTime, maxTimes, intervalTimes); 37 | } 38 | } 39 | } 40 | 41 | private RetryPolicyHelper() { 42 | //Utility class 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/spi/cluster/zookeeper/impl/SubsMapHelper.java: -------------------------------------------------------------------------------- 1 | package io.vertx.spi.cluster.zookeeper.impl; 2 | 3 | import io.vertx.core.Completable; 4 | import io.vertx.core.Future; 5 | import io.vertx.core.Promise; 6 | import io.vertx.core.VertxException; 7 | import io.vertx.core.buffer.Buffer; 8 | import io.vertx.core.internal.VertxInternal; 9 | import io.vertx.core.internal.logging.Logger; 10 | import io.vertx.core.internal.logging.LoggerFactory; 11 | import io.vertx.core.spi.cluster.RegistrationInfo; 12 | import io.vertx.core.spi.cluster.RegistrationListener; 13 | import io.vertx.core.spi.cluster.RegistrationUpdateEvent; 14 | import org.apache.curator.framework.CuratorFramework; 15 | import org.apache.curator.framework.api.CuratorEventType; 16 | import org.apache.curator.framework.recipes.cache.ChildData; 17 | import org.apache.curator.framework.recipes.cache.TreeCache; 18 | import org.apache.curator.framework.recipes.cache.TreeCacheEvent; 19 | import org.apache.curator.framework.recipes.cache.TreeCacheListener; 20 | import org.apache.zookeeper.CreateMode; 21 | 22 | import java.util.*; 23 | import java.util.concurrent.ConcurrentHashMap; 24 | import java.util.concurrent.ConcurrentMap; 25 | import java.util.function.BiFunction; 26 | import java.util.function.Function; 27 | 28 | public class SubsMapHelper implements TreeCacheListener { 29 | 30 | private final CuratorFramework curator; 31 | private final TreeCache treeCache; 32 | private final VertxInternal vertx; 33 | private final RegistrationListener registrationListener; 34 | private final String nodeId; 35 | private final ConcurrentMap> ownSubs = new ConcurrentHashMap<>(); 36 | private final ConcurrentMap> localSubs = new ConcurrentHashMap<>(); 37 | 38 | private static final String VERTX_SUBS_NAME = "/__vertx.subs"; 39 | private static final Logger log = LoggerFactory.getLogger(SubsMapHelper.class); 40 | 41 | private static final Function keyPath = address -> VERTX_SUBS_NAME + "/" + address; 42 | private static final Function valuePath = registrationInfo -> registrationInfo.nodeId() + "-" + registrationInfo.seq(); 43 | private static final BiFunction fullPath = (address, registrationInfo) -> keyPath.apply(address) + "/" + valuePath.apply(registrationInfo); 44 | 45 | public SubsMapHelper(CuratorFramework curator, VertxInternal vertx, RegistrationListener registrationListener, String nodeId) { 46 | this.curator = curator; 47 | this.vertx = vertx; 48 | this.treeCache = new TreeCache(curator, VERTX_SUBS_NAME); 49 | this.treeCache.getListenable().addListener(this); 50 | try { 51 | this.treeCache.start(); 52 | } catch (Exception e) { 53 | throw new VertxException(e); 54 | } 55 | this.registrationListener = registrationListener; 56 | this.nodeId = nodeId; 57 | } 58 | 59 | public void close() { 60 | treeCache.close(); 61 | } 62 | 63 | public void put(String address, RegistrationInfo registrationInfo, Completable promise) { 64 | if (registrationInfo.localOnly()) { 65 | localSubs.compute(address, (add, curr) -> addToSet(registrationInfo, curr)); 66 | fireRegistrationUpdateEvent(address); 67 | promise.succeed(); 68 | } else { 69 | try { 70 | Buffer buffer = Buffer.buffer(); 71 | registrationInfo.writeToBuffer(buffer); 72 | curator.create().orSetData().creatingParentContainersIfNeeded().withMode(CreateMode.EPHEMERAL).inBackground((c, e) -> { 73 | if (e.getType() == CuratorEventType.CREATE || e.getType() == CuratorEventType.SET_DATA) { 74 | vertx.runOnContext(Avoid -> { 75 | ownSubs.compute(address, (add, curr) -> addToSet(registrationInfo, curr)); 76 | promise.succeed(); 77 | }); 78 | } 79 | }).withUnhandledErrorListener(log::error).forPath(fullPath.apply(address, registrationInfo), buffer.getBytes()); 80 | } catch (Exception e) { 81 | log.error(String.format("create subs address %s failed.", address), e); 82 | } 83 | } 84 | } 85 | 86 | private Set addToSet(RegistrationInfo registrationInfo, Set curr) { 87 | Set res = curr != null ? curr : Collections.synchronizedSet(new LinkedHashSet<>()); 88 | res.add(registrationInfo); 89 | return res; 90 | } 91 | 92 | public List get(String address) { 93 | Map map = treeCache.getCurrentChildren(keyPath.apply(address)); 94 | Collection remote = (map == null) ? Collections.emptyList() : map.values(); 95 | 96 | List list; 97 | int size; 98 | size = remote.size(); 99 | Set local = localSubs.get(address); 100 | if (local != null) { 101 | synchronized (local) { 102 | size += local.size(); 103 | if (size == 0) { 104 | return Collections.emptyList(); 105 | } 106 | list = new ArrayList<>(size); 107 | list.addAll(local); 108 | } 109 | } else if (size == 0) { 110 | return Collections.emptyList(); 111 | } else { 112 | list = new ArrayList<>(size); 113 | } 114 | for (ChildData childData : remote) { 115 | list.add(toRegistrationInfo(childData)); 116 | } 117 | return list; 118 | } 119 | 120 | private static RegistrationInfo toRegistrationInfo(ChildData childData) { 121 | RegistrationInfo registrationInfo = new RegistrationInfo(); 122 | Buffer buffer = Buffer.buffer(childData.getData()); 123 | registrationInfo.readFromBuffer(0, buffer); 124 | return registrationInfo; 125 | } 126 | 127 | public void remove(String address, RegistrationInfo registrationInfo, Completable promise) { 128 | try { 129 | if (registrationInfo.localOnly()) { 130 | localSubs.computeIfPresent(address, (add, curr) -> removeFromSet(registrationInfo, curr)); 131 | fireRegistrationUpdateEvent(address); 132 | promise.succeed(); 133 | } else { 134 | curator.delete().guaranteed().inBackground((c, e) -> { 135 | if (e.getType() == CuratorEventType.DELETE) { 136 | vertx.runOnContext(aVoid -> { 137 | ownSubs.computeIfPresent(address, (add, curr) -> removeFromSet(registrationInfo, curr)); 138 | promise.succeed(); 139 | }); 140 | } 141 | }).forPath(fullPath.apply(address, registrationInfo)); 142 | } 143 | } catch (Exception e) { 144 | log.error(String.format("remove subs address %s failed.", address), e); 145 | promise.fail(e); 146 | } 147 | } 148 | 149 | private Set removeFromSet(RegistrationInfo registrationInfo, Set curr) { 150 | curr.remove(registrationInfo); 151 | return curr.isEmpty() ? null : curr; 152 | } 153 | 154 | @Override 155 | public void childEvent(CuratorFramework client, TreeCacheEvent event) { 156 | switch (event.getType()) { 157 | case NODE_ADDED: 158 | case NODE_UPDATED: 159 | case NODE_REMOVED: 160 | // /__vertx.subs/AddressName/NodeId -> AddressName 161 | String[] pathElements = event.getData().getPath().split("/", 4); 162 | if (pathElements.length <= 3) { 163 | // "/__vertx.subs" and "/__vertx.subs/XX" added event 164 | break; 165 | } 166 | String addr = pathElements[2]; 167 | vertx.>executeBlocking(() -> get(addr), false).onComplete(ar -> { 168 | if (ar.succeeded()) { 169 | registrationListener.registrationsUpdated(new RegistrationUpdateEvent(addr, ar.result())); 170 | } else { 171 | log.trace("A failure occured while retrieving the updated registrations", ar.cause()); 172 | registrationListener.registrationsUpdated(new RegistrationUpdateEvent(addr, Collections.emptyList())); 173 | } 174 | }); 175 | break; 176 | case CONNECTION_SUSPENDED: 177 | log.warn(String.format("vertx node %s which connected to zookeeper have been suspended.", nodeId)); 178 | break; 179 | case CONNECTION_LOST: 180 | log.warn(String.format("vertx node %s which connected to zookeeper has lost", nodeId)); 181 | break; 182 | case CONNECTION_RECONNECTED: 183 | log.info(String.format("vertx node %s have reconnected to zookeeper", nodeId)); 184 | vertx.runOnContext(aVoid -> { 185 | List> futures = new ArrayList<>(); 186 | for (Map.Entry> entry : ownSubs.entrySet()) { 187 | for (RegistrationInfo registrationInfo : entry.getValue()) { 188 | Promise promise = Promise.promise(); 189 | put(entry.getKey(), registrationInfo, promise); 190 | futures.add(promise.future()); 191 | } 192 | } 193 | Future.all(futures).onComplete(ar -> { 194 | if (ar.failed()) { 195 | log.error("recover node subs information failed.", ar.cause()); 196 | } else { 197 | log.info("recover node subs success."); 198 | } 199 | }); 200 | }); 201 | break; 202 | } 203 | } 204 | 205 | private void fireRegistrationUpdateEvent(String address) { 206 | registrationListener.registrationsUpdated(new RegistrationUpdateEvent(address, get(address))); 207 | } 208 | 209 | } 210 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/spi/cluster/zookeeper/impl/ZKAsyncMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2016 The original author or authors 3 | * ------------------------------------------------------ 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.spi.cluster.zookeeper.impl; 17 | 18 | import io.vertx.core.Future; 19 | import io.vertx.core.Promise; 20 | import io.vertx.core.Vertx; 21 | import io.vertx.core.internal.VertxInternal; 22 | import io.vertx.core.shareddata.AsyncMap; 23 | import org.apache.curator.framework.CuratorFramework; 24 | import org.apache.curator.framework.api.CuratorEventType; 25 | import org.apache.zookeeper.data.Stat; 26 | 27 | import java.io.IOException; 28 | import java.time.Instant; 29 | import java.util.*; 30 | 31 | /** 32 | * Created by Stream.Liu 33 | */ 34 | public class ZKAsyncMap extends ZKMap implements AsyncMap { 35 | 36 | public ZKAsyncMap(Vertx vertx, CuratorFramework curator, String mapName) { 37 | super(curator, vertx, ZK_PATH_ASYNC_MAP, mapName); 38 | } 39 | 40 | @Override 41 | public Future get(K k) { 42 | return assertKeyIsNotNull(k) 43 | .compose(aVoid -> checkExists(k)) 44 | .compose(checkResult -> { 45 | Promise promise = Promise.promise(); 46 | if (checkResult) { 47 | try { 48 | curator.getData().inBackground((c,e) -> { 49 | if (e.getType() == CuratorEventType.GET_DATA) { 50 | V value = asObject(e.getData()); 51 | vertx.runOnContext(aVoid -> promise.complete(value)); 52 | } 53 | }).forPath(keyPath(k)); 54 | } catch (Exception e) { 55 | promise.fail(e); 56 | } 57 | } else { 58 | //ignore 59 | promise.complete(); 60 | } 61 | return promise.future(); 62 | }); 63 | } 64 | 65 | @Override 66 | public Future put(K k, V v) { 67 | return put(k, v, Optional.empty()); 68 | } 69 | 70 | @Override 71 | public Future put(K k, V v, long ttl) { 72 | return put(k, v, Optional.of(ttl)); 73 | } 74 | 75 | private Future put(K k, V v, Optional timeoutOptional) { 76 | return assertKeyAndValueAreNotNull(k, v) 77 | .compose(aVoid -> checkExists(k)) 78 | .compose(checkResult -> checkResult ? setData(k, v) : create(k, v, timeoutOptional)) 79 | .compose(stat -> Future.succeededFuture()); 80 | } 81 | 82 | @Override 83 | public Future putIfAbsent(K k, V v) { 84 | return putIfAbsent(k, v, Optional.empty()); 85 | } 86 | 87 | @Override 88 | public Future putIfAbsent(K k, V v, long ttl) { 89 | return putIfAbsent(k, v, Optional.of(ttl)); 90 | } 91 | 92 | private Future putIfAbsent(K k, V v, Optional timeoutOptional) { 93 | return assertKeyAndValueAreNotNull(k, v) 94 | .compose(aVoid -> get(k)) 95 | .compose(value -> { 96 | if (value == null) { 97 | if (timeoutOptional.isPresent()) { 98 | return put(k, v, timeoutOptional).compose(aVoid -> Future.succeededFuture(null)); 99 | } else { 100 | return put(k, v).compose(aVoid -> Future.succeededFuture(null)); 101 | } 102 | } else { 103 | return Future.succeededFuture(value); 104 | } 105 | }); 106 | } 107 | 108 | @Override 109 | public Future remove(K k) { 110 | return assertKeyIsNotNull(k).compose(aVoid -> { 111 | Promise promise = Promise.promise(); 112 | get(k).onComplete(promise); 113 | return promise.future(); 114 | }).compose(value -> { 115 | Promise promise = Promise.promise(); 116 | if (value != null) { 117 | return delete(k, value); 118 | } else { 119 | promise.complete(); 120 | } 121 | return promise.future(); 122 | }); 123 | } 124 | 125 | @Override 126 | public Future removeIfPresent(K k, V v) { 127 | return assertKeyAndValueAreNotNull(k, v) 128 | .compose(aVoid -> { 129 | Promise promise = Promise.promise(); 130 | get(k).onComplete(promise); 131 | return promise.future(); 132 | }) 133 | .compose(value -> { 134 | Promise promise = Promise.promise(); 135 | if (Objects.equals(value, v)) { 136 | delete(k, v).onComplete(deleteResult -> { 137 | if (deleteResult.succeeded()) promise.complete(true); 138 | else promise.fail(deleteResult.cause()); 139 | }); 140 | } else { 141 | promise.complete(false); 142 | } 143 | return promise.future(); 144 | }); 145 | } 146 | 147 | @Override 148 | public Future replace(K k, V v) { 149 | return assertKeyAndValueAreNotNull(k, v) 150 | .compose(aVoid -> { 151 | Promise innerPromise = Promise.promise(); 152 | vertx.executeBlocking(() -> { 153 | long startTime = Instant.now().toEpochMilli(); 154 | int retries = 0; 155 | 156 | for (; ; ) { 157 | Stat stat = new Stat(); 158 | String path = keyPath(k); 159 | V currentValue = getData(stat, path); 160 | //do not replace value if previous value is null 161 | if (currentValue == null) { 162 | return null; 163 | } 164 | if (compareAndSet(startTime, retries++, stat, path, currentValue, v)) { 165 | return currentValue; 166 | } 167 | } 168 | }, false).onComplete(innerPromise); 169 | return innerPromise.future(); 170 | }); 171 | } 172 | 173 | @Override 174 | public Future replaceIfPresent(K k, V oldValue, V newValue) { 175 | return assertKeyIsNotNull(k) 176 | .compose(aVoid -> assertValueIsNotNull(oldValue)) 177 | .compose(aVoid -> assertValueIsNotNull(newValue)) 178 | .compose(aVoid -> { 179 | Promise innerPromise = Promise.promise(); 180 | vertx.executeBlocking(() -> { 181 | long startTime = Instant.now().toEpochMilli(); 182 | int retries = 0; 183 | 184 | for (; ; ) { 185 | Stat stat = new Stat(); 186 | String path = keyPath(k); 187 | V currentValue = getData(stat, path); 188 | if (!currentValue.equals(oldValue)) { 189 | return false; 190 | } 191 | if (compareAndSet(startTime, retries++, stat, path, oldValue, newValue)) { 192 | return true; 193 | } 194 | } 195 | }, false).onComplete(innerPromise); 196 | return innerPromise.future(); 197 | }); 198 | } 199 | 200 | @Override 201 | public Future clear() { 202 | //just remove parent node 203 | return delete(mapPath, null).mapEmpty(); 204 | } 205 | 206 | @Override 207 | public Future size() { 208 | Promise promise = ((VertxInternal)vertx).getOrCreateContext().promise(); 209 | try { 210 | curator.getChildren().inBackground((client, event) -> 211 | promise.tryComplete(event.getChildren().size())) 212 | .forPath(mapPath); 213 | } catch (Exception e) { 214 | promise.tryFail(e); 215 | } 216 | return promise.future(); 217 | } 218 | 219 | @Override 220 | public Future> keys() { 221 | Promise> promise = ((VertxInternal)vertx).getOrCreateContext().promise(); 222 | try { 223 | curator.getChildren().inBackground((client, event) -> { 224 | Set keys = new HashSet<>(); 225 | for (String base64Key : event.getChildren()) { 226 | byte[] binaryKey = Base64.getUrlDecoder().decode(base64Key); 227 | K key; 228 | try { 229 | key = asObject(binaryKey); 230 | } catch (Exception e) { 231 | promise.tryFail(e); 232 | return; 233 | } 234 | keys.add(key); 235 | } 236 | promise.tryComplete(keys); 237 | }).forPath(mapPath); 238 | } catch (Exception e) { 239 | promise.tryFail(e); 240 | } 241 | return promise.future(); 242 | } 243 | 244 | @Override 245 | public Future> values() { 246 | Promise> keysPromise = ((VertxInternal)vertx).getOrCreateContext().promise(); 247 | keys().onComplete(keysPromise); 248 | return keysPromise.future().compose(keys -> { 249 | List> futures = new ArrayList<>(keys.size()); 250 | for (K k : keys) { 251 | Promise valuePromise = Promise.promise(); 252 | get(k).onComplete(valuePromise); 253 | futures.add(valuePromise.future()); 254 | } 255 | return Future.all(futures).map(compositeFuture -> { 256 | List values = new ArrayList<>(compositeFuture.size()); 257 | for (int i = 0; i < compositeFuture.size(); i++) { 258 | values.add(compositeFuture.resultAt(i)); 259 | } 260 | return values; 261 | }); 262 | }); 263 | } 264 | 265 | @Override 266 | public Future> entries() { 267 | Promise> keysPromise = ((VertxInternal)vertx).getOrCreateContext().promise(); 268 | keys().onComplete(keysPromise); 269 | return keysPromise.future().map(ArrayList::new).compose(keys -> { 270 | List> futures = new ArrayList<>(keys.size()); 271 | for (K k : keys) { 272 | Promise valuePromise = Promise.promise(); 273 | get(k).onComplete(valuePromise); 274 | futures.add(valuePromise.future()); 275 | } 276 | return Future.all(futures).map(compositeFuture -> { 277 | Map map = new HashMap<>(); 278 | for (int i = 0; i < compositeFuture.size(); i++) { 279 | map.put(keys.get(i), compositeFuture.resultAt(i)); 280 | } 281 | return map; 282 | }); 283 | }); 284 | } 285 | 286 | @Override 287 | String keyPath(K k) { 288 | try { 289 | return keyPathPrefix() + Base64.getUrlEncoder().encodeToString(asByte(k)); 290 | } catch (IOException e) { 291 | throw new RuntimeException(e); 292 | } 293 | } 294 | 295 | private String keyPathPrefix() { 296 | return mapPath + "/"; 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/spi/cluster/zookeeper/impl/ZKCounter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2020 The original author or authors 3 | * ------------------------------------------------------ 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | 17 | package io.vertx.spi.cluster.zookeeper.impl; 18 | 19 | import io.vertx.core.Future; 20 | import io.vertx.core.VertxException; 21 | import io.vertx.core.internal.VertxInternal; 22 | import io.vertx.core.shareddata.Counter; 23 | import org.apache.curator.RetryPolicy; 24 | import org.apache.curator.framework.CuratorFramework; 25 | import org.apache.curator.framework.recipes.atomic.DistributedAtomicLong; 26 | 27 | public class ZKCounter implements Counter { 28 | 29 | private static final String ZK_PATH_COUNTERS = "/counters/"; 30 | 31 | private final VertxInternal vertx; 32 | private final DistributedAtomicLong atomicLong; 33 | 34 | public ZKCounter(VertxInternal vertx, CuratorFramework curator, String nodeName, RetryPolicy retryPolicy) { 35 | this.vertx = vertx; 36 | String counterPath = ZK_PATH_COUNTERS + nodeName; 37 | this.atomicLong = new DistributedAtomicLong(curator, counterPath, retryPolicy); 38 | } 39 | 40 | @Override 41 | public Future get() { 42 | return vertx.executeBlocking(() -> { 43 | try { 44 | return atomicLong.get().preValue(); 45 | } catch (Exception e) { 46 | throw new VertxException(e, true); 47 | } 48 | }); 49 | } 50 | 51 | @Override 52 | public Future incrementAndGet() { 53 | return increment(true); 54 | } 55 | 56 | @Override 57 | public Future getAndIncrement() { 58 | return increment(false); 59 | } 60 | 61 | private Future increment(boolean post) { 62 | return vertx.executeBlocking(() -> { 63 | try { 64 | long returnValue = 0; 65 | if (atomicLong.get().succeeded()) returnValue = atomicLong.get().preValue(); 66 | if (atomicLong.increment().succeeded()) { 67 | return post ? atomicLong.get().postValue() : returnValue; 68 | } else { 69 | throw new VertxException("increment value failed.", true); 70 | } 71 | } catch (Exception e) { 72 | throw new VertxException(e, true); 73 | } 74 | }); 75 | } 76 | 77 | @Override 78 | public Future decrementAndGet() { 79 | return vertx.executeBlocking(() -> { 80 | try { 81 | if (atomicLong.decrement().succeeded()) { 82 | return atomicLong.get().postValue(); 83 | } else { 84 | throw new VertxException("decrement value failed.", true); 85 | } 86 | } catch (Exception e) { 87 | throw new VertxException(e, true); 88 | } 89 | }); 90 | } 91 | 92 | @Override 93 | public Future addAndGet(long value) { 94 | return add(value, true); 95 | } 96 | 97 | @Override 98 | public Future getAndAdd(long value) { 99 | return add(value, false); 100 | } 101 | 102 | private Future add(long value, boolean post) { 103 | return vertx.executeBlocking(() -> { 104 | try { 105 | long returnValue = 0; 106 | if (atomicLong.get().succeeded()) returnValue = atomicLong.get().preValue(); 107 | if (atomicLong.add(value).succeeded()) { 108 | return post ? atomicLong.get().postValue() : returnValue; 109 | } else { 110 | throw new VertxException("add value failed.", true); 111 | } 112 | } catch (Exception e) { 113 | throw new VertxException(e, true); 114 | } 115 | }); 116 | } 117 | 118 | @Override 119 | public Future compareAndSet(long expected, long value) { 120 | return vertx.executeBlocking(() -> { 121 | try { 122 | if (atomicLong.get().succeeded() && atomicLong.get().preValue() == 0) this.atomicLong.initialize(0L); 123 | return atomicLong.compareAndSet(expected, value).succeeded(); 124 | } catch (Exception e) { 125 | throw new VertxException(e, true); 126 | } 127 | }); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/spi/cluster/zookeeper/impl/ZKLock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2020 The original author or authors 3 | * ------------------------------------------------------ 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | 17 | package io.vertx.spi.cluster.zookeeper.impl; 18 | 19 | import io.vertx.core.internal.logging.Logger; 20 | import io.vertx.core.internal.logging.LoggerFactory; 21 | import io.vertx.core.shareddata.Lock; 22 | import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex; 23 | 24 | import java.util.concurrent.ExecutorService; 25 | 26 | /** 27 | * Lock implementation. 28 | */ 29 | public class ZKLock implements Lock { 30 | 31 | private static final Logger log = LoggerFactory.getLogger(ZKLock.class); 32 | 33 | private final InterProcessSemaphoreMutex lock; 34 | private final ExecutorService lockReleaseExec; 35 | 36 | public ZKLock(InterProcessSemaphoreMutex lock, ExecutorService lockReleaseExec) { 37 | this.lock = lock; 38 | this.lockReleaseExec = lockReleaseExec; 39 | } 40 | 41 | public InterProcessSemaphoreMutex getLock() { 42 | return lock; 43 | } 44 | 45 | @Override 46 | public void release() { 47 | lockReleaseExec.execute(() -> { 48 | try { 49 | lock.release(); 50 | } catch (Exception e) { 51 | log.error(e); 52 | } 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/spi/cluster/zookeeper/impl/ZKMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2016 The original author or authors 3 | * ------------------------------------------------------ 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.spi.cluster.zookeeper.impl; 17 | 18 | import io.vertx.core.Future; 19 | import io.vertx.core.Promise; 20 | import io.vertx.core.Vertx; 21 | import io.vertx.core.VertxException; 22 | import io.vertx.core.buffer.Buffer; 23 | import io.vertx.core.shareddata.ClusterSerializable; 24 | import org.apache.curator.RetryLoop; 25 | import org.apache.curator.RetryPolicy; 26 | import org.apache.curator.framework.CuratorFramework; 27 | import org.apache.curator.framework.api.ACLBackgroundPathAndBytesable; 28 | import org.apache.curator.framework.api.CuratorEventType; 29 | import org.apache.curator.retry.ExponentialBackoffRetry; 30 | import org.apache.zookeeper.CreateMode; 31 | import org.apache.zookeeper.KeeperException; 32 | import org.apache.zookeeper.data.Stat; 33 | 34 | import java.io.*; 35 | import java.lang.reflect.Constructor; 36 | import java.time.Instant; 37 | import java.util.Base64; 38 | import java.util.Objects; 39 | import java.util.Optional; 40 | import java.util.function.Predicate; 41 | import java.util.stream.Stream; 42 | 43 | /** 44 | * Created by Stream.Liu 45 | */ 46 | abstract class ZKMap { 47 | 48 | final CuratorFramework curator; 49 | protected final Vertx vertx; 50 | final String mapPath; 51 | 52 | static final String ZK_PATH_ASYNC_MAP = "asyncMap"; 53 | static final String ZK_PATH_SYNC_MAP = "syncMap"; 54 | static final Predicate pathChecker = path -> { 55 | Objects.requireNonNull(path, "zookeeper node path can not be null."); 56 | if (path.contains("/")) throw new IllegalArgumentException("can not contain forward slash char in ZK node path"); 57 | return true; 58 | }; 59 | 60 | private final RetryPolicy retryPolicy = new ExponentialBackoffRetry(100, 5); 61 | 62 | ZKMap(CuratorFramework curator, Vertx vertx, String mapType, String mapName) { 63 | this.curator = curator; 64 | this.vertx = vertx; 65 | pathChecker.test(mapName); 66 | this.mapPath = "/" + mapType + "/" + mapName; 67 | } 68 | 69 | String keyPath(K k) { 70 | pathChecker.test(k.toString()); 71 | return mapPath + "/" + k.toString(); 72 | } 73 | 74 | Future assertKeyIsNotNull(Object key) { 75 | boolean result = key == null; 76 | if (result) return Future.failedFuture("key can not be null."); 77 | else return Future.succeededFuture(); 78 | } 79 | 80 | Future assertValueIsNotNull(Object value) { 81 | boolean result = value == null; 82 | if (result) return Future.failedFuture("value can not be null."); 83 | else return Future.succeededFuture(); 84 | } 85 | 86 | Future assertKeyAndValueAreNotNull(Object key, Object value) { 87 | return assertKeyIsNotNull(key).compose(aVoid -> assertValueIsNotNull(value)); 88 | } 89 | 90 | byte[] asByte(Object object) throws IOException { 91 | ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); 92 | DataOutput dataOutput = new DataOutputStream(byteOut); 93 | if (object instanceof ClusterSerializable) { 94 | ClusterSerializable clusterSerializable = (ClusterSerializable) object; 95 | dataOutput.writeBoolean(true); 96 | dataOutput.writeUTF(object.getClass().getName()); 97 | Buffer buffer = Buffer.buffer(); 98 | clusterSerializable.writeToBuffer(buffer); 99 | byte[] bytes = buffer.getBytes(); 100 | dataOutput.writeInt(bytes.length); 101 | dataOutput.write(bytes); 102 | } else { 103 | dataOutput.writeBoolean(false); 104 | ByteArrayOutputStream javaByteOut = new ByteArrayOutputStream(); 105 | ObjectOutput objectOutput = new ObjectOutputStream(javaByteOut); 106 | objectOutput.writeObject(object); 107 | dataOutput.write(javaByteOut.toByteArray()); 108 | } 109 | return byteOut.toByteArray(); 110 | } 111 | 112 | T asObject(byte[] bytes) throws Exception { 113 | if (bytes == null) return null; //TTL 114 | ByteArrayInputStream byteIn = new ByteArrayInputStream(bytes); 115 | DataInputStream in = new DataInputStream(byteIn); 116 | boolean isClusterSerializable = in.readBoolean(); 117 | if (isClusterSerializable) { 118 | String className = in.readUTF(); 119 | Class clazz = Thread.currentThread().getContextClassLoader().loadClass(className); 120 | int length = in.readInt(); 121 | byte[] body = new byte[length]; 122 | in.readFully(body); 123 | try { 124 | ClusterSerializable clusterSerializable; 125 | //check clazz if have a public Constructor method. 126 | if (clazz.getConstructors().length == 0) { 127 | Constructor constructor = (Constructor) clazz.getDeclaredConstructor(); 128 | constructor.setAccessible(true); 129 | clusterSerializable = (ClusterSerializable) constructor.newInstance(); 130 | } else { 131 | clusterSerializable = (ClusterSerializable) clazz.newInstance(); 132 | } 133 | clusterSerializable.readFromBuffer(0, Buffer.buffer(body)); 134 | return (T) clusterSerializable; 135 | } catch (Exception e) { 136 | throw new IllegalStateException("Failed to load class " + e.getMessage(), e); 137 | } 138 | } else { 139 | byte[] body = new byte[in.available()]; 140 | in.readFully(body); 141 | ObjectInputStream objectIn = new ObjectInputStream(new ByteArrayInputStream(body)); 142 | return (T) objectIn.readObject(); 143 | } 144 | } 145 | 146 | /** 147 | * get data with Stat 148 | * 149 | * @param stat new Stat 150 | * @param path node path 151 | * @param result 152 | * @return T 153 | * @throws Exception 154 | */ 155 | T getData(Stat stat, String path) throws Exception { 156 | T result = null; 157 | if (null != curator.checkExists().forPath(path)) { 158 | result = asObject(curator.getData().storingStatIn(stat).forPath(path)); 159 | } else { 160 | curator.create().creatingParentsIfNeeded().forPath(path, asByte(null)); 161 | } 162 | return result; 163 | } 164 | 165 | /** 166 | * CAS Operation. 167 | * 168 | * @param startTime start time for backOff 169 | * @param retries retry times backOff 170 | * @param path node path 171 | * @param expect the value expect in path. 172 | * @param update the value should be update to the path. 173 | * @return boolean 174 | * @throws Exception 175 | */ 176 | boolean compareAndSet(long startTime, int retries, Stat stat, String path, V expect, V update) throws Exception { 177 | V currentValue = getData(stat, path); 178 | if (currentValue == expect || currentValue.equals(expect)) { 179 | try { 180 | curator.setData().withVersion(stat.getVersion()).forPath(path, asByte(update)); 181 | } catch (KeeperException.BadVersionException | KeeperException.NoNodeException e) { 182 | // If the version has changed, block on the retry policy if necessary. If no more retries are remaining, 183 | // fail the operation. 184 | if (!retryPolicy.allowRetry(retries, Instant.now().toEpochMilli() - startTime, RetryLoop.getDefaultRetrySleeper())) { 185 | throw new VertxException("failed to acquire optimistic lock"); 186 | } 187 | } 188 | return true; 189 | } else { 190 | return false; 191 | } 192 | } 193 | 194 | Future checkExists(K k) { 195 | return checkExists(keyPath(k)); 196 | } 197 | 198 | Future checkExists(String path) { 199 | Promise future = Promise.promise(); 200 | try { 201 | curator.sync().inBackground((clientSync, eventSync) -> { 202 | try { 203 | if (eventSync.getType() == CuratorEventType.SYNC) { 204 | curator.checkExists().inBackground((clientCheck, eventCheck) -> { 205 | if (eventCheck.getType() == CuratorEventType.EXISTS) { 206 | if (eventCheck.getStat() == null) { 207 | vertx.runOnContext(aVoid -> future.complete(false)); 208 | } else { 209 | vertx.runOnContext(aVoid -> future.complete(true)); 210 | } 211 | } 212 | }).forPath(path); 213 | } 214 | } catch (Exception ex) { 215 | vertx.runOnContext(aVoid -> future.fail(ex)); 216 | } 217 | }).forPath(path); 218 | } catch (Exception ex) { 219 | vertx.runOnContext(aVoid -> future.fail(ex)); 220 | } 221 | return future.future(); 222 | } 223 | 224 | Future create(K k, V v, Optional timeToLive) { 225 | return create(keyPath(k), v, timeToLive); 226 | } 227 | 228 | Future create(String path, V v, Optional timeToLive) { 229 | Promise future = Promise.promise(); 230 | try { 231 | ACLBackgroundPathAndBytesable creator = timeToLive.isPresent() 232 | ? curator.create().withTtl(timeToLive.get()).creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT_WITH_TTL) 233 | : curator.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT); 234 | creator.inBackground((cl, el) -> { 235 | if (el.getType() == CuratorEventType.CREATE) { 236 | vertx.runOnContext(aVoid -> future.complete(el.getStat())); 237 | } 238 | }).forPath(path, asByte(v)); 239 | } catch (Exception ex) { 240 | vertx.runOnContext(event -> future.fail(ex)); 241 | } 242 | return future.future(); 243 | } 244 | 245 | Future setData(K k, V v) { 246 | return setData(keyPath(k), v); 247 | } 248 | 249 | Future setData(String path, V v) { 250 | Promise future = Promise.promise(); 251 | try { 252 | curator.setData().inBackground((client, event) -> { 253 | if (event.getType() == CuratorEventType.SET_DATA) { 254 | vertx.runOnContext(e -> future.complete(event.getStat())); 255 | } 256 | }).forPath(path, asByte(v)); 257 | } catch (Exception ex) { 258 | vertx.runOnContext(event -> future.fail(ex)); 259 | } 260 | return future.future(); 261 | } 262 | 263 | Future delete(K k, V v) { 264 | return delete(keyPath(k), v); 265 | } 266 | 267 | Future delete(String path, V v) { 268 | Promise future = Promise.promise(); 269 | try { 270 | curator.delete().deletingChildrenIfNeeded().inBackground((client, event) -> { 271 | if (event.getType() == CuratorEventType.DELETE) { 272 | //clean parent node if doesn't have child node. 273 | String[] paths = path.split("/"); 274 | String parentNodePath = Stream.of(paths).limit(paths.length - 1).reduce((previous, current) -> previous + "/" + current).get(); 275 | curator.getChildren().inBackground((childClient, childEvent) -> { 276 | if (childEvent.getChildren().size() == 0) { 277 | curator.delete().inBackground((deleteClient, deleteEvent) -> { 278 | if (deleteEvent.getType() == CuratorEventType.DELETE) 279 | vertx.runOnContext(ea -> future.complete(v)); 280 | }).forPath(parentNodePath); 281 | } else { 282 | vertx.runOnContext(ea -> future.complete(v)); 283 | } 284 | }).forPath(parentNodePath); 285 | } 286 | }).forPath(path); 287 | } catch (Exception ex) { 288 | vertx.runOnContext(aVoid -> future.fail(ex)); 289 | } 290 | return future.future(); 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/spi/cluster/zookeeper/impl/ZKSyncMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2016 The original author or authors 3 | * ------------------------------------------------------ 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | package io.vertx.spi.cluster.zookeeper.impl; 17 | 18 | import io.vertx.core.VertxException; 19 | import io.vertx.core.internal.logging.Logger; 20 | import io.vertx.core.internal.logging.LoggerFactory; 21 | import org.apache.curator.framework.CuratorFramework; 22 | import org.apache.curator.framework.api.CuratorEventType; 23 | import org.apache.curator.framework.imps.CuratorFrameworkState; 24 | import org.apache.zookeeper.CreateMode; 25 | import org.apache.zookeeper.KeeperException; 26 | 27 | import java.io.Serializable; 28 | import java.util.Collection; 29 | import java.util.HashMap; 30 | import java.util.Map; 31 | import java.util.Set; 32 | import java.util.concurrent.CountDownLatch; 33 | import java.util.concurrent.TimeUnit; 34 | import java.util.stream.Collectors; 35 | 36 | /** 37 | * Created by Stream.Liu 38 | */ 39 | public class ZKSyncMap extends ZKMap implements Map { 40 | 41 | private static final Logger logger = LoggerFactory.getLogger(ZKSyncMap.class); 42 | 43 | public ZKSyncMap(CuratorFramework curator, String mapName) { 44 | super(curator, null, ZK_PATH_SYNC_MAP, mapName); 45 | } 46 | 47 | @Override 48 | public int size() { 49 | try { 50 | return curator.getChildren().forPath(mapPath).size(); 51 | } catch (Exception e) { 52 | throw new VertxException(e); 53 | } 54 | } 55 | 56 | @Override 57 | public boolean isEmpty() { 58 | try { 59 | syncKeyPath(mapPath); 60 | return curator.getChildren().forPath(mapPath).isEmpty(); 61 | } catch (Exception e) { 62 | throw new VertxException(e); 63 | } 64 | } 65 | 66 | @Override 67 | public boolean containsKey(Object key) { 68 | try { 69 | String keyPath = keyPath((K) key); 70 | syncKeyPath(keyPath); 71 | return curator.checkExists().forPath(keyPath) != null; 72 | } catch (Exception e) { 73 | throw new VertxException(e); 74 | } 75 | } 76 | 77 | @Override 78 | public boolean containsValue(Object value) { 79 | try { 80 | syncKeyPath(mapPath); 81 | return curator.getChildren().forPath(mapPath).stream().anyMatch(k -> { 82 | try { 83 | byte[] bytes = curator.getData().forPath(keyPath((K) k)); 84 | KeyValue keyValue = asObject(bytes); 85 | return keyValue.getValue().equals(value); 86 | } catch (Exception ex) { 87 | throw new VertxException(ex); 88 | } 89 | }); 90 | } catch (Exception e) { 91 | throw new VertxException(e); 92 | } 93 | } 94 | 95 | @Override 96 | public V get(Object key) { 97 | try { 98 | String keyPath = keyPath((K) key); 99 | syncKeyPath(keyPath); 100 | if (null == curator.checkExists().forPath(keyPath)) { 101 | return null; 102 | } else { 103 | KeyValue keyValue = asObject(curator.getData().forPath(keyPath)); 104 | return keyValue.getValue(); 105 | } 106 | } catch (Exception e) { 107 | if (e instanceof VertxException && e.getCause() instanceof KeeperException.NoNodeException) { 108 | logger.warn("zookeeper node lost. " + e.getCause().getMessage()); 109 | } else { 110 | throw new VertxException(e); 111 | } 112 | } 113 | return null; 114 | } 115 | 116 | @Override 117 | public V put(K key, V value) { 118 | try { 119 | String keyPath = keyPath(key); 120 | KeyValue keyValue = new KeyValue<>(key, value); 121 | byte[] valueBytes = asByte(keyValue); 122 | if (get(key) != null) { 123 | curator.setData().forPath(keyPath, valueBytes); 124 | } else { 125 | curator.create().creatingParentsIfNeeded().withMode(CreateMode.CONTAINER).forPath(keyPath, valueBytes); 126 | } 127 | return value; 128 | } catch (Exception e) { 129 | throw new VertxException(e); 130 | } 131 | } 132 | 133 | @Override 134 | public V remove(Object key) { 135 | try { 136 | V result = get(key); 137 | if (result != null) curator.delete().deletingChildrenIfNeeded().forPath(keyPath((K) key)); 138 | return result; 139 | } catch (Exception e) { 140 | throw new VertxException(e); 141 | } 142 | } 143 | 144 | @Override 145 | public void putAll(Map m) { 146 | m.entrySet().stream().forEach(entry -> put(entry.getKey(), entry.getValue())); 147 | } 148 | 149 | @Override 150 | public void clear() { 151 | try { 152 | curator.delete().deletingChildrenIfNeeded().forPath(mapPath); 153 | curator.create().creatingParentsIfNeeded().withMode(CreateMode.CONTAINER).forPath(mapPath); 154 | } catch (Exception e) { 155 | throw new VertxException(e); 156 | } 157 | } 158 | 159 | @Override 160 | public Set keySet() { 161 | try { 162 | syncKeyPath(mapPath); 163 | return curator.getChildren().forPath(mapPath).stream().map(k -> { 164 | try { 165 | KeyValue keyValue = asObject(curator.getData().forPath(keyPath((K) k))); 166 | return keyValue.getKey(); 167 | } catch (KeeperException.NoNodeException nodeLostEx) { 168 | logger.warn("node lost " + nodeLostEx.getMessage()); 169 | return null; 170 | } catch (Exception ex) { 171 | throw new VertxException(ex); 172 | } 173 | }).collect(Collectors.toSet()); 174 | } catch (Exception ex) { 175 | throw new VertxException(ex); 176 | } 177 | } 178 | 179 | @Override 180 | public Collection values() { 181 | try { 182 | syncKeyPath(mapPath); 183 | return curator.getChildren().forPath(mapPath).stream() 184 | .map(k -> { 185 | try { 186 | KeyValue keyValue = asObject(curator.getData().forPath(keyPath((K) k))); 187 | return keyValue.getValue(); 188 | } catch (Exception ex) { 189 | throw new VertxException(ex); 190 | } 191 | } 192 | ).collect(Collectors.toSet()); 193 | } catch (Exception ex) { 194 | throw new VertxException(ex); 195 | } 196 | } 197 | 198 | private void syncKeyPath(String path) { 199 | //sync always run in background, so we have to using latch to wait callback. 200 | CountDownLatch latch = new CountDownLatch(1); 201 | try { 202 | curator.sync().inBackground((client, event) -> { 203 | if (client.getState() == CuratorFrameworkState.STOPPED) { 204 | latch.countDown(); 205 | return; 206 | } 207 | if (event.getPath().equals(path) && event.getType() == CuratorEventType.SYNC) { 208 | latch.countDown(); 209 | } 210 | }).forPath(path); 211 | latch.await(3L, TimeUnit.SECONDS); 212 | } catch (Exception e) { 213 | if (e instanceof InterruptedException) { 214 | Thread.currentThread().interrupt(); 215 | } 216 | if (!(e instanceof KeeperException.NoNodeException)) { 217 | throw new VertxException(e); 218 | } 219 | } 220 | } 221 | 222 | @Override 223 | public Set> entrySet() { 224 | return keySet().stream().map(k -> { 225 | V v = get(k); 226 | return new HashMap.SimpleImmutableEntry<>(k, v); 227 | }).collect(Collectors.toSet()); 228 | } 229 | 230 | static class KeyValue implements Serializable { 231 | private static final long serialVersionUID = 6529685098267757690L; 232 | private K key; 233 | private V value; 234 | 235 | public KeyValue(K key, V value) { 236 | this.key = key; 237 | this.value = value; 238 | } 239 | 240 | public K getKey() { 241 | return key; 242 | } 243 | 244 | public V getValue() { 245 | return value; 246 | } 247 | } 248 | 249 | } 250 | -------------------------------------------------------------------------------- /src/main/java/io/vertx/spi/cluster/zookeeper/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2016 The original author or authors 3 | * ------------------------------------------------------ 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | 17 | package io.vertx.spi.cluster.zookeeper; 18 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/io.vertx.core.spi.VertxServiceProvider: -------------------------------------------------------------------------------- 1 | io.vertx.spi.cluster.zookeeper.ZookeeperClusterManager -------------------------------------------------------------------------------- /src/main/resources/default-zookeeper.json: -------------------------------------------------------------------------------- 1 | { 2 | "zookeeperHosts":"127.0.0.1", 3 | "sessionTimeout":20000, 4 | "connectTimeout":3000, 5 | "rootPath":"io.vertx", 6 | "retry": { 7 | "policy": "exponential_backoff", 8 | "initialSleepTime":100, 9 | "intervalTimes":10000, 10 | "maxTimes":5 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/core/ProgrammaticZKClusterManagerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.core; 18 | 19 | import io.vertx.core.json.JsonObject; 20 | import io.vertx.spi.cluster.zookeeper.MockZKCluster; 21 | import io.vertx.spi.cluster.zookeeper.ZookeeperClusterManager; 22 | import io.vertx.test.core.AsyncTestBase; 23 | import org.apache.curator.RetryPolicy; 24 | import org.apache.curator.framework.CuratorFramework; 25 | import org.apache.curator.framework.CuratorFrameworkFactory; 26 | import org.apache.curator.framework.imps.CuratorFrameworkState; 27 | import org.apache.curator.retry.ExponentialBackoffRetry; 28 | import org.apache.curator.retry.RetryOneTime; 29 | import org.junit.Test; 30 | 31 | import java.util.Arrays; 32 | import java.util.UUID; 33 | import java.util.concurrent.TimeUnit; 34 | import java.util.concurrent.atomic.AtomicReference; 35 | 36 | /** 37 | * Created by Stream.Liu 38 | */ 39 | public class ProgrammaticZKClusterManagerTest extends AsyncTestBase { 40 | 41 | private MockZKCluster zkCluster = new MockZKCluster(); 42 | private RetryPolicy retryPolicy = new ExponentialBackoffRetry(2000, 5, 10000); 43 | 44 | private void testProgrammatic(ZookeeperClusterManager mgr, JsonObject config) throws Exception { 45 | mgr.setConfig(config); 46 | assertEquals(config, mgr.getConfig()); 47 | Vertx.builder().withClusterManager(mgr).buildClustered().onComplete(res -> { 48 | assertTrue(res.succeeded()); 49 | assertNotNull(mgr.getCuratorFramework()); 50 | res.result().close().onComplete(res2 -> { 51 | assertTrue(res2.succeeded()); 52 | testComplete(); 53 | }); 54 | }); 55 | await(); 56 | } 57 | 58 | @Test 59 | public void testProgrammaticSetConfig() throws Exception { 60 | JsonObject config = zkCluster.getDefaultConfig(); 61 | ZookeeperClusterManager mgr = new ZookeeperClusterManager(); 62 | mgr.setConfig(config); 63 | testProgrammatic(mgr, config); 64 | } 65 | 66 | @Test 67 | public void testProgrammaticSetWithConstructor() throws Exception { 68 | JsonObject config = zkCluster.getDefaultConfig(); 69 | ZookeeperClusterManager mgr = new ZookeeperClusterManager(config); 70 | testProgrammatic(mgr, config); 71 | } 72 | 73 | @Test 74 | public void testProgrammaticSetRetryPolicyDefault() throws Exception { 75 | JsonObject config = zkCluster.getDefaultConfig(); 76 | ZookeeperClusterManager mgr = new ZookeeperClusterManager(config); 77 | Vertx.builder().withClusterManager(mgr).buildClustered().onComplete(res -> { 78 | assertTrue(res.succeeded()); 79 | assertNotNull(mgr.getCuratorFramework()); 80 | assertTrue(mgr.getCuratorFramework().getZookeeperClient().getRetryPolicy() instanceof ExponentialBackoffRetry); 81 | testComplete(); 82 | }); 83 | await(); 84 | } 85 | 86 | public void testProgrammaticSetRetryPolicy() throws Exception { 87 | JsonObject config = zkCluster.getDefaultConfig(); 88 | config.put("retry", new JsonObject().put("policy","one_time")); 89 | 90 | ZookeeperClusterManager mgr = new ZookeeperClusterManager(config); 91 | Vertx.builder().withClusterManager(mgr).buildClustered().onComplete(res -> { 92 | assertTrue(res.succeeded()); 93 | assertNotNull(mgr.getCuratorFramework()); 94 | assertTrue(mgr.getCuratorFramework().getZookeeperClient().getRetryPolicy() instanceof RetryOneTime); 95 | testComplete(); 96 | }); 97 | await(); 98 | } 99 | 100 | @Test 101 | public void testCustomCuratorFramework() throws Exception { 102 | JsonObject config = zkCluster.getDefaultConfig(); 103 | CuratorFramework curator = CuratorFrameworkFactory.builder() 104 | .connectString(config.getString("zookeeperHosts")) 105 | .namespace(config.getString("rootPath")) 106 | .retryPolicy(retryPolicy).build(); 107 | curator.start(); 108 | ZookeeperClusterManager mgr = new ZookeeperClusterManager(curator); 109 | testProgrammatic(mgr, config); 110 | } 111 | 112 | @Test 113 | public void testEventBusWhenUsingACustomCurator() throws Exception { 114 | JsonObject config = zkCluster.getDefaultConfig(); 115 | CuratorFramework curator1 = CuratorFrameworkFactory.builder() 116 | .connectString(config.getString("zookeeperHosts")) 117 | .namespace(config.getString("rootPath")) 118 | .retryPolicy(retryPolicy).build(); 119 | curator1.start(); 120 | 121 | CuratorFramework curator2 = CuratorFrameworkFactory.builder() 122 | .connectString(config.getString("zookeeperHosts")) 123 | .namespace(config.getString("rootPath")) 124 | .retryPolicy(retryPolicy).build(); 125 | curator2.start(); 126 | 127 | ZookeeperClusterManager mgr1 = new ZookeeperClusterManager(curator1); 128 | ZookeeperClusterManager mgr2 = new ZookeeperClusterManager(curator2); 129 | 130 | AtomicReference vertx1 = new AtomicReference<>(); 131 | AtomicReference vertx2 = new AtomicReference<>(); 132 | 133 | Vertx.builder().withClusterManager(mgr1).buildClustered().onComplete(res -> { 134 | assertTrue(res.succeeded()); 135 | assertNotNull(mgr1.getCuratorFramework()); 136 | res.result().eventBus().consumer("news", message -> { 137 | assertNotNull(message); 138 | assertTrue(message.body().equals("hello")); 139 | message.reply("hi"); 140 | }); 141 | vertx1.set(res.result()); 142 | }); 143 | 144 | assertWaitUntil(() -> vertx1.get() != null); 145 | 146 | Vertx.builder().withClusterManager(mgr2).buildClustered().onComplete(res -> { 147 | assertTrue(res.succeeded()); 148 | assertNotNull(mgr2.getCuratorFramework()); 149 | vertx2.set(res.result()); 150 | res.result().eventBus().request("news", "hello").onComplete(ar -> { 151 | assertTrue(ar.succeeded()); 152 | testComplete(); 153 | }); 154 | }); 155 | 156 | await(); 157 | 158 | vertx1.get().close().onComplete(ar -> vertx1.set(null)); 159 | vertx2.get().close().onComplete(ar -> vertx2.set(null)); 160 | 161 | assertTrue(curator1.getState() == CuratorFrameworkState.STARTED); 162 | assertTrue(curator2.getState() == CuratorFrameworkState.STARTED); 163 | 164 | assertWaitUntil(() -> vertx1.get() == null && vertx2.get() == null); 165 | 166 | curator1.close(); 167 | curator2.close(); 168 | 169 | } 170 | 171 | @Test 172 | public void testSharedDataUsingCustomCurator() throws Exception { 173 | JsonObject config = zkCluster.getDefaultConfig(); 174 | CuratorFramework curator1 = CuratorFrameworkFactory.builder() 175 | .connectString(config.getString("zookeeperHosts")) 176 | .namespace(config.getString("rootPath")) 177 | .retryPolicy(retryPolicy).build(); 178 | curator1.start(); 179 | 180 | CuratorFramework curator2 = CuratorFrameworkFactory.builder() 181 | .connectString(config.getString("zookeeperHosts")) 182 | .namespace(config.getString("rootPath")) 183 | .retryPolicy(retryPolicy).build(); 184 | curator2.start(); 185 | 186 | ZookeeperClusterManager mgr1 = new ZookeeperClusterManager(curator1); 187 | ZookeeperClusterManager mgr2 = new ZookeeperClusterManager(curator2); 188 | VertxOptions options1 = new VertxOptions(); 189 | options1.getEventBusOptions().setHost("127.0.0.1"); 190 | VertxOptions options2 = new VertxOptions(); 191 | options2.getEventBusOptions().setHost("127.0.0.1"); 192 | 193 | AtomicReference vertx1 = new AtomicReference<>(); 194 | AtomicReference vertx2 = new AtomicReference<>(); 195 | 196 | Vertx.builder().with(options1).withClusterManager(mgr1).buildClustered().onComplete(res -> { 197 | assertTrue(res.succeeded()); 198 | assertNotNull(mgr1.getCuratorFramework()); 199 | res.result().sharedData().getClusterWideMap("mymap1").onComplete(ar -> { 200 | ar.result().put("news", "hello").onComplete(v -> { 201 | vertx1.set(res.result()); 202 | }); 203 | }); 204 | }); 205 | 206 | assertWaitUntil(() -> vertx1.get() != null); 207 | 208 | Vertx.builder().with(options2).withClusterManager(mgr2).buildClustered().onComplete(res -> { 209 | assertTrue(res.succeeded()); 210 | assertNotNull(mgr2.getCuratorFramework()); 211 | vertx2.set(res.result()); 212 | res.result().sharedData().getClusterWideMap("mymap1").onComplete(ar -> { 213 | ar.result().get("news").onComplete(r -> { 214 | assertEquals("hello", r.result()); 215 | testComplete(); 216 | }); 217 | }); 218 | }); 219 | 220 | await(); 221 | 222 | vertx1.get().close().onComplete(ar -> vertx1.set(null)); 223 | vertx2.get().close().onComplete(ar -> vertx2.set(null)); 224 | 225 | assertWaitUntil(() -> vertx1.get() == null && vertx2.get() == null); 226 | 227 | assertTrue(curator1.getState() == CuratorFrameworkState.STOPPED); 228 | assertTrue(curator2.getState() == CuratorFrameworkState.STOPPED); 229 | 230 | curator1.close(); 231 | curator2.close(); 232 | } 233 | 234 | @Test 235 | public void testThatExternalCuratorCanBeShutdown() { 236 | // This instance won't be used by vert.x 237 | JsonObject config = zkCluster.getDefaultConfig(); 238 | CuratorFramework curator = CuratorFrameworkFactory.builder() 239 | .connectString(config.getString("zookeeperHosts")) 240 | .namespace(config.getString("rootPath")) 241 | .retryPolicy(retryPolicy).build(); 242 | curator.start(); 243 | String nodeID = UUID.randomUUID().toString(); 244 | 245 | ZookeeperClusterManager mgr = new ZookeeperClusterManager(curator, nodeID); 246 | VertxOptions options = new VertxOptions(); 247 | options.getEventBusOptions().setHost("127.0.0.1"); 248 | 249 | AtomicReference vertx1 = new AtomicReference<>(); 250 | 251 | Vertx.builder().with(options).withClusterManager(mgr).buildClustered().onComplete(res -> { 252 | assertTrue(res.succeeded()); 253 | assertNotNull(mgr.getCuratorFramework()); 254 | res.result().sharedData().getClusterWideMap("mymap1").onComplete(ar -> { 255 | ar.result().put("news", "hello").onComplete(v -> { 256 | vertx1.set(res.result()); 257 | }); 258 | }); 259 | }); 260 | 261 | assertWaitUntil(() -> vertx1.get() != null); 262 | int size = mgr.getNodes().size(); 263 | assertTrue(size > 0); 264 | assertTrue(mgr.getNodes().contains(nodeID)); 265 | 266 | // Retrieve the value inserted by vert.x 267 | try { 268 | byte[] content = curator.getData().forPath("/asyncMap/mymap1/news"); 269 | //There is header in bytes. 270 | String result = new String(Arrays.copyOfRange(content, 8, 13)); 271 | assertEquals("hello", result); 272 | } catch (Exception e) { 273 | e.printStackTrace(); 274 | } 275 | curator.close(); 276 | 277 | assertWaitUntil(() -> mgr.getNodes().size() == size - 1); 278 | vertx1.get().close(); 279 | vertx1.get().close().onComplete(ar -> vertx1.set(null)); 280 | 281 | assertWaitUntil(() -> vertx1.get() == null); 282 | } 283 | 284 | @Test 285 | public void testSharedDataUsingCustomCuratorFrameworks() throws Exception { 286 | JsonObject config = zkCluster.getDefaultConfig(); 287 | CuratorFramework dataNode1 = CuratorFrameworkFactory.builder() 288 | .connectString(config.getString("zookeeperHosts")) 289 | .namespace(config.getString("rootPath")) 290 | .retryPolicy(retryPolicy).build(); 291 | dataNode1.start(); 292 | 293 | CuratorFramework dataNode2 = CuratorFrameworkFactory.builder() 294 | .connectString(config.getString("zookeeperHosts")) 295 | .namespace(config.getString("rootPath")) 296 | .retryPolicy(retryPolicy).build(); 297 | dataNode2.start(); 298 | 299 | CuratorFramework curator1 = CuratorFrameworkFactory.builder() 300 | .connectString(config.getString("zookeeperHosts")) 301 | .namespace(config.getString("rootPath")) 302 | .retryPolicy(retryPolicy).build(); 303 | curator1.start(); 304 | 305 | CuratorFramework curator2 = CuratorFrameworkFactory.builder() 306 | .connectString(config.getString("zookeeperHosts")) 307 | .namespace(config.getString("rootPath")) 308 | .retryPolicy(retryPolicy).build(); 309 | curator2.start(); 310 | 311 | ZookeeperClusterManager mgr1 = new ZookeeperClusterManager(curator1); 312 | ZookeeperClusterManager mgr2 = new ZookeeperClusterManager(curator2); 313 | VertxOptions options1 = new VertxOptions(); 314 | options1.getEventBusOptions().setHost("127.0.0.1"); 315 | VertxOptions options2 = new VertxOptions(); 316 | options2.getEventBusOptions().setHost("127.0.0.1"); 317 | 318 | AtomicReference vertx1 = new AtomicReference<>(); 319 | AtomicReference vertx2 = new AtomicReference<>(); 320 | 321 | Vertx.builder().with(options1).withClusterManager(mgr1).buildClustered().onComplete(res -> { 322 | assertTrue(res.succeeded()); 323 | assertNotNull(mgr1.getCuratorFramework()); 324 | res.result().sharedData().getClusterWideMap("mymap1").onComplete(ar -> { 325 | ar.result().put("news", "hello").onComplete(v -> { 326 | vertx1.set(res.result()); 327 | }); 328 | }); 329 | }); 330 | 331 | assertWaitUntil(() -> vertx1.get() != null); 332 | 333 | Vertx.builder().with(options2).withClusterManager(mgr2).buildClustered().onComplete(res -> { 334 | assertTrue(res.succeeded()); 335 | assertNotNull(mgr2.getCuratorFramework()); 336 | vertx2.set(res.result()); 337 | res.result().sharedData().getClusterWideMap("mymap1").onComplete(ar -> { 338 | ar.result().get("news").onComplete(r -> { 339 | assertEquals("hello", r.result()); 340 | testComplete(); 341 | }); 342 | }); 343 | }); 344 | 345 | await(); 346 | 347 | vertx1.get().close().onComplete(ar -> vertx1.set(null)); 348 | vertx2.get().close().onComplete(ar -> vertx2.set(null)); 349 | 350 | assertWaitUntil(() -> vertx1.get() == null && vertx2.get() == null); 351 | 352 | assertTrue(curator1.getState() == CuratorFrameworkState.STOPPED); 353 | assertTrue(curator2.getState() == CuratorFrameworkState.STOPPED); 354 | 355 | curator1.close(); 356 | curator2.close(); 357 | 358 | assertTrue(dataNode1.getState() == CuratorFrameworkState.STARTED); 359 | assertTrue(dataNode2.getState() == CuratorFrameworkState.STARTED); 360 | 361 | dataNode1.close(); 362 | dataNode2.close(); 363 | } 364 | 365 | } 366 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/core/ZKClusteredComplexHATest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.core; 18 | 19 | import io.vertx.core.spi.cluster.ClusterManager; 20 | import io.vertx.spi.cluster.zookeeper.MockZKCluster; 21 | 22 | /** 23 | * Created by stream.Liu 24 | */ 25 | public class ZKClusteredComplexHATest extends io.vertx.tests.ha.ComplexHATest { 26 | 27 | private MockZKCluster zkClustered = new MockZKCluster(); 28 | 29 | public void after() throws Exception { 30 | super.after(); 31 | zkClustered.stop(); 32 | } 33 | 34 | @Override 35 | protected ClusterManager getClusterManager() { 36 | return zkClustered.getClusterManager(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/core/ZKClusteredHATest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.core; 18 | 19 | import io.vertx.core.spi.cluster.ClusterManager; 20 | import io.vertx.spi.cluster.zookeeper.MockZKCluster; 21 | 22 | /** 23 | * Created by stream.Liu 24 | */ 25 | public class ZKClusteredHATest extends io.vertx.tests.ha.HATest { 26 | 27 | private MockZKCluster zkClustered = new MockZKCluster(); 28 | 29 | public void after() throws Exception { 30 | super.after(); 31 | zkClustered.stop(); 32 | } 33 | 34 | @Override 35 | protected ClusterManager getClusterManager() { 36 | return zkClustered.getClusterManager(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/core/eventbus/ZKClusteredEventbusTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.core.eventbus; 18 | 19 | import io.vertx.core.AsyncResult; 20 | import io.vertx.core.Handler; 21 | import io.vertx.core.Vertx; 22 | import io.vertx.core.spi.cluster.ClusterManager; 23 | import io.vertx.spi.cluster.zookeeper.MockZKCluster; 24 | import io.vertx.test.core.TestUtils; 25 | import org.junit.Test; 26 | 27 | import java.util.Map; 28 | import java.util.concurrent.TimeUnit; 29 | import java.util.concurrent.atomic.AtomicInteger; 30 | import java.util.function.Consumer; 31 | 32 | /** 33 | * 34 | */ 35 | 36 | public class ZKClusteredEventbusTest extends io.vertx.tests.eventbus.ClusteredEventBusTest { 37 | 38 | private final MockZKCluster zkClustered = new MockZKCluster(); 39 | 40 | @Override 41 | public void tearDown() throws Exception { 42 | super.tearDown(); 43 | zkClustered.stop(); 44 | } 45 | 46 | public void await(long delay, TimeUnit timeUnit) { 47 | //fail fast if test blocking 48 | super.await(10, TimeUnit.SECONDS); 49 | } 50 | 51 | @Override 52 | protected void testSend(T val, R received, Consumer consumer, DeliveryOptions options) { 53 | if (vertices == null) { 54 | startNodes(2); 55 | } 56 | 57 | MessageConsumer reg = vertices[1].eventBus().consumer(ADDRESS1).handler((Message msg) -> { 58 | if (consumer == null) { 59 | assertTrue(msg.isSend()); 60 | assertEquals(received, msg.body()); 61 | if (options != null) { 62 | assertNotNull(msg.headers()); 63 | int numHeaders = options.getHeaders() != null ? options.getHeaders().size() : 0; 64 | assertEquals(numHeaders, msg.headers().size()); 65 | if (numHeaders != 0) { 66 | for (Map.Entry entry : options.getHeaders().entries()) { 67 | assertEquals(msg.headers().get(entry.getKey()), entry.getValue()); 68 | } 69 | } 70 | } 71 | } else { 72 | consumer.accept(msg.body()); 73 | } 74 | testComplete(); 75 | }); 76 | reg.completion().onComplete(ar -> { 77 | assertTrue(ar.succeeded()); 78 | vertices[1].setTimer(200L, along -> { 79 | if (options == null) { 80 | vertices[0].eventBus().send(ADDRESS1, val); 81 | } else { 82 | vertices[0].eventBus().send(ADDRESS1, val, options); 83 | } 84 | }); 85 | }); 86 | await(); 87 | } 88 | 89 | @Override 90 | protected void testReply(T val, R received, Consumer consumer, DeliveryOptions options) { 91 | if (vertices == null) { 92 | startNodes(2); 93 | } 94 | String str = TestUtils.randomUnicodeString(1000); 95 | MessageConsumer reg = vertices[1].eventBus().consumer(ADDRESS1).handler(msg -> { 96 | assertEquals(str, msg.body()); 97 | if (options == null) { 98 | msg.reply(val); 99 | } else { 100 | msg.reply(val, options); 101 | } 102 | }); 103 | reg.completion().onComplete(ar -> { 104 | assertTrue(ar.succeeded()); 105 | vertices[1].setTimer(200L, along -> { 106 | vertices[0].eventBus().request(ADDRESS1, str).onComplete(onSuccess((Message reply) -> { 107 | if (consumer == null) { 108 | assertTrue(reply.isSend()); 109 | assertEquals(received, reply.body()); 110 | if (options != null && options.getHeaders() != null) { 111 | assertNotNull(reply.headers()); 112 | assertEquals(options.getHeaders().size(), reply.headers().size()); 113 | for (Map.Entry entry : options.getHeaders().entries()) { 114 | assertEquals(reply.headers().get(entry.getKey()), entry.getValue()); 115 | } 116 | } 117 | } else { 118 | consumer.accept(reply.body()); 119 | } 120 | testComplete(); 121 | })); 122 | }); 123 | }); 124 | await(); 125 | } 126 | 127 | @Test 128 | public void testLocalHandlerClusteredPublish() throws Exception { 129 | startNodes(2); 130 | waitFor(2); 131 | vertices[1].eventBus().consumer(ADDRESS1, msg -> complete()).completion().onComplete(v1 -> { 132 | vertices[0].eventBus().localConsumer(ADDRESS1, msg -> complete()).completion().onComplete(v2 -> { 133 | vertices[1].setTimer(200L, aLong -> { 134 | vertices[0].eventBus().publish(ADDRESS1, "foo"); 135 | }); 136 | }); 137 | }); 138 | await(); 139 | } 140 | 141 | @Override 142 | protected void testPublish(T val, Consumer consumer) { 143 | int numNodes = 3; 144 | startNodes(numNodes); 145 | AtomicInteger count = new AtomicInteger(); 146 | class MyHandler implements Handler> { 147 | @Override 148 | public void handle(Message msg) { 149 | if (consumer == null) { 150 | assertFalse(msg.isSend()); 151 | assertEquals(val, msg.body()); 152 | } else { 153 | consumer.accept(msg.body()); 154 | } 155 | if (count.incrementAndGet() == numNodes - 1) { 156 | testComplete(); 157 | } 158 | } 159 | } 160 | AtomicInteger registerCount = new AtomicInteger(0); 161 | class MyRegisterHandler implements Handler> { 162 | @Override 163 | public void handle(AsyncResult ar) { 164 | assertTrue(ar.succeeded()); 165 | if (registerCount.incrementAndGet() == 2) { 166 | vertices[0].setTimer(300L, h -> { 167 | vertices[0].eventBus().publish(ADDRESS1, val); 168 | }); 169 | } 170 | } 171 | } 172 | MessageConsumer reg = vertices[2].eventBus().consumer(ADDRESS1).handler(new MyHandler()); 173 | reg.completion().onComplete(new MyRegisterHandler()); 174 | reg = vertices[1].eventBus().consumer(ADDRESS1).handler(new MyHandler()); 175 | reg.completion().onComplete(new MyRegisterHandler()); 176 | await(); 177 | } 178 | 179 | @Override 180 | protected ClusterManager getClusterManager() { 181 | return zkClustered.getClusterManager(); 182 | } 183 | 184 | } 185 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/core/eventbus/ZKFaultToleranceTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2020 The original author or authors 3 | * ------------------------------------------------------ 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | 17 | package io.vertx.core.eventbus; 18 | 19 | import io.vertx.core.Future; 20 | import io.vertx.core.Vertx; 21 | import io.vertx.core.VertxOptions; 22 | import io.vertx.core.buffer.Buffer; 23 | import io.vertx.core.json.JsonObject; 24 | import io.vertx.core.spi.cluster.ClusterManager; 25 | import io.vertx.spi.cluster.zookeeper.MockZKCluster; 26 | import org.junit.Assert; 27 | import org.junit.Ignore; 28 | import org.junit.Test; 29 | 30 | import java.util.Optional; 31 | import java.util.concurrent.CountDownLatch; 32 | import java.util.concurrent.TimeUnit; 33 | import java.util.stream.Stream; 34 | 35 | public class ZKFaultToleranceTest extends io.vertx.tests.eventbus.FaultToleranceTest { 36 | 37 | private MockZKCluster zkClustered = new MockZKCluster(); 38 | 39 | protected void startNodes(int numNodes, VertxOptions options) { 40 | CountDownLatch latch = new CountDownLatch(numNodes); 41 | vertices = new Vertx[numNodes]; 42 | for (int i = 0; i < numNodes; i++) { 43 | int index = i; 44 | options.getEventBusOptions().setHost("localhost").setPort(0); 45 | Future res = clusteredVertx(options); 46 | res.onComplete(ar -> { 47 | try { 48 | if (ar.failed()) { 49 | ar.cause().printStackTrace(); 50 | } 51 | assertTrue("Failed to start node", ar.succeeded()); 52 | Vertx vertx = ar.result(); 53 | vertices[index] = vertx; 54 | // 55 | String classpath = System.getProperty("java.class.path"); 56 | JsonObject zkClusterConfig = zkClustered.getDefaultConfig(); 57 | Optional testPath = Stream.of(classpath.split(":")).filter(path -> path.contains("test-classes")).findFirst(); 58 | Assert.assertTrue(testPath.isPresent()); 59 | String zkConfigFileName = testPath.get() + "/zookeeper.json"; 60 | vertx.fileSystem().deleteBlocking(zkConfigFileName); 61 | vertx.fileSystem().createFileBlocking(zkConfigFileName); 62 | vertx.fileSystem().writeFileBlocking(zkConfigFileName, Buffer.buffer(zkClusterConfig.encode())); 63 | } finally { 64 | latch.countDown(); 65 | } 66 | }); 67 | } 68 | try { 69 | Thread.sleep(1500L); 70 | } catch (InterruptedException e) { 71 | // 72 | } 73 | try { 74 | assertTrue(latch.await(2, TimeUnit.MINUTES)); 75 | } catch (InterruptedException e) { 76 | fail(e.getMessage()); 77 | } 78 | } 79 | 80 | @Ignore 81 | @Test 82 | public void testFaultTolerance() throws Exception { 83 | 84 | } 85 | 86 | @Override 87 | protected void tearDown() throws Exception { 88 | super.tearDown(); 89 | zkClustered.stop(); 90 | } 91 | 92 | @Override 93 | protected ClusterManager getClusterManager() { 94 | return zkClustered.getClusterManager(); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/core/eventbus/ZKNodeInfoTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2020 The original author or authors 3 | * ------------------------------------------------------ 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | 17 | package io.vertx.core.eventbus; 18 | 19 | import io.vertx.core.spi.cluster.ClusterManager; 20 | import io.vertx.spi.cluster.zookeeper.MockZKCluster; 21 | 22 | public class ZKNodeInfoTest extends io.vertx.tests.eventbus.NodeInfoTest { 23 | 24 | private MockZKCluster zkClustered = new MockZKCluster(); 25 | 26 | @Override 27 | protected void tearDown() throws Exception { 28 | super.tearDown(); 29 | zkClustered.stop(); 30 | } 31 | 32 | @Override 33 | protected ClusterManager getClusterManager() { 34 | return zkClustered.getClusterManager(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/core/shareddata/ZKClusteredAsyncMapTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.core.shareddata; 18 | 19 | import io.vertx.core.buffer.Buffer; 20 | import io.vertx.core.spi.cluster.ClusterManager; 21 | import io.vertx.spi.cluster.zookeeper.MockZKCluster; 22 | import org.junit.Ignore; 23 | import org.junit.Test; 24 | 25 | import java.util.Objects; 26 | 27 | /** 28 | * 29 | */ 30 | public class ZKClusteredAsyncMapTest extends io.vertx.tests.shareddata.ClusteredAsyncMapTest { 31 | 32 | private MockZKCluster zkClustered = new MockZKCluster(); 33 | 34 | public void after() throws Exception { 35 | super.after(); 36 | zkClustered.stop(); 37 | } 38 | 39 | @Test 40 | @Override 41 | public void testMapReplaceIfPresentTtl() { 42 | getVertx().sharedData().getAsyncMap("foo").onComplete(onSuccess(map -> { 43 | map.replaceIfPresent("key", "old", "new", 100) 44 | .onSuccess(b -> fail("operation should not be implemented")) 45 | .onFailure(t -> { 46 | assertTrue("operation not implemented", t instanceof UnsupportedOperationException); 47 | complete(); 48 | }); 49 | })); 50 | await(); 51 | } 52 | 53 | @Test 54 | @Override 55 | public void testMapReplaceIfPresentTtlWhenNotPresent() { 56 | getVertx().sharedData().getAsyncMap("foo").onComplete(onSuccess(map -> { 57 | map.replaceIfPresent("key", "old", "new", 100) 58 | .onSuccess(b -> fail("operation should not be implemented")) 59 | .onFailure(t -> { 60 | assertTrue("operation not implemented", t instanceof UnsupportedOperationException); 61 | complete(); 62 | }); 63 | })); 64 | await(); 65 | } 66 | 67 | @Test 68 | @Override 69 | public void testMapReplaceTtl() { 70 | getVertx().sharedData().getAsyncMap("foo").onComplete(onSuccess(map -> { 71 | map.replace("key", "new", 100) 72 | .onSuccess(b -> fail("operation should not be implemented")) 73 | .onFailure(t -> { 74 | assertTrue("operation not implemented", t instanceof UnsupportedOperationException); 75 | complete(); 76 | }); 77 | })); 78 | await(); 79 | } 80 | 81 | @Test 82 | @Override 83 | public void testMapReplaceTtlWithPreviousValue() { 84 | getVertx().sharedData().getAsyncMap("foo").onComplete(onSuccess(map -> { 85 | map.replace("key", "new", 100) 86 | .onSuccess(b -> fail("operation should not be implemented")) 87 | .onFailure(t -> { 88 | assertTrue("operation not implemented", t instanceof UnsupportedOperationException); 89 | complete(); 90 | }); 91 | })); 92 | await(); 93 | } 94 | 95 | @Override 96 | protected ClusterManager getClusterManager() { 97 | return zkClustered.getClusterManager(); 98 | } 99 | 100 | @Test 101 | public void testStoreAndGetBuffer() { 102 | getVertx().sharedData().getAsyncMap("foo").onComplete(onSuccess(map -> { 103 | map.put("test", Buffer.buffer().appendString("Hello")).onComplete(onSuccess(putResult -> map.get("test").onComplete(onSuccess(myBuffer -> { 104 | assertEquals("Hello", myBuffer.toString()); 105 | testComplete(); 106 | })))); 107 | })); 108 | await(); 109 | } 110 | 111 | @Ignore 112 | @Override 113 | public void testMapPutThenPutTtl() { 114 | // This test fails upstream, the test is doing: 115 | 116 | // 1. get the async map: foo 117 | // 2. store the value "molo" under the key "pipo" 118 | // 3. store the value "mili" under the key "pipo" with TTL 15 119 | // 4. get the async map: foo 120 | // N. every 15, check if key "pipo" is NULL <-- THIS NEVER HAPPENS 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/core/shareddata/ZKClusteredAsynchronousLockTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.core.shareddata; 18 | 19 | import io.vertx.core.spi.cluster.ClusterManager; 20 | import io.vertx.spi.cluster.zookeeper.MockZKCluster; 21 | 22 | /** 23 | * Created by stream.Liu 24 | */ 25 | public class ZKClusteredAsynchronousLockTest extends io.vertx.tests.shareddata.ClusteredAsynchronousLockTest { 26 | 27 | private MockZKCluster zkClustered = new MockZKCluster(); 28 | 29 | public void after() throws Exception { 30 | super.after(); 31 | zkClustered.stop(); 32 | } 33 | 34 | @Override 35 | protected ClusterManager getClusterManager() { 36 | return zkClustered.getClusterManager(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/core/shareddata/ZKClusteredSharedCounterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.core.shareddata; 18 | 19 | import io.vertx.core.spi.cluster.ClusterManager; 20 | import io.vertx.spi.cluster.zookeeper.MockZKCluster; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | 24 | /** 25 | * 26 | */ 27 | public class ZKClusteredSharedCounterTest extends io.vertx.tests.shareddata.ClusteredSharedCounterTest { 28 | 29 | private MockZKCluster zkClustered = new MockZKCluster(); 30 | 31 | public void await(long delay, TimeUnit timeUnit) { 32 | super.await(15, TimeUnit.SECONDS); 33 | } 34 | 35 | public void after() throws Exception { 36 | super.after(); 37 | zkClustered.stop(); 38 | } 39 | 40 | @Override 41 | protected ClusterManager getClusterManager() { 42 | return zkClustered.getClusterManager(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/ext/web/sstore/ZKClusteredSessionHandlerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Red Hat, Inc. 3 | * 4 | * Red Hat licenses this file to you under the Apache License, version 2.0 5 | * (the "License"); you may not use this file except in compliance with the 6 | * License. You may obtain a copy of the License at: 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package io.vertx.ext.web.sstore; 18 | 19 | import io.vertx.core.spi.cluster.ClusterManager; 20 | import io.vertx.ext.web.it.sstore.ClusteredSessionHandlerTest; 21 | import io.vertx.spi.cluster.zookeeper.MockZKCluster; 22 | import org.junit.Ignore; 23 | import org.junit.Test; 24 | 25 | /** 26 | * @author Thomas Segismont 27 | */ 28 | public class ZKClusteredSessionHandlerTest extends ClusteredSessionHandlerTest { 29 | 30 | private MockZKCluster zkClustered = new MockZKCluster(); 31 | 32 | @Override 33 | public void tearDown() throws Exception { 34 | super.tearDown(); 35 | zkClustered.stop(); 36 | } 37 | 38 | @Override 39 | protected ClusterManager getClusterManager() { 40 | return zkClustered.getClusterManager(); 41 | } 42 | 43 | @Ignore @Test 44 | public void testSessionExpires() { 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/servicediscovery/impl/ZKDiscoveryImplClusteredTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2020 The original author or authors 3 | * ------------------------------------------------------ 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | 17 | package io.vertx.servicediscovery.impl; 18 | 19 | import io.vertx.core.Vertx; 20 | import io.vertx.core.VertxOptions; 21 | import io.vertx.servicediscovery.ServiceDiscoveryOptions; 22 | import io.vertx.spi.cluster.zookeeper.MockZKCluster; 23 | import org.junit.After; 24 | import org.junit.Before; 25 | 26 | import static com.jayway.awaitility.Awaitility.await; 27 | 28 | public class ZKDiscoveryImplClusteredTest extends DiscoveryImplTestBase { 29 | 30 | private MockZKCluster zkClustered = new MockZKCluster(); 31 | 32 | @Before 33 | public void setUp() { 34 | VertxOptions options = new VertxOptions(); 35 | options.getEventBusOptions().setHost("localhost").setPort(0); 36 | Vertx.builder().with(options).withClusterManager(zkClustered.getClusterManager()).buildClustered().onComplete(ar -> { 37 | vertx = ar.result(); 38 | }); 39 | await().until(() -> vertx != null); 40 | discovery = new DiscoveryImpl(vertx, new ServiceDiscoveryOptions()); 41 | } 42 | 43 | @After 44 | public void tearDown() { 45 | super.tearDown(); 46 | zkClustered.stop(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/spi/cluster/zookeeper/ConsumerRoundRobinTest.java: -------------------------------------------------------------------------------- 1 | package io.vertx.spi.cluster.zookeeper; 2 | 3 | import io.vertx.core.Vertx; 4 | import io.vertx.core.spi.cluster.ClusterManager; 5 | import io.vertx.test.core.VertxTestBase; 6 | import org.junit.Test; 7 | 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | import java.util.concurrent.CompletableFuture; 11 | import java.util.concurrent.CountDownLatch; 12 | import java.util.concurrent.atomic.AtomicInteger; 13 | 14 | /** 15 | * Created by stream. 16 | */ 17 | public class ConsumerRoundRobinTest extends VertxTestBase { 18 | 19 | private static final String MESSAGE_ADDRESS = "consumerAddress"; 20 | 21 | protected ClusterManager getClusterManager() { 22 | MockZKCluster zkCluster = new MockZKCluster(); 23 | return zkCluster.getClusterManager(); 24 | } 25 | 26 | 27 | private CompletableFuture addConsumer(int index) { 28 | CompletableFuture future = new CompletableFuture<>(); 29 | vertices[0].eventBus(). 30 | consumer(MESSAGE_ADDRESS, message -> message.reply(index)). 31 | completion().onComplete(event -> { 32 | if (event.succeeded()) { 33 | future.complete(null); 34 | } else { 35 | future.completeExceptionally(event.cause()); 36 | } 37 | }); 38 | return future; 39 | } 40 | 41 | @Override 42 | public void setUp() throws Exception { 43 | super.setUp(); 44 | startNodes(1); 45 | CountDownLatch latch = new CountDownLatch(1); 46 | addConsumer(0).thenCompose(aVoid -> addConsumer(1)).thenCompose(aVoid -> addConsumer(2)). 47 | whenComplete((aVoid, throwable) -> { 48 | if (throwable != null) { 49 | fail(throwable); 50 | } else { 51 | latch.countDown(); 52 | } 53 | }); 54 | awaitLatch(latch); 55 | } 56 | 57 | @Test 58 | public void roundRobin() { 59 | AtomicInteger counter = new AtomicInteger(0); 60 | Set results = new HashSet<>(); 61 | Vertx vertx = vertices[0]; 62 | vertx.setPeriodic(500, aLong -> vertx.eventBus().request(MESSAGE_ADDRESS, "Hi").onComplete(message -> { 63 | if (message.failed()) { 64 | fail(message.cause()); 65 | } else { 66 | Integer result = (Integer) message.result().body(); 67 | results.add(result); 68 | if (counter.incrementAndGet() == 3) { 69 | assertEquals(results.size(), counter.get()); 70 | testComplete(); 71 | } 72 | } 73 | })); 74 | await(); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/spi/cluster/zookeeper/MockZKCluster.java: -------------------------------------------------------------------------------- 1 | package io.vertx.spi.cluster.zookeeper; 2 | 3 | import io.vertx.core.json.JsonObject; 4 | import io.vertx.core.spi.cluster.ClusterManager; 5 | import org.apache.curator.framework.CuratorFramework; 6 | import org.apache.curator.test.InstanceSpec; 7 | import org.apache.curator.test.TestingServer; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * Created by Stream.Liu 14 | */ 15 | public class MockZKCluster { 16 | private final InstanceSpec spec = new InstanceSpec(null, -1, -1, -1, true, -1, 10000, 120); 17 | private TestingServer server; 18 | private final List clusterManagers = new ArrayList<>(); 19 | 20 | static { 21 | System.setProperty("zookeeper.extendedTypesEnabled", "true"); 22 | } 23 | 24 | public MockZKCluster() { 25 | try { 26 | server = new TestingServer(spec, true); 27 | } catch (Exception e) { 28 | e.printStackTrace(); 29 | } 30 | } 31 | 32 | public JsonObject getDefaultConfig() { 33 | JsonObject config = new JsonObject(); 34 | config.put("zookeeperHosts", server.getConnectString()); 35 | config.put("sessionTimeout", 10000); 36 | config.put("connectTimeout", 5000); 37 | config.put("rootPath", "io.vertx"); 38 | config.put("retry", new JsonObject() 39 | .put("initialSleepTime", 500) 40 | .put("maxTimes", 2)); 41 | return config; 42 | } 43 | 44 | public CuratorFramework curator; 45 | 46 | public void stop() { 47 | try { 48 | clusterManagers.clear(); 49 | if (server == null) { 50 | server = new TestingServer(spec, false); 51 | } 52 | server.restart(); 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | } 56 | } 57 | 58 | public ClusterManager getClusterManager() { 59 | if (server == null) { 60 | try { 61 | server = new TestingServer(spec, true); 62 | } catch (Exception e) { 63 | e.printStackTrace(); 64 | } 65 | } 66 | 67 | ZookeeperClusterManager zookeeperClusterManager = new ZookeeperClusterManager(getDefaultConfig()); 68 | clusterManagers.add(zookeeperClusterManager); 69 | return zookeeperClusterManager; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/spi/cluster/zookeeper/RetryPolicyTest.java: -------------------------------------------------------------------------------- 1 | package io.vertx.spi.cluster.zookeeper; 2 | 3 | import io.vertx.core.json.JsonObject; 4 | import io.vertx.spi.cluster.zookeeper.impl.RetryPolicyHelper; 5 | import org.apache.curator.RetryPolicy; 6 | import org.apache.curator.retry.ExponentialBackoffRetry; 7 | import org.apache.curator.retry.RetryOneTime; 8 | import org.junit.Assert; 9 | import org.junit.Test; 10 | 11 | public class RetryPolicyTest { 12 | 13 | @Test 14 | public void createDefaultRetryPolicy(){ 15 | JsonObject config = new JsonObject(); 16 | RetryPolicy policy = RetryPolicyHelper.createRetryPolicy(config); 17 | Assert.assertTrue(policy instanceof ExponentialBackoffRetry); 18 | } 19 | 20 | @Test 21 | public void createOneTimeRetryPolicy(){ 22 | JsonObject config = new JsonObject().put("policy","one_time"); 23 | RetryPolicy policy = RetryPolicyHelper.createRetryPolicy(config); 24 | Assert.assertTrue(policy instanceof RetryOneTime); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/io/vertx/spi/cluster/zookeeper/ZKSyncMapTest.java: -------------------------------------------------------------------------------- 1 | package io.vertx.spi.cluster.zookeeper; 2 | 3 | import io.vertx.spi.cluster.zookeeper.impl.ZKSyncMap; 4 | import org.apache.curator.RetryPolicy; 5 | import org.apache.curator.framework.CuratorFramework; 6 | import org.apache.curator.framework.CuratorFrameworkFactory; 7 | import org.apache.curator.retry.ExponentialBackoffRetry; 8 | import org.apache.curator.test.TestingServer; 9 | import org.apache.curator.test.Timing; 10 | import org.junit.Test; 11 | 12 | import static org.junit.Assert.*; 13 | 14 | /** 15 | * 16 | */ 17 | public class ZKSyncMapTest { 18 | 19 | @Test 20 | public void syncMapOperation() throws Exception { 21 | Timing timing = new Timing(); 22 | TestingServer server = new TestingServer(); 23 | 24 | RetryPolicy retryPolicy = new ExponentialBackoffRetry(100, 3); 25 | CuratorFramework curator = CuratorFrameworkFactory.builder() 26 | .namespace("io.vertx") 27 | .sessionTimeoutMs(timing.session()) 28 | .connectionTimeoutMs(timing.connection()) 29 | .connectString(server.getConnectString()) 30 | .retryPolicy(retryPolicy) 31 | .build(); 32 | curator.start(); 33 | 34 | String k = "myKey"; 35 | String v = "myValue"; 36 | 37 | ZKSyncMap syncMap = new ZKSyncMap<>(curator, "mapTest"); 38 | 39 | syncMap.put(k, v); 40 | assertFalse(syncMap.isEmpty()); 41 | 42 | assertEquals(syncMap.get(k), v); 43 | 44 | assertTrue(syncMap.size() > 0); 45 | assertTrue(syncMap.containsKey(k)); 46 | assertTrue(syncMap.containsValue(v)); 47 | 48 | assertTrue(syncMap.keySet().contains(k)); 49 | assertTrue(syncMap.values().contains(v)); 50 | 51 | syncMap.entrySet().forEach(entry -> { 52 | assertEquals(k, entry.getKey()); 53 | assertEquals(v, entry.getValue()); 54 | }); 55 | 56 | String value = syncMap.remove(k); 57 | assertEquals(value, v); 58 | assertNull(syncMap.get(k)); 59 | 60 | syncMap.clear(); 61 | assertTrue(syncMap.isEmpty()); 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Set root logger level to DEBUG and its only appender to CONSOLE. 2 | log4j.rootLogger=INFO, CONSOLE 3 | 4 | # CONSOLE 5 | log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 6 | log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout 7 | log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] %-5p %C{1} : %m%n 8 | -------------------------------------------------------------------------------- /src/test/resources/zookeeper.json: -------------------------------------------------------------------------------- 1 | { 2 | "zookeeperHosts":"127.0.0.1", 3 | "sessionTimeout":5000, 4 | "connectTimeout":3000, 5 | "rootPath":"io.vertx", 6 | "retry": { 7 | "initialSleepTime":100, 8 | "intervalTimes":10000, 9 | "maxTimes":5 10 | } 11 | } --------------------------------------------------------------------------------