@Override
  protected EntryPosition findStartPosition(ErosaConnection connection) {
    // 处理逻辑
    // 1. 首先查询上一次解析成功的最后一条记录
    // 2. 存在最后一条记录,判断一下当前记录是否发生过主备切换
    // // a. 无机器切换,直接返回
    // // b. 存在机器切换,按最后一条记录的stamptime进行查找
    // 3. 不存在最后一条记录,则从默认的位置开始启动
    LogPosition logPosition = logPositionManager.getLatestIndexBy(destination);
    if (logPosition == null) { // 找不到历史成功记录
      EntryPosition entryPosition = masterPosition;

      // 判断一下是否需要按时间订阅
      if (StringUtils.isEmpty(entryPosition.getJournalName())) {
        // 如果没有指定binlogName,尝试按照timestamp进行查找
        if (entryPosition.getTimestamp() != null) {
          return new EntryPosition(entryPosition.getTimestamp());
        }
      } else {
        if (entryPosition.getPosition() != null) {
          // 如果指定binlogName + offest,直接返回
          return entryPosition;
        } else {
          return new EntryPosition(entryPosition.getTimestamp());
        }
      }
    } else {
      return logPosition.getPostion();
    }

    return null;
  }
  /** 检查是否存在需要get的数据,并且数量>=batchSize */
  private boolean checkUnGetSlotAt(LogPosition startPosition, int batchSize) {
    if (batchMode.isItemSize()) {
      long current = getSequence.get();
      long maxAbleSequence = putSequence.get();
      long next = current;
      if (startPosition == null
          || !startPosition.getPostion().isIncluded()) { // 第一次订阅之后,需要包含一下start位置,防止丢失第一条记录
        next = next + 1; // 少一条数据
      }

      if (current < maxAbleSequence && next + batchSize - 1 <= maxAbleSequence) {
        return true;
      } else {
        return false;
      }
    } else {
      // 处理内存大小判断
      long currentSize = getMemSize.get();
      long maxAbleSize = putMemSize.get();

      if (maxAbleSize - currentSize >= batchSize * bufferMemUnit) {
        return true;
      } else {
        return false;
      }
    }
  }
Exemple #3
0
  /** 根据给定的时间戳,在指定的binlog中找到最接近于该时间戳(必须是小于时间戳)的一个事务起始位置。针对最后一个binlog会给定endPosition,避免无尽的查询 */
  private EntryPosition findAsPerTimestampInSpecificLogFile(
      MysqlConnection mysqlConnection,
      final Long startTimestamp,
      final EntryPosition endPosition,
      final String searchBinlogFile) {

    final LogPosition logPosition = new LogPosition();
    try {
      mysqlConnection.reconnect();
      // 开始遍历文件
      mysqlConnection.seek(
          searchBinlogFile,
          4L,
          new SinkFunction<LogEvent>() {

            private LogPosition lastPosition;

            public boolean sink(LogEvent event) {
              EntryPosition entryPosition = null;
              try {
                CanalEntry.Entry entry = parseAndProfilingIfNecessary(event);
                if (entry == null) {
                  return true;
                }

                String logfilename = entry.getHeader().getLogfileName();
                Long logfileoffset = entry.getHeader().getLogfileOffset();
                Long logposTimestamp = entry.getHeader().getExecuteTime();

                if (CanalEntry.EntryType.TRANSACTIONBEGIN.equals(entry.getEntryType())) {
                  logger.debug(
                      "compare exit condition:{},{},{}, startTimestamp={}...",
                      new Object[] {logfilename, logfileoffset, logposTimestamp, startTimestamp});
                  // 寻找第一条记录时间戳,如果最小的一条记录都不满足条件,可直接退出
                  if (logposTimestamp >= startTimestamp) {
                    return false;
                  }
                }

                if (StringUtils.equals(endPosition.getJournalName(), logfilename)
                    && endPosition.getPosition() <= (logfileoffset + event.getEventLen())) {
                  return false;
                }

                // 记录一下上一个事务结束的位置,即下一个事务的position
                // position = current + data.length,代表该事务的下一条offest,避免多余的事务重复
                if (CanalEntry.EntryType.TRANSACTIONEND.equals(entry.getEntryType())) {
                  entryPosition =
                      new EntryPosition(
                          logfilename, logfileoffset + event.getEventLen(), logposTimestamp);
                  logger.debug(
                      "set {} to be pending start position before finding another proper one...",
                      entryPosition);
                  logPosition.setPostion(entryPosition);
                }

                lastPosition = buildLastPosition(entry);
              } catch (Exception e) {
                processError(e, lastPosition, searchBinlogFile, 4L);
              }

              return running;
            }
          });

    } catch (IOException e) {
      logger.error("ERROR ## findAsPerTimestampInSpecificLogFile has an error", e);
    }

    if (logPosition.getPostion() != null) {
      return logPosition.getPostion();
    } else {
      return null;
    }
  }
