Talk about the Prometheus service of canal

Keywords: Programming Java jvm

order

This paper focuses on the Prometheus service of canal

CanalMetricsService

canal-1.1.4/server/src/main/java/com/alibaba/otter/canal/spi/CanalMetricsService.java

public interface CanalMetricsService {

    /**
     * Initialization on canal server startup.
     */
    void initialize();

    /**
     * Clean-up at canal server stop phase.
     */
    void terminate();

    /**
     * @return {@code true} if the metrics service is running, otherwise {@code false}.
     */
    boolean isRunning();

    /**
     * Register instance level metrics for specified instance.
     * @param instance {@link CanalInstance}
     */
    void register(CanalInstance instance);

    /**
     * Unregister instance level metrics for specified instance.
     * @param instance {@link CanalInstance}
     */
    void unregister(CanalInstance instance);

    /**
     * @param port server port for pull
     */
    void setServerPort(int port);

}
  • The CanalMetricsService interface defines the initialize, terminate, isRunning, register, unregister, setServerPort methods

PrometheusService

canal-1.1.4/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/PrometheusService.java

public class PrometheusService implements CanalMetricsService {

    private static final Logger          logger          = LoggerFactory.getLogger(PrometheusService.class);
    private final CanalInstanceExports   instanceExports;
    private volatile boolean             running         = false;
    private int                          port;
    private HTTPServer                   server;
    private final ClientInstanceProfiler clientProfiler;

    private PrometheusService() {
        this.instanceExports = CanalInstanceExports.instance();
        this.clientProfiler = PrometheusClientInstanceProfiler.instance();
    }

    private static class SingletonHolder {
        private static final PrometheusService SINGLETON = new PrometheusService();
    }

    public static PrometheusService getInstance() {
        return SingletonHolder.SINGLETON;
    }

    @Override
    public void initialize() {
        try {
            logger.info("Start prometheus HTTPServer on port {}.", port);
            //TODO 2.Https?
            server = new HTTPServer(port);
        } catch (IOException e) {
            logger.warn("Unable to start prometheus HTTPServer.", e);
            return;
        }
        try {
            // JVM exports
            DefaultExports.initialize();
            instanceExports.initialize();
            if (!clientProfiler.isStart()) {
                clientProfiler.start();
            }
            profiler().setInstanceProfiler(clientProfiler);
        } catch (Throwable t) {
            logger.warn("Unable to initialize server exports.", t);
        }

        running = true;
    }

    @Override
    public void terminate() {
        running = false;
        try {
            instanceExports.terminate();
            if (clientProfiler.isStart()) {
                clientProfiler.stop();
            }
            profiler().setInstanceProfiler(NOP);
            if (server != null) {
                server.stop();
            }
        } catch (Throwable t) {
            logger.warn("Something happened while terminating.", t);
        }
    }

    @Override
    public boolean isRunning() {
        return running;
    }

    @Override
    public void register(CanalInstance instance) {
        if (instance.isStart()) {
            logger.warn("Cannot register metrics for destination {} that is running.", instance.getDestination());
            return;
        }
        try {
            instanceExports.register(instance);
        } catch (Throwable t) {
            logger.warn("Unable to register instance exports for {}.", instance.getDestination(), t);
        }
        logger.info("Register metrics for destination {}.", instance.getDestination());
    }

    @Override
    public void unregister(CanalInstance instance) {
        if (instance.isStart()) {
            logger.warn("Try unregister metrics after destination {} is stopped.", instance.getDestination());
        }
        try {
            instanceExports.unregister(instance);
        } catch (Throwable t) {
            logger.warn("Unable to unregister instance exports for {}.", instance.getDestination(), t);
        }
        logger.info("Unregister metrics for destination {}.", instance.getDestination());
    }

    @Override
    public void setServerPort(int port) {
        this.port = port;
    }

}
  • PrometheusService implements the CanalMetricsService interface, and its constructor constructs CanalInstanceExports and ClientInstanceProfiler. Its initialize creates HTTP server, and then executes DefaultExports.initialize(), instanceExports.initialize(), clientProfiler.start(); its terminate method executes instanceExports.terminate() and clientProfiler.stop(); its register party Method executes instanceExports.register(instance); its unregister method executes instanceExports.unregister(instance)

DefaultExports

simpleclient_hotspot-0.4.0-sources.jar!/io/prometheus/client/hotspot/DefaultExports.java

