
org.symphonyoss.s2.fugue.pubsub.AbstractPullSubscriberManager Maven / Gradle / Ivy
/*
*
*
* Copyright 2018 Symphony Communication Services, LLC.
*
* Licensed to The Symphony Software Foundation (SSF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.symphonyoss.s2.fugue.pubsub;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.symphonyoss.s2.common.concurrent.NamedThreadFactory;
import org.symphonyoss.s2.common.fault.ProgramFault;
import org.symphonyoss.s2.fugue.Fugue;
import org.symphonyoss.s2.fugue.config.IConfiguration;
import org.symphonyoss.s2.fugue.counter.IBusyCounter;
import org.symphonyoss.s2.fugue.counter.ITopicBusyCounterFactory;
import org.symphonyoss.s2.fugue.naming.Name;
import org.symphonyoss.s2.fugue.naming.SubscriptionName;
/**
* Base class for synchronous pull type implementations.
*
* @author Bruce Skingle
*
* @param Type of concrete manager, needed for fluent methods.
*/
public abstract class AbstractPullSubscriberManager> extends AbstractSubscriberManager
{
private static final Logger log_ = LoggerFactory
.getLogger(AbstractPullSubscriberManager.class);
private final ITopicBusyCounterFactory busyCounterFactory_;
private int subscriberThreadPoolSize_;
private int handlerThreadPoolSize_;
private final LinkedBlockingQueue executorQueue_ = new LinkedBlockingQueue();
private final LinkedBlockingQueue handlerQueue_ = new LinkedBlockingQueue();
private ThreadPoolExecutor subscriberExecutor_;
private ThreadPoolExecutor handlerExecutor_;
protected AbstractPullSubscriberManager(Class type, Builder,P,T> builder)
{
super(type, builder);
busyCounterFactory_ = builder.busyCounterFactory_;
IConfiguration subscriberConfig = config_.getConfiguration(builder.getConfigPath());
subscriberThreadPoolSize_ = subscriberConfig.getInt("subscriberThreadPoolSize", 4);
handlerThreadPoolSize_ = subscriberConfig.getInt("handlerThreadPoolSize", 9 * subscriberThreadPoolSize_);
// subscriberThreadPoolSize_ = 4; //8 * getTotalSubscriptionCnt();
}
/**
* Builder.
*
* @author Bruce Skingle
*
* @param The concrete type returned by fluent methods.
* @param The concrete type of the built object.
*/
public static abstract class Builder, P, B extends AbstractPullSubscriberManager>
extends AbstractSubscriberManager.Builder
implements IPullSubscriberManagerBuilder
{
private ITopicBusyCounterFactory busyCounterFactory_;
protected Builder(Class type)
{
super(type);
}
@Override
public T withBusyCounterFactory(ITopicBusyCounterFactory busyCounterFactory)
{
busyCounterFactory_ = busyCounterFactory;
return self();
}
protected abstract String getConfigPath();
}
protected ITopicBusyCounterFactory getBusyCounterFactory()
{
return busyCounterFactory_;
}
@Override
public void start()
{
if(totalSubscriptionCnt_ == 0)
{
log_.info("No subscriptions, not starting.");
return;
}
if(Fugue.isDebugSingleThread())
{
subscriberThreadPoolSize_ = totalSubscriptionCnt_ == 0 ? 1 : totalSubscriptionCnt_;
handlerThreadPoolSize_ = 1;
}
else
{
int min = getTotalSubscriptionCnt() * 2;
if(subscriberThreadPoolSize_ < min)
{
log_.warn("Configured for " + subscriberThreadPoolSize_ +
" subscriber threads for a total of " +
getTotalSubscriptionCnt() + " subscriptions, using " + min + " subscriber threads");
subscriberThreadPoolSize_ = min;
}
min = subscriberThreadPoolSize_ * 2;
if(handlerThreadPoolSize_ < min)
{
log_.warn("Configured for " + handlerThreadPoolSize_ + " handler threads for " +
subscriberThreadPoolSize_ + " subscriber treads, using " + min + " handler threads");
handlerThreadPoolSize_ = min;
}
}
log_.info("Starting AbstractPullSubscriberManager with " + subscriberThreadPoolSize_ +
" subscriber threads and " + handlerThreadPoolSize_ + " handler threads for a total of " +
getTotalSubscriptionCnt() + " subscriptions...");
subscriberExecutor_ = new ThreadPoolExecutor(subscriberThreadPoolSize_, subscriberThreadPoolSize_,
10000L, TimeUnit.MILLISECONDS,
executorQueue_, new NamedThreadFactory("PubSub-subscriber"));
handlerExecutor_ = new ThreadPoolExecutor(handlerThreadPoolSize_, handlerThreadPoolSize_,
10000L, TimeUnit.MILLISECONDS,
handlerQueue_, new NamedThreadFactory("PubSub-handler", true));
super.start();
}
@Override
protected void stopSubscriptions()
{
if(subscriberExecutor_ != null)
subscriberExecutor_.shutdown();
if(handlerExecutor_ != null)
handlerExecutor_.shutdown();
if(subscriberExecutor_ != null)
stop(subscriberExecutor_, 60);
if(handlerExecutor_ != null)
stop(handlerExecutor_, 10);
}
private void stop(ThreadPoolExecutor executor, int delay)
{
try {
// Wait a while for existing tasks to terminate
if (!executor.awaitTermination(delay, TimeUnit.SECONDS)) {
executor.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being cancelled
if (!executor.awaitTermination(delay, TimeUnit.SECONDS))
System.err.println("Pool did not terminate");
}
} catch (InterruptedException ie) {
// (Re-)Cancel if current thread also interrupted
executor.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
}
protected void submit(Runnable subscriber, boolean force)
{
if(force || executorQueue_.size() < subscriberThreadPoolSize_)
subscriberExecutor_.submit(subscriber);
}
protected void printQueueSize()
{
log_.debug("Queue size " + executorQueue_.size());
}
// public IBatch newBatch()
// {
// return new ExecutorBatch(handlerExecutor_);
// }
ThreadPoolExecutor getHandlerExecutor()
{
return handlerExecutor_;
}
protected IBusyCounter createBusyCounter(Name subscriptionName)
{
if(getBusyCounterFactory() == null)
return null;
if(subscriptionName instanceof SubscriptionName)
{
return getBusyCounterFactory().create(((SubscriptionName)subscriptionName).getTopicName().getTopicId());
}
throw new ProgramFault("BusyCounter can only be set with SubscriptionName");
}
}