Flink1.13版本能用 CDC2.0版本
代码敲三遍:
正常情况下DWD层(明细层)直接到DWS层,
DWS是一个宽表,说明会需要DWD层的多张表进行构建,多个宽表可能会公用DWD层的一些明细表。可能会存在对明细表进行加工,可能会有重复加工的操作。
所以避免重复加工操作。
比如说 在不同维度下,如地区维度,访客维度,他们的UV指标都需要对page_log中的mid要进行去重操作,这样就重复计算了
所以可以将mid去重操作后的page_log存放在DWM层中,可以直接拿取地区维度,访客维度,的UV指标
视频P073
分层, 提高数数据复用性,会牺牲一定的时效性。
kafka?
维度数据最重要的是查询,事实表根据ID去查维度表来补充事实数据。
Kafka默认保存数据为7天,维度数据不能删除,需要永久保存。
Kafka查询数据不方便。
解放方法: 1. 更改源码,不使用文件名进行判断,只看iNode值; 2. 要使用不更名的打印日志框架(logback)
不可以,因为存在零点飘逸,22.30挂了,还持续给log中写数据,然后过了零点后文件更名就监控不到,然后数据丢失了
数据流,监控,优化,配置。
知识点相关的东西一定要记牢
Kafka:
Producer:ACK 0 1 -1;拦截器,序列化器,分区器;发送流程;幂等性,事务;分区规则–》有分区则发往指定分区,没有指定分区,则根据Kay值hash,没有指定分区也没有Key的时候,轮询(粘性)
如何保证生产者不丢数据? 问的是ACK
Broker:
Topic:副本(高可靠,ISR:LEO、HW),分区(高并发,负载均衡防止热点)
Comsumer:
分区分配规则: range,roundRobin,Sticky
offset保存:默认保存在__consumer_offsets主题,其他:手动维护Offset《Mysql》 ,保存数据&保存Offset写到一个事务,支持事务可以做到精准一次消费
先保证数据后保存Offset 重复数据+幂等性(精准一次性消费)
先保存Offset后保存数据 丢失数据
优化,ist
解析器,编译器,优化器,执行器
数据存储地方不一样
FlinkCDC放在CheckPoint里
MaxWell放在MySQL里
Canal放在本地磁盘里
都支持断点续传
新增数据时, 涉及到多行语句的更新,Canal默认只有一个更新语句,所有的数据都在一起,不方便操作。
项目 | Value | 地方 | 地方 |
---|---|---|---|
sql与数据的关系 | 无 | 无 | 一对一(炸开) |
初始化功能 | 有(多库多表) | 有(单表) | 无(单独查询) |
断点续传 | CK | MySQL | 本地磁盘 |
封装格式 | 自定义 | json | json(c/s自定义) |
高可用 | 运行集群高可用 | 无 | 集群(ZK) |
1. 行为采集
行为采集框架: FlinkCDC, Maxwell, Canal
2. 动态分流
业务数据采集:
业务数据库有非常多的表,如何进行拆分呢?
以及当业务数据增加了一张表之后,我们如何在不停止计算任务的情况下,能够动态的将新增加的表数据采集到呢?–动态分流
3. 多流join, 多流union
事实表的关联
4. 关联维表
旁路缓存—异步IO
新,Flink 1.12.0
全
细
语言:Java
框架:Hadoop Kafka Hbase Zookeeper Redis
为什么要分层?
分为几层?这几层分别存到哪?为什么要这样做?
离线数仓为什么要分层?复杂问题简单化;较少重复计算,提高复用性;解耦,隔离原始数据
普通的实时计算优先考虑时效性,所以从数据源采集经过实时计算直接得到结果。如此做时效性更好,但是弊端是由于计算过程中的中间结果没有沉淀下来,所以当面对大量实时需求的时候,计算的复用性较差,开发成本随着需求增加直线上升。
实时数仓基于一定的数据仓库理念,对数据处理流程进行规划、分层,目的是提高数据的复用性。
最重要的区别是分层, 提高数数据复用性,会牺牲一定的时效性。
分了五层,
ODS:原始数据,日志和业务数据,放在Kafka中,因为要进行实时计算
DWD:根据数据对象为单位进行分流,比如订单、页面访问等等,可以通过测输出流来达到分流效果
DIM:维度数据,放在了Hbase
DWM:对于部分数据对象进行进一步加工,比如独立访问、跳出行为,也可以和维度进行关联,形成宽表,依旧是明细数据。公用的部分抽取出来了,放在了Kafka
DWS:根据某个主题将多个事实数据轻度聚合,形成主题宽表。放在了clickHouse中
ADS:把ClickHouse中的数据根据可视化需进行筛选聚合。不落盘,数据接口
听个热闹
离线计算:就是在计算开始前已知所有输入数据,输入数据不会产生变化,一般计算量级较大,计算时间也较长。例如今天早上一点,把昨天累积的日志,计算出所需结果。最经典的就是 Hadoop 的 MapReduce 方式;
一般是根据前一日的数据生成报表,虽然统计指标、报表繁多,但是对时效性不敏感。从技术操作的角度,这部分属于批处理的操作。即根据确定范围的数据一次性计算。
实时计算:输入数据是可以以序列化的方式一个个输入并进行处理的,也就是说在开始的时候并不需要知道所有的输入数据。与离线计算相比,运行时间短,计算量级相对较小。
强调计算过程的时间要短,即所查当下给出结果。主要侧重于对当日数据的实时监控,通常业务逻辑相对离线需求简单一下,统计指标也少一些,但是更注重数据的时效性,以及用户的交互性。从技术操作的角度,这部分属于流处理的操作。根据数据源源不断地到达进行实时的运算。
即席查询:需求的临时性
离线计算和实时计算: 需求的固定性
Presto: 当场计算(基于内存速度快)
Kylin:预计算(提前算好),多维分析(Hive With Cube)
自己能说出来,
对比两种架构
架构分析:
Sqoop导入数据的方式:全量,增量,新增及变化,特殊
Flume:
不能用Sqoop进行导入了,原理是MR,太慢了。
可以采用Canal/Maxwell/FlickCDC都是监控binlog的方式(行级别)。
区别1:行为数据由日志服务器直接发送到了Kafka,没有经过落盘处理。(好处:快,减少磁盘IO;缺点:耦合性高,)
ODS只有俩个主题:行为数据和业务数据
DWD要用Flink将ODS层的数据消费,使用测输出流分流分到不同的主题里面。
Flink会再次使用消费DWD的数据,并且可能关联维度表Hbase,形成DWM的数据
然后Flink会消费DWD和DWM的数据再写到Click House
理线架构:
优点:耦合性低,稳定性高。
缺点:时效性差一点
读取数据先落盘,再从磁盘中读取出来,为什么不直接拿过来用呢?因为
1.项目经理是大公司出来的,追求系统的稳定性。
2.耦合性低,稳定性高。
3.考虑到工作未来的发展,数据量一定会变得很大。
4.早期的时候实时业务使用的是SparkStreaming(微批次)
实时架构:
优点:时效性好
缺点:耦合性低,稳定性低。
说明:1.时效性好
2.Kakfa集群高可用,挂一台两台没有问题
3.数据量小,所有机器存在于同一个机房,传输没有问题
4.架构是公司项目经理架构师定的
mock代替web/app,
先写SpringBoot代码,先用直连的方式做测试(单机部署)
然后搭建Nginx做负载均衡,再连接102,103,104进行集群测试。
这里提供了一个模拟生成数据的 jar 包,可以将日志发送给某一个指定的端口,需要大数据程序员了解如何从指定端口接收数据并数据进行处理的流程。
Lombok: 添加依赖
SpringBoot: Controller,Service,DAO(Mapper),DAO再对持久化层进行操作
1) 有了 springboot 我们就可以…
不再需要那些千篇一律,繁琐的 xml 文件。
➢ 内嵌 Tomcat,不再需要外部的 Tomcat
➢ 更方便的和各个第三方工具(mysql,redis,elasticsearch,dubbo,kafka 等等整合),而只要维护一个配置文件即可。
2) springboot 和 ssm 的关系
springboot 整合了 springmvc,spring 等核心功能。也就是说本质上实现功能的还是原有的 spring ,springmvc 的包,但是 springboot 单独包装了一层,这样用户就不必直接对 springmvc,spring 等,在 xml 中配置。
3) 没有 xml,我们要去哪配置
springboot 实际上就是把以前需要用户手工配置的部分,全部作为默认项。除非用户需要额外更改不然不用配置。这就是所谓的:“约定大于配置”
如果需要特别配置的时候,去修改application.properties(application.yml)
package www.atguigu.gmalllogger.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
//@Controller
@RestController // @Controller + @ResponseBody
@Slf4j
public class LoggerController {
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
@RequestMapping("test")
//@ResponseBody // 表示不返回页面,返回普通的java对象
public String test1(){
System.out.println("success");
return "success.html";
}
@RequestMapping("test2")
public String test2(@RequestParam("name") String nn,
@RequestParam(value="age", defaultValue = "13") int age){
System.out.println(nn + ": " + age);
return "success";
}
@RequestMapping("applog")
public String getLog(@RequestParam("param") String jsonStr){
// 打印数据
//System.out.println(jsonStr);
// 将数据落盘
log.info(jsonStr);
// log.warn();
// log.error();
// log.trace();
// log.debug();
// 将数据写入Kafka
kafkaTemplate.send("ods_base_log", jsonStr);
return "success";
}
}
# 应用名称
spring.application.name=gmall-logger
# 应用服务 WEB 访问端口
server.port=8081
#============== kafka ===================
# 指定 kafka 代理地址,可以多个
spring.kafka.bootstrap-servers=hadoop102:9092
# 指定消息 key 和消息体的编解码方式
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOG_HOME" value="f:/logs" />
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<appender name="rollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/app.%d{yyyy-MM-dd}.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<!-- 将某一个包下日志单独打印日志 -->
<logger name="www.atguigu.gmalllogger.controller.LoggerController"
level="INFO" additivity="false">
<appender-ref ref="rollingFile" />
<appender-ref ref="console" />
</logger>
<root level="error" additivity="false">
<appender-ref ref="console" />
</root>
</configuration>
zk.sh start
kf.sh start
bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic ods_base_log
<property name="LOG_HOME" value="/opt/module/gmall-flink/rt_applog/logs" />
注意:路径和上面创建的路径保持一致,根据自己的实际情况进行修改
/opt/module/gmall-flink/rt_applog 目录下
[atguigu@hadoop102 rt_applog]$ ll
总用量 29984
-rw-rw-r--. 1 atguigu atguigu 30700347 8 月 10 11:35 gmall2021-logger-0.0.1-SNAPSHOT.jar
#http 模式下,发送的地址
mock.url=http://hadoop102:8081/applog
➢ 运行 hadoop102 上的 rt_gmall 下的日志处理 jar 包
➢ 运行 rt_applog 下的 jar 包
➢ 启动 kafka 消费者进行测试
bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic ods_base_log
[atguigu@hadoop102 module]$ xsync gmall-flink
发送到的服务器路径修改为 nginx 的
[atguigu@hadoop102 rt_applog]$ vim application.yml
# 外部配置打开
#logging.config=./logback.xml
#业务日期
mock.date=2020-07-13
#模拟数据发送模式
mock.type=http
#http 模式下,发送的地址
mock.url=http://hadoop102/applog
➢ 运行 kafka 消费者,准备消费数据
bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic ods_base_log
➢ 启动 nginx 服务
/opt/module/nginx/sbin/nginx
➢ 运行采集数据的 jar
[atguigu@hadoop102 rt_applog]$ java -jar gmall2021-logger-0.0.1-SNAPSHOT.jar
[atguigu@hadoop103 rt_applog]$ java -jar gmall2021-logger-0.0.1-SNAPSHOT.jar
[atguigu@hadoop104 rt_applog]$ java -jar gmall2021-logger-0.0.1-SNAPSHOT.jar
➢ 运行模拟生成数据的 jar
[atguigu@hadoop102 rt_applog]$ java -jar gmall2020-mock-log-2020-12-18.jar
将采集日志服务(nginx 和采集日志数据的 jar 启动服务)放到脚本中。
在/home/atguigu/bin 目录下创建 logger.sh,并授予执行权限
#!/bin/bash
JAVA_BIN=/opt/module/jdk1.8.0_212/bin/java
APPNAME=gmall-logger.jar
case $1 in
"start")
{
for i in hadoop102 hadoop103 hadoop104
do
echo "========: $i==============="
ssh $i "$JAVA_BIN -Xms32m -Xmx64m -jar /opt/module/gmall-flink/rt_applog/$APPNAME >/dev/null 2>&1 &"
done
};;
"stop")
{
for i in hadoop102 hadoop103 hadoop104
do
echo "========: $i==============="
ssh $i "ps -ef|grep $APPNAME | grep -v grep|awk '{print \$2}'| xargs kill" >/dev/null 2>&1
done
};;
esac
➢ 运行 kafka 消费者,准备消费数据
[atguigu@hadoop102 kafka]$ bin/kafka-console-consumer.sh --bootstrap-server
hadoop102:9092 –topic ods_base_log
➢ 启动 nginx 服务采集服务集群
[atguigu@hadoop102 rt_applog]$ logger.sh start
➢ 运行模拟生成数据的 jar
[atguigu@hadoop102 rt_applog]$ java -jar gmall2020-mock-log-2020-12-18.jar
FlinkCDC:
DateStream
优点:多库多表
缺点:需要自定义反序列化器(灵活)
FlinkSQL:
优点:不需要自定义反序列化器
缺点:单表查询
[atguigu@hadoop102 module]$ sudo vim /etc/my.cnf
server-id = 1
log-bin=mysql-bin
binlog_format=row
binlog-do-db=gmall2021
注意:binlog-do-db 根据自己的情况进行修改,指定具体要同步的数据库
sudo systemctl restart mysqld
到/var/lib/mysql 目录下查看初始文件大小 154
➢ 把 / 资 料 / 数据生成脚本 / 业 务 数 据 里 面 的 jar 和 properties 文 件 上 传 到/opt/module/gmall-flink/rt_db 目录下
➢ 修改 application.properties 中数据库连接信息
注意:如果生成较慢,可根据配置情况适当调整配置项
➢ 运行 jar 包
[atguigu@hadoop102 rt_dblog]$ java -jar gmall2020-mock-db-2020-11-27.jar
➢ 再次到/var/lib/mysql 目录下,查看 index 文件的大小
1)在 pom.xml 添加如下配置
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
<java.version>1.8java.version>
<maven.compiler.source>${java.version}maven.compiler.source>
<maven.compiler.target>${java.version}maven.compiler.target>
<flink.version>1.12.0flink.version>
<scala.version>2.12scala.version>
<hadoop.version>3.1.3hadoop.version>
properties>
<dependencies>
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-javaartifactId>
<version>${flink.version}version>
dependency>
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-streaming-java_${scala.version}artifactId>
<version>${flink.version}version>
dependency>
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-connector-kafka_${scala.version}artifactId>
<version>${flink.version}version>
dependency>
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-clients_${scala.version}artifactId>
<version>${flink.version}version>
dependency>
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-cep_${scala.version}artifactId>
<version>${flink.version}version>
dependency>
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-jsonartifactId>
<version>${flink.version}version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.68version>
dependency>
<dependency>
<groupId>org.apache.hadoopgroupId>
<artifactId>hadoop-clientartifactId>
<version>${hadoop.version}version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.49version>
dependency>
<dependency>
<groupId>com.alibaba.ververicagroupId>
<artifactId>flink-connector-mysql-cdcartifactId>
<version>1.2.0version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.20version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.25version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.7.25version>
dependency>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-to-slf4jartifactId>
<version>2.14.0version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-assembly-pluginartifactId>
<version>3.0.0version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependenciesdescriptorRef>
descriptorRefs>
configuration>
<executions>
<execution>
<id>make-assemblyid>
<phase>packagephase>
<goals>
<goal>singlegoal>
goals>
execution>
executions>
plugin>
plugins>
build>
2)在 resources 目录下创建 log4j.properties 配置文件
log4j.rootLogger=error,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
package com.atguigu.utils;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer;
public class MyKafaUtil {
public static FlinkKafkaProducer<String> getKafkaProducer(String topic){
return new FlinkKafkaProducer<String>("hadoop102:9092,hadoop103:9092,hadoop104:9092"
,topic
,new SimpleStringSchema() );
}
}
package com.atguigu.app.ods;
import com.alibaba.ververica.cdc.connectors.mysql.MySQLSource;
import com.alibaba.ververica.cdc.connectors.mysql.table.StartupOptions;
import com.alibaba.ververica.cdc.debezium.DebeziumSourceFunction;
import com.atguigu.app.function.CustomerDeserialization;
import com.atguigu.utils.MyKafaUtil;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer;
public class FlinkCDC {
public static void main(String[] args) throws Exception {
// 1. 获取执行环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
// 2. 通过FlinkCDC构建SourceFunction并读取数据
DebeziumSourceFunction<String> sourceFunction = MySQLSource.<String>builder()
.hostname("hadoop102")
.port(3306)
.username("root")
.password("000000")
.databaseList("gmall-flink")
//.tableList("gmall-flink.base_trademark")
// 如果不添加该参数,则消费指定数据库中所有表的数据,如果指定,指定方式为db.table
.deserializer(new CustomerDeserialization())
// initial 获取历史数据,并且监控最新数据
//.startupOptions(StartupOptions.initial())
// laest 直接监控最新数据
.startupOptions(StartupOptions.latest())
.build();
DataStreamSource<String> streamSource = env.addSource(sourceFunction);
// 3. 打印数据 并将数据写入Kafka
streamSource.print();
String sinkTopic = "ods_base_db";
streamSource.addSink(MyKafaUtil.getKafkaProducer(sinkTopic));
// 4. 启动任务
env.execute("FlinkCDC");
}
}
[wanghaha@hadoop102 kafka]$ bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic ods_base_db
点击代码运行即可!
Nginx (“engine x”) 是一个高性能的 HTTP 和反向代理服务器,特点是占有内存少,并发能力强,事实上 nginx 的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用 nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。
作用
正向代理类似一个跳板机,代理访问外部资源。比如:我是一个用户,我访问不了某网站,但是我能访问一个代理服务器,这个代理服务器,它能访问那个我不能访问的网站,于是我先连上代理服务器,告诉它我需要那个无法访问网站的内容,代理服务器去取回来,然后返回给我。
反向代理(Reverse Proxy)方式是指以代理服务器来接受 internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器;
Nginx 是一个 HTTP 的 web 服务器,可以将服务器上的静态文件(如 HTML、图片等)通过 HTTP 协议返回给浏览器客户端。
在网站创立初期,我们一般都使用单台机器对外提供集中式服务。随着业务量的增大,我们一台服务器不够用,此时就会把多台机器组成一个集群对外提供服务,但是,我们网站对外提供的访问入口通常只有一个,比如 www.web.com。那么当用户在浏览器输入www.web.com 进行访问的时候,如何将用户的请求分发到集群中不同的机器上呢,这就是负载均衡要做的事情。
负载均衡通常是指将请求"均匀"分摊到集群中多个服务器节点上执行,这里的均匀是指在一个比较大的统计范围内是基本均匀的,并不是完全均匀
把所有静态资源的访问改为访问 nginx,而不是访问 tomcat,这种方式叫静态代理。因为 nginx 更擅长于静态资源的处理,性能更好,效率更高。
所以在实际应用中,我们将静态资源比如图片、css、html、js 等交给 nginx 处理,而不是由 tomcat 处理。
Nginx 的负载均衡和静态代理结合在一起,我们可以实现动静分离,这是实际应用中常见的一种场景。
动态资源,如 jsp 由 tomcat 或其他 web 服务器完成
静态资源,如图片、css、js 等由 nginx 服务器完成
它们各司其职,专注于做自己擅长的事情动静分离充分利用了它们各自的优势,从而达到更高效合理的架构
➢ 在 hadoop102 上运行 yum,安装相关依赖包
sudo yum -y install openssl openssl-devel pcre pcre-devel zlib zlib-devel gcc gcc-c++
➢ 将/2.资料/工具下的 nginx-1.12.2.tar.gz 上传到/opt/software 下
➢ 在/opt/module/software 下解压缩 nginx-1.12.2.tar.gz 包
➢ 进入解压缩目录,执行
./configure --prefix=/opt/module/nginx
make && make install
--prefix=要安装到的目录
➢ 安装成功后,/opt/module/nginx 目录下结构
➢ 启动 Nginx
在/opt/module/nginx/sbin 目录下执行 ./nginx
原因:nginx 占用 80 端口,默认情况下非 root 用户不允许使用 1024 以下端口
解决:让当前用户的某个应用也可以使用 1024 以下的端口
sudo setcap cap_net_bind_service=+eip /opt/module/nginx/sbin/nginx
注意:要根据自己的实际路径进行配置
➢ 查看启动情况
ps -ef |grep nginx
因为 nginx 不是用 java 写的,所以不能通过 jps 查看
➢ 在浏览器中输入 http://hadoop102/访问
➢ 重启 Nginx
./nginx -s reload
➢ 关闭 Nginx
./nginx -s stop
➢ 通过配置文件启动
./nginx -c /opt/module/nginx/conf/nginx.conf
/opt/module/nginx/sbin/nginx -c /opt/module/nginx/conf/nginx.conf
其中-c 是指定配置文件,而且配置文件路径必须指定绝对路径
➢ 配置检查
当修改 Nginx 配置文件后,可以使用 Nginx 命令进行配置文件语法检查,用于检查 Nginx 配置文件是否正确
/opt/module /nginx/sbin/nginx -c /opt/module/nginx/conf/nginx.conf –t
➢ 如果 80 端口号被占用 httpd
sudo systemctl stop httpd
sudo systemctl disable httpd
➢ 部分机器启动时报错:
/usr/local/nginx/sbin/nginx: error while loading shared libraries: libpcre.so.1: cannot
open shared object file: No such file or directory
解决:ln -s /usr/local/lib/libpcre.so.1 /lib64
模拟数据以后应该发给 nginx, 然后 nginx 再转发给我们的日志服务器.
日志服务器我们会分别配置在 hadoop102,hadoop103,hadoop104 三台设备上.
1. 打开 nginx 配置文件
cd /opt/module/nginx/conf
vim nginx.conf
2. 修改如下配置
http {
# 启动省略 设置变量
upstream logcluster{
server hadoop102:8081 weight=1;
server hadoop103:8081 weight=1;
server hadoop104:8081 weight=1;
}
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
#root html;
#index index.html index.htm;
# 代理的服务器集群 命名随意, 但是不能出现下划线
proxy_pass http://logcluster;
proxy_connect_timeout 10;
}
# 其他省略
}
Maxwell 是由美国 Zendesk 开源,用 Java 编写的 MySQL 实时抓取软件。 实时读取MySQL 二进制日志 Binlog,并生成 JSON 格式的消息,作为生产者发送给 Kafka,Kinesis、RabbitMQ、Redis、Google Cloud Pub/Sub、文件或其它平台的应用程序。
官网地址:http://maxwells-daemon.io/
➢ Master 主库将改变记录,写到二进制日志(binary log)中
➢ Slave 从库向 mysql master 发送 dump 协议,将 master 主库的 binary log events拷贝到它的中继日志(relay log);
➢ Slave 从库读取并重做中继日志中的事件,将改变的数据同步到自己的数据库。
很简单,就是把自己伪装成 slave,假装从 master 复制数据
(1) 什么是 binlog
MySQL 的二进制日志可以说 MySQL 最重要的日志了,它记录了所有的 DDL 和DML(除了数据查询语句)语句,以事件形式记录,还包含语句所执行的消耗的时间,MySQL的二进制日志是事务安全型的。
一般来说开启二进制日志大概会有 1%的性能损耗。二进制有两个最重要的使用场景:
➢ 其一:MySQL Replication 在 Master 端开启 binlog,Master 把它的二进制日志传递给 slaves 来达到 master-slave 数据一致的目的。
➢ 其二:自然就是数据恢复了,通过使用 mysqlbinlog 工具来使恢复数据。
二进制日志包括两类文件:二进制日志索引文件(文件名后缀为.index)用于记录所有的二进制文件,二进制日志文件(文件名后缀为.00000*)记录数据库所有的 DDL 和 DML(除了数据查询语句)语句事件。
(2) binlog 的开启
➢ 找到 MySQL 配置文件的位置
➢ Linux: /etc/my.cnf
如果/etc 目录下没有,可以通过 locate my.cnf 查找位置
➢ Windows: \my.ini
➢ 在 mysql 的配置文件下,修改配置
在[mysqld] 区块,设置/添加 log-bin=mysql-bin
这个表示 binlog 日志的前缀是 mysql-bin,以后生成的日志文件就是
mysql-bin.123456 的文件后面的数字按顺序生成,每次 mysql 重启或者到达单个文件大小的阈值时,新生一个文件,按顺序编号。
(3) binlog 的分类设置
mysql binlog 的格式有三种,分别是 STATEMENT,MIXED,ROW。
在配置文件中可以选择配置 binlog_format= statement|mixed|row
➢ 三种格式的区别:
◼ statement
语句级,binlog 会记录每次一执行写操作的语句。
相对 row 模式节省空间,但是可能产生不一致性,比如
update tt set create_date=now()
如果用 binlog 日志进行恢复,由于执行时间不同可能产生的数据就不同。
优点: 节省空间
缺点: 有可能造成数据不一致。
◼ row
行级, binlog 会记录每次操作后每行记录的变化。
优点:保持数据的绝对一致性。因为不管 sql 是什么,引用了什么函数,他只记录执行后的效果。
缺点:占用较大空间。
◼ mixed
statement 的升级版,一定程度上解决了,因为一些情况而造成的 statement 模式不一致问题
默认还是 statement,在某些情况下譬如:
当函数中包含 UUID() 时;
包含 AUTO_INCREMENT 字段的表被更新时;
执行 INSERT DELAYED 语句时;
用 UDF 时;
会按照 ROW 的方式进行处理
优点:节省空间,同时兼顾了一定的一致性。
缺点:还有些极个别情况依旧会造成不一致,另外 statement 和 mixed 对于需要对 binlog 的监控的情况都不方便。
综合上面对比,Maxwell 想做监控分析,选择 row 格式比较合适
阿里巴巴 B2B 公司,因为业务的特性,卖家主要集中在国内,买家主要集中在国外,所以衍生出了同步杭州和美国异地机房的需求,从 2010 年开始,阿里系公司开始逐步的尝试基于数据库的日志解析,获取增量变更进行同步,由此衍生出了增量订阅&消费的业务。
Canal 是用 java 开发的基于数据库增量日志解析,提供增量数据订阅&消费的中间件。目前,Canal 主要支持了 MySQL 的 Binlog 解析,解析完成后才利用 Canal Client 来处理获得的相关数据。(数据库同步需要阿里的 Otter 中间件,基于 Canal)。
(1) 原始场景: 阿里 Otter 中间件的一部分
Otter 是阿里用于进行异地数据库之间的同步框架,Canal 是其中一部分。
(2) 常见场景1:更新缓存
(3) 常见场景2:抓取业务数据新增变化表,用于制作拉链表。
(4) 常见场景3:抓取业务表的新增变化数据,用于制作实时统计(我们就是这种场景)
CDC 是 Change Data Capture(变更数据获取)的简称。核心思想是,监测并捕获数据库的变动(包括数据或数据表的插入、更新以及删除等),将这些变更按发生的顺序完整记录下来,写入到消息中间件中以供其他服务进行订阅及消费。
(1) MySQL 主从复制过程
(2) Canal 的工作原理
很简单,就是把自己伪装成 Slave,假装从 Master 复制数据
同一个数据一天发送多次变化,基于查询的CDC就无法捕捉中间的变化,只能获取最终的结果
Flink 社区开发了 flink-cdc-connectors 组件,这是一个可以直接从 MySQL、PostgreSQL 等数据库直接读取全量数据和增量变更数据的 source 组件。目前也已开源,开源地址:https://github.com/ververica/flink-cdc-connectors
<dependencies>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-planner-blink_2.12</artifactId>
<version>1.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-java</artifactId>
<version>1.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java_2.12</artifactId>
<version>1.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients_2.12</artifactId>
<version>1.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>com.alibaba.ververica</groupId>
<artifactId>flink-connector-mysql-cdc</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
package com.atguigu;
import com.alibaba.ververica.cdc.connectors.mysql.MySQLSource;
import com.alibaba.ververica.cdc.connectors.mysql.table.StartupOptions;
import com.alibaba.ververica.cdc.debezium.DebeziumSourceFunction;
import com.alibaba.ververica.cdc.debezium.StringDebeziumDeserializationSchema;
import org.apache.flink.api.common.restartstrategy.RestartStrategies;
import org.apache.flink.runtime.state.filesystem.FsStateBackend;
import org.apache.flink.streaming.api.CheckpointingMode;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
public class FlinkCDC {
public static void main(String[] args) throws Exception {
// 1. 获取执行环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
// 1.1 开启ck checkpoint 并且指定状态后端为 FS
//Flink-CDC 将读取 binlog 的位置信息以状态的方式保存在 CK,如果想要做到断点
//续传,需要从 Checkpoint 或者 Savepoint 启动程序
env.setStateBackend(new FsStateBackend("hdfs://hadoop102:8020/gmall-flink/ck")); // 设置状态后端
// 开启 Checkpoint,每隔 5 秒钟做一次 CK
env.enableCheckpointing(5000L);// 5秒钟触发一次checkpoint
env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE); //指定 CK 的一致性语义
env.getCheckpointConfig().setCheckpointTimeout(10000L); // 10 秒的超时事件
env.getCheckpointConfig().setMaxConcurrentCheckpoints(2); //
env.getCheckpointConfig().setMinPauseBetweenCheckpoints(3000);
// env.setRestartStrategy(RestartStrategies.failureRateRestart()); 新版本不用配
// 2. 通过FlinkCDC构建SourceFunction并读取数据
DebeziumSourceFunction<String> sourceFunction = MySQLSource.<String>builder()
.hostname("hadoop102")
.port(3306)
.username("root")
.password("000000")
.databaseList("gmall-flink")
.tableList("gmall-flink.base_trademark")
// 如果不添加该参数,则消费指定数据库中所有表的数据,如果指定,指定方式为db.table
.deserializer(new StringDebeziumDeserializationSchema())
// initial 获取历史数据,并且监控最新数据
.startupOptions(StartupOptions.initial())
// laest 直接监控最新数据
//.startupOptions(StartupOptions.latest())
.build();
DataStreamSource<String> streamSource = env.addSource(sourceFunction);
// 3. 打印数据
streamSource.print();
// 4. 启动任务
env.execute("FlinkCDC");
}
}
1)打包并上传至 Linux
2)开启 MySQL Binlog 并重启 MySQL
3)启动 Flink 集群
[atguigu@hadoop102 flink-standalone]$ bin/start-cluster.sh
4)启动 HDFS 集群
[atguigu@hadoop102 flink-standalone]$ start-dfs.sh
5)启动程序
[atguigu@hadoop102 flink-standalone]$ bin/flink run -c com.atguigu.FlinkCDC flink-1.0-
SNAPSHOT-jar-with-dependencies.jar
6)在 MySQL 的 gmall-flink.z_user_info 表中添加、修改或者删除数据
7)给当前的 Flink 程序创建 Savepoint
[atguigu@hadoop102 flink-standalone]$ bin/flink savepoint JobId
hdfs://hadoop102:8020/flink/save
8)关闭程序以后从 Savepoint 重启程序
[atguigu@hadoop102 flink-standalone]$ bin/flink run -s hdfs://hadoop102:8020/flink/save/... -c
com.atguigu.FlinkCDC flink-1.0-SNAPSHOT-jar-with-dependencies.jar
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-planner-blink_2.12</artifactId>
<version>1.12.0</version>
</dependency>
package com.atguigu;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.Table;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
import org.apache.flink.types.Row;
public class FlinkCDCWithSQL {
public static void main(String[] args) throws Exception {
// 1. 获取执行环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1) ;
StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);
// 2. DDL方式建表
tableEnv.executeSql("CREATE TABLE mysql_binlog ( " +
" id string not null, " +
" tm_name STRING, " +
" logo_url STRING " +
" ) WITH ( " +
" 'connector' = 'mysql-cdc', " +
" 'hostname' = 'hadoop102', " +
" 'port' = '3306', " +
" 'username' = 'root', " +
" 'password' = '000000', " +
" 'database-name' = 'gmall-flink', " +
" 'table-name' = 'base_trademark')");
// 3. 查询数据
Table table = tableEnv.sqlQuery("select * from mysql_binlog");
// 4. 将动态表转化为流
DataStream<Tuple2<Boolean, Row>> retractStream = tableEnv.toRetractStream(table, Row.class);
retractStream.print();
// 5. 启动任务
env.execute("FlinkCDCWithSQL");
}
}
package com.atguigu;
import com.alibaba.ververica.cdc.debezium.DebeziumDeserializationSchema;
import io.debezium.data.Envelope;
import net.minidev.json.JSONObject;
import org.apache.flink.api.common.typeinfo.BasicTypeInfo;
import org.apache.flink.api.common.typeinfo.TypeInformation;
import org.apache.flink.util.Collector;
import org.apache.kafka.connect.data.Field;
import org.apache.kafka.connect.data.Schema;
import org.apache.kafka.connect.data.Struct;
import org.apache.kafka.connect.source.SourceRecord;
import java.util.List;
import java.util.Locale;
public class CustomerDeserialization implements DebeziumDeserializationSchema<String> {
/**
* 封装的数据格式
* {
* ”database“:”“,
* ”tableName“:”“,
* ”type“:”c u d“,
* ”before“:”{"":"", "":"", "":"", ....}“,
* ”after“:”{"":"", "":"", "":"", ....}“,
* //"ts":1344676232132564
* }
*/
@Override
public void deserialize(SourceRecord sourceRecord, Collector<String> collector) throws Exception {
// 1. 创建json对象, 用于存储数据
JSONObject result = new JSONObject();
// 2. 获取库名和表名
String topic = sourceRecord.topic();
String[] fields = topic.split("\\.");
String database = fields[1];
String tableName = fields[2];
Struct value = (Struct) sourceRecord.value();
// 3. 获取”before“数据
Struct before = value.getStruct("before");
JSONObject beforeJson = new JSONObject();
if(before != null ){
Schema beforeSchema = before.schema();
List<Field> beforeFields = beforeSchema.fields();
for (Field f : beforeFields) {
Object beforeValue = before.get(f);
beforeJson.put(f.name(), beforeValue);
}
}
// 4. 获取”after“数据
Struct after = value.getStruct("after");
JSONObject afterJson = new JSONObject();
if(after != null ){
Schema afterSchema = after.schema();
List<Field> afterFields = afterSchema.fields();
for (Field f : afterFields) {
Object afterValue = after.get(f);
afterJson.put(f.name(), afterValue);
}
}
// 5. 获取操作类型
Envelope.Operation operation = Envelope.operationFor(sourceRecord);
//System.out.println(operation);
String type = operation.toString().toLowerCase();
if("create".equals(type)){
type = "insert";
}
// 6. 将字符按写入jSON对象
result.put("database",database);
result.put("tableName", tableName);
result.put("before", beforeJson);
result.put("after", afterJson);
result.put("type", type);
// 7. 输出对象
collector.collect(result.toJSONString());
}
@Override
public TypeInformation<String> getProducedType() {
return BasicTypeInfo.STRING_TYPE_INFO;
}
}
package com.atguigu;
import com.alibaba.ververica.cdc.connectors.mysql.MySQLSource;
import com.alibaba.ververica.cdc.connectors.mysql.table.StartupOptions;
import com.alibaba.ververica.cdc.debezium.DebeziumSourceFunction;
import com.alibaba.ververica.cdc.debezium.StringDebeziumDeserializationSchema;
import org.apache.flink.runtime.state.filesystem.FsStateBackend;
import org.apache.flink.streaming.api.CheckpointingMode;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
public class FlinkCDCWithCustomerDeserialization {
public static void main(String[] args) throws Exception {
// 1. 获取执行环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
// 2. 通过FlinkCDC构建SourceFunction并读取数据
DebeziumSourceFunction<String> sourceFunction = MySQLSource.<String>builder()
.hostname("hadoop102")
.port(3306)
.username("root")
.password("000000")
.databaseList("gmall-flink")
.tableList("gmall-flink.base_trademark")
// 如果不添加该参数,则消费指定数据库中所有表的数据,如果指定,指定方式为db.table
.deserializer(new CustomerDeserialization())
// initial 获取历史数据,并且监控最新数据
.startupOptions(StartupOptions.initial())
// laest 直接监控最新数据
//.startupOptions(StartupOptions.latest())
.build();
DataStreamSource<String> streamSource = env.addSource(sourceFunction);
// 3. 打印数据
streamSource.print();
// 4. 启动任务
env.execute("FlinkCDCWithCustomerDeserialization");
}
}