大数据之实时项目 第11天 canal安装&canal代码&kafka发送

上篇:大数据之实时项目 第10天 es查询分时


1、为什么要用canal?

作用:同步mysql使用canal的理由
做拉链表某些情况无法从日志中获取信息,而又无法利用sqoop等EL工具对数据实时的监控
更新redis

2、canal工作原理

如图所示:
在这里插入图片描述

·
canal的工作原理很简单,就是把自己伪装成slave,假装从master复制数据。
在这里插入图片描述


3、了解mysql的binlog

binlog开启binlog的分类
在mysql的配置文件(Linux: /etc/my.cnf , Windows: \my.ini)下,修改配置mysql binlog的格式,那就是有三种,分别是STATEMENT,MIXED,ROW。
在[mysqld] 区块在配置文件中可以选择配置
设置/添加 :log-bin=mysql-binbinlog_format=row
这个表示binlog日志的前缀是mysql-bin ,以后生成的日志文件就是 mysql-bin.123456 的文件后面的数字按顺序生成。 每次mysql重启或者到达单个文件大小的阈值时,新生一个文件,按顺序编号。区别:statement 语句级,binlog会记录每次一执行写操作的语句。相对row模式节省空间,但是可能产生不一致性,比如update tt set create_date=now() 如果用binlog日志进行恢复,由于执行时间不同可能产生的数据就不同。
binlog的优点binlog缺点
节省空间有可能造成数据不一致。
保持数据的绝对一致性。因为不管sql是什么,引用了什么函数,他只记录执行后的效果。占用较大空间。
行级, binlog会记录每次操作后每行记录的变化。
实际操作

(1)编辑/etc/my.cnf
在这里插入图片描述

#添加配置参数
#binlog数据中包含server_id,标识该数据是由那个server同步过来的
server-id = 1
##mysql向文件名前缀添加数字后缀来按顺序创建二进制日志文件如mysql-binlog.000006 
log-bin = mysql-bin
##选择基于行的日志记录方式
binlog_format = ROW

(2)在mysql数据库执行sql脚本(gmall1205.sql)
注意:
自己使用的是sqlyog工具执行,navicat工具执行有点问题
在这里插入图片描述
(3) 虚拟机重新启动mysql服务,执行命令

[root@flink102 ~]# systemctl restart mysqld.service
[root@flink102 ~]# 
//进入mysql目录
[root@flink102 etc]# cd /var/lib/mysql
//查看
[root@flink102 mysql]# ls -l

如图所示:
在这里插入图片描述
sqlyog执行语句

  CALL init_data ('2019-06-04',10,2,TRUE)

在这里插入图片描述
注意,我们还需要给mysql赋权限

//赋canal权限
  GRANT  ALL PRIVILEGES ON *.* TO canal@'%' IDENTIFIED BY 'canal'

4、canal安装

(1)上传canal安装包到虚拟机上,并解压文件
在这里插入图片描述

//解压文件
[root@flink102 canal]# tar -zxvf canal.deployer-1.1.2.tar.gz -C module/

(2) 配置canal文件

[root@flink102 conf]# vim example/instance.properties 

cannal.instance.mysql.slaveId=20
canal.instance.master.address=Flink102:3306

在这里插入图片描述
(3) 启动canal

[root@flink102 bin]# ll
total 16
-rwxr-xr-x 1 root root   39 Nov 26  2018 restart.sh
-rwxr-xr-x 1 root root 1145 Nov 26  2018 startup.bat
-rwxr-xr-x 1 root root 2956 Nov 26  2018 startup.sh
-rwxr-xr-x 1 root root 1356 Nov 26  2018 stop.sh
//启动canal
[root@flink102 bin]# ./startup.sh 
//查看进程
[root@flink102 bin]# jps
6672 CanalLauncher   //这个canal进来成功启动了
2131 Kafka
6697 Jps
[root@flink102 bin]# 

在这里插入图片描述


5、canal客户端代码

(1)先创建一个子模块gmall0315-canal
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
子模块canal创建ok
在这里插入图片描述
(2)pom文件依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>dw-char</artifactId>
        <groupId>com.study.gmall0315</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>gmall0315-canal</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.study.gmall0315</groupId>
            <artifactId>gmall0315-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.alibaba.otter/canal.client -->
        <dependency>
            <groupId>com.alibaba.otter</groupId>
            <artifactId>canal.client</artifactId>
            <version>1.1.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka-clients -->
        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-clients</artifactId>
            <version>0.11.0.0</version>
        </dependency>

    </dependencies>

</project>

(3) 编写代码
在这里插入图片描述

CanalApp.java

package com.study.gmall0315.canal.app;

import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.alibaba.otter.canal.protocol.Message;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.study.gmall0315.canal.handler.CanlHandler;

import java.net.InetSocketAddress;
import java.util.List;

