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)