聊聊cheddar的MessageHandlerExecutor

本文主要研究一下cheddar的MessageHandlerExecutor

MessageHandlerExecutor

Cheddar/cheddar/cheddar-messaging/src/main/java/com/clicktravel/cheddar/infrastructure/messaging/pooled/listener/MessageHandlerExecutor.java

public class MessageHandlerExecutor extends ThreadPoolExecutor {

    public MessageHandlerExecutor(final String queueName, final int numThreads) {
        super(numThreads, numThreads, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
                new MessageHandlerWorkerThreadFactory(queueName));
    }

}

MessageHandlerExecutor继承了ThreadPoolExecutor,其使用的queue为LinkedBlockingQueue;threadFactory为MessageHandlerWorkerThreadFactory

MessageHandlerWorkerThreadFactory

Cheddar/cheddar/cheddar-messaging/src/main/java/com/clicktravel/cheddar/infrastructure/messaging/pooled/listener/MessageHandlerWorkerThreadFactory.java

public class MessageHandlerWorkerThreadFactory implements ThreadFactory {

    private final AtomicInteger threadSequenceNumber = new AtomicInteger();
    private final String queueName;

    public MessageHandlerWorkerThreadFactory(final String queueName) {
        this.queueName = queueName;
    }

    @Override
    public Thread newThread(final Runnable r) {
        final int seq = threadSequenceNumber.incrementAndGet();
        return new Thread(r, "MessageHandler:" + queueName + ":" + seq);
    }
}

MessageHandlerWorkerThreadFactory实现了ThreadFactory接口,其newThread方法使用queueName及threadSequenceNumber来命名thread

MessageHandlerWorker

Cheddar/cheddar/cheddar-messaging/src/main/java/com/clicktravel/cheddar/infrastructure/messaging/pooled/listener/MessageHandlerWorker.java

public class MessageHandlerWorker<T extends Message> implements Runnable {

    private final Logger logger = LoggerFactory.getLogger(getClass());
    private final T message;
    private final MessageHandler<T> messageHandler;
    private final PooledMessageListener<T> pooledMessageListener;

    public MessageHandlerWorker(final PooledMessageListener<T> pooledMessageListener, final T message,
            final MessageHandler<T> messageHandler) {
        this.message = message;
        this.messageHandler = messageHandler;
        this.pooledMessageListener = pooledMessageListener;
    }

