Using Exectutor in Kafka Node. NEVER use Fork-Join pool with parallelism 1

This commit is contained in:
Andrii Shvaika 2020-06-05 12:54:27 +03:00
parent 3358c061da
commit cc4f746b1d
11 changed files with 61 additions and 38 deletions

View File

@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.DefaultTbActorSystem; import org.thingsboard.server.actors.DefaultTbActorSystem;
import org.thingsboard.server.actors.TbActorId; import org.thingsboard.server.actors.TbActorId;
@ -83,10 +84,10 @@ public class DefaultActorService implements ActorService {
TbActorSystemSettings settings = new TbActorSystemSettings(actorThroughput, schedulerPoolSize, maxActorInitAttempts); TbActorSystemSettings settings = new TbActorSystemSettings(actorThroughput, schedulerPoolSize, maxActorInitAttempts);
system = new DefaultTbActorSystem(settings); system = new DefaultTbActorSystem(settings);
system.createDispatcher(APP_DISPATCHER_NAME, initDispatcherExecutor(appDispatcherSize)); system.createDispatcher(APP_DISPATCHER_NAME, initDispatcherExecutor(APP_DISPATCHER_NAME, appDispatcherSize));
system.createDispatcher(TENANT_DISPATCHER_NAME, initDispatcherExecutor(tenantDispatcherSize)); system.createDispatcher(TENANT_DISPATCHER_NAME, initDispatcherExecutor(TENANT_DISPATCHER_NAME, tenantDispatcherSize));
system.createDispatcher(DEVICE_DISPATCHER_NAME, initDispatcherExecutor(deviceDispatcherSize)); system.createDispatcher(DEVICE_DISPATCHER_NAME, initDispatcherExecutor(DEVICE_DISPATCHER_NAME, deviceDispatcherSize));
system.createDispatcher(RULE_DISPATCHER_NAME, initDispatcherExecutor(ruleDispatcherSize)); system.createDispatcher(RULE_DISPATCHER_NAME, initDispatcherExecutor(RULE_DISPATCHER_NAME, ruleDispatcherSize));
actorContext.setActorSystem(system); actorContext.setActorSystem(system);
@ -99,13 +100,13 @@ public class DefaultActorService implements ActorService {
log.info("Actor system initialized."); log.info("Actor system initialized.");
} }
private ExecutorService initDispatcherExecutor(int poolSize) { private ExecutorService initDispatcherExecutor(String dispatcherName, int poolSize) {
if (poolSize == 0) { if (poolSize == 0) {
int cores = Runtime.getRuntime().availableProcessors(); int cores = Runtime.getRuntime().availableProcessors();
poolSize = Math.max(1, cores / 2); poolSize = Math.max(1, cores / 2);
} }
if (poolSize == 1) { if (poolSize == 1) {
return Executors.newFixedThreadPool(1); return Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(dispatcherName));
} else { } else {
return Executors.newWorkStealingPool(poolSize); return Executors.newWorkStealingPool(poolSize);
} }

View File

@ -164,7 +164,7 @@ public class DefaultTbRuleEngineRpcService implements TbRuleEngineDeviceRpcServi
} }
private void scheduleTimeout(ToDeviceRpcRequest request, UUID requestId) { private void scheduleTimeout(ToDeviceRpcRequest request, UUID requestId) {
long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis()); long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis()) + TimeUnit.SECONDS.toMillis(1);
log.trace("[{}] processing the request: [{}]", this.hashCode(), requestId); log.trace("[{}] processing the request: [{}]", this.hashCode(), requestId);
scheduler.schedule(() -> { scheduler.schedule(() -> {
log.trace("[{}] timeout the request: [{}]", this.hashCode(), requestId); log.trace("[{}] timeout the request: [{}]", this.hashCode(), requestId);

View File

@ -26,7 +26,9 @@ import java.util.Arrays;
@RunWith(ClasspathSuite.class) @RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({ @ClasspathSuite.ClassnameFilters({
"org.thingsboard.server.mqtt.rpc.sql.*Test", "org.thingsboard.server.mqtt.telemetry.sql.*Test"}) "org.thingsboard.server.mqtt.rpc.sql.*Test",
"org.thingsboard.server.mqtt.telemetry.sql.*Test"
})
public class MqttSqlTestSuite { public class MqttSqlTestSuite {
@ClassRule @ClassRule

View File

@ -136,7 +136,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}"; String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}";
String deviceId = savedDevice.getId().getId().toString(); String deviceId = savedDevice.getId().getId().toString();
doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(), doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409),
asyncContextTimeoutToUseRpcPlugin); asyncContextTimeoutToUseRpcPlugin);
} }

