diff --git a/common/src/main/java/com/alibaba/otter/canal/common/utils/SerializedLongAdder.java b/common/src/main/java/com/alibaba/otter/canal/common/utils/SerializedLongAdder.java new file mode 100644 index 00000000..4d86a99e --- /dev/null +++ b/common/src/main/java/com/alibaba/otter/canal/common/utils/SerializedLongAdder.java @@ -0,0 +1,31 @@ +package com.alibaba.otter.canal.common.utils; + +/** + * 用于dump记录counter + * 这种场景下, + * 写操作>>读操作 + * 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; + } + +} diff --git a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/AbstractEventParser.java b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/AbstractEventParser.java index 0034ba2b..62096b6a 100644 --- a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/AbstractEventParser.java +++ b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/AbstractEventParser.java @@ -9,6 +9,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; 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.exception.ExceptionUtils; import org.apache.commons.lang.math.RandomUtils; @@ -100,6 +101,7 @@ public abstract class AbstractEventParser extends AbstractCanalLifeCycle + protected abstract BinlogParser buildParser(); protected abstract ErosaConnection buildErosaConnection(); @@ -643,4 +645,5 @@ public abstract class AbstractEventParser extends AbstractCanalLifeCycle public void setServerId(long serverId) { this.serverId = serverId; } + } diff --git a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/AbstractMysqlEventParser.java b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/AbstractMysqlEventParser.java index 17eb08f4..5209b178 100644 --- a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/AbstractMysqlEventParser.java +++ b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/AbstractMysqlEventParser.java @@ -2,6 +2,7 @@ package com.alibaba.otter.canal.parse.inbound.mysql; import java.nio.charset.Charset; +import com.alibaba.otter.canal.common.utils.SerializedLongAdder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,6 +39,7 @@ public abstract class AbstractMysqlEventParser extends AbstractEventParser { protected boolean filterRows = false; protected boolean filterTableError = false; protected boolean useDruidDdlFilter = true; + private final SerializedLongAdder eventsPublishBlockingTime = new SerializedLongAdder(0L); protected BinlogParser buildParser() { LogEventConvert convert = new LogEventConvert(); @@ -131,11 +133,13 @@ public abstract class AbstractMysqlEventParser extends AbstractEventParser { } protected MultiStageCoprocessor buildMultiStageCoprocessor() { - return new MysqlMultiStageCoprocessor(parallelBufferSize, - parallelThreadSize, - (LogEventConvert) binlogParser, - transactionBuffer, - destination); + MysqlMultiStageCoprocessor mysqlMultiStageCoprocessor = new MysqlMultiStageCoprocessor(parallelBufferSize, + parallelThreadSize, + (LogEventConvert) binlogParser, + transactionBuffer, + destination); + mysqlMultiStageCoprocessor.setEventsPublishBlockingTime(eventsPublishBlockingTime); + return mysqlMultiStageCoprocessor; } // ============================ setter / getter ========================= @@ -203,5 +207,9 @@ public abstract class AbstractMysqlEventParser extends AbstractEventParser { public void setTableMetaTSDBFactory(TableMetaTSDBFactory tableMetaTSDBFactory) { this.tableMetaTSDBFactory = tableMetaTSDBFactory; } + + public SerializedLongAdder getEventsPublishBlockingTime() { + return this.eventsPublishBlockingTime; + } } diff --git a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlConnection.java b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlConnection.java index 5f681a54..c0c658fb 100644 --- a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlConnection.java +++ b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlConnection.java @@ -8,6 +8,7 @@ import java.nio.charset.Charset; import java.util.List; import java.util.concurrent.TimeUnit; +import com.alibaba.otter.canal.common.utils.SerializedLongAdder; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.slf4j.Logger; @@ -39,18 +40,19 @@ import com.taobao.tddl.dbsync.binlog.LogEvent; public class MysqlConnection implements ErosaConnection { - private static final Logger logger = LoggerFactory.getLogger(MysqlConnection.class); + private static final Logger logger = LoggerFactory.getLogger(MysqlConnection.class); - private MysqlConnector connector; - private long slaveId; - private Charset charset = Charset.forName("UTF-8"); - private BinlogFormat binlogFormat; - private BinlogImage binlogImage; + private MysqlConnector connector; + private long slaveId; + private Charset charset = Charset.forName("UTF-8"); + private BinlogFormat binlogFormat; + private BinlogImage binlogImage; // tsdb releated - private AuthenticationInfo authInfo; - protected int connTimeout = 5 * 1000; // 5秒 - protected int soTimeout = 60 * 60 * 1000; // 1小时 + private AuthenticationInfo authInfo; + protected int connTimeout = 5 * 1000; // 5秒 + protected int soTimeout = 60 * 60 * 1000; // 1小时 + private SerializedLongAdder receivedBinlogBytes; public MysqlConnection(){ } @@ -126,6 +128,7 @@ public class MysqlConnection implements ErosaConnection { decoder.handle(LogEvent.XID_EVENT); LogContext context = new LogContext(); while (fetcher.fetch()) { + accumulateReceivedBytes(fetcher.limit()); LogEvent event = null; event = decoder.decode(fetcher, context); @@ -148,6 +151,7 @@ public class MysqlConnection implements ErosaConnection { LogDecoder decoder = new LogDecoder(LogEvent.UNKNOWN_EVENT, LogEvent.ENUM_END_EVENT); LogContext context = new LogContext(); while (fetcher.fetch()) { + accumulateReceivedBytes(fetcher.limit()); LogEvent event = null; event = decoder.decode(fetcher, context); @@ -176,6 +180,7 @@ public class MysqlConnection implements ErosaConnection { LogDecoder decoder = new LogDecoder(LogEvent.UNKNOWN_EVENT, LogEvent.ENUM_END_EVENT); LogContext context = new LogContext(); while (fetcher.fetch()) { + accumulateReceivedBytes(fetcher.limit()); LogEvent event = null; event = decoder.decode(fetcher, context); @@ -206,6 +211,7 @@ public class MysqlConnection implements ErosaConnection { try { fetcher.start(connector.getChannel()); while (fetcher.fetch()) { + accumulateReceivedBytes(fetcher.limit()); LogBuffer buffer = fetcher.duplicate(); fetcher.consume(fetcher.limit()); if (!coprocessor.publish(buffer)) { @@ -232,6 +238,7 @@ public class MysqlConnection implements ErosaConnection { try { fetcher.start(connector.getChannel()); while (fetcher.fetch()) { + accumulateReceivedBytes(fetcher.limit()); LogBuffer buffer = fetcher.duplicate(); fetcher.consume(fetcher.limit()); if (!coprocessor.publish(buffer)) { @@ -346,7 +353,6 @@ public class MysqlConnection implements ErosaConnection { *
  • net_read_timeout
  • * * - * @param channel * @throws IOException */ private void updateSettings() throws IOException { @@ -465,6 +471,14 @@ public class MysqlConnection implements ErosaConnection { } } + private void accumulateReceivedBytes(long x) { + if (receivedBinlogBytes != null) { + receivedBinlogBytes.add(x); + } + } + + + public static enum BinlogFormat { STATEMENT("STATEMENT"), ROW("ROW"), MIXED("MIXED"); @@ -604,4 +618,8 @@ public class MysqlConnection implements ErosaConnection { this.authInfo = authInfo; } + public void setReceivedBinlogBytes(SerializedLongAdder receivedBinlogBytes) { + this.receivedBinlogBytes = receivedBinlogBytes; + } + } diff --git a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlEventParser.java b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlEventParser.java index 8faaaa61..2b1e65e2 100644 --- a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlEventParser.java +++ b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlEventParser.java @@ -10,6 +10,7 @@ import java.util.Map; import java.util.TimerTask; import java.util.concurrent.atomic.AtomicLong; +import com.alibaba.otter.canal.common.utils.SerializedLongAdder; import org.apache.commons.lang.StringUtils; import org.springframework.util.CollectionUtils; @@ -68,8 +69,11 @@ public class MysqlEventParser extends AbstractMysqlEventParser implements CanalE private BinlogImage[] supportBinlogImages; // 支持的binlogImage,如果设置会执行强校验 // update by yishun.chen,特殊异常处理参数 - private int dumpErrorCount = 0; // binlogDump失败异常计数 - private int dumpErrorCountThreshold = 2; // binlogDump失败异常计数阀值 + private int dumpErrorCount = 0; // binlogDump失败异常计数 + private int dumpErrorCountThreshold = 2; // binlogDump失败异常计数阀值 + + // instance received binlog bytes + private final SerializedLongAdder receivedBinlogBytes = new SerializedLongAdder(0L); protected ErosaConnection buildErosaConnection() { return buildMysqlConnection(this.runningInfo); @@ -313,6 +317,7 @@ public class MysqlEventParser extends AbstractMysqlEventParser implements CanalE connection.getConnector().setSendBufferSize(sendBufferSize); connection.getConnector().setSoTimeout(defaultConnectionTimeoutInSeconds * 1000); connection.setCharset(connectionCharset); + connection.setReceivedBinlogBytes(receivedBinlogBytes); // 随机生成slaveId if (this.slaveId <= 0) { this.slaveId = generateUniqueServerId(); @@ -910,4 +915,10 @@ public class MysqlEventParser extends AbstractMysqlEventParser implements CanalE this.dumpErrorCountThreshold = dumpErrorCountThreshold; } + + + public SerializedLongAdder getReceivedBinlogBytes() { + return this.receivedBinlogBytes; + } + } diff --git a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlMultiStageCoprocessor.java b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlMultiStageCoprocessor.java index 6acc5056..e348c386 100644 --- a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlMultiStageCoprocessor.java +++ b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlMultiStageCoprocessor.java @@ -4,6 +4,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.LockSupport; +import com.alibaba.otter.canal.common.utils.SerializedLongAdder; import org.apache.commons.lang.StringUtils; import com.alibaba.otter.canal.common.AbstractCanalLifeCycle; @@ -52,6 +53,7 @@ import com.taobao.tddl.dbsync.binlog.event.WriteRowsLogEvent; */ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implements MultiStageCoprocessor { + private static final int maxFullTimes = 10; private LogEventConvert logEventConvert; private EventTransactionBuffer transactionBuffer; private ErosaConnection connection; @@ -63,6 +65,7 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement private ExecutorService stageExecutor; private String destination; private volatile CanalParseException exception; + private SerializedLongAdder eventsPublishBlockingTime; public MysqlMultiStageCoprocessor(int ringBufferSize, int parserThreadCount, LogEventConvert logEventConvert, EventTransactionBuffer transactionBuffer, String destination){ @@ -161,6 +164,8 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement throw exception; } boolean interupted = false; + long blockingStart = 0L; + int fullTimes = 0; do { try { long next = disruptorMsgBuffer.tryNext(); @@ -170,16 +175,34 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement event.setBinlogFileName(binlogFileName); } disruptorMsgBuffer.publish(next); + if (fullTimes > 0) { + eventsPublishBlockingTime.add(System.nanoTime() - blockingStart); + } break; } catch (InsufficientCapacityException e) { + if (fullTimes == 0) { + blockingStart = System.nanoTime(); + } // park - LockSupport.parkNanos(1L); + //LockSupport.parkNanos(1L); + applyWait(++fullTimes); interupted = Thread.interrupted(); } } while (!interupted && isStart()); return isStart(); } + // 处理无数据的情况,避免空循环挂死 + private void applyWait(int fullTimes) { + int newFullTimes = fullTimes > maxFullTimes ? maxFullTimes : fullTimes; + if (fullTimes <= 3) { // 3次以内 + Thread.yield(); + } else { // 超过3次,最多只sleep 1ms + LockSupport.parkNanos(100 * 1000L * newFullTimes); + } + + } + @Override public void reset() { if (isStart()) { @@ -189,6 +212,7 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement start(); } + private class SimpleParserStage implements EventHandler, LifecycleAware { private LogDecoder decoder; @@ -433,4 +457,8 @@ public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implement this.connection = connection; } + public void setEventsPublishBlockingTime(SerializedLongAdder eventsPublishBlockingTime) { + this.eventsPublishBlockingTime = eventsPublishBlockingTime; + } + } diff --git a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/CanalInstanceExports.java b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/CanalInstanceExports.java index 8acf40b8..19759154 100644 --- a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/CanalInstanceExports.java +++ b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/CanalInstanceExports.java @@ -1,9 +1,7 @@ package com.alibaba.otter.canal.prometheus; import com.alibaba.otter.canal.instance.core.CanalInstance; -import com.alibaba.otter.canal.prometheus.impl.InstanceMetaCollector; -import com.alibaba.otter.canal.prometheus.impl.MemoryStoreCollector; -import com.alibaba.otter.canal.prometheus.impl.PrometheusCanalEventDownStreamHandler; +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; @@ -15,30 +13,31 @@ import org.slf4j.LoggerFactory; import java.util.Collections; import java.util.List; +import static com.alibaba.otter.canal.server.netty.CanalServerWithNettyProfiler.profiler; + /** * @author Chuanyi Li */ public class CanalInstanceExports { - private static final Logger logger = LoggerFactory.getLogger(CanalInstanceExports.class); - - public static final String[] labels = {"destination"}; - - public static final List labelList = Collections.singletonList(labels[0]); - + private static final Logger logger = LoggerFactory.getLogger(CanalInstanceExports.class); + public static final String DESTINATION = "destination"; + public static final String[] DEST_LABELS = {DESTINATION}; + public static final List DEST_LABEL_LIST = Collections.singletonList(DESTINATION); private final String destination; - private Collector storeCollector; - private Collector delayCollector; - private Collector metaCollector; + private Collector sinkCollector; + private Collector parserCollector; private CanalInstanceExports(CanalInstance instance) { this.destination = instance.getDestination(); - initDelayGauge(instance); + initEventsMetrics(instance); initStoreCollector(instance); initMetaCollector(instance); + initSinkCollector(instance); + initParserCollector(instance); } @@ -48,6 +47,7 @@ public class CanalInstanceExports { } void register() { + profiler().start(destination); if (delayCollector != null) { delayCollector.register(); } @@ -57,9 +57,16 @@ public class CanalInstanceExports { if (metaCollector != null) { metaCollector.register(); } + if (sinkCollector != null) { + sinkCollector.register(); + } + if (parserCollector != null) { + parserCollector.register(); + } } void unregister() { + profiler().stop(destination); if (delayCollector != null) { CollectorRegistry.defaultRegistry.unregister(delayCollector); } @@ -69,9 +76,15 @@ public class CanalInstanceExports { if (metaCollector != null) { CollectorRegistry.defaultRegistry.unregister(metaCollector); } + if (sinkCollector != null) { + CollectorRegistry.defaultRegistry.unregister(sinkCollector); + } + if (parserCollector != null) { + CollectorRegistry.defaultRegistry.unregister(parserCollector); + } } - private void initDelayGauge(CanalInstance instance) { + private void initEventsMetrics(CanalInstance instance) { CanalEventSink sink = instance.getEventSink(); if (sink instanceof EntryEventSink) { EntryEventSink entryEventSink = (EntryEventSink) sink; @@ -95,4 +108,12 @@ public class CanalInstanceExports { private void initMetaCollector(CanalInstance instance) { metaCollector = new InstanceMetaCollector(instance); } + + private void initSinkCollector(CanalInstance instance) { + sinkCollector = new EntrySinkCollector(instance.getEventSink(), instance.getDestination()); + } + + private void initParserCollector(CanalInstance instance) { + parserCollector = new MysqlParserCollector(instance.getEventParser(), instance.getDestination()); + } } diff --git a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/CanalServerExports.java b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/CanalServerExports.java index 8eef79ba..ded44fb8 100644 --- a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/CanalServerExports.java +++ b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/CanalServerExports.java @@ -1,7 +1,5 @@ package com.alibaba.otter.canal.prometheus; -import com.alibaba.otter.canal.prometheus.impl.InboundThroughputAspect; -import com.alibaba.otter.canal.prometheus.impl.OutboundThroughputAspect; /** * @author Chuanyi Li @@ -12,8 +10,6 @@ public class CanalServerExports { public static synchronized void initialize() { if (!initialized) { - InboundThroughputAspect.getCollector().register(); - OutboundThroughputAspect.getCollector().register(); initialized = true; } } diff --git a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/PrometheusService.java b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/PrometheusService.java index c7942235..2aa7dcd0 100644 --- a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/PrometheusService.java +++ b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/PrometheusService.java @@ -1,6 +1,8 @@ package com.alibaba.otter.canal.prometheus; import com.alibaba.otter.canal.instance.core.CanalInstance; +import com.alibaba.otter.canal.prometheus.impl.PrometheusClientInstanceProfilerFactory; +import com.alibaba.otter.canal.server.netty.CanalServerWithNettyProfiler; import com.alibaba.otter.canal.spi.CanalMetricsService; import io.prometheus.client.exporter.HTTPServer; import io.prometheus.client.hotspot.DefaultExports; @@ -11,6 +13,9 @@ import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import static com.alibaba.otter.canal.server.netty.CanalServerWithNettyProfiler.DISABLED; +import static com.alibaba.otter.canal.server.netty.CanalServerWithNettyProfiler.profiler; + /** * @author Chuanyi Li */ @@ -50,6 +55,7 @@ public class PrometheusService implements CanalMetricsService { DefaultExports.initialize(); // Canal server level exports CanalServerExports.initialize(); + profiler().setInstanceProfilerFactory(new PrometheusClientInstanceProfilerFactory()); } catch (Throwable t) { logger.warn("Unable to initialize server exports.", t); } @@ -66,6 +72,7 @@ public class PrometheusService implements CanalMetricsService { for (CanalInstanceExports ie : exports.values()) { ie.unregister(); } + profiler().setInstanceProfilerFactory(DISABLED); if (server != null) { server.stop(); } diff --git a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/EntrySinkCollector.java b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/EntrySinkCollector.java new file mode 100644 index 00000000..54775842 --- /dev/null +++ b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/EntrySinkCollector.java @@ -0,0 +1,45 @@ +package com.alibaba.otter.canal.prometheus.impl; + +import com.alibaba.otter.canal.sink.CanalEventSink; +import com.alibaba.otter.canal.sink.entry.EntryEventSink; +import io.prometheus.client.Collector; +import io.prometheus.client.CounterMetricFamily; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +import static com.alibaba.otter.canal.prometheus.CanalInstanceExports.DEST_LABEL_LIST; + +/** + * @author Chuanyi Li + */ +public class EntrySinkCollector extends Collector { + + private static final String SINK_BLOCKING_TIME = "canal_instance_sink_blocking_time"; + private final String sinkBlockTimeHelp; + private final List destLabelValues; + private final AtomicLong eventsSinkBlockingTime; + + + public EntrySinkCollector(CanalEventSink sink, String destination) { + if (!(sink instanceof EntryEventSink)) { + throw new IllegalArgumentException("CanalEventSink must be EntryEventSink"); + } + this.destLabelValues = Collections.singletonList(destination); + EntryEventSink entrySink = (EntryEventSink) sink; + this.sinkBlockTimeHelp = "Sink blocking time of instance " + destination; + eventsSinkBlockingTime = entrySink.getEventsSinkBlockingTime(); + } + + @Override public List collect() { + List mfs = new ArrayList(); + CounterMetricFamily blockingCounter = new CounterMetricFamily(SINK_BLOCKING_TIME, + sinkBlockTimeHelp, DEST_LABEL_LIST); + blockingCounter.addMetric(destLabelValues, eventsSinkBlockingTime.doubleValue()); + mfs.add(blockingCounter); + return mfs; + } + +} diff --git a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/InboundThroughputAspect.java b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/InboundThroughputAspect.java deleted file mode 100644 index eabc256c..00000000 --- a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/InboundThroughputAspect.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.alibaba.otter.canal.prometheus.impl; - -import io.prometheus.client.Collector; -import io.prometheus.client.CounterMetricFamily; -import org.aspectj.lang.annotation.After; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; -import org.jctools.maps.ConcurrentAutoTable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author Chuanyi Li - */ -@Aspect -public class InboundThroughputAspect { - - private static final Logger logger = LoggerFactory.getLogger(InboundThroughputAspect.class); - - /** - * Support highly scalable counters - * @see ConcurrentAutoTable - */ - private static final ConcurrentAutoTable total = new ConcurrentAutoTable(); - - private static final Collector collector = new InboundThroughputCollector(); - - public static Collector getCollector() { - return collector; - } - - @Pointcut("call(byte[] com.alibaba.otter.canal.parse.driver.mysql.socket.SocketChannel.read(..))") - public void read() {} - - @Pointcut("call(void com.alibaba.otter.canal.parse.driver.mysql.socket.SocketChannel.read(..)) ") - public void readBytes() {} - - //nested read, just eliminate them. - @Pointcut("withincode(* com.alibaba.otter.canal.parse.driver.mysql.socket.SocketChannel.read(..))") - public void nestedCall() {} - - @After("read() && !nestedCall() && args(len, ..)") - public void recordRead(int len) { - accumulateBytes(len); - } - - @After("readBytes() && !nestedCall() && args(.., len, timeout)") - public void recordReadBytes(int len, int timeout) { - accumulateBytes(len); - } - - private void accumulateBytes(int count) { - try { - total.add(count); - } catch (Throwable t) { - //Catch every Throwable, rather than break the business logic. - logger.warn("Error while accumulate inbound bytes.", t); - } - } - - public static class InboundThroughputCollector extends Collector { - - private InboundThroughputCollector() {} - - @Override - public List collect() { - List mfs = new ArrayList(); - CounterMetricFamily bytes = new CounterMetricFamily("canal_net_inbound_bytes", - "Total socket inbound bytes of canal server.", - total.get()); - mfs.add(bytes); - return mfs; - } - } - -} diff --git a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/InstanceMetaCollector.java b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/InstanceMetaCollector.java index 5562b8fd..2e8b7fc4 100644 --- a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/InstanceMetaCollector.java +++ b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/InstanceMetaCollector.java @@ -55,7 +55,7 @@ public class InstanceMetaCollector extends Collector { List subs = metaManager.listAllSubscribeInfo(destination); GaugeMetricFamily subscriptions = new GaugeMetricFamily( "canal_instance_subscription", - subsHelp, CanalInstanceExports.labelList); + subsHelp, CanalInstanceExports.DEST_LABEL_LIST); subscriptions.addMetric(Arrays.asList(destination), subs.size()); mfs.add(subscriptions); } diff --git a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/MemoryStoreCollector.java b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/MemoryStoreCollector.java index 79fdca4a..4c212c4c 100644 --- a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/MemoryStoreCollector.java +++ b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/MemoryStoreCollector.java @@ -20,15 +20,10 @@ import java.util.concurrent.atomic.AtomicLong; public class MemoryStoreCollector extends Collector { private static final Class clazz = MemoryEventStoreWithBuffer.class; - private final String destination; - private final AtomicLong putSequence; - private final AtomicLong ackSequence; - private final String putHelp; - private final String ackHelp; public MemoryStoreCollector(CanalEventStore store, String destination) { @@ -47,11 +42,11 @@ public class MemoryStoreCollector extends Collector { public List collect() { List mfs = new ArrayList(); CounterMetricFamily put = new CounterMetricFamily("canal_instance_store_produce_seq", - putHelp, Arrays.asList(CanalInstanceExports.labels)); + putHelp, Arrays.asList(CanalInstanceExports.DEST_LABELS)); put.addMetric(Collections.singletonList(destination), putSequence.doubleValue()); mfs.add(put); CounterMetricFamily ack = new CounterMetricFamily("canal_instance_store_consume_seq", - ackHelp, Arrays.asList(CanalInstanceExports.labels)); + ackHelp, Arrays.asList(CanalInstanceExports.DEST_LABELS)); ack.addMetric(Collections.singletonList(destination), ackSequence.doubleValue()); mfs.add(ack); return mfs; diff --git a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/MysqlParserCollector.java b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/MysqlParserCollector.java new file mode 100644 index 00000000..02d35209 --- /dev/null +++ b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/MysqlParserCollector.java @@ -0,0 +1,74 @@ +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.DESTINATION; +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 destLabelValues; + private final List modeLabelValues; + private final SerializedLongAdder receivedBinlogBytes; + private final SerializedLongAdder eventsPublishBlockingTime; + private final String publishBlockingTimeHelp; + private final String receivedBinlogBytesHelp; + private final String modeHelp; + private final List 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(DESTINATION, MODE_LABEL); + this.modeLabelValues = Arrays.asList(destination, isParallel.toString()); + } + + @Override public List collect() { + List mfs = new ArrayList(); + // 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; + } +} diff --git a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/OutboundThroughputAspect.java b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/OutboundThroughputAspect.java deleted file mode 100644 index 60dd808d..00000000 --- a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/OutboundThroughputAspect.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.alibaba.otter.canal.prometheus.impl; - -import io.prometheus.client.Collector; -import io.prometheus.client.CounterMetricFamily; -import org.aspectj.lang.annotation.After; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; -import org.jboss.netty.channel.Channel; -import org.jctools.maps.ConcurrentAutoTable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; - -import static com.alibaba.otter.canal.server.netty.NettyUtils.HEADER_LENGTH; - -/** - * @author Chuanyi Li - */ -@Aspect -public class OutboundThroughputAspect { - private static final Logger logger = LoggerFactory.getLogger(OutboundThroughputAspect.class); - - /** - * Support highly scalable counters - * @see ConcurrentAutoTable - */ - private static final ConcurrentAutoTable total = new ConcurrentAutoTable(); - - private static final Collector collector = new OutboundThroughputCollector(); - - public static Collector getCollector() { - return collector; - } - - @Pointcut("call(* com.alibaba.otter.canal.server.netty.NettyUtils.write(..))") - public void write() {} - - //nested read, just eliminate them. - @Pointcut("withincode(* com.alibaba.otter.canal.server.netty.NettyUtils.write(..))") - public void nestedCall() {} - - @After("write() && !nestedCall() && args(ch, bytes, ..)") - public void recordWriteBytes(Channel ch, byte[] bytes) { - if (bytes != null) { - accumulateBytes(HEADER_LENGTH + bytes.length); - } - } - - @After("write() && !nestedCall() && args(ch, buf, ..)") - public void recordWriteBuffer(Channel ch, ByteBuffer buf) { - if (buf != null) { - total.add(HEADER_LENGTH + buf.limit()); - } - } - private void accumulateBytes(int count) { - try { - total.add(count); - } catch (Throwable t) { - //Catch every Throwable, rather than break the business logic. - logger.warn("Error while accumulate inbound bytes.", t); - } - } - - public static class OutboundThroughputCollector extends Collector { - - private OutboundThroughputCollector() {} - - @Override public List collect() { - List mfs = new ArrayList(); - CounterMetricFamily bytes = new CounterMetricFamily("canal_net_outbound_bytes", - "Total socket outbound bytes of canal server.", - total.get()); - mfs.add(bytes); - return mfs; - } - } -} diff --git a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/PrometheusCanalEventDownStreamHandler.java b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/PrometheusCanalEventDownStreamHandler.java index c55a7eac..47d4824d 100644 --- a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/PrometheusCanalEventDownStreamHandler.java +++ b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/PrometheusCanalEventDownStreamHandler.java @@ -36,7 +36,7 @@ public class PrometheusCanalEventDownStreamHandler extends AbstractCanalEventDow GaugeMetricFamily delay = new GaugeMetricFamily( DELAY_NAME, delayHelpName, - CanalInstanceExports.labelList); + CanalInstanceExports.DEST_LABEL_LIST); double d = 0.0; if (latestExecuteTime > 0) { d = now - latestExecuteTime; diff --git a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/PrometheusClientInstanceProfiler.java b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/PrometheusClientInstanceProfiler.java new file mode 100644 index 00000000..d3413455 --- /dev/null +++ b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/PrometheusClientInstanceProfiler.java @@ -0,0 +1,134 @@ +package com.alibaba.otter.canal.prometheus.impl; + +import com.alibaba.otter.canal.protocol.CanalPacket.PacketType; +import com.alibaba.otter.canal.server.netty.ClientInstanceProfiler; +import com.alibaba.otter.canal.server.netty.listener.ChannelFutureAggregator.ClientRequestResult; +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.Counter; +import io.prometheus.client.Histogram; + +import static com.alibaba.otter.canal.prometheus.CanalInstanceExports.DESTINATION; +import static com.alibaba.otter.canal.prometheus.CanalInstanceExports.DEST_LABELS; + +/** + * @author Chuanyi Li + */ +public class PrometheusClientInstanceProfiler implements ClientInstanceProfiler { + + 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 volatile boolean running = false; + private final String destination; + private final String[] destLabelValues; + private final Counter outboundCounter; + private final Counter packetsCounter; + private final Counter emptyBatchesCounter; + private final Counter errorsCounter; + private final Histogram responseLatency; + + public PrometheusClientInstanceProfiler(String destination) { + this.destination = destination; + this.destLabelValues = new String[]{destination}; + this.outboundCounter = Counter.build() + .labelNames(DEST_LABELS) + .name(OUTBOUND_BYTES) + .help("Send bytes to client of instance " + destination) + .create(); + this.packetsCounter = Counter.build() + .labelNames(new String[]{DESTINATION, "packetType"}) + .name(PACKET_TYPE) + .help("Send packets to client of instance " + destination) + .create(); + this.emptyBatchesCounter = Counter.build() + .labelNames(DEST_LABELS) + .name(EMPTY_BATCHES) + .help("Send empty batches to client of instance " + destination) + .create(); + this.errorsCounter = Counter.build() + .labelNames(new String[]{DESTINATION, "errorCode"}) + .name(ERRORS) + .help("Client request errors of instance " + destination) + .create(); + this.responseLatency = Histogram.build() + .labelNames(DEST_LABELS) + .name(LATENCY) + .help("Client request latency of instance " + destination) + // buckets in milliseconds + .buckets(1.0, 2.5, 5.0, 10.0, 25.0, 50.0, 100.0) + .create(); + } + + @Override + public String getDestination() { + return this.destination; + } + + @Override + public void profiling(ClientRequestResult result) { + PacketType type = result.getType(); + outboundCounter.labels(destLabelValues).inc(result.getAmount()); + packetsCounter.labels(destination, type.name()).inc(); + short errorCode = result.getErrorCode(); + if (errorCode > 0) { + errorsCounter.labels(destination, Short.toString(errorCode)).inc(); + } + long latency = result.getLatency(); + responseLatency.labels(destLabelValues).observe(latency / 1000000); + switch (type) { + case GET: + boolean empty = result.getEmpty(); + if (empty) { + emptyBatchesCounter.labels(destLabelValues).inc(); + } + break; + // reserve for others + default: + 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; + } +} diff --git a/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/PrometheusClientInstanceProfilerFactory.java b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/PrometheusClientInstanceProfilerFactory.java new file mode 100644 index 00000000..6f80c221 --- /dev/null +++ b/prometheus/src/main/java/com/alibaba/otter/canal/prometheus/impl/PrometheusClientInstanceProfilerFactory.java @@ -0,0 +1,15 @@ +package com.alibaba.otter.canal.prometheus.impl; + +import com.alibaba.otter.canal.server.netty.ClientInstanceProfiler; +import com.alibaba.otter.canal.server.netty.ClientInstanceProfilerFactory; + +/** + * @author Chuanyi Li + */ +public class PrometheusClientInstanceProfilerFactory implements ClientInstanceProfilerFactory { + + @Override + public ClientInstanceProfiler create(String destination) { + return new PrometheusClientInstanceProfiler(destination); + } +} diff --git a/prometheus/src/main/resources/META-INF/aop.xml b/prometheus/src/main/resources/META-INF/aop.xml deleted file mode 100644 index 8bd31d0c..00000000 --- a/prometheus/src/main/resources/META-INF/aop.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/server/src/main/java/com/alibaba/otter/canal/server/netty/CanalServerWithNettyProfiler.java b/server/src/main/java/com/alibaba/otter/canal/server/netty/CanalServerWithNettyProfiler.java index 9e9d9a39..cfd05ee6 100644 --- a/server/src/main/java/com/alibaba/otter/canal/server/netty/CanalServerWithNettyProfiler.java +++ b/server/src/main/java/com/alibaba/otter/canal/server/netty/CanalServerWithNettyProfiler.java @@ -10,7 +10,7 @@ import java.util.concurrent.ConcurrentMap; */ public class CanalServerWithNettyProfiler { - private static final ClientInstanceProfilerFactory DISABLED = new DefaultClientInstanceProfilerFactory(); + public static final ClientInstanceProfilerFactory DISABLED = new DefaultClientInstanceProfilerFactory(); private volatile ClientInstanceProfilerFactory factory; private final ConcurrentMap cliPfs; private final CanalServerWithEmbedded server; @@ -29,34 +29,47 @@ public class CanalServerWithNettyProfiler { return SingletonHolder.SINGLETON; } - public void profiling(String dest, ClientRequestResult result) { + public void profiling(String destination, ClientRequestResult result) { if (isDisabled()) { return; } - ClientInstanceProfiler profiler = tryGet(dest); + ClientInstanceProfiler profiler = cliPfs.get(destination); if (profiler != null) { profiler.profiling(result); } } - /** - * Remove instance profiler for specified instance. - * Only accepted while instance is not running. - * @param dest canal instance destination - */ - public void remove(String dest) throws IllegalStateException { + public void start(String destination) { if (isDisabled()) { return; } - synchronized (cliPfs) { - if (server.isStart(dest)) { - throw new IllegalStateException("Instance profiler should not be removed while running."); - } - cliPfs.remove(dest); + if (server.isStart(destination)) { + throw new IllegalStateException("Instance profiler should not be start while running."); + } + ClientInstanceProfiler profiler = factory.create(destination); + profiler.start(); + cliPfs.put(destination, profiler); + } + + /** + * Remove instance profiler for specified instance. + * Only accepted while instance is not running. + * @param destination canal instance destination + */ + public void stop(String destination) { + if (isDisabled()) { + return; + } + if (server.isStart(destination)) { + throw new IllegalStateException("Instance profiler should not be stop while running."); + } + ClientInstanceProfiler profiler = cliPfs.remove(destination); + if (profiler != null && profiler.isStart()) { + profiler.stop(); } } - public void setFactory(ClientInstanceProfilerFactory factory) { + public void setInstanceProfilerFactory(ClientInstanceProfilerFactory factory) { this.factory = factory; } @@ -64,15 +77,18 @@ public class CanalServerWithNettyProfiler { return factory == DISABLED || factory == null; } - private ClientInstanceProfiler tryGet(String dest) { + private ClientInstanceProfiler tryGet(String destination) { //try fast get - ClientInstanceProfiler profiler = cliPfs.get(dest); + ClientInstanceProfiler profiler = cliPfs.get(destination); if (profiler == null) { synchronized (cliPfs) { - if (server.isStart(dest)) { + if (server.isStart(destination)) { // avoid overwriting - cliPfs.putIfAbsent(dest, factory.create()); - profiler = cliPfs.get(dest); + cliPfs.putIfAbsent(destination, factory.create(destination)); + profiler = cliPfs.get(destination); + if (!profiler.isStart()) { + profiler.start(); + } } } } @@ -81,7 +97,7 @@ public class CanalServerWithNettyProfiler { private static class DefaultClientInstanceProfilerFactory implements ClientInstanceProfilerFactory { @Override - public ClientInstanceProfiler create() { + public ClientInstanceProfiler create(String destination) { throw new UnsupportedOperationException(); } } diff --git a/server/src/main/java/com/alibaba/otter/canal/server/netty/ClientInstanceProfiler.java b/server/src/main/java/com/alibaba/otter/canal/server/netty/ClientInstanceProfiler.java index 6c9ffc8b..bd4e2dbc 100644 --- a/server/src/main/java/com/alibaba/otter/canal/server/netty/ClientInstanceProfiler.java +++ b/server/src/main/java/com/alibaba/otter/canal/server/netty/ClientInstanceProfiler.java @@ -1,11 +1,12 @@ package com.alibaba.otter.canal.server.netty; +import com.alibaba.otter.canal.common.CanalLifeCycle; import com.alibaba.otter.canal.server.netty.listener.ChannelFutureAggregator; /** * @author Chuanyi Li */ -public interface ClientInstanceProfiler { +public interface ClientInstanceProfiler extends CanalLifeCycle { String getDestination(); diff --git a/server/src/main/java/com/alibaba/otter/canal/server/netty/ClientInstanceProfilerFactory.java b/server/src/main/java/com/alibaba/otter/canal/server/netty/ClientInstanceProfilerFactory.java index 30075ba1..12b54bc6 100644 --- a/server/src/main/java/com/alibaba/otter/canal/server/netty/ClientInstanceProfilerFactory.java +++ b/server/src/main/java/com/alibaba/otter/canal/server/netty/ClientInstanceProfilerFactory.java @@ -5,6 +5,6 @@ package com.alibaba.otter.canal.server.netty; */ public interface ClientInstanceProfilerFactory { - ClientInstanceProfiler create(); + ClientInstanceProfiler create(String destination); } diff --git a/server/src/main/java/com/alibaba/otter/canal/server/netty/handler/SessionHandler.java b/server/src/main/java/com/alibaba/otter/canal/server/netty/handler/SessionHandler.java index 7ea861d4..2d09f87e 100644 --- a/server/src/main/java/com/alibaba/otter/canal/server/netty/handler/SessionHandler.java +++ b/server/src/main/java/com/alibaba/otter/canal/server/netty/handler/SessionHandler.java @@ -3,6 +3,7 @@ package com.alibaba.otter.canal.server.netty.handler; import java.util.List; import java.util.concurrent.TimeUnit; +import com.alibaba.otter.canal.server.netty.listener.ChannelFutureAggregator; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.ExceptionUtils; import org.jboss.netty.buffer.ChannelBuffer; diff --git a/server/src/main/java/com/alibaba/otter/canal/server/netty/listener/ChannelFutureAggregator.java b/server/src/main/java/com/alibaba/otter/canal/server/netty/listener/ChannelFutureAggregator.java index 37ee88e2..19a33667 100644 --- a/server/src/main/java/com/alibaba/otter/canal/server/netty/listener/ChannelFutureAggregator.java +++ b/server/src/main/java/com/alibaba/otter/canal/server/netty/listener/ChannelFutureAggregator.java @@ -15,18 +15,27 @@ public class ChannelFutureAggregator implements ChannelFutureListener { private ClientRequestResult result; - public ChannelFutureAggregator(String dest, GeneratedMessage request, CanalPacket.PacketType type, int amount, long latency) { - this(dest, request, type, amount, latency, 0); + public ChannelFutureAggregator(String destination, GeneratedMessage request, CanalPacket.PacketType type, int amount, long latency, boolean empty) { + this(destination, request, type, amount, latency, empty, (short) 0); } - private ChannelFutureAggregator(String dest, GeneratedMessage request, CanalPacket.PacketType type, int amount, long latency, int errorCode) { + public ChannelFutureAggregator(String destination, GeneratedMessage request, CanalPacket.PacketType type, int amount, long latency) { + this(destination, request, type, amount, latency, false, (short) 0); + } + + public ChannelFutureAggregator(String destination, GeneratedMessage request, CanalPacket.PacketType type, int amount, long latency, short errorCode) { + this(destination, request, type, amount, latency, false, errorCode); + } + + private ChannelFutureAggregator(String destination, GeneratedMessage request, CanalPacket.PacketType type, int amount, long latency, boolean empty, short errorCode) { this.result = new ClientRequestResult.Builder() - .dest(dest) + .destination(destination) .type(type) .request(request) .amount(amount) .latency(latency) .errorCode(errorCode) + .empty(empty) .build(); } @@ -36,7 +45,7 @@ public class ChannelFutureAggregator implements ChannelFutureListener { if (future.getCause() != null) { result.channelError = future.getCause(); } - profiler().profiling(result.dest, result); + profiler().profiling(result.destination, result); } /** @@ -44,39 +53,42 @@ public class ChannelFutureAggregator implements ChannelFutureListener { */ public static class ClientRequestResult { - private String dest; + private String destination; private CanalPacket.PacketType type; private GeneratedMessage request; - private int amount; - private long latency; - private int errorCode; - private Throwable channelError; + private int amount; + private long latency; + private short errorCode; + private boolean empty; + private Throwable channelError; private ClientRequestResult() {} private ClientRequestResult(Builder builder) { - this.dest = Preconditions.checkNotNull(builder.dest); + this.destination = Preconditions.checkNotNull(builder.destination); this.type = Preconditions.checkNotNull(builder.type); this.request = Preconditions.checkNotNull(builder.request); this.amount = builder.amount; this.latency = builder.latency; this.errorCode = builder.errorCode; + this.empty = builder.empty; this.channelError = builder.channelError; } // auto-generated public static class Builder { - private String dest; + private String destination; private CanalPacket.PacketType type; private GeneratedMessage request; private int amount; private long latency; - private int errorCode; + private short errorCode; + private boolean empty; private Throwable channelError; - Builder dest(String dest) { - this.dest = dest; + Builder destination(String destination) { + this.destination = destination; return this; } @@ -100,23 +112,29 @@ public class ChannelFutureAggregator implements ChannelFutureListener { return this; } - Builder errorCode(int errorCode) { + Builder errorCode(short errorCode) { this.errorCode = errorCode; return this; } + Builder empty(boolean empty) { + this.empty = empty; + return this; + } + public Builder channelError(Throwable channelError) { this.channelError = channelError; return this; } public Builder fromPrototype(ClientRequestResult prototype) { - dest = prototype.dest; + destination = prototype.destination; type = prototype.type; request = prototype.request; amount = prototype.amount; latency = prototype.latency; errorCode = prototype.errorCode; + empty = prototype.empty; channelError = prototype.channelError; return this; } @@ -126,8 +144,8 @@ public class ChannelFutureAggregator implements ChannelFutureListener { } } // getters - public String getDest() { - return dest; + public String getDestination() { + return destination; } public CanalPacket.PacketType getType() { @@ -146,10 +164,14 @@ public class ChannelFutureAggregator implements ChannelFutureListener { return latency; } - public int getErrorCode() { + public short getErrorCode() { return errorCode; } + public boolean getEmpty() { + return empty; + } + public Throwable getChannelError() { return channelError; } diff --git a/sink/src/main/java/com/alibaba/otter/canal/sink/entry/EntryEventSink.java b/sink/src/main/java/com/alibaba/otter/canal/sink/entry/EntryEventSink.java index 6511e6ba..23c5071b 100644 --- a/sink/src/main/java/com/alibaba/otter/canal/sink/entry/EntryEventSink.java +++ b/sink/src/main/java/com/alibaba/otter/canal/sink/entry/EntryEventSink.java @@ -39,6 +39,7 @@ public class EntryEventSink extends AbstractCanalEventSink> handler : getHandlers()) { events = handler.before(events); } - + long blockingStart = 0L; int fullTimes = 0; do { if (eventStore.tryPut(events)) { + if (fullTimes > 0) { + eventsSinkBlockingTime.addAndGet(System.nanoTime() - blockingStart); + } for (CanalEventDownStreamHandler> handler : getHandlers()) { events = handler.after(events); } return true; } else { + if (fullTimes == 0) { + blockingStart = System.nanoTime(); + } applyWait(++fullTimes); } @@ -202,4 +209,8 @@ public class EntryEventSink extends AbstractCanalEventSink