public class DefaultExports {
  private static boolean initialized = false;
  /**
   * Register the default Hotspot collectors.
   */
  public static synchronized void initialize() {
    if (!initialized) {
      new StandardExports().register();
      new MemoryPoolsExports().register();
      new BufferPoolsExports().register();
      new GarbageCollectorExports().register();
      new ThreadExports().register();
      new ClassLoadingExports().register();
      new VersionInfoExports().register();
      initialized = true;
    }
  }

}
  • The initialize method of DefaultExports registers StandardExports, MemoryPoolsExports, BufferPoolsExports, garbage collectorexports, ThreadExports, ClassLoadingExports, VersionInfoExports

CanalInstanceExports

canal-1.1.4/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/CanalInstanceExports.java

public class CanalInstanceExports {

    private static final Logger      logger           = LoggerFactory.getLogger(CanalInstanceExports.class);
    public static final String       DEST             = "destination";
    public static final String[]     DEST_LABELS      = {DEST};
    public static final List<String> DEST_LABELS_LIST = Collections.singletonList(DEST);
    private final Collector          storeCollector;
    private final Collector          entryCollector;
    private final Collector          metaCollector;
    private final Collector          sinkCollector;
    private final Collector          parserCollector;

    private CanalInstanceExports() {
        this.storeCollector = StoreCollector.instance();
        this.entryCollector = EntryCollector.instance();
        this.metaCollector = MetaCollector.instance();
        this.sinkCollector = SinkCollector.instance();
        this.parserCollector = ParserCollector.instance();
    }

    private static class SingletonHolder {
        private static final CanalInstanceExports SINGLETON = new CanalInstanceExports();
    }

    public static CanalInstanceExports instance() {
        return SingletonHolder.SINGLETON;
    }

    public void initialize() {
        storeCollector.register();
        entryCollector.register();
        metaCollector.register();
        sinkCollector.register();
        parserCollector.register();
    }

    public void terminate() {
        CollectorRegistry.defaultRegistry.unregister(storeCollector);
        CollectorRegistry.defaultRegistry.unregister(entryCollector);
        CollectorRegistry.defaultRegistry.unregister(metaCollector);
        CollectorRegistry.defaultRegistry.unregister(sinkCollector);
        CollectorRegistry.defaultRegistry.unregister(parserCollector);
    }

    void register(CanalInstance instance) {
        requiredInstanceRegistry(storeCollector).register(instance);
        requiredInstanceRegistry(entryCollector).register(instance);
        requiredInstanceRegistry(metaCollector).register(instance);
        requiredInstanceRegistry(sinkCollector).register(instance);
        requiredInstanceRegistry(parserCollector).register(instance);
        logger.info("Successfully register metrics for instance {}.", instance.getDestination());
    }

    void unregister(CanalInstance instance) {
        requiredInstanceRegistry(storeCollector).unregister(instance);
        requiredInstanceRegistry(entryCollector).unregister(instance);
        requiredInstanceRegistry(metaCollector).unregister(instance);
        requiredInstanceRegistry(sinkCollector).unregister(instance);
        requiredInstanceRegistry(parserCollector).unregister(instance);
        logger.info("Successfully unregister metrics for instance {}.", instance.getDestination());
    }

    private InstanceRegistry requiredInstanceRegistry(Collector collector) {
        if (!(collector instanceof InstanceRegistry)) {
            throw new IllegalArgumentException("Canal prometheus collector need to implement InstanceRegistry.");
        }
        return (InstanceRegistry) collector;
    }

}
  • The constructor instance of CanalInstanceExports creates storeCollector, entryCollector, metaCollector, sinkCollector, parserCollector; its initialize method executes the register methods of these collectors; its register method registers instances to these collectors; its unregister method unregisters the instances from these collectors; its terminate method logs them Actor unregisters from CollectorRegistry.defaultRegistry

PrometheusClientInstanceProfiler

canal-1.1.4/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/PrometheusClientInstanceProfiler.java

public class PrometheusClientInstanceProfiler implements ClientInstanceProfiler {

    private static final long   NANO_PER_MILLI = 1000 * 1000L;
    private static final String PACKET_TYPE    = "canal_instance_client_packets";
    private static final String OUTBOUND_BYTES = "canal_instance_client_bytes";
    private static final String EMPTY_BATCHES  = "canal_instance_client_empty_batches";
    private static final String ERRORS         = "canal_instance_client_request_error";
    private static final String LATENCY        = "canal_instance_client_request_latency";
    private final Counter       outboundCounter;
    private final Counter       packetsCounter;
    private final Counter       emptyBatchesCounter;
    private final Counter       errorsCounter;
    private final Histogram     responseLatency;
    private volatile boolean    running        = false;

