Refactor metrics code.

This commit is contained in:
Chuanyi Li L
2018-08-09 23:54:00 +08:00
committed by Chuanyi Li
parent 75de57c102
commit fec9803aae
17 changed files with 476 additions and 333 deletions
@@ -1,31 +0,0 @@
package com.alibaba.otter.canal.common.utils;
/**
* 用于dump记录counter
* 这种场景下,
* <strong>写操作>>读操作</strong>
* dump线程为串行,读取counter时,并不严格要求最新值可见,使用普通long即可。
*
* @author Chuanyi Li
*/
public class SerializedLongAdder {
private long value = 0;
public SerializedLongAdder(long initial) {
this.value = initial;
}
public void add(long x) {
value += x;
}
public void increment() {
value ++;
}
public long get() {
return value;
}
}
@@ -9,7 +9,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import com.alibaba.otter.canal.parse.exception.PositionNotFoundException; import com.alibaba.otter.canal.parse.exception.PositionNotFoundException;
import com.alibaba.otter.canal.common.utils.SerializedLongAdder;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang.math.RandomUtils; import org.apache.commons.lang.math.RandomUtils;
@@ -1,8 +1,8 @@
package com.alibaba.otter.canal.parse.inbound.mysql; package com.alibaba.otter.canal.parse.inbound.mysql;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.concurrent.atomic.AtomicLong;
import com.alibaba.otter.canal.common.utils.SerializedLongAdder;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -22,24 +22,24 @@ import com.alibaba.otter.canal.protocol.position.EntryPosition;
public abstract class AbstractMysqlEventParser extends AbstractEventParser { public abstract class AbstractMysqlEventParser extends AbstractEventParser {
protected final Logger logger = LoggerFactory.getLogger(this.getClass()); protected final Logger logger = LoggerFactory.getLogger(this.getClass());
protected static final long BINLOG_START_OFFEST = 4L; protected static final long BINLOG_START_OFFEST = 4L;
protected TableMetaTSDBFactory tableMetaTSDBFactory = new DefaultTableMetaTSDBFactory(); protected TableMetaTSDBFactory tableMetaTSDBFactory = new DefaultTableMetaTSDBFactory();
protected boolean enableTsdb = false; protected boolean enableTsdb = false;
protected String tsdbSpringXml; protected String tsdbSpringXml;
protected TableMetaTSDB tableMetaTSDB; protected TableMetaTSDB tableMetaTSDB;
// 编码信息 // 编码信息
protected byte connectionCharsetNumber = (byte) 33; protected byte connectionCharsetNumber = (byte) 33;
protected Charset connectionCharset = Charset.forName("UTF-8"); protected Charset connectionCharset = Charset.forName("UTF-8");
protected boolean filterQueryDcl = false; protected boolean filterQueryDcl = false;
protected boolean filterQueryDml = false; protected boolean filterQueryDml = false;
protected boolean filterQueryDdl = false; protected boolean filterQueryDdl = false;
protected boolean filterRows = false; protected boolean filterRows = false;
protected boolean filterTableError = false; protected boolean filterTableError = false;
protected boolean useDruidDdlFilter = true; protected boolean useDruidDdlFilter = true;
private final SerializedLongAdder eventsPublishBlockingTime = new SerializedLongAdder(0L); private final AtomicLong eventsPublishBlockingTime = new AtomicLong(0L);
protected BinlogParser buildParser() { protected BinlogParser buildParser() {
LogEventConvert convert = new LogEventConvert(); LogEventConvert convert = new LogEventConvert();
@@ -207,8 +207,8 @@ public abstract class AbstractMysqlEventParser extends AbstractEventParser {
public void setTableMetaTSDBFactory(TableMetaTSDBFactory tableMetaTSDBFactory) { public void setTableMetaTSDBFactory(TableMetaTSDBFactory tableMetaTSDBFactory) {
this.tableMetaTSDBFactory = tableMetaTSDBFactory; this.tableMetaTSDBFactory = tableMetaTSDBFactory;
} }
public SerializedLongAdder getEventsPublishBlockingTime() { public AtomicLong getEventsPublishBlockingTime() {
return this.eventsPublishBlockingTime; return this.eventsPublishBlockingTime;
} }
@@ -7,8 +7,8 @@ import java.net.InetSocketAddress;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import com.alibaba.otter.canal.common.utils.SerializedLongAdder;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.lang.math.NumberUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -52,7 +52,7 @@ public class MysqlConnection implements ErosaConnection {
private AuthenticationInfo authInfo; private AuthenticationInfo authInfo;
protected int connTimeout = 5 * 1000; // 5秒 protected int connTimeout = 5 * 1000; // 5秒
protected int soTimeout = 60 * 60 * 1000; // 1小时 protected int soTimeout = 60 * 60 * 1000; // 1小时
private SerializedLongAdder receivedBinlogBytes; private AtomicLong receivedBinlogBytes;
public MysqlConnection(){ public MysqlConnection(){
} }
@@ -473,7 +473,7 @@ public class MysqlConnection implements ErosaConnection {
private void accumulateReceivedBytes(long x) { private void accumulateReceivedBytes(long x) {
if (receivedBinlogBytes != null) { if (receivedBinlogBytes != null) {
receivedBinlogBytes.add(x); receivedBinlogBytes.addAndGet(x);
} }
} }
@@ -618,7 +618,7 @@ public class MysqlConnection implements ErosaConnection {
this.authInfo = authInfo; this.authInfo = authInfo;
} }
public void setReceivedBinlogBytes(SerializedLongAdder receivedBinlogBytes) { public void setReceivedBinlogBytes(AtomicLong receivedBinlogBytes) {
this.receivedBinlogBytes = receivedBinlogBytes; this.receivedBinlogBytes = receivedBinlogBytes;
} }
@@ -10,7 +10,6 @@ import java.util.Map;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import com.alibaba.otter.canal.common.utils.SerializedLongAdder;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@@ -73,7 +72,7 @@ public class MysqlEventParser extends AbstractMysqlEventParser implements CanalE
private int dumpErrorCountThreshold = 2; // binlogDump失败异常计数阀值 private int dumpErrorCountThreshold = 2; // binlogDump失败异常计数阀值
// instance received binlog bytes // instance received binlog bytes
private final SerializedLongAdder receivedBinlogBytes = new SerializedLongAdder(0L); private final AtomicLong receivedBinlogBytes = new AtomicLong(0L);
protected ErosaConnection buildErosaConnection() { protected ErosaConnection buildErosaConnection() {
return buildMysqlConnection(this.runningInfo); return buildMysqlConnection(this.runningInfo);
@@ -516,7 +515,7 @@ public class MysqlEventParser extends AbstractMysqlEventParser implements CanalE
private Long findTransactionBeginPosition(ErosaConnection mysqlConnection, final EntryPosition entryPosition) private Long findTransactionBeginPosition(ErosaConnection mysqlConnection, final EntryPosition entryPosition)
throws IOException { throws IOException {
// 针对开始的第一条为非Begin记录,需要从该binlog扫描 // 针对开始的第一条为非Begin记录,需要从该binlog扫描
final AtomicLong preTransactionStartPosition = new AtomicLong(0L); final java.util.concurrent.atomic.AtomicLong preTransactionStartPosition = new java.util.concurrent.atomic.AtomicLong(0L);
mysqlConnection.reconnect(); mysqlConnection.reconnect();
mysqlConnection.seek(entryPosition.getJournalName(), 4L, new SinkFunction<LogEvent>() { mysqlConnection.seek(entryPosition.getJournalName(), 4L, new SinkFunction<LogEvent>() {
@@ -917,7 +916,7 @@ public class MysqlEventParser extends AbstractMysqlEventParser implements CanalE
public SerializedLongAdder getReceivedBinlogBytes() { public AtomicLong getReceivedBinlogBytes() {
return this.receivedBinlogBytes; return this.receivedBinlogBytes;
} }
@@ -2,9 +2,9 @@ package com.alibaba.otter.canal.parse.inbound.mysql;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.LockSupport;
import com.alibaba.otter.canal.common.utils.SerializedLongAdder;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import com.alibaba.otter.canal.common.AbstractCanalLifeCycle; import com.alibaba.otter.canal.common.AbstractCanalLifeCycle;
@@ -65,7 +65,7 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement
private ExecutorService stageExecutor; private ExecutorService stageExecutor;
private String destination; private String destination;
private volatile CanalParseException exception; private volatile CanalParseException exception;
private SerializedLongAdder eventsPublishBlockingTime; private AtomicLong eventsPublishBlockingTime;
public MysqlMultiStageCoprocessor(int ringBufferSize, int parserThreadCount, LogEventConvert logEventConvert, public MysqlMultiStageCoprocessor(int ringBufferSize, int parserThreadCount, LogEventConvert logEventConvert,
EventTransactionBuffer transactionBuffer, String destination){ EventTransactionBuffer transactionBuffer, String destination){
@@ -176,7 +176,7 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement
} }
disruptorMsgBuffer.publish(next); disruptorMsgBuffer.publish(next);
if (fullTimes > 0) { if (fullTimes > 0) {
eventsPublishBlockingTime.add(System.nanoTime() - blockingStart); eventsPublishBlockingTime.addAndGet(System.nanoTime() - blockingStart);
} }
break; break;
} catch (InsufficientCapacityException e) { } catch (InsufficientCapacityException e) {
@@ -457,7 +457,7 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement
this.connection = connection; this.connection = connection;
} }
public void setEventsPublishBlockingTime(SerializedLongAdder eventsPublishBlockingTime) { public void setEventsPublishBlockingTime(AtomicLong eventsPublishBlockingTime) {
this.eventsPublishBlockingTime = eventsPublishBlockingTime; this.eventsPublishBlockingTime = eventsPublishBlockingTime;
} }
@@ -2,9 +2,6 @@ package com.alibaba.otter.canal.prometheus;
import com.alibaba.otter.canal.instance.core.CanalInstance; import com.alibaba.otter.canal.instance.core.CanalInstance;
import com.alibaba.otter.canal.prometheus.impl.*; import com.alibaba.otter.canal.prometheus.impl.*;
import com.alibaba.otter.canal.sink.CanalEventSink;
import com.alibaba.otter.canal.sink.entry.EntryEventSink;
import com.alibaba.otter.canal.store.CanalStoreException;
import io.prometheus.client.Collector; import io.prometheus.client.Collector;
import io.prometheus.client.CollectorRegistry; import io.prometheus.client.CollectorRegistry;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -18,98 +15,71 @@ import java.util.List;
*/ */
public class CanalInstanceExports { public class CanalInstanceExports {
private static final Logger logger = LoggerFactory.getLogger(CanalInstanceExports.class); private static final Logger logger = LoggerFactory.getLogger(CanalInstanceExports.class);
public static final String DEST = "destination"; public static final String DEST = "destination";
public static final String[] DEST_LABELS = {DEST}; public static final String[] DEST_LABELS = {DEST};
public static final List<String> DEST_LABEL_LIST = Collections.singletonList(DEST); public static final List<String> DEST_LABELS_LIST = Collections.singletonList(DEST);
// private final String destination; private final Collector storeCollector;
private Collector storeCollector; private final Collector entryCollector;
private Collector delayCollector; private final Collector metaCollector;
private Collector metaCollector; private final Collector sinkCollector;
private Collector sinkCollector; private final Collector parserCollector;
private Collector parserCollector;
public CanalInstanceExports() { private CanalInstanceExports() {
// this.destination = instance.getDestination(); this.storeCollector = StoreCollector.instance();
// initEventsMetrics(instance); this.entryCollector = EntryCollector.instance();
// initStoreCollector(instance); this.metaCollector = MetaCollector.instance();
// initMetaCollector(instance); this.sinkCollector = SinkCollector.instance();
// initSinkCollector(instance); this.parserCollector = ParserCollector.instance();
// initParserCollector(instance);
} }
private static class SingletonHolder {
private static final CanalInstanceExports SINGLETON = new CanalInstanceExports();
}
public static CanalInstanceExports instance() {
return SingletonHolder.SINGLETON;
}
// static CanalInstanceExports forInstance(CanalInstance instance) { public void initialize() {
// return new CanalInstanceExports(instance); 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) { void register(CanalInstance instance) {
if (delayCollector != null) { requiredInstanceRegistry(storeCollector).register(instance);
delayCollector.register(); requiredInstanceRegistry(entryCollector).register(instance);
} requiredInstanceRegistry(metaCollector).register(instance);
if (storeCollector != null) { requiredInstanceRegistry(sinkCollector).register(instance);
storeCollector.register(); requiredInstanceRegistry(parserCollector).register(instance);
} logger.info("Successfully register metrics for instance {}.", instance.getDestination());
if (metaCollector != null) {
metaCollector.register();
}
if (sinkCollector != null) {
sinkCollector.register();
}
if (parserCollector != null) {
parserCollector.register();
}
} }
void unregister(CanalInstance instance) { void unregister(CanalInstance instance) {
if (delayCollector != null) { requiredInstanceRegistry(storeCollector).unregister(instance);
CollectorRegistry.defaultRegistry.unregister(delayCollector); requiredInstanceRegistry(entryCollector).unregister(instance);
} requiredInstanceRegistry(metaCollector).unregister(instance);
if (storeCollector != null) { requiredInstanceRegistry(sinkCollector).unregister(instance);
CollectorRegistry.defaultRegistry.unregister(storeCollector); requiredInstanceRegistry(parserCollector).unregister(instance);
} logger.info("Successfully unregister metrics for instance {}.", instance.getDestination());
if (metaCollector != null) {
CollectorRegistry.defaultRegistry.unregister(metaCollector);
}
if (sinkCollector != null) {
CollectorRegistry.defaultRegistry.unregister(sinkCollector);
}
if (parserCollector != null) {
CollectorRegistry.defaultRegistry.unregister(parserCollector);
}
} }
private void initEventsMetrics(CanalInstance instance) { private InstanceRegistry requiredInstanceRegistry(Collector collector) {
CanalEventSink sink = instance.getEventSink(); if (!(collector instanceof InstanceRegistry)) {
if (sink instanceof EntryEventSink) { throw new IllegalArgumentException("Canal collector need to implement InstanceRegistry.");
EntryEventSink entryEventSink = (EntryEventSink) sink;
// TODO ensure not to add handler again
// PrometheusCanalEventDownStreamHandler handler = new PrometheusCanalEventDownStreamHandler(destination);
// entryEventSink.addHandler(handler);
// delayCollector = handler.getCollector();
} else {
logger.warn("This impl register metrics for only EntryEventSink, skip.");
} }
return (InstanceRegistry) collector;
} }
// private void initStoreCollector(CanalInstance instance) {
// try {
// storeCollector = new StoreCollector(instance.getEventStore(), destination);
// } catch (CanalStoreException cse) {
// logger.warn("Failed to register metrics for destination {}.", destination, cse);
// }
// }
private void initMetaCollector(CanalInstance instance) {
metaCollector = new MetaCollector(instance);
}
private void initSinkCollector(CanalInstance instance) {
// sinkCollector = new EntryCollector(instance.getEventSink(), instance.getDestination());
}
private void initParserCollector(CanalInstance instance) {
parserCollector = new MysqlParserCollector(instance.getEventParser(), instance.getDestination());
}
} }
@@ -1,17 +0,0 @@
package com.alibaba.otter.canal.prometheus;
/**
* @author Chuanyi Li
*/
public class CanalServerExports {
private static boolean initialized = false;
public static synchronized void initialize() {
if (!initialized) {
initialized = true;
}
}
}
@@ -20,12 +20,13 @@ import static com.alibaba.otter.canal.server.netty.CanalServerWithNettyProfiler.
public class PrometheusService implements CanalMetricsService { public class PrometheusService implements CanalMetricsService {
private static final Logger logger = LoggerFactory.getLogger(PrometheusService.class); private static final Logger logger = LoggerFactory.getLogger(PrometheusService.class);
private final CanalInstanceExports instanceExports = new CanalInstanceExports(); private final CanalInstanceExports instanceExports;
private volatile boolean running = false; private volatile boolean running = false;
private HTTPServer server; private HTTPServer server;
private ClientInstanceProfiler clientProfiler = new PrometheusClientInstanceProfiler(); private ClientInstanceProfiler clientProfiler = new PrometheusClientInstanceProfiler();
private PrometheusService() { private PrometheusService() {
this.instanceExports = CanalInstanceExports.instance();
} }
private static class SingletonHolder { private static class SingletonHolder {
@@ -49,8 +50,7 @@ public class PrometheusService implements CanalMetricsService {
try { try {
// JVM exports // JVM exports
DefaultExports.initialize(); DefaultExports.initialize();
// Canal server level exports instanceExports.initialize();
CanalServerExports.initialize();
if (!clientProfiler.isStart()) { if (!clientProfiler.isStart()) {
clientProfiler.start(); clientProfiler.start();
} }
@@ -65,13 +65,17 @@ public class PrometheusService implements CanalMetricsService {
@Override @Override
public void terminate() { public void terminate() {
running = false; running = false;
instanceExports.unregister(); try {
if (clientProfiler.isStart()) { instanceExports.terminate();
clientProfiler.stop(); if (clientProfiler.isStart()) {
} clientProfiler.stop();
profiler().setInstanceProfiler(NOP); }
if (server != null) { profiler().setInstanceProfiler(NOP);
server.stop(); if (server != null) {
server.stop();
}
} catch (Throwable t) {
logger.warn("Something happened while terminating.", t);
} }
} }
@@ -87,9 +91,7 @@ public class PrometheusService implements CanalMetricsService {
return; return;
} }
try { try {
CanalInstanceExports export = CanalInstanceExports.forInstance(instance); instanceExports.register(instance);
export.register();
exports.put(instance.getDestination(), export);
} catch (Throwable t) { } catch (Throwable t) {
logger.warn("Unable to register instance exports for {}.", instance.getDestination(), t); logger.warn("Unable to register instance exports for {}.", instance.getDestination(), t);
} }
@@ -102,10 +104,7 @@ public class PrometheusService implements CanalMetricsService {
logger.warn("Try unregister metrics after destination {} is stopped.", instance.getDestination()); logger.warn("Try unregister metrics after destination {} is stopped.", instance.getDestination());
} }
try { try {
CanalInstanceExports export = exports.remove(instance.getDestination()); instanceExports.unregister(instance);
if (export != null) {
export.unregister();
}
} catch (Throwable t) { } catch (Throwable t) {
logger.warn("Unable to unregister instance exports for {}.", instance.getDestination(), t); logger.warn("Unable to unregister instance exports for {}.", instance.getDestination(), t);
} }
@@ -2,10 +2,12 @@ package com.alibaba.otter.canal.prometheus.impl;
import com.alibaba.otter.canal.instance.core.CanalInstance; import com.alibaba.otter.canal.instance.core.CanalInstance;
import com.alibaba.otter.canal.prometheus.InstanceRegistry; import com.alibaba.otter.canal.prometheus.InstanceRegistry;
import com.alibaba.otter.canal.sink.CanalEventDownStreamHandler;
import com.alibaba.otter.canal.sink.CanalEventSink; import com.alibaba.otter.canal.sink.CanalEventSink;
import com.alibaba.otter.canal.sink.entry.EntryEventSink; import com.alibaba.otter.canal.sink.entry.EntryEventSink;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import io.prometheus.client.Collector; import io.prometheus.client.Collector;
import io.prometheus.client.CounterMetricFamily;
import io.prometheus.client.GaugeMetricFamily; import io.prometheus.client.GaugeMetricFamily;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -17,27 +19,43 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import static com.alibaba.otter.canal.prometheus.CanalInstanceExports.DEST_LABEL_LIST; import static com.alibaba.otter.canal.prometheus.CanalInstanceExports.DEST_LABELS_LIST;
/** /**
* @author Chuanyi Li * @author Chuanyi Li
*/ */
public class EntryCollector extends Collector implements InstanceRegistry { public class EntryCollector extends Collector implements InstanceRegistry {
private static final Logger logger = LoggerFactory.getLogger(SinkCollector.class); private static final Logger logger = LoggerFactory.getLogger(SinkCollector.class);
private static final String DELAY_NAME = "canal_instance_traffic_delay"; private static final String DELAY = "canal_instance_traffic_delay";
private static final String delayHelpName = "Traffic delay of canal instance"; private static final String TRANSACTION = "canal_instance_transactions";
private final ConcurrentMap<String, EntryMetricsHolder> instances = new ConcurrentHashMap<String, EntryMetricsHolder>(); private static final String DELAY_HELP = "Traffic delay of canal instance";
private static final String TRANSACTION_HELP = "Transactions counter of canal instance";
private final ConcurrentMap<String, EntryMetricsHolder> instances = new ConcurrentHashMap<String, EntryMetricsHolder>();
private EntryCollector() {}
private static class SingletonHolder {
private static final EntryCollector SINGLETON = new EntryCollector();
}
public static EntryCollector instance() {
return SingletonHolder.SINGLETON;
}
@Override @Override
public List<MetricFamilySamples> collect() { public List<MetricFamilySamples> collect() {
List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>(); List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>();
GaugeMetricFamily delay = new GaugeMetricFamily(DELAY_NAME, GaugeMetricFamily delay = new GaugeMetricFamily(DELAY,
delayHelpName, DEST_LABEL_LIST); DELAY_HELP, DEST_LABELS_LIST);
CounterMetricFamily transactions = new CounterMetricFamily(TRANSACTION,
TRANSACTION_HELP, DEST_LABELS_LIST);
for (EntryMetricsHolder emh : instances.values()) { for (EntryMetricsHolder emh : instances.values()) {
delay.addMetric(emh.destLabelValues, emh.latestExecTime.doubleValue()); delay.addMetric(emh.destLabelValues, emh.latestExecTime.doubleValue());
transactions.addMetric(emh.destLabelValues, emh.transactionCounter.doubleValue());
} }
mfs.add(delay); mfs.add(delay);
mfs.add(transactions);
return mfs; return mfs;
} }
@@ -51,25 +69,61 @@ public class EntryCollector extends Collector implements InstanceRegistry {
throw new IllegalArgumentException("CanalEventSink must be EntryEventSink"); throw new IllegalArgumentException("CanalEventSink must be EntryEventSink");
} }
EntryEventSink entrySink = (EntryEventSink) sink; EntryEventSink entrySink = (EntryEventSink) sink;
PrometheusCanalEventDownStreamHandler handler = new PrometheusCanalEventDownStreamHandler(); PrometheusCanalEventDownStreamHandler handler = assembleHandler(entrySink);
holder.latestExecTime = handler.getLatestExecuteTime(); holder.latestExecTime = handler.getLatestExecuteTime();
entrySink.addHandler(handler, 0); holder.transactionCounter = handler.getTransactionCounter();
Preconditions.checkNotNull(holder.destLabelValues);
Preconditions.checkNotNull(holder.latestExecTime); Preconditions.checkNotNull(holder.latestExecTime);
EntryMetricsHolder old = instances.putIfAbsent(destination, holder); Preconditions.checkNotNull(holder.transactionCounter);
EntryMetricsHolder old = instances.put(destination, holder);
if (old != null) { if (old != null) {
logger.warn("Ignore repeated EntryCollector register for instance {}.", destination); logger.warn("Remove stale EntryCollector for instance {}.", destination);
} }
} }
@Override @Override
public void unregister(CanalInstance instance) { public void unregister(CanalInstance instance) {
final String destination = instance.getDestination(); final String destination = instance.getDestination();
CanalEventSink sink = instance.getEventSink();
if (!(sink instanceof EntryEventSink)) {
throw new IllegalArgumentException("CanalEventSink must be EntryEventSink");
}
unloadHandler((EntryEventSink) sink);
instances.remove(destination); instances.remove(destination);
} }
private PrometheusCanalEventDownStreamHandler assembleHandler(EntryEventSink entrySink) {
PrometheusCanalEventDownStreamHandler ph = new PrometheusCanalEventDownStreamHandler();
List<CanalEventDownStreamHandler> handlers = entrySink.getHandlers();
for (CanalEventDownStreamHandler handler : handlers) {
if (handler instanceof PrometheusCanalEventDownStreamHandler) {
throw new IllegalStateException("PrometheusCanalEventDownStreamHandler already exists in handlers.");
}
}
entrySink.addHandler(ph, 0);
return ph;
}
private void unloadHandler(EntryEventSink entrySink) {
List<CanalEventDownStreamHandler> handlers = entrySink.getHandlers();
int i = 0;
for (; i < handlers.size(); i++) {
if (handlers.get(i) instanceof PrometheusCanalEventDownStreamHandler) {
break;
}
}
entrySink.removeHandler(i);
// Ensure no PrometheusCanalEventDownStreamHandler
handlers = entrySink.getHandlers();
for (CanalEventDownStreamHandler handler : handlers) {
if (handler instanceof PrometheusCanalEventDownStreamHandler) {
throw new IllegalStateException("Multiple prometheusCanalEventDownStreamHandler exists in handlers.");
}
}
}
private class EntryMetricsHolder { private class EntryMetricsHolder {
private AtomicLong latestExecTime; private AtomicLong latestExecTime;
private AtomicLong transactionCounter;
private List<String> destLabelValues; private List<String> destLabelValues;
} }
@@ -23,18 +23,28 @@ import java.util.concurrent.ConcurrentMap;
*/ */
public class MetaCollector extends Collector implements InstanceRegistry { public class MetaCollector extends Collector implements InstanceRegistry {
private static final List<String> InfoLabel = Arrays.asList("destination", "mode"); private static final List<String> InfoLabel = Arrays.asList("destination", "mode");
private static final Logger logger = LoggerFactory.getLogger(MetaCollector.class); private static final Logger logger = LoggerFactory.getLogger(MetaCollector.class);
private static final String SUBSCRIPTION = "canal_instance"; private static final String SUBSCRIPTION = "canal_instance";
private static final String subscriptionHelp = "Canal instance"; private static final String SUBSCRIPTION_HELP = "Canal instance";
private final ConcurrentMap<String, MetaMetricsHolder> instances = new ConcurrentHashMap<String, MetaMetricsHolder>(); private final ConcurrentMap<String, MetaMetricsHolder> instances = new ConcurrentHashMap<String, MetaMetricsHolder>();
private MetaCollector() {}
private static class SingletonHolder {
private static final MetaCollector SINGLETON = new MetaCollector();
}
public static MetaCollector instance() {
return SingletonHolder.SINGLETON;
}
@Override @Override
public List<MetricFamilySamples> collect() { public List<MetricFamilySamples> collect() {
List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>(); List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>();
GaugeMetricFamily instanceInfo = new GaugeMetricFamily( GaugeMetricFamily instanceInfo = new GaugeMetricFamily(
SUBSCRIPTION, SUBSCRIPTION,
subscriptionHelp, SUBSCRIPTION_HELP,
InfoLabel); InfoLabel);
for (Map.Entry<String, MetaMetricsHolder> nme : instances.entrySet()) { for (Map.Entry<String, MetaMetricsHolder> nme : instances.entrySet()) {
final String destination = nme.getKey(); final String destination = nme.getKey();
@@ -54,11 +64,10 @@ public class MetaCollector extends Collector implements InstanceRegistry {
String mode = (instance instanceof CanalInstanceWithSpring) ? "spring" : "manager"; String mode = (instance instanceof CanalInstanceWithSpring) ? "spring" : "manager";
holder.infoLabelValues = Arrays.asList(destination, mode); holder.infoLabelValues = Arrays.asList(destination, mode);
holder.metaManager = instance.getMetaManager(); holder.metaManager = instance.getMetaManager();
Preconditions.checkNotNull(holder.infoLabelValues);
Preconditions.checkNotNull(holder.metaManager); Preconditions.checkNotNull(holder.metaManager);
MetaMetricsHolder old = instances.putIfAbsent(destination, holder); MetaMetricsHolder old = instances.put(destination, holder);
if (old != null) { if (old != null) {
logger.warn("Ignore repeated MetaCollector register for instance {}.", destination); logger.warn("Remove stale MetaCollector for instance {}.", destination);
} }
} }
@@ -1,74 +0,0 @@
package com.alibaba.otter.canal.prometheus.impl;
import com.alibaba.otter.canal.common.utils.SerializedLongAdder;
import com.alibaba.otter.canal.parse.CanalEventParser;
import com.alibaba.otter.canal.parse.inbound.mysql.MysqlEventParser;
import io.prometheus.client.Collector;
import io.prometheus.client.CounterMetricFamily;
import io.prometheus.client.GaugeMetricFamily;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static com.alibaba.otter.canal.prometheus.CanalInstanceExports.DEST;
import static com.alibaba.otter.canal.prometheus.CanalInstanceExports.DEST_LABEL_LIST;
/**
* @author Chuanyi Li
*/
public class MysqlParserCollector extends Collector {
private static final String PUBLISH_BLOCKING_TIME = "canal_instance_publish_blocking_time";
private static final String RECEIVED_BINLOG_BYTES = "canal_instance_received_binlog_bytes";
private static final String PARSER_MODE = "canal_instance_parser_mode";
private static final String MODE_LABEL = "parallel";
private final List<String> destLabelValues;
private final List<String> modeLabelValues;
private final SerializedLongAdder receivedBinlogBytes;
private final SerializedLongAdder eventsPublishBlockingTime;
private final String publishBlockingTimeHelp;
private final String receivedBinlogBytesHelp;
private final String modeHelp;
private final List<String> modeLabels;
private final Boolean isParallel;
public MysqlParserCollector(CanalEventParser parser, String destination) {
if (!(parser instanceof MysqlEventParser)) {
throw new IllegalArgumentException("CanalEventParser must be MysqlEventParser");
}
this.destLabelValues = Collections.singletonList(destination);
this.eventsPublishBlockingTime = ((MysqlEventParser)parser).getEventsPublishBlockingTime();
this.publishBlockingTimeHelp = "Blocking time of publishing of instance " + destination;
this.receivedBinlogBytes = ((MysqlEventParser)parser).getReceivedBinlogBytes();
this.receivedBinlogBytesHelp = "Received binlog bytes of instance" + destination;
this.isParallel = ((MysqlEventParser)parser).isParallel();
this.modeHelp = "Parser mode of instance" + destination;
this.modeLabels = Arrays.asList(DEST, MODE_LABEL);
this.modeLabelValues = Arrays.asList(destination, isParallel.toString());
}
@Override public List<MetricFamilySamples> collect() {
List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>();
// canal_instance_received_binlog_bytes
CounterMetricFamily bytesCounter = new CounterMetricFamily(RECEIVED_BINLOG_BYTES,
receivedBinlogBytesHelp, DEST_LABEL_LIST);
bytesCounter.addMetric(destLabelValues, receivedBinlogBytes.get());
mfs.add(bytesCounter);
// canal_instance_parser_mode
GaugeMetricFamily modeGauge = new GaugeMetricFamily(PARSER_MODE,
modeHelp,
modeLabels);
modeGauge.addMetric(modeLabelValues, 1);
mfs.add(modeGauge);
// canal_instance_publish_blocking_time
if (isParallel) {
CounterMetricFamily blockingCounter = new CounterMetricFamily(PUBLISH_BLOCKING_TIME,
publishBlockingTimeHelp, DEST_LABEL_LIST);
blockingCounter.addMetric(destLabelValues, eventsPublishBlockingTime.get());
mfs.add(blockingCounter);
}
return mfs;
}
}
@@ -0,0 +1,114 @@
package com.alibaba.otter.canal.prometheus.impl;
import com.alibaba.otter.canal.instance.core.CanalInstance;
import com.alibaba.otter.canal.parse.CanalEventParser;
import com.alibaba.otter.canal.parse.inbound.mysql.MysqlEventParser;
import com.alibaba.otter.canal.prometheus.InstanceRegistry;
import com.google.common.base.Preconditions;
import io.prometheus.client.Collector;
import io.prometheus.client.CounterMetricFamily;
import io.prometheus.client.GaugeMetricFamily;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
import static com.alibaba.otter.canal.prometheus.CanalInstanceExports.DEST;
import static com.alibaba.otter.canal.prometheus.CanalInstanceExports.DEST_LABELS_LIST;
/**
* @author Chuanyi Li
*/
public class ParserCollector extends Collector implements InstanceRegistry {
private static final Logger logger = LoggerFactory.getLogger(ParserCollector.class);
private static final String PUBLISH_BLOCKING = "canal_instance_publish_blocking_time";
private static final String RECEIVED_BINLOG = "canal_instance_received_binlog_bytes";
private static final String PARSER_MODE = "canal_instance_parser_mode";
private static final String MODE_LABEL = "parallel";
private static final String PUBLISH_BLOCKING_HELP = "Publish blocking time of dump thread";
private static final String RECEIVED_BINLOG_HELP = "Received binlog bytes";
private static final String MODE_HELP = "Parser mode(parallel/serial) of instance";
private final List<String> modeLabels = Arrays.asList(DEST, MODE_LABEL);
private final ConcurrentMap<String, ParserMetricsHolder> instances = new ConcurrentHashMap<String, ParserMetricsHolder>();
private ParserCollector() {}
private static class SingletonHolder {
private static final ParserCollector SINGLETON = new ParserCollector();
}
public static ParserCollector instance() {
return SingletonHolder.SINGLETON;
}
@Override
public List<MetricFamilySamples> collect() {
List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>();
boolean hasParallel = false;
CounterMetricFamily bytesCounter = new CounterMetricFamily(RECEIVED_BINLOG,
RECEIVED_BINLOG_HELP, DEST_LABELS_LIST);
GaugeMetricFamily modeGauge = new GaugeMetricFamily(PARSER_MODE,
MODE_HELP, modeLabels);
CounterMetricFamily blockingCounter = new CounterMetricFamily(PUBLISH_BLOCKING,
PUBLISH_BLOCKING_HELP, DEST_LABELS_LIST);
for (ParserMetricsHolder emh : instances.values()) {
if (emh.isParallel) {
blockingCounter.addMetric(emh.destLabelValues, emh.eventsPublishBlockingTime.doubleValue());
hasParallel = true;
}
modeGauge.addMetric(emh.modeLabelValues, 1);
bytesCounter.addMetric(emh.destLabelValues, emh.receivedBinlogBytes.doubleValue());
}
mfs.add(bytesCounter);
mfs.add(modeGauge);
if (hasParallel) {
mfs.add(blockingCounter);
}
return mfs;
}
@Override
public void register(CanalInstance instance) {
final String destination = instance.getDestination();
ParserMetricsHolder holder = new ParserMetricsHolder();
CanalEventParser parser = instance.getEventParser();
if (!(parser instanceof MysqlEventParser)) {
throw new IllegalArgumentException("CanalEventParser must be MysqlEventParser");
}
MysqlEventParser mysqlParser = (MysqlEventParser) parser;
holder.destLabelValues = Collections.singletonList(destination);
holder.modeLabelValues = Arrays.asList(destination, Boolean.toString(mysqlParser.isParallel()));
holder.eventsPublishBlockingTime = mysqlParser.getEventsPublishBlockingTime();
holder.receivedBinlogBytes = mysqlParser.getReceivedBinlogBytes();
holder.isParallel = mysqlParser.isParallel();
Preconditions.checkNotNull(holder.eventsPublishBlockingTime);
Preconditions.checkNotNull(holder.receivedBinlogBytes);
ParserMetricsHolder old = instances.put(destination, holder);
if (old != null) {
logger.warn("Remove stale ParserCollector for instance {}.", destination);
}
}
@Override
public void unregister(CanalInstance instance) {
final String destination = instance.getDestination();
instances.remove(destination);
}
private class ParserMetricsHolder {
private List<String> destLabelValues;
private List<String> modeLabelValues;
private AtomicLong receivedBinlogBytes;
private AtomicLong eventsPublishBlockingTime;
private boolean isParallel;
}
}
@@ -1,7 +1,9 @@
package com.alibaba.otter.canal.prometheus.impl; package com.alibaba.otter.canal.prometheus.impl;
import com.alibaba.otter.canal.protocol.CanalEntry.EntryType;
import com.alibaba.otter.canal.sink.AbstractCanalEventDownStreamHandler; import com.alibaba.otter.canal.sink.AbstractCanalEventDownStreamHandler;
import com.alibaba.otter.canal.store.model.Event; import com.alibaba.otter.canal.store.model.Event;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@@ -10,28 +12,71 @@ import java.util.concurrent.atomic.AtomicLong;
*/ */
public class PrometheusCanalEventDownStreamHandler extends AbstractCanalEventDownStreamHandler<List<Event>> { public class PrometheusCanalEventDownStreamHandler extends AbstractCanalEventDownStreamHandler<List<Event>> {
private final AtomicLong latestExecuteTime = new AtomicLong(0L); private final AtomicLong latestExecuteTime = new AtomicLong(0L);
private final AtomicLong transactionCounter = new AtomicLong(0L);
private final AtomicLong rowEventCounter = new AtomicLong(0L);
private final AtomicLong rowsCounter = new AtomicLong(0L);
@Override @Override
public List<Event> before(List<Event> events) { public List<Event> before(List<Event> events) {
// TODO utilize MySQL master heartbeat packet to refresh delay if always no more events coming long localExecTime = 0L;
// see: https://dev.mysql.com/worklog/task/?id=342
// heartbeats are sent by the master only if there is no
// more unsent events in the actual binlog file for a period longer that
// master_heartbeat_period.
if (events != null && !events.isEmpty()) { if (events != null && !events.isEmpty()) {
Event last = events.get(events.size() - 1); for (Event e : events) {
long ts = last.getExecuteTime(); EntryType type = e.getEntryType();
long ls = latestExecuteTime.get(); if (type == null) continue;
if (ts > ls) { long exec = e.getExecuteTime();
latestExecuteTime.lazySet(ts); if (exec > 0) localExecTime = exec;
switch (type) {
case ROWDATA:
// TODO 当前proto无法直接获得荣威change的变更行数(需要parse),可考虑放到header里面
break;
case TRANSACTIONEND:
transactionCounter.incrementAndGet();
break;
case HEARTBEAT:
// TODO 确认一下不是canal自己产生的
// EventType eventType = e.getEventType();
// TODO utilize MySQL master heartbeat packet to refresh delay if always no more events coming
// see: https://dev.mysql.com/worklog/task/?id=342
// heartbeats are sent by the master only if there is no
// more unsent events in the actual binlog file for a period longer that
// master_heartbeat_period.
break;
default:
break;
}
}
if (localExecTime > 0) {
latestExecuteTime.lazySet(localExecTime);
} }
} }
return events; return events;
} }
@Override
public void start() {
super.start();
}
@Override
public void stop() {
super.stop();
}
public AtomicLong getLatestExecuteTime() { public AtomicLong getLatestExecuteTime() {
return latestExecuteTime; return latestExecuteTime;
} }
public AtomicLong getTransactionCounter() {
return transactionCounter;
}
public AtomicLong getRowsCounter() {
return rowsCounter;
}
public AtomicLong getRowEventCounter() {
return rowEventCounter;
}
} }
@@ -17,23 +17,33 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import static com.alibaba.otter.canal.prometheus.CanalInstanceExports.DEST_LABEL_LIST; import static com.alibaba.otter.canal.prometheus.CanalInstanceExports.DEST_LABELS_LIST;
/** /**
* @author Chuanyi Li * @author Chuanyi Li
*/ */
public class SinkCollector extends Collector implements InstanceRegistry { public class SinkCollector extends Collector implements InstanceRegistry {
private static final Logger logger = LoggerFactory.getLogger(SinkCollector.class); private static final Logger logger = LoggerFactory.getLogger(SinkCollector.class);
private static final String SINK_BLOCKING_TIME = "canal_instance_sink_blocking_time"; private static final String SINK_BLOCKING_TIME = "canal_instance_sink_blocking_time";
private static final String sinkBlockTimeHelp = "Total sink blocking time"; private static final String SINK_BLOCK_TIME_HELP = "Total sink blocking time";
private final ConcurrentMap<String, SinkMetricsHolder> instances = new ConcurrentHashMap<String, SinkMetricsHolder>(); private final ConcurrentMap<String, SinkMetricsHolder> instances = new ConcurrentHashMap<String, SinkMetricsHolder>();
private SinkCollector() {}
private static class SingletonHolder {
private static final SinkCollector SINGLETON = new SinkCollector();
}
public static SinkCollector instance() {
return SingletonHolder.SINGLETON;
}
@Override @Override
public List<MetricFamilySamples> collect() { public List<MetricFamilySamples> collect() {
List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>(); List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>();
CounterMetricFamily blockingCounter = new CounterMetricFamily(SINK_BLOCKING_TIME, CounterMetricFamily blockingCounter = new CounterMetricFamily(SINK_BLOCKING_TIME,
sinkBlockTimeHelp, DEST_LABEL_LIST); SINK_BLOCK_TIME_HELP, DEST_LABELS_LIST);
for (SinkMetricsHolder smh : instances.values()) { for (SinkMetricsHolder smh : instances.values()) {
blockingCounter.addMetric(smh.destLabelValues, smh.eventsSinkBlockingTime.doubleValue()); blockingCounter.addMetric(smh.destLabelValues, smh.eventsSinkBlockingTime.doubleValue());
} }
@@ -52,11 +62,10 @@ public class SinkCollector extends Collector implements InstanceRegistry {
} }
EntryEventSink entrySink = (EntryEventSink) sink; EntryEventSink entrySink = (EntryEventSink) sink;
holder.eventsSinkBlockingTime = entrySink.getEventsSinkBlockingTime(); holder.eventsSinkBlockingTime = entrySink.getEventsSinkBlockingTime();
Preconditions.checkNotNull(holder.destLabelValues);
Preconditions.checkNotNull(holder.eventsSinkBlockingTime); Preconditions.checkNotNull(holder.eventsSinkBlockingTime);
SinkMetricsHolder old = instances.putIfAbsent(destination, holder); SinkMetricsHolder old = instances.put(destination, holder);
if (old != null) { if (old != null) {
logger.warn("Ignore repeated SinkCollector register for instance {}.", destination); logger.warn("Remote stale SinkCollector for instance {}.", destination);
} }
} }
@@ -1,15 +1,17 @@
package com.alibaba.otter.canal.prometheus.impl; package com.alibaba.otter.canal.prometheus.impl;
import com.alibaba.otter.canal.instance.core.CanalInstance; import com.alibaba.otter.canal.instance.core.CanalInstance;
import com.alibaba.otter.canal.prometheus.CanalInstanceExports;
import com.alibaba.otter.canal.prometheus.InstanceRegistry; import com.alibaba.otter.canal.prometheus.InstanceRegistry;
import com.alibaba.otter.canal.store.CanalEventStore; import com.alibaba.otter.canal.store.CanalEventStore;
import com.alibaba.otter.canal.store.CanalStoreException;
import com.alibaba.otter.canal.store.memory.MemoryEventStoreWithBuffer; import com.alibaba.otter.canal.store.memory.MemoryEventStoreWithBuffer;
import com.alibaba.otter.canal.store.model.BatchMode;
import com.google.common.base.Preconditions;
import io.prometheus.client.Collector; import io.prometheus.client.Collector;
import io.prometheus.client.CounterMetricFamily; import io.prometheus.client.CounterMetricFamily;
import io.prometheus.client.GaugeMetricFamily;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@@ -18,68 +20,113 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import static com.alibaba.otter.canal.prometheus.CanalInstanceExports.DEST;
import static com.alibaba.otter.canal.prometheus.CanalInstanceExports.DEST_LABELS_LIST;
/** /**
* @author Chuanyi Li * @author Chuanyi Li
*/ */
public class StoreCollector extends Collector implements InstanceRegistry { public class StoreCollector extends Collector implements InstanceRegistry {
private static final Class<MemoryEventStoreWithBuffer> clazz = MemoryEventStoreWithBuffer.class; private static final Logger logger = LoggerFactory.getLogger(SinkCollector.class);
private static final String PRODUCE = "canal_instance_store_produce_seq"; private static final String PRODUCE = "canal_instance_store_produce_seq";
private static final String CONSUME = "canal_instance_store_consume_seq"; private static final String CONSUME = "canal_instance_store_consume_seq";
private static final String produceHelp = "Produced sequence of canal instance"; private static final String STORE = "canal_instance_store";
private static final String consumeHelp = "Consumed sequence of canal instance"; private static final String PRODUCE_MEM = "canal_instance_store_produce_mem";
private final ConcurrentMap<String, StoreMetricsHolder> instances = new ConcurrentHashMap<String, StoreMetricsHolder>(); private static final String CONSUME_MEM = "canal_instance_store_consume_mem";
private static final String PRODUCE_HELP = "Produced events counter of canal instance";
private static final String CONSUME_HELP = "Consumed events counter of canal instance";
private static final String STORE_HELP = "Canal instance info";
private static final String PRODUCE_MEM_HELP = "Produced mem bytes of canal instance";
private static final String CONSUME_MEM_HELP = "Consumed mem bytes of canal instance";
private final ConcurrentMap<String, StoreMetricsHolder> instances = new ConcurrentHashMap<String, StoreMetricsHolder>();
private final List<String> storeLabelsList = Arrays.asList(DEST, "batchMode");
private StoreCollector() {}
public StoreCollector(CanalEventStore store, String destination) { private static class SingletonHolder {
this.destination = destination; private static final StoreCollector SINGLETON = new StoreCollector();
if (!(store instanceof MemoryEventStoreWithBuffer)) { }
throw new IllegalArgumentException("EventStore must be MemoryEventStoreWithBuffer");
} public static StoreCollector instance() {
MemoryEventStoreWithBuffer ms = (MemoryEventStoreWithBuffer) store; return SingletonHolder.SINGLETON;
putSequence = getDeclaredValue(ms, "putSequence");
ackSequence = getDeclaredValue(ms, "ackSequence");
} }
@Override @Override
public List<MetricFamilySamples> collect() { public List<MetricFamilySamples> collect() {
List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>(); List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>();
CounterMetricFamily put = new CounterMetricFamily("canal_instance_store_produce_seq", CounterMetricFamily put = new CounterMetricFamily(PRODUCE,
putHelp, Arrays.asList(CanalInstanceExports.DEST_LABELS)); PRODUCE_HELP, DEST_LABELS_LIST);
put.addMetric(Collections.singletonList(destination), putSequence.doubleValue()); CounterMetricFamily ack = new CounterMetricFamily(CONSUME,
CONSUME_HELP, DEST_LABELS_LIST);
GaugeMetricFamily store = new GaugeMetricFamily(STORE,
STORE_HELP, storeLabelsList);
CounterMetricFamily putMem = new CounterMetricFamily(PRODUCE_MEM,
PRODUCE_MEM_HELP, DEST_LABELS_LIST);
CounterMetricFamily ackMem = new CounterMetricFamily(CONSUME_MEM,
CONSUME_MEM_HELP, DEST_LABELS_LIST);
boolean hasMem = false;
for (StoreMetricsHolder smh : instances.values()) {
final boolean isMem = smh.batchMode.isMemSize();
put.addMetric(smh.destLabelValues, smh.putSeq.doubleValue());
ack.addMetric(smh.destLabelValues, smh.ackSeq.doubleValue());
store.addMetric(smh.storeLabelValues, 1);
if (isMem) {
hasMem = true;
putMem.addMetric(smh.destLabelValues, smh.putMemSize.doubleValue());
ackMem.addMetric(smh.destLabelValues, smh.ackMemSize.doubleValue());
}
}
mfs.add(put); mfs.add(put);
CounterMetricFamily ack = new CounterMetricFamily("canal_instance_store_consume_seq",
ackHelp, Arrays.asList(CanalInstanceExports.DEST_LABELS));
ack.addMetric(Collections.singletonList(destination), ackSequence.doubleValue());
mfs.add(ack); mfs.add(ack);
mfs.add(store);
if (hasMem) {
mfs.add(putMem);
mfs.add(ackMem);
}
return mfs; return mfs;
} }
@SuppressWarnings("unchecked")
private static <T> T getDeclaredValue(MemoryEventStoreWithBuffer store, String name) {
T value;
try {
Field putField = clazz.getDeclaredField(name);
putField.setAccessible(true);
value = (T) putField.get(store);
} catch (NoSuchFieldException e) {
throw new CanalStoreException(e);
} catch (IllegalAccessException e) {
throw new CanalStoreException(e);
}
return value;
}
@Override public void register(CanalInstance instance) { @Override public void register(CanalInstance instance) {
final String destination = instance.getDestination();
StoreMetricsHolder holder = new StoreMetricsHolder();
CanalEventStore store = instance.getEventStore();
if (!(store instanceof MemoryEventStoreWithBuffer)) {
throw new IllegalArgumentException("EventStore must be MemoryEventStoreWithBuffer");
}
MemoryEventStoreWithBuffer memStore = (MemoryEventStoreWithBuffer) store;
holder.batchMode = memStore.getBatchMode();
holder.putSeq = memStore.getPutSequence();
holder.ackSeq = memStore.getAckSequence();
holder.destLabelValues = Collections.singletonList(destination);
holder.storeLabelValues = Arrays.asList(destination, memStore.getBatchMode().name());
Preconditions.checkNotNull(holder.batchMode);
Preconditions.checkNotNull(holder.putSeq);
Preconditions.checkNotNull(holder.ackSeq);
if (holder.batchMode.isMemSize()) {
holder.putMemSize = memStore.getPutMemSize();
holder.ackMemSize = memStore.getAckMemSize();
Preconditions.checkNotNull(holder.putMemSize);
Preconditions.checkNotNull(holder.ackMemSize);
}
StoreMetricsHolder old = instances.putIfAbsent(destination, holder);
if (old != null) {
logger.warn("Remote stale StoreCollector for instance {}.", destination);
}
} }
@Override public void unregister(CanalInstance instance) { @Override public void unregister(CanalInstance instance) {
final String destination = instance.getDestination();
instances.remove(destination);
} }
private class StoreMetricsHolder { private class StoreMetricsHolder {
private AtomicLong putSeq; private AtomicLong putSeq;
private AtomicLong ackSeq; private AtomicLong ackSeq;
private BatchMode batchMode;
private AtomicLong putMemSize;
private AtomicLong ackMemSize;
private List<String> destLabelValues;
private List<String> storeLabelValues;
} }
} }
@@ -562,4 +562,23 @@ public class MemoryEventStoreWithBuffer extends AbstractCanalStoreScavenge imple
this.ddlIsolation = ddlIsolation; this.ddlIsolation = ddlIsolation;
} }
public AtomicLong getPutSequence() {
return putSequence;
}
public AtomicLong getAckSequence() {
return ackSequence;
}
public AtomicLong getPutMemSize() {
return putMemSize;
}
public AtomicLong getAckMemSize() {
return ackMemSize;
}
public BatchMode getBatchMode() {
return batchMode;
}
} }