Exemple #4
0
  protected EntryPosition findStartPositionInternal(ErosaConnection connection) {
    MysqlConnection mysqlConnection = (MysqlConnection) connection;
    LogPosition logPosition = logPositionManager.getLatestIndexBy(destination);
    if (logPosition == null) { // 找不到历史成功记录
      EntryPosition entryPosition = null;
      if (masterInfo != null
          && mysqlConnection.getConnector().getAddress().equals(masterInfo.getAddress())) {
        entryPosition = masterPosition;
      } else if (standbyInfo != null
          && mysqlConnection.getConnector().getAddress().equals(standbyInfo.getAddress())) {
        entryPosition = standbyPosition;
      }

      if (entryPosition == null) {
        entryPosition = findEndPosition(mysqlConnection); // 默认从当前最后一个位置进行消费
      }

      // 判断一下是否需要按时间订阅
      if (StringUtils.isEmpty(entryPosition.getJournalName())) {
        // 如果没有指定binlogName,尝试按照timestamp进行查找
        if (entryPosition.getTimestamp() != null && entryPosition.getTimestamp() > 0L) {
          logger.warn(
              "prepare to find start position {}:{}:{}",
              new Object[] {"", "", entryPosition.getTimestamp()});
          return findByStartTimeStamp(mysqlConnection, entryPosition.getTimestamp());
        } else {
          logger.warn("prepare to find start position just show master status");
          return findEndPosition(mysqlConnection); // 默认从当前最后一个位置进行消费
        }
      } else {
        if (entryPosition.getPosition() != null && entryPosition.getPosition() > 0L) {
          // 如果指定binlogName + offest,直接返回
          logger.warn(
              "prepare to find start position {}:{}:{}",
              new Object[] {entryPosition.getJournalName(), entryPosition.getPosition(), ""});
          return entryPosition;
        } else {
          EntryPosition specificLogFilePosition = null;
          if (entryPosition.getTimestamp() != null && entryPosition.getTimestamp() > 0L) {
            // 如果指定binlogName + timestamp,但没有指定对应的offest,尝试根据时间找一下offest
            EntryPosition endPosition = findEndPosition(mysqlConnection);
            if (endPosition != null) {
              logger.warn(
                  "prepare to find start position {}:{}:{}",
                  new Object[] {entryPosition.getJournalName(), "", entryPosition.getTimestamp()});
              specificLogFilePosition =
                  findAsPerTimestampInSpecificLogFile(
                      mysqlConnection,
                      entryPosition.getTimestamp(),
                      endPosition,
                      entryPosition.getJournalName());
            }
          }

          if (specificLogFilePosition == null) {
            // position不存在,从文件头开始
            entryPosition.setPosition(BINLOG_START_OFFEST);
            return entryPosition;
          } else {
            return specificLogFilePosition;
          }
        }
      }
    } else {
      if (logPosition
          .getIdentity()
          .getSourceAddress()
          .equals(mysqlConnection.getConnector().getAddress())) {
        logger.warn("prepare to find start position just last position");
        return logPosition.getPostion();
      } else {
        // 针对切换的情况,考虑回退时间
        long newStartTimestamp =
            logPosition.getPostion().getTimestamp() - fallbackIntervalInSeconds * 1000;
        logger.warn(
            "prepare to find start position by switch {}:{}:{}",
            new Object[] {"", "", logPosition.getPostion().getTimestamp()});
        return findByStartTimeStamp(mysqlConnection, newStartTimestamp);
      }
    }
  }
  private Events<Event> doGet(Position start, int batchSize) throws CanalStoreException {
    LogPosition startPosition = (LogPosition) start;

    long current = getSequence.get();
    long maxAbleSequence = putSequence.get();
    long next = current;
    long end = current;
    // 如果startPosition为null,说明是第一次,默认+1处理
    if (startPosition == null
        || !startPosition.getPostion().isIncluded()) { // 第一次订阅之后,需要包含一下start位置,防止丢失第一条记录
      next = next + 1;
    }

    if (current >= maxAbleSequence) {
      return new Events<Event>();
    }

    Events<Event> result = new Events<Event>();
    List<Event> entrys = result.getEvents();
    long memsize = 0;
    if (batchMode.isItemSize()) {
      end = (next + batchSize - 1) < maxAbleSequence ? (next + batchSize - 1) : maxAbleSequence;
      // 提取数据并返回
      for (; next <= end; next++) {
        Event event = entries[getIndex(next)];
        if (ddlIsolation && isDdl(event.getEntry().getHeader().getEventType())) {
          // 如果是ddl隔离,直接返回
          if (entrys.size() == 0) {
            entrys.add(event); // 如果没有DML事件,加入当前的DDL事件
            end = next; // 更新end为当前
          } else {
            // 如果之前已经有DML事件,直接返回了,因为不包含当前next这记录,需要回退一个位置
            end = next - 1; // next-1一定大于current,不需要判断
          }
          break;
        } else {
          entrys.add(event);
        }
      }
    } else {
      long maxMemSize = batchSize * bufferMemUnit;
      for (; memsize <= maxMemSize && next <= maxAbleSequence; next++) {
        // 永远保证可以取出第一条的记录,避免死锁
        Event event = entries[getIndex(next)];
        if (ddlIsolation && isDdl(event.getEntry().getHeader().getEventType())) {
          // 如果是ddl隔离,直接返回
          if (entrys.size() == 0) {
            entrys.add(event); // 如果没有DML事件,加入当前的DDL事件
            end = next; // 更新end为当前
          } else {
            // 如果之前已经有DML事件,直接返回了,因为不包含当前next这记录,需要回退一个位置
            end = next - 1; // next-1一定大于current,不需要判断
          }
          break;
        } else {
          entrys.add(event);
          memsize += calculateSize(event);
          end = next; // 记录end位点
        }
      }
    }

    PositionRange<LogPosition> range = new PositionRange<LogPosition>();
    result.setPositionRange(range);

    range.setStart(CanalEventUtils.createPosition(entrys.get(0)));
    range.setEnd(CanalEventUtils.createPosition(entrys.get(result.getEvents().size() - 1)));
    // 记录一下是否存在可以被ack的点

    for (int i = entrys.size() - 1; i >= 0; i--) {
      Event event = entrys.get(i);
      if (CanalEntry.EntryType.TRANSACTIONBEGIN == event.getEntry().getEntryType()
          || CanalEntry.EntryType.TRANSACTIONEND == event.getEntry().getEntryType()
          || isDdl(event.getEntry().getHeader().getEventType())) {
        // 将事务头/尾设置可被为ack的点
        range.setAck(CanalEventUtils.createPosition(event));
        break;
      }
    }

    if (getSequence.compareAndSet(current, end)) {
      getMemSize.addAndGet(memsize);
      notFull.signal();
      return result;
    } else {
      return new Events<Event>();
    }
  }