    @Override
    public void run() {
        try {
            messageHandler.handle(message);
        } catch (final Exception e) {
            logger.error("Error handling message: " + message, e);
        } finally {
            try {
                pooledMessageListener.completeMessageProcessing(message);
            } catch (final InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

}

MessageHandlerWorker实现了Runnable接口,其run方法执行messageHandler.handle方法,最后执行pooledMessageListener.completeMessageProcessing(message)

PooledMessageListener

Cheddar/cheddar/cheddar-messaging/src/main/java/com/clicktravel/cheddar/infrastructure/messaging/pooled/listener/PooledMessageListener.java

public abstract class PooledMessageListener<T extends Message> implements MessageListener, Runnable {

    /**
     * Maximum number of messages to receive from the queue at a time. Using larger numbers decreases the number of
     * calls to receive and thus increases throughput, at the possible expense of latency.
     */
    protected static final int DEFAULT_MAX_RECEIVED_MESSAGES = 10;

    /**
     * Controls when messages are received from the queue by setting an ideal minimum number of runnable tasks for each
     * thread. This minimum includes the currently executing tasks and those on the thread pool work queue. When the
     * number of runnable tasks dips below the ideal, more messages are received.
     */
    protected static final int IDEAL_RUNNABLES_PER_THREAD = 2; // Each thread has 1 executing + 1 queued runnable

    /**
     * The default number of worker threads to use in a fixed size thread pool
     */
    protected static final int DEFAULT_NUM_WORKER_THREADS = 10;

    /**
     * Maximum duration (in seconds) to wait for messages on the queue during normal processing. If there is at least
     * one message on the queue, the actual duration will be shorter.
     */
    private static final int LONG_POLL_DURATION_SECONDS = 20;

    /**
     * Maximum duration (in seconds) to wait for messages on the queue during handing over to a new application instance
     * in a blue-green deployment. This is shorter to enable prompt termination of this message processor.
     */
    private static final int SHORT_POLL_DURATION_SECONDS = 2;

    /**
     * Time (in milliseconds) to pause when receive message request returns an error
     */
    private static final long RECEIVE_MESSAGE_ERROR_PAUSE_MILLIS = 500;

    /**
     * Maximum number of attempts to delete message from queue
     */
    private static final int MAX_DELETE_MESSAGE_ATTEMPTS = 5;

    /**
     * Time (in milliseconds) to pause when delete message request returns an error
     */
    private static final long DELETE_MESSAGE_ERROR_PAUSE_MILLIS = 1500;

    private final Logger logger = LoggerFactory.getLogger(getClass());
    private final MessageQueue<T> messageQueue;
    private final ThreadPoolExecutor threadPoolExecutor;
    private final RateLimiter rateLimiter;
    private final Semaphore semaphore;
    private final int maxReceivedMessages;
    private volatile boolean started;
    private volatile boolean shutdownRequested;
    private volatile boolean shutdownRequestImminent;

    public PooledMessageListener(final MessageQueue<T> messageQueue, final RateLimiter rateLimiter,
            final ThreadPoolExecutor threadPoolExecutor, final Semaphore semaphore, final int maxReceivedMessages) {
        this.messageQueue = messageQueue;
        this.rateLimiter = rateLimiter;
        this.threadPoolExecutor = threadPoolExecutor;
        this.semaphore = semaphore;
        this.maxReceivedMessages = maxReceivedMessages;
    }

    protected abstract MessageHandler<T> getHandlerForMessage(T message);

    protected abstract void listenerStarted();

    @Override
    public void start() {
        new Thread(this).start();
    }

    @Override
    public void run() {
        try {
            started = true;
            listenerStarted();
            final String limiterSummary = rateLimiter != null ? ("using " + rateLimiter.toString())
                    : "not rate limited";
            logger.debug(String.format("Listener for queue [%s] has pool of %d threads and is %s", queueName(),
                    threadPoolExecutor.getMaximumPoolSize(), limiterSummary));
            processMessagesUntilShutdownRequested();
        } catch (final InterruptedException e) {
            Thread.currentThread().interrupt();
        } catch (final Throwable e) {
            logger.error(e.getMessage(), e);
            throw e;
        } finally {
            logger.debug(String.format(
                    "Message listener for queue [%s] has stopped receiving messages. Initiating shutdown of task executor",
                    queueName()));
            threadPoolExecutor.shutdown();
        }
    }

    private void processMessagesUntilShutdownRequested() throws InterruptedException {
        while (!shutdownRequested) {
            // Block until there is capacity to handle up to maxReceivedMessages
            semaphore.acquire(maxReceivedMessages);
            List<T> messages = Collections.emptyList();
            try {
                if (!shutdownRequested) {
                    final int pollSeconds = shutdownRequestImminent ? SHORT_POLL_DURATION_SECONDS
                            : LONG_POLL_DURATION_SECONDS;
                    try {
                        messages = messageQueue.receive(pollSeconds, maxReceivedMessages);
                    } catch (final MessageReceiveException e) {
                        logger.warn("Error receiving messages on queue:[" + queueName() + "]", e);
                        Thread.sleep(RECEIVE_MESSAGE_ERROR_PAUSE_MILLIS);
                    }
                }
            } finally {
                // Release over-allocated permits
                semaphore.release(maxReceivedMessages - messages.size());
            }
            for (final T message : messages) {
                processMessage(message); // Must complete processing each message to release permit
            }
        }
    }

    /**
     * Processes a message by getting the appropriate message handler and scheduling a task to execute the handler. In
     * case of problems (e.g. the message cannot be parsed), the message processing is completed to ensure the message
     * is deleted from the queue and the associated permit is released.
     * @param message {@link Message} to process
     */
    private void processMessage(final T message) throws InterruptedException {
        boolean workerAssigned = false;
        try {
            final MessageHandler<T> messageHandler = getHandlerForMessage(message);
            if (messageHandler != null) {
                applyRateLimiter();
                threadPoolExecutor.execute(new MessageHandlerWorker<T>(this, message, messageHandler));
                workerAssigned = true;
            }
        } catch (final Exception e) {
            logger.error("Unable to process received message", e);
        }

        if (!workerAssigned) {
            completeMessageProcessing(message);
        }
    }

    /**
     * Completes message processing by deleting it from the queue and releasing the associated permit.
     * @param message {@link Message} to complete processing
     */
    public void completeMessageProcessing(final T message) throws InterruptedException {
        deleteMessage(message);
        semaphore.release();
    }

    private void deleteMessage(final T message) throws InterruptedException {
        for (int attempts = 0; attempts < MAX_DELETE_MESSAGE_ATTEMPTS; attempts++) {
            try {
                messageQueue.delete(message);
                return;
            } catch (final MessageDeleteException e) {
                logger.warn(String.format("Failed attempt to delete message with id [%s] from queue [%s]",
                        message.getMessageId(), queueName()), e);
                Thread.sleep(DELETE_MESSAGE_ERROR_PAUSE_MILLIS);
            }
        }
        logger.error(String.format("Failed all attempts to delete message with id [%s] from queue [%s]",
                message.getMessageId(), queueName()));
    }

    private void applyRateLimiter() {
        if (rateLimiter != null) {
            try {
                rateLimiter.takeToken();
            } catch (final InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    protected String queueName() {
        return messageQueue.getName();
    }

    @Override
    public void prepareForShutdown() {
        logger.debug(String.format(
                "Message listener for queue [%s] is preparing for imminent shutdown. Reducing queue poll time.",
                queueName()));
        shutdownRequestImminent = true;
    }

    @Override
    public void shutdownListener() {
        logger.debug(String.format("Message listener for queue [%s] is shutting down", queueName()));
        shutdownRequested = true;
        if (!started) {
            threadPoolExecutor.shutdown();
        }
    }

    @Override
    public boolean awaitShutdownComplete(final long timeoutMillis) {
        boolean terminated = false;
        try {
            terminated = threadPoolExecutor.awaitTermination(timeoutMillis, TimeUnit.MILLISECONDS);
            if (terminated) {
                logger.debug(String.format("Message listener for queue [%s] shutdown has completed", queueName()));
            } else {
                logger.warn(String.format(
                        "Message listener for queue [%s] has not shutdown as message handler worker threads have not completed",
                        queueName()));
            }
        } catch (final InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return terminated;
    }
}

PooledMessageListener的run方法先标记started为true,然后执行processMessagesUntilShutdownRequested;processMessagesUntilShutdownRequested方法循环messageQueue.receive(pollSeconds, maxReceivedMessages)拉取消息;然后遍历执行processMessage;processMessage方法先获取messageHandler,然后创建MessageHandlerWorker放到线程池执行

小结

cheddar的MessageHandlerExecutor继承了ThreadPoolExecutor,其使用的queue为LinkedBlockingQueue;threadFactory为MessageHandlerWorkerThreadFactory;MessageHandlerWorker实现了Runnable接口,其run方法执行messageHandler.handle方法,最后执行pooledMessageListener.completeMessageProcessing(message)。

doc

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
不使用LINQ查询和操作集合 改进代码 namespace SandwichCalories { class Program { static void Main(string[] args) { // sandwich ingredients and their associated calories Dictionary<string, int> ingredients = new Dictionary<string, int>() { { "Bread", 66 }, { "Ham", 72 }, { "Bologna", 57 }, { "Chicken", 17 }, { "Corned Beef", 53 }, { "Salami", 40 }, { "Cheese, American", 104 }, { "Cheese, Cheddar", 113 }, { "Cheese, Havarti", 105 }, { "Mayonnaise", 94 }, { "Mustard", 10 }, { "Butter", 102 }, { "Garlic Aioli", 100 }, { "Sriracha", 15 }, { "Dressing, Ranch", 73 }, { "Dressing, 1000 Island", 59 }, { "Lettuce", 5 }, { "Tomato", 4 }, { "Cucumber", 4 }, { "Banana Pepper", 10 }, { "Green Pepper", 3 }, { "Red Onion", 6 }, { "Spinach", 7 }, { "Avocado", 64 } }; // prompt user for calorie range Console.Write("Enter minimum calories: "); int min_calories = int.Parse(Console.ReadLine()); Console.Write("Enter maximum calories: "); int max_calories = int.Parse(Console.ReadLine()); // calculate the minimum and maximum calories for the sandwich int min_sandwich_calories = 2 * ingredients["Bread"] + ingredients.Values.Min() * 2; int max_sandwich_calories = 2 * ingredients["Bread"] + ingredients.Values.Max() * 2; // check if the calorie range is valid if (max_calories < min_sandwich_calories) { Console.WriteLine("Sorry, it is impossible to create a sandwich within the given calorie range."); } else { // create the sandwich List<string> sandwich = new List<string> { "Bread" }; int sandwich_calories = 1 * ingredients["Bread"]; while (sandwich_calories < min_calories) { // add random ingredient string ingredient = ingredients.Keys.ElementAt(new Random().Next(ingredients.Count)); sandwich.Add(ingredient); sandwich_calories += ingredients[ingredient]; } while (sandwich_calories <= max_calories) { // add random ingredient string ingredient = ingredients.Keys.ElementAt(new Random().Next(ingredients.Count)); // check if the ingredient is the same as the previous one if (sandwich.Count >= 3 && ingredient == sandwich[sandwich.Count - 2]) { continue; } sandwich.Add(ingredient); sandwich_calories += ingredients[ingredient]; // check if the sandwich is already at the maximum calorie limit if (sandwich_calories == max_sandwich_calories) { break; } } // add the last slice of bread sandwich.Add("Bread"); // print the sandwich and its total calories Console.WriteLine("Your sandwich: " + string.Join(", ", sandwich)); Console.WriteLine("Total calories: " + sandwich_calories); } } } }
06-10

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值