    private static class SingletonHolder {
        private static final PrometheusClientInstanceProfiler SINGLETON = new PrometheusClientInstanceProfiler();
    }

    public static PrometheusClientInstanceProfiler instance() {
        return SingletonHolder.SINGLETON;
    }

    private PrometheusClientInstanceProfiler() {
        this.outboundCounter = Counter.build()
                .labelNames(DEST_LABELS)
                .name(OUTBOUND_BYTES)
                .help("Total bytes sent to client.")
                .create();
        this.packetsCounter = Counter.build()
                .labelNames(new String[]{DEST, "packetType"})
                .name(PACKET_TYPE)
                .help("Total packets sent to client.")
                .create();
        this.emptyBatchesCounter = Counter.build()
                .labelNames(DEST_LABELS)
                .name(EMPTY_BATCHES)
                .help("Total empty batches sent to client.")
                .create();
        this.errorsCounter = Counter.build()
                .labelNames(new String[]{DEST, "errorCode"})
                .name(ERRORS)
                .help("Total client request errors.")
                .create();
        this.responseLatency = Histogram.build()
                .labelNames(DEST_LABELS)
                .name(LATENCY)
                .help("Client request latency.")
                // buckets in milliseconds
                .buckets(2.5, 10.0, 25.0, 100.0)
                .create();
    }

    @Override
    public void profiling(ClientRequestResult result) {
        String destination = result.getDestination();
        PacketType type = result.getType();
        outboundCounter.labels(destination).inc(result.getAmount());
        short errorCode = result.getErrorCode();
        if (errorCode > 0) {
            errorsCounter.labels(destination, Short.toString(errorCode)).inc();
        }
        long latency = result.getLatency();
        responseLatency.labels(destination).observe(((double) latency) / NANO_PER_MILLI);
        switch (type) {
            case GET:
                boolean empty = result.getEmpty();
                // Distinguish between empty packages
                if (empty) {
                    emptyBatchesCounter.labels(destination).inc();
                } else {
                    packetsCounter.labels(destination, type.name()).inc();
                }
                break;
            // reserve for others
            default:
                packetsCounter.labels(destination, type.name()).inc();
                break;
        }
    }

    @Override
    public void start() {
        if (outboundCounter != null) {
            outboundCounter.register();
        }
        if (packetsCounter != null) {
            packetsCounter.register();
        }
        if (emptyBatchesCounter != null) {
            emptyBatchesCounter.register();
        }
        if (errorsCounter != null) {
            errorsCounter.register();
        }
        if (responseLatency != null) {
            responseLatency.register();
        }
        running = true;
    }

    @Override
    public void stop() {
        running = false;
        if (outboundCounter != null) {
            CollectorRegistry.defaultRegistry.unregister(outboundCounter);
        }
        if (packetsCounter != null) {
            CollectorRegistry.defaultRegistry.unregister(packetsCounter);
        }
        if (emptyBatchesCounter != null) {
            CollectorRegistry.defaultRegistry.unregister(emptyBatchesCounter);
        }
        if (errorsCounter != null) {
            CollectorRegistry.defaultRegistry.unregister(errorsCounter);
        }
        if (responseLatency != null) {
            CollectorRegistry.defaultRegistry.unregister(responseLatency);
        }
    }

    @Override
    public boolean isStart() {
        return running;
    }
}
  • The constructor of PrometheusClientInstanceProfiler creates the metrics of outboundCounter, packetsCounter, emptyBatchesCounter, errorsCounter and responseLatency; its start method registers the metrics respectively; its stop method unregisters the metrics from CollectorRegistry.defaultRegistry; its profiling method updates the metrics according to clientrequesresult

Summary

PrometheusService implements the CanalMetricsService interface, and its constructor constructs CanalInstanceExports and ClientInstanceProfiler. Its initialize creates HTTP server, and then executes DefaultExports.initialize(), instanceExports.initialize(), clientProfiler.start(); its terminate method executes instanceExports.terminate() and clientProfiler.stop(); its register party Method executes instanceExports.register(instance); its unregister method executes instanceExports.unregister(instance)

doc

Posted by DataRater on Sat, 11 Apr 2020 07:29:43 -0700