Canal Scala API Demo

原理

官方网站

canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议
MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )
canal 解析 binary log 对象(原始为 byte 流)

当前的 canal 支持源端 MySQL 版本包括 5.1.x , 5.5.x , 5.6.x , 5.7.x , 8.0.x

安装

  1. mysql canall用户赋予权限GRANT ALL PRIVILEGES ON *.* TO canal@'%' IDENTIFIED BY 'canal'; 使用root用户省略

  2. 创建/打开 /etc/my.cnf 写入一下配置开启binary log

    [mysqld]
    server-id= 1
    log-bin= mysql-bin
    binlog_format= row
    
    
  3. 重启mysql 生效配置 service mysql restart

  4. 登录mysql检查是否生效 show variables like 'log_%';

    在这里插入图片描述

  5. 下载canal

    wget https://github.com/alibaba/canal/releases/download/canal-1.1.2/canal.deployer-1.1.2.tar.gz
    
  6. 解压到安装目录

  7. 修改 conf/canal.properties 端口号等信息 默认11111

  8. 修改conf/example/instance.properties 针对要追踪的 mysql 的实例配置

    #canal.instance.standby.address =
    #################################################
    ## mysql serverId , v1.0.26+ will autoGen
    # slaveId 必须配置, 不能和 mysql 的 id 重复
    canal.instance.mysql.slaveId=100
    
    # enable gtid use true/false
    canal.instance.gtidon=false
    
    # position info
    # mysql 的位置信息
    canal.instance.master.address=dw-node03:3306
    
    # table regex
    # 要监控的表 正则表达式  表示gmallstream数据库下所有的表
    canal.instance.filter.regex=gmallstream\\..*
    
    # mysql用户名密码
    canal.instance.dbUsername=root	
    canal.instance.dbPassword=123456
    canal.instance.connectionCharset=utf-8
    

启动与关闭

启动: bin/starup.sh 会开启CanalLauncher jps进程

关闭: bin/stop.sh

案例:读取数据到kafka

Scala API 写入

package com.chen.gmall.canal

import java.net.InetSocketAddress
import java.util

import com.alibaba.fastjson.JSONObject
import com.alibaba.otter.canal.client.{CanalConnector, CanalConnectors}
import com.alibaba.otter.canal.protocol.CanalEntry.{EntryType, EventType, RowChange}
import com.alibaba.otter.canal.protocol.{CanalEntry, Message}
import com.google.protobuf.ByteString
import scala.collection.JavaConversions._

object CanalClient {
  def main(args: Array[String]): Unit = {
    val address: InetSocketAddress = new InetSocketAddress("ubuntu", 11111)
    // 1. 链接到cannl example 对应conf/example/instance.properties中的实力目录
    val connector: CanalConnector = CanalConnectors.newSingleConnector(address, "example", "", "")  
    // 连接
    connector.connect()
    // 订阅 gmallstream中所有的表
    connector.subscribe("gmallstream.*")
    // 读取数据
    while (true) {
      // 一次拉取最多100条sql语句生成的数据
      val msg: Message = connector.get(100)
      val entriesOption: Option[util.List[CanalEntry.Entry]] = if (msg.getEntries != null) Some(msg.getEntries) else None
      if (entriesOption.isDefined && entriesOption.get.nonEmpty) {
        val entries: util.List[CanalEntry.Entry] = entriesOption.get
        for (entry <- entries) {
          // 保证是ROWDATA类型
          if (entry != null && entry.hasEntryType && entry.getEntryType == EntryType.ROWDATA) {
            // 从每个entry中获取storeValue
            val storeValue: ByteString = entry.getStoreValue
            // 使用RowChange 解析
            val rowChange: RowChange = RowChange.parseFrom(storeValue)
            // 一个storeValue 中有多个rowdatas 每个rowdatas 表示一行数据的变化
            val rowdatas: util.List[CanalEntry.RowData] = rowChange.getRowDatasList
            // 解析rowdatas 中每行每列数据
            handleData(entry.getHeader.getTableName, rowdatas, rowChange.getEventType)
          }
        }
      } else {
        println("未拉取到数据 2s之后重新拉去")
        Thread.sleep(2000)
      }
    }
    // 2. 读取数据

  }

  /**
   * 解析rowdatas 中每行每列数据
   *
   */
  def handleData(tableName: String, rowDatas: util.List[CanalEntry.RowData], eventType: CanalEntry.EventType) = {
    // 判断是否是要处理的表 只获取insert 数据
    if ("order_info" == tableName && eventType == EventType.INSERT && rowDatas != null && rowDatas.nonEmpty) {
      for (rowData <- rowDatas) {
        val result = new JSONObject()
        // 1. 获取变化后的列使用After    between为变化前的列
        val columeList: util.List[CanalEntry.Column] = rowData.getAfterColumnsList
        // 2. 取出一行
        for (colume <- columeList) {
          val key: String = colume.getName //列名
          val value: String = colume.getValue //列的值
          result.put(key, value)
        }
        println(result.toJSONString)
      }
    }
  }
}

配置文件直接写入

https://github.com/alibaba/canal/wiki/Canal-Kafka-RocketMQ-QuickStart

下面是一个使用 Canal 客户端连接 Canal 集群的简单示例代码,可以供参考: ```java import com.alibaba.otter.canal.client.CanalConnector; import com.alibaba.otter.canal.client.CanalConnectors; import com.alibaba.otter.canal.common.utils.AddressUtils; import com.alibaba.otter.canal.protocol.CanalEntry; import com.alibaba.otter.canal.protocol.Message; import java.net.InetSocketAddress; import java.util.List; public class CanalClientDemo { public static void main(String[] args) { // 创建连接器对象 CanalConnector connector = CanalConnectors.newSingleConnector( new InetSocketAddress(AddressUtils.getHostIp(), 11111), "example", "", ""); try { // 连接 Canal 服务器 connector.connect(); connector.subscribe(".*\\..*"); connector.rollback(); // 循环读取 Canal 中的数据变更 while (true) { Message message = connector.getWithoutAck(100); long batchId = message.getId(); int size = message.getEntries().size(); if (batchId == -1 || size == 0) { Thread.sleep(1000); } else { List<CanalEntry.Entry> entries = message.getEntries(); for (CanalEntry.Entry entry : entries) { if (entry.getEntryType() == CanalEntry.EntryType.ROWDATA) { CanalEntry.RowChange rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue()); String schemaName = entry.getHeader().getSchemaName(); String tableName = entry.getHeader().getTableName(); String eventType = rowChange.getEventType().toString(); for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) { // 处理数据变更 System.out.println(schemaName + "." + tableName + ": " + eventType + " " + rowData); } } } connector.ack(batchId); } } } catch (Exception e) { e.printStackTrace(); } finally { connector.disconnect(); } } } ``` 以上代码实现了一个简单的 Canal 客户端,连接到 Canal 服务器后,订阅所有的数据库和表,并循环读取 Canal 中的数据变更。对于每个数据变更,打印出相关的信息,并调用 ack() 方法确认消息已经处理完毕。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值