|
@@ -18,14 +18,17 @@
|
|
|
|
|
|
package org.apache.zookeeper.server;
|
|
|
|
|
|
-
|
|
|
+import org.apache.zookeeper.AsyncCallback.DataCallback;
|
|
|
import org.apache.zookeeper.CreateMode;
|
|
|
import org.apache.zookeeper.KeeperException;
|
|
|
import org.apache.zookeeper.ZooDefs.Ids;
|
|
|
import org.apache.zookeeper.ZooKeeper;
|
|
|
+import org.apache.zookeeper.common.ClientX509Util;
|
|
|
+import org.apache.zookeeper.data.Stat;
|
|
|
import org.apache.zookeeper.test.TestByteBufAllocator;
|
|
|
import org.apache.zookeeper.server.quorum.BufferStats;
|
|
|
import org.apache.zookeeper.test.ClientBase;
|
|
|
+import org.apache.zookeeper.test.SSLAuthTest;
|
|
|
import org.junit.Assert;
|
|
|
import org.junit.Test;
|
|
|
import org.slf4j.Logger;
|
|
@@ -34,7 +37,11 @@ import org.slf4j.LoggerFactory;
|
|
|
import java.io.IOException;
|
|
|
import java.net.ProtocolException;
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
+import java.util.Random;
|
|
|
+import java.util.concurrent.CountDownLatch;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
+import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
|
import static org.hamcrest.Matchers.equalTo;
|
|
|
import static org.hamcrest.Matchers.greaterThan;
|
|
@@ -203,4 +210,135 @@ public class NettyServerCnxnTest extends ClientBase {
|
|
|
assertArrayEquals("unexpected data", "test".getBytes(StandardCharsets.UTF_8), contents);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testEnableDisableThrottling_secure_random() throws Exception {
|
|
|
+ runEnableDisableThrottling(true, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testEnableDisableThrottling_secure_sequentially() throws Exception {
|
|
|
+ runEnableDisableThrottling(true, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testEnableDisableThrottling_nonSecure_random() throws Exception {
|
|
|
+ runEnableDisableThrottling(false, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testEnableDisableThrottling_nonSecure_sequentially() throws Exception {
|
|
|
+ runEnableDisableThrottling(false, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void runEnableDisableThrottling(boolean secure, boolean randomDisableEnable) throws Exception {
|
|
|
+ ClientX509Util x509Util = null;
|
|
|
+ if (secure) {
|
|
|
+ x509Util = SSLAuthTest.setUpSecure();
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ NettyServerCnxnFactory factory = (NettyServerCnxnFactory) serverFactory;
|
|
|
+ factory.setAdvancedFlowControlEnabled(true);
|
|
|
+ if (secure) {
|
|
|
+ factory.setSecure(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ final String path = "/testEnableDisableThrottling";
|
|
|
+ try (ZooKeeper zk = createClient()) {
|
|
|
+ zk.create(path, new byte[1], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
|
|
|
+
|
|
|
+ // meanwhile start another thread to enable and disable recv
|
|
|
+ AtomicBoolean stopped = new AtomicBoolean(false);
|
|
|
+ Random random = new Random();
|
|
|
+
|
|
|
+ Thread enableDisableThread = null;
|
|
|
+ if (randomDisableEnable) {
|
|
|
+ enableDisableThread = new Thread() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ while (!stopped.get()) {
|
|
|
+ for (final ServerCnxn cnxn : serverFactory.cnxns) {
|
|
|
+ boolean shouldDisableEnable = random.nextBoolean();
|
|
|
+ if (shouldDisableEnable) {
|
|
|
+ cnxn.disableRecv();
|
|
|
+ } else {
|
|
|
+ cnxn.enableRecv();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ Thread.sleep(10);
|
|
|
+ } catch (InterruptedException e) { /* ignore */ }
|
|
|
+ }
|
|
|
+ // always enable the recv at end
|
|
|
+ for (final ServerCnxn cnxn : serverFactory.cnxns) {
|
|
|
+ cnxn.enableRecv();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ enableDisableThread = new Thread() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ while (!stopped.get()) {
|
|
|
+ for (final ServerCnxn cnxn : serverFactory.cnxns) {
|
|
|
+ try {
|
|
|
+ cnxn.disableRecv();
|
|
|
+ Thread.sleep(10);
|
|
|
+ cnxn.enableRecv();
|
|
|
+ Thread.sleep(10);
|
|
|
+ } catch (InterruptedException e) { /* ignore */ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+ enableDisableThread.start();
|
|
|
+ LOG.info("started thread to enable and disable recv");
|
|
|
+
|
|
|
+ // start a thread to keep sending requests
|
|
|
+ int totalRequestsNum = 100000;
|
|
|
+ AtomicInteger successResponse = new AtomicInteger();
|
|
|
+ CountDownLatch responseReceivedLatch = new CountDownLatch(totalRequestsNum);
|
|
|
+ Thread clientThread = new Thread() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ int requestIssued = 0;
|
|
|
+ while (requestIssued++ < totalRequestsNum) {
|
|
|
+ zk.getData(path, null, new DataCallback() {
|
|
|
+ @Override
|
|
|
+ public void processResult(int rc, String path,
|
|
|
+ Object ctx, byte data[], Stat stat) {
|
|
|
+ if (rc == 0) {
|
|
|
+ successResponse.addAndGet(1);
|
|
|
+ } else {
|
|
|
+ LOG.info("failed response is {}", rc);
|
|
|
+ }
|
|
|
+ responseReceivedLatch.countDown();
|
|
|
+ }
|
|
|
+ }, null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ clientThread.start();
|
|
|
+ LOG.info("started thread to issue {} async requests", totalRequestsNum);
|
|
|
+
|
|
|
+ // and verify the response received is same as what we issued
|
|
|
+ Assert.assertTrue(responseReceivedLatch.await(60, TimeUnit.SECONDS));
|
|
|
+ LOG.info("received all {} responses", totalRequestsNum);
|
|
|
+
|
|
|
+ stopped.set(true);
|
|
|
+ enableDisableThread.join();
|
|
|
+ LOG.info("enable and disable recv thread exited");
|
|
|
+
|
|
|
+ // wait another second for the left requests to finish
|
|
|
+ LOG.info("waiting another 1s for the requests to go through");
|
|
|
+ Thread.sleep(1000);
|
|
|
+ Assert.assertEquals(successResponse.get(), totalRequestsNum);
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ if (secure) {
|
|
|
+ SSLAuthTest.clearSecureSetting(x509Util);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|