/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.runtime.core.processor.strategy;
import static org.mule.runtime.core.api.processor.ReactiveProcessor.ProcessingType.BLOCKING;
import static org.mule.runtime.core.api.processor.ReactiveProcessor.ProcessingType.CPU_INTENSIVE;
import static org.mule.runtime.core.api.processor.ReactiveProcessor.ProcessingType.CPU_LITE;
import static reactor.core.publisher.Flux.from;
import static reactor.core.publisher.Flux.just;
import static reactor.core.scheduler.Schedulers.fromExecutorService;
import org.mule.runtime.api.exception.MuleException;
import org.mule.runtime.api.scheduler.Scheduler;
import org.mule.runtime.core.api.MuleContext;
import org.mule.runtime.core.api.processor.ReactiveProcessor;
import org.mule.runtime.core.api.processor.ReactiveProcessor.ProcessingType;
import org.mule.runtime.core.api.processor.strategy.ProcessingStrategy;
import org.mule.runtime.core.api.scheduler.SchedulerService;
import java.util.function.Supplier;
/**
* Creates {@link ReactorProcessingStrategyFactory.ReactorProcessingStrategy} instance that implements the proactor pattern by
* de-multiplexing incoming events onto a single event-loop using a ring-buffer and then using using the
* {@link SchedulerService#cpuLightScheduler()} to process these events from the ring-buffer. In contrast to the
* {@link ReactorStreamProcessingStrategy} the proactor pattern treats {@link ProcessingType#CPU_INTENSIVE} and
* {@link ProcessingType#BLOCKING} processors differently and schedules there execution on dedicated
* {@link SchedulerService#cpuIntensiveScheduler()} and {@link SchedulerService#ioScheduler()} ()} schedulers.
* <p/>
* This processing strategy is not suitable for transactional flows and will fail if used with an active transaction.
*
* @since 4.0
*/
public class ProactorStreamProcessingStrategyFactory extends ReactorStreamProcessingStrategyFactory {
@Override
public ProcessingStrategy create(MuleContext muleContext, String schedulersNamePrefix) {
if (getMaxConcurrency() == 1) {
return new ReactorProcessingStrategyFactory().create(muleContext, schedulersNamePrefix);
} else {
return new ProactorStreamProcessingStrategy(() -> muleContext.getSchedulerService()
.customScheduler(muleContext.getSchedulerBaseConfig()
.withName(schedulersNamePrefix + RING_BUFFER_SCHEDULER_NAME_SUFFIX)
.withMaxConcurrentTasks(getSubscriberCount() + 1)),
getBufferSize(),
getSubscriberCount(),
getWaitStrategy(), () -> muleContext.getSchedulerService()
.cpuLightScheduler(muleContext.getSchedulerBaseConfig()
.withName(schedulersNamePrefix + "." + CPU_LITE.name())),
() -> muleContext.getSchedulerService()
.ioScheduler(muleContext.getSchedulerBaseConfig()
.withName(schedulersNamePrefix + "." + BLOCKING.name())),
() -> muleContext.getSchedulerService()
.cpuIntensiveScheduler(muleContext.getSchedulerBaseConfig()
.withName(schedulersNamePrefix + "." + CPU_INTENSIVE.name())),
getMaxConcurrency());
}
}
static class ProactorStreamProcessingStrategy extends ReactorStreamProcessingStrategy {
private Supplier<Scheduler> blockingSchedulerSupplier;
private Supplier<Scheduler> cpuIntensiveSchedulerSupplier;
private Scheduler blockingScheduler;
private Scheduler cpuIntensiveScheduler;
public ProactorStreamProcessingStrategy(Supplier<Scheduler> ringBufferSchedulerSupplier,
int bufferSize,
int subscriberCount,
String waitStrategy,
Supplier<Scheduler> cpuLightSchedulerSupplier,
Supplier<Scheduler> blockingSchedulerSupplier,
Supplier<Scheduler> cpuIntensiveSchedulerSupplier,
int maxConcurrency)
{
super(ringBufferSchedulerSupplier, bufferSize, subscriberCount, waitStrategy, cpuLightSchedulerSupplier, maxConcurrency);
this.blockingSchedulerSupplier = blockingSchedulerSupplier;
this.cpuIntensiveSchedulerSupplier = cpuIntensiveSchedulerSupplier;
}
@Override
public void start() throws MuleException {
super.start();
this.blockingScheduler = blockingSchedulerSupplier.get();
this.cpuIntensiveScheduler = cpuIntensiveSchedulerSupplier.get();
}
@Override
public void stop() throws MuleException {
if (blockingScheduler != null) {
blockingScheduler.stop();
}
if (cpuIntensiveScheduler != null) {
cpuIntensiveScheduler.stop();
}
super.stop();
}
@Override
public ReactiveProcessor onProcessor(ReactiveProcessor processor) {
if (processor.getProcessingType() == BLOCKING && maxConcurrency > subscribers) {
return proactor(processor, blockingScheduler);
} else if (processor.getProcessingType() == CPU_INTENSIVE && maxConcurrency > subscribers) {
return proactor(processor, cpuIntensiveScheduler);
} else {
return super.onProcessor(processor);
}
}
private ReactiveProcessor proactor(ReactiveProcessor processor, Scheduler scheduler) {
return publisher -> from(publisher)
.flatMap(event -> just(event).transform(processor)
.publishOn(fromExecutorService(decorateScheduler(getCpuLightScheduler())))
.subscribeOn(fromExecutorService(decorateScheduler(scheduler))), maxConcurrency);
}
}
}