All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alipay.sofa.common.thread.ThreadPoolMonitorRunner Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) 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 com.alipay.sofa.common.thread;

import com.alipay.sofa.common.thread.log.ThreadLogger;
import com.alipay.sofa.common.tracer.TracerIdAdapter;

import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Map;

/**
 * The runner to monitor the thread pool
 * @author huzijie
 * @version TheadPoolRunner.java, v 0.1 2020年10月26日 4:42 下午 huzijie Exp $
 */
public class ThreadPoolMonitorRunner implements Runnable {
    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(
                                                           "yyyy-MM-dd HH:mm:ss,SSS").withZone(
                                                           ZoneId.systemDefault());
    private final ThreadPoolConfig         config;
    private final ThreadPoolStatistics     statistics;

    public ThreadPoolMonitorRunner(ThreadPoolConfig threadPoolConfig,
                                   ThreadPoolStatistics threadPoolStatistics) {
        this.statistics = threadPoolStatistics;
        this.config = threadPoolConfig;
    }

    @Override
    public void run() {
        try {
            if (ThreadPoolGovernor.getInstance().isGlobalMonitorLoggable()) {
                int decayedTaskCount = 0;
                for (Map.Entry entry : statistics.getExecutingTasks()
                    .entrySet()) {
                    decayedTaskCount = calculateDecayedTaskCounts(decayedTaskCount, entry);
                }

                // threadPoolName, #queue, #executing, #idle, #pool, #decayed
                ThreadLogger.info("Thread pool '{}' info: [{},{},{},{},{}]", config.getIdentity(),
                    statistics.getQueueSize(), statistics.getExecutingTasks().size(),
                    statistics.getPoolSize() - statistics.getExecutingTasks().size(),
                    statistics.getPoolSize(), decayedTaskCount);
                if (statistics.getTotalTaskCount() != 0) {
                    // just log for thread pool which has task executed
                    // SofaScheduledThreadPoolExecutor don't count the in queue time, it's always 0
                    // threadPoolName, #averageStayInQueueTime, #averageRunningTime
                    ThreadLogger.info("Thread pool '{}' average static info: [{},{}]",
                        config.getIdentity(), statistics.getAverageStayInQueueTime(),
                        statistics.getAverageRunningTime());
                    statistics.resetAverageStatics();
                }
            }
        } catch (Throwable e) {
            ThreadLogger.warn("ThreadPool '{}' is interrupted when running: {}",
                this.config.getIdentity(), e);
        }
    }

    /**
     * Calculate the count of decayed tasks
     * @param decayedTaskCount the count to update
     * @param entry the executing tasks
     * @return the updated count
     */
    private int calculateDecayedTaskCounts(int decayedTaskCount,
                                           Map.Entry entry) {
        ExecutingRunnable task = entry.getKey();
        // SofaThreadPoolExecutor use task.getDequeueTime(), SofaScheduledThreadPoolExecutor use entry.getValue()
        long executionTime = System.currentTimeMillis()
                             - (task.getDequeueTime() == 0 ? entry.getValue() : task
                                 .getDequeueTime());

        if (executionTime >= config.getTaskTimeoutMilli()) {
            ++decayedTaskCount;
            printStackTrace(task, entry.getKey().getThread());
        }
        return decayedTaskCount;
    }

    /**
     * Print the decayed task's stack trace
     * @param task the decayed task
     * @param executingThread the execute thread of the task
     */
    private void printStackTrace(ExecutingRunnable task, Thread executingThread) {
        if (!task.isPrinted()) {
            task.setPrinted(true);
            StringBuilder sb = new StringBuilder();
            for (StackTraceElement e : executingThread.getStackTrace()) {
                sb.append("    ").append(e).append("\n");
            }
            String traceId = TracerIdAdapter.getInstance().traceIdSafari(executingThread);
            try {
                ThreadLogger
                    .warn(
                        "Task {} in thread pool {} started on {}{} exceeds the limit of {} execution time with stack trace:\n    {}",
                        task, config.getIdentity(), DATE_FORMAT.format(Instant.ofEpochMilli(task
                            .getDequeueTime())), traceId == null ? "" : " with traceId " + traceId,
                        config.getTaskTimeout() + config.getTimeUnit().toString(), sb.toString()
                            .trim());
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy