Springboot adds tp90 monitoring

  springboot

tp90

For the concept of tp90, seeWhat performance indexes do tp90 and tp99 refer to?

Springboot configuration

Springboot can be done with metrics.

@Order(Ordered.HIGHEST_PRECEDENCE)
public final class DropwizardMetricsFilter extends OncePerRequestFilter {

    private static final String ATTRIBUTE_STOP_WATCH = DropwizardMetricsFilter.class.getName()
            + ".StopWatch";

    private static final int UNDEFINED_HTTP_STATUS = 999;

    private static final String UNKNOWN_PATH_SUFFIX = "/unmapped";

    private static final Log logger = LogFactory.getLog(DropwizardMetricsFilter.class);

    private static final Set<PatternReplacer> STATUS_REPLACERS;

    static {
        Set<PatternReplacer> replacements = new LinkedHashSet<>();
        replacements.add(new PatternReplacer("[{}]", 0, "-"));
        replacements.add(new PatternReplacer("**", Pattern.LITERAL, "-star-star-"));
        replacements.add(new PatternReplacer("*", Pattern.LITERAL, "-star-"));
        replacements.add(new PatternReplacer("/-", Pattern.LITERAL, "/"));
        replacements.add(new PatternReplacer("-/", Pattern.LITERAL, "/"));
        STATUS_REPLACERS = Collections.unmodifiableSet(replacements);
    }

    private static final Set<PatternReplacer> KEY_REPLACERS;

    static {
        Set<PatternReplacer> replacements = new LinkedHashSet<PatternReplacer>();
        replacements.add(new PatternReplacer("/", Pattern.LITERAL, "."));
        replacements.add(new PatternReplacer("..", Pattern.LITERAL, "."));
        KEY_REPLACERS = Collections.unmodifiableSet(replacements);
    }

    private final MetricRegistry metricRegistry;

    public DropwizardMetricsFilter(MetricRegistry metricRegistry) {
        this.metricRegistry = metricRegistry;
    }

    @Override
    protected boolean shouldNotFilterAsyncDispatch() {
        return false;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        StopWatch stopWatch = createStopWatchIfNecessary(request);
        String path = new UrlPathHelper().getPathWithinApplication(request);
        int status = HttpStatus.INTERNAL_SERVER_ERROR.value();
        try {
            chain.doFilter(request, response);
            status = getStatus(response);
        } finally {
            if (!request.isAsyncStarted()) {
                stopWatch.stop();
                request.removeAttribute(ATTRIBUTE_STOP_WATCH);
                recordTime(request, path, status, stopWatch.getTotalTimeMillis());
            }
        }
    }

    private StopWatch createStopWatchIfNecessary(HttpServletRequest request) {
        StopWatch stopWatch = (StopWatch) request.getAttribute(ATTRIBUTE_STOP_WATCH);
        if (stopWatch == null) {
            stopWatch = new StopWatch();
            stopWatch.start();
            request.setAttribute(ATTRIBUTE_STOP_WATCH, stopWatch);
        }
        return stopWatch;
    }

    private int getStatus(HttpServletResponse response) {
        try {
            return response.getStatus();
        } catch (Exception ex) {
            return UNDEFINED_HTTP_STATUS;
        }
    }

    private void recordTime(HttpServletRequest request, String path, int status, long time) {
        String suffix = getFinalStatus(request, path, status);
        String timerName = getKey("timer." + request.getMethod() + "." + status + suffix);
        try {
            Timer timer = this.metricRegistry.timer(timerName);
            timer.update(time, TimeUnit.MILLISECONDS);
        } catch (Exception ex) {
            logger.warn("Unable to submit timer '" + timerName + "'", ex);
        }
    }

    private String getFinalStatus(HttpServletRequest request, String path, int status) {
        Object bestMatchingPattern = request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
        if (bestMatchingPattern != null) {
            return fixSpecialCharacters(bestMatchingPattern.toString());
        }
        HttpStatus.Series series = getSeries(status);
        if (HttpStatus.Series.CLIENT_ERROR.equals(series) || HttpStatus.Series.REDIRECTION.equals(series)) {
            return UNKNOWN_PATH_SUFFIX;
        }
        return path;
    }

    private String fixSpecialCharacters(String value) {
        String result = value;
        for (PatternReplacer replacer : STATUS_REPLACERS) {
            result = replacer.apply(result);
        }
        if (result.endsWith("-")) {
            result = result.substring(0, result.length() - 1);
        }
        if (result.startsWith("-")) {
            result = result.substring(1);
        }
        return result;
    }

    private HttpStatus.Series getSeries(int status) {
        try {
            return HttpStatus.valueOf(status).series();
        } catch (Exception ex) {
            return null;
        }
    }

    private String getKey(String string) {
        // graphite compatible metric names
        String key = string;
        for (PatternReplacer replacer : KEY_REPLACERS) {
            key = replacer.apply(key);
        }
        if (key.endsWith(".")) {
            key = key + "root";
        }
        if (key.startsWith("_")) {
            key = key.substring(1);
        }
        return key;
    }

    private static class PatternReplacer {

        private final Pattern pattern;
        private final String replacement;

        PatternReplacer(String regex, int flags, String replacement) {
            this.pattern = Pattern.compile(regex, flags);
            this.replacement = replacement;
        }

        public String apply(String input) {
            return this.pattern.matcher(input)
                    .replaceAll(Matcher.quoteReplacement(this.replacement));
        }

    }
}

Disable native filter

@SpringBootApplication(exclude = MetricFilterAutoConfiguration.class)

Enable DropwizardMetricsFilter

@Bean
    public DropwizardMetricsFilter metricFilter(MetricRegistry metricRegistry) {
        return new DropwizardMetricsFilter(metricRegistry);
    }

Screenshots

clipboard.png

Problem

One problem is that using graphite takes up too much disk space, running thousands of requests and taking up more than 1G at a time, so I haven’t found out how to clean it regularly for the time being. At present, this plan is still not suitable for production environment, so as not to explode the disk. This kind of performance monitoring is still more suitable for big data.

docs