View File

@ -54,7 +54,6 @@ public final class TbActorMailbox implements TbActorCtx {
dispatcher.getExecutor().execute(() -> tryInit(1)); dispatcher.getExecutor().execute(() -> tryInit(1));
} }
private void tryInit(int attempt) { private void tryInit(int attempt) {
try { try {
log.debug("[{}] Trying to init actor, attempt: {}", selfId, attempt); log.debug("[{}] Trying to init actor, attempt: {}", selfId, attempt);

View File

@ -40,23 +40,19 @@ import java.util.concurrent.atomic.AtomicLong;
public class ActorSystemTest { public class ActorSystemTest {
public static final String ROOT_DISPATCHER = "root-dispatcher"; public static final String ROOT_DISPATCHER = "root-dispatcher";
private static final int _1M = 1024 * 1024; private static final int _100K = 100 * 1024;
private volatile TbActorSystem actorSystem; private volatile TbActorSystem actorSystem;
private volatile ExecutorService submitPool; private volatile ExecutorService submitPool;
private int parallelism;
@Before @Before
public void initActorSystem() { public void initActorSystem() {
int cores = Runtime.getRuntime().availableProcessors(); int cores = Runtime.getRuntime().availableProcessors();
int parallelism = Math.max(1, cores / 2); parallelism = Math.max(2, cores / 2);
TbActorSystemSettings settings = new TbActorSystemSettings(5, parallelism, 42); TbActorSystemSettings settings = new TbActorSystemSettings(5, parallelism, 42);
actorSystem = new DefaultTbActorSystem(settings); actorSystem = new DefaultTbActorSystem(settings);
submitPool = Executors.newWorkStealingPool(parallelism); submitPool = Executors.newWorkStealingPool(parallelism);
// actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newCachedThreadPool());
// actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newFixedThreadPool(parallelism));
actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
// actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(1));
// actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newFixedThreadPool(1));
} }
@After @After
@ -66,32 +62,44 @@ public class ActorSystemTest {
} }
@Test @Test
public void test1actorsAnd1MMessages() throws InterruptedException { public void test1actorsAnd100KMessages() throws InterruptedException {
testActorsAndMessages(1, _1M, 5); actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
testActorsAndMessages(1, _100K, 1);
} }
@Test @Test
public void test10actorsAnd1MMessages() throws InterruptedException { public void test10actorsAnd100KMessages() throws InterruptedException {
testActorsAndMessages(10, _1M, 5); actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
testActorsAndMessages(10, _100K, 1);
} }
@Test @Test
public void test1MActorsAnd1Messages5times() throws InterruptedException { public void test100KActorsAnd1Messages5timesSingleThread() throws InterruptedException {
testActorsAndMessages(_1M, 1, 5); actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newSingleThreadExecutor());
testActorsAndMessages(_100K, 1, 5);
} }
@Test @Test
public void test1MActorsAnd10Messages() throws InterruptedException { public void test100KActorsAnd1Messages5times() throws InterruptedException {
testActorsAndMessages(_1M, 10, 1); actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
testActorsAndMessages(_100K, 1, 5);
}
@Test
public void test100KActorsAnd10Messages() throws InterruptedException {
actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
testActorsAndMessages(_100K, 10, 1);
} }
@Test @Test
public void test1KActorsAnd1KMessages() throws InterruptedException { public void test1KActorsAnd1KMessages() throws InterruptedException {
actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
testActorsAndMessages(1000, 1000, 10); testActorsAndMessages(1000, 1000, 10);
} }
@Test @Test
public void testNoMessagesAfterDestroy() throws InterruptedException { public void testNoMessagesAfterDestroy() throws InterruptedException {
actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
ActorTestCtx testCtx1 = getActorTestCtx(1); ActorTestCtx testCtx1 = getActorTestCtx(1);
ActorTestCtx testCtx2 = getActorTestCtx(1); ActorTestCtx testCtx2 = getActorTestCtx(1);
@ -105,11 +113,12 @@ public class ActorSystemTest {
actorSystem.stop(actorId1); actorSystem.stop(actorId1);
Assert.assertTrue(testCtx2.getLatch().await(1, TimeUnit.SECONDS)); Assert.assertTrue(testCtx2.getLatch().await(1, TimeUnit.SECONDS));
Assert.assertFalse(testCtx1.getLatch().await(2, TimeUnit.SECONDS)); Assert.assertFalse(testCtx1.getLatch().await(1, TimeUnit.SECONDS));
} }
@Test @Test
public void testOneActorCreated() throws InterruptedException { public void testOneActorCreated() throws InterruptedException {
actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
ActorTestCtx testCtx1 = getActorTestCtx(1); ActorTestCtx testCtx1 = getActorTestCtx(1);
ActorTestCtx testCtx2 = getActorTestCtx(1); ActorTestCtx testCtx2 = getActorTestCtx(1);
TbActorId actorId = new TbEntityActorId(new DeviceId(UUID.randomUUID())); TbActorId actorId = new TbEntityActorId(new DeviceId(UUID.randomUUID()));
@ -119,12 +128,13 @@ public class ActorSystemTest {
Thread.sleep(1000); Thread.sleep(1000);
actorSystem.tell(actorId, new IntTbActorMsg(42)); actorSystem.tell(actorId, new IntTbActorMsg(42));
Assert.assertTrue(testCtx1.getLatch().await(3, TimeUnit.SECONDS)); Assert.assertTrue(testCtx1.getLatch().await(1, TimeUnit.SECONDS));
Assert.assertFalse(testCtx2.getLatch().await(3, TimeUnit.SECONDS)); Assert.assertFalse(testCtx2.getLatch().await(1, TimeUnit.SECONDS));
} }
@Test @Test
public void testActorCreatorCalledOnce() throws InterruptedException { public void testActorCreatorCalledOnce() throws InterruptedException {
actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
ActorTestCtx testCtx = getActorTestCtx(1); ActorTestCtx testCtx = getActorTestCtx(1);
TbActorId actorId = new TbEntityActorId(new DeviceId(UUID.randomUUID())); TbActorId actorId = new TbEntityActorId(new DeviceId(UUID.randomUUID()));
for (int i = 0; i < 1000; i++) { for (int i = 0; i < 1000; i++) {

View File

@ -95,8 +95,20 @@ public class TbKafkaNode implements TbNode {
@Override @Override
public void onMsg(TbContext ctx, TbMsg msg) { public void onMsg(TbContext ctx, TbMsg msg) {
String topic = TbNodeUtils.processPattern(config.getTopicPattern(), msg.getMetaData()); String topic = TbNodeUtils.processPattern(config.getTopicPattern(), msg.getMetaData());
try {
ctx.getExternalCallExecutor().executeAsync(() -> {
publish(ctx, msg, topic);
return null;
});
} catch (Exception e) {
ctx.tellFailure(msg, e);
}
}
protected void publish(TbContext ctx, TbMsg msg, String topic) {
try { try {
if (!addMetadataKeyValuesAsKafkaHeaders) { if (!addMetadataKeyValuesAsKafkaHeaders) {
//TODO: external system executor
producer.send(new ProducerRecord<>(topic, msg.getData()), producer.send(new ProducerRecord<>(topic, msg.getData()),
(metadata, e) -> processRecord(ctx, msg, metadata, e)); (metadata, e) -> processRecord(ctx, msg, metadata, e));
} else { } else {
@ -105,9 +117,8 @@ public class TbKafkaNode implements TbNode {
producer.send(new ProducerRecord<>(topic, null, null, null, msg.getData(), headers), producer.send(new ProducerRecord<>(topic, null, null, null, msg.getData(), headers),
(metadata, e) -> processRecord(ctx, msg, metadata, e)); (metadata, e) -> processRecord(ctx, msg, metadata, e));
} }
} catch (Exception e) { } catch (Exception e) {
ctx.tellFailure(msg, e); log.debug("[{}] Failed to process message: {}", ctx.getSelfId(), msg, e);
} }
} }