public class CanalApp {
    public static void main(String[] args) {
        //初始化创建canal的连接器
        CanalConnector canalConnector = CanalConnectors.newSingleConnector(new InetSocketAddress("Flink102", 11111), "example", " ", " ");
        //不停往canal抓数据
        while (true){
            //连接、订阅、抓取数据
         canalConnector.connect();
         canalConnector.subscribe("gmall0315.order_info");
            //抓数据不一定都有100条
             Message message = canalConnector.get(100);
             int size = message.getEntries().size();
             //判断,如果抓数据为0,表示没抓到
            if(size==0){
                try {
                    System.out.println("没有数据,休息一会");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else {
                //遍历
                for (CanalEntry.Entry entry  : message.getEntries()) {
                    //判断事件类型,只处理行变化业务
                    if (entry.getEntryType().equals(CanalEntry.EntryType.ROWDATA)){
                        //把数据集进行反序列化
                         ByteString storeValue = entry.getStoreValue();
                        CanalEntry.RowChange rowChange=null;
                        try {
                            //ByteString类型无法直接使用,需要做类型转换
                             rowChange = CanalEntry.RowChange.parseFrom(storeValue);
                            //InvalidProtocolBufferException序列化格式,目前是市面上最快的
                        } catch (InvalidProtocolBufferException e) {
                            e.printStackTrace();
                        }
                        //获得行集
                         List<CanalEntry.RowData> rowDatasList = rowChange.getRowDatasList();
                        //操作类型
                         CanalEntry.EventType eventType = rowChange.getEventType();
                         //操作表名
                         String tableName = entry.getHeader().getTableName();
                        //传参数:名字、类型、数据
                        CanlHandler.handle(tableName,eventType,rowDatasList);
                    }

                }
            }
        }

    }
}

CanlHandler.java

package com.study.gmall0315.canal.handler;

import com.alibaba.otter.canal.protocol.CanalEntry;

import java.util.List;

public class CanlHandler {
    //执行表名的方法
    public static void handle(String tableName, CanalEntry.EventType eventType, List<CanalEntry.RowData> rowDatasList ){
        //判断
        if ("order_info".equals(tableName)&&CanalEntry.EventType.INSERT.equals(eventType)){
           //下单操作遍历
            for (CanalEntry.RowData rowData : rowDatasList) {
                //行集展开
                 List<CanalEntry.Column> afterColumnsList = rowData.getAfterColumnsList();
                 //再次循环打印
                ///列集展开
                for (CanalEntry.Column column : afterColumnsList) {
                  //打印
                    System.out.println(column.getName() + ":::" + column.getValue());


                }
            }
            
        }
    }

}

(4) 运行程序,控制台打印信息:
在这里插入图片描述
之后,在mysql客户端工具执行语句

CALL `init_data`('2019-06-04',10,2,FALSE)

再次,进入文件目录查看/var/lib/mysql

[root@flink102 ~]# cd /var/lib/mysql
[root@flink102 mysql]# ls -l

数据进来了
在这里插入图片描述


6、汇总canal的关系

CanalClient
message:一次cabal从日志中抓取的信息,一个message包括多个sql(even)
entry:相当于一个sql命令,一个sql可能会对 多行记录造成影响,分为三当面:1、type–>用于区分是数据变化,还是事务变化;2、header.tableName表名;3、storevalue得到rowchage
rowchage:entry经过反序列化得到对象,包括了多行记录的变化值,包括两方面:1、eventype–>数据的变化类型:insert、update、delete、create、alter、drop ; 2、rowdatalist
RowDatas:一个rowchage里包括的数据变化集,其中每一个rowdata里面包含一行的多字段:afterColumnList、beforeColumnList
Column:一个RowData里包含了多个column,每个column包含name和value,即:columnName、columnValue

7、发送kafka

(1) 代码实现
在这里插入图片描述
CanlHandler类的kafka代码

//kafka操作
JSONObject jsonObject = new JSONObject();
  //转成小写工具
 String propertyName = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, column.getName());
 jsonObject.put(propertyName,column.getValue());

                }
 MyKafkaSender.send(GmallConstant.KAFKA_TOPIC_ORDER,jsonObject.toJSONString());

在这里插入图片描述

MyKafkaSender.java

package com.study.gmall0315.canal.util;

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;

import java.util.Properties;

public class MyKafkaSender {
    public static KafkaProducer<String, String> kafkaProducer=null;


    public  static KafkaProducer<String, String> createKafkaProducer(){
        Properties properties = new Properties();
        properties.put("bootstrap.servers", "Flink102:9092");
        properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        KafkaProducer<String, String> producer = null;
        try {
            producer = new KafkaProducer<String, String>(properties);

        }catch (Exception e){
            e.printStackTrace();
        }
        return producer;
    }

    public static  void send(String topic,String msg){
        if(kafkaProducer==null){
            kafkaProducer=createKafkaProducer();
        }
        kafkaProducer.send(new ProducerRecord<String, String>(topic,msg));


    }

}

(2)启动kafka和kafka的消费者

//启动kafka
[root@flink102 kafka-2.11]# bin/kafka-server-start.sh config/server.properties &
//启动kafka的消费者
[root@flink102 kafka-2.11]# bin/kafka-console-consumer.sh --bootstrap-server Flink102:9092  --topic  GMALL_ORDER


(3)在mysql的客户端执行sql语句

CALL `init_data`('2019-06-04',10,2,FALSE);

kafka的消费者也将会打印相关的消费数据
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值