935 | # 参数说明:
936 | -e:文件是否存在,存在返回0
937 | -z:文件是否为空,为空返回0
938 | -d:是否是路径(目录),是返回0
939 | # 如下,存在则返回OK
940 | [root@ben01 ~]# hdfs dfs -test -e /data/test.txt && echo "OK" || echo "NO"
941 | OK
942 | # 已知道没有test111.txt这个文件
943 | [root@ben01 ~]# hdfs dfs -test -e /data/test111.txt && echo "OK" || echo "NO"
944 | NO
945 | ~~~
946 |
947 |
948 |
949 | ### 四. HDFS块的概念
950 |
951 | #### 4.1 传统型分布式文件系统的缺点
952 |
953 | 现在想象一下这种情况:有四个文件0.5TB的file1,1.2TB的file2,50GB的file3,100GB的file4;有7个服务器,每个服务器上有10个1TB的硬盘。
954 |
955 | 
956 |
957 | 在存储方式上,我们可以将四个文件存储在同一服务器上(当然大于1TB的文件需要切分),那么缺点也暴露了出来:
958 |
959 | 1. 负载不均衡
960 |
961 | ~~~
962 | 因为文件大小不一致,势必会导致有的节点磁盘的利用率高,有的利用率低
963 | ~~~
964 |
965 | 2. 网络瓶颈问题
966 |
967 | ~~~
968 | 一个过大的文件存储在一个节点磁盘上,当并行处理时,每个线程都需要从这个节点磁盘上读取这个文件内容,那么就会出现网络瓶颈问题,不利于分布式的数据处理。
969 | ~~~
970 |
971 | #### 4.2 HDFS块
972 |
973 | HDFS与其它普通文件系统一样,同样引入了块(Block)的概念,并且块的大小是固定的。块是HDFS系统当中的最小存储单位,在Hadoop2.x中默认大小为128MB。在HDFS上的文件会被拆分成多个块,每个块作为独立的单元进行存储。多个块存放在不同的DataNode上,整个过程中HDFS系统会保证一个块存储在一个数据节点上。另外,如果某个文件大小或最后一个块没有达到128MB,则不会占用整个块空间。
974 |
975 | 以下图为例:
976 |
977 | 
978 |
979 | > 可以看到50G的file3文件会以128MB一个块切分到7台DataNode节点上,每个节点均衡1~2个块
980 |
981 |
982 |
983 | #### 4.3 HDFS块的大小
984 |
985 | HDFS块大小为什么为128MB
986 |
987 | 1. 目的是为了最小化寻址开销时间。
988 | 1. 在I/O开销中,机械硬盘的寻址时间是最耗时的部分,一旦找到第一条记录,剩下的顺序读取效率是非常高的,因此,合适的block大小有助于减少硬盘寻道时间(平衡了硬盘寻道时间、IO时间),提高系统吞吐量。
989 | 2. HDFS寻址开销不仅包括磁盘寻道开销,还包括数据块的定位开销,客户端访问一个文件的流程:名称节点获取组成这个文件的数据块位置列表——>根据位置列表获取到每个数据块的数据节点位置——>数据节点根据数据块信息在本地Linux文件系统中找到对应文件,并返回客户端。
990 | 3. 磁盘的寻址时间为大约5~15ms之间,平均值为10ms,而最小化寻址开销时间普遍认为占1秒的百分之一为最优,那么块的大小就参考1秒的传输速度。
991 | 2. 为了节省内存的使用率
992 | 1. 一个块的元数据大约为150个字节。1亿个块,无论大小,都会占用20G左右的内存。因此块越大,集群相对存储数据就越多。这也暴露了HDFS的一个缺点,不适合存储小文件。
993 | - 不适合小文件的解释,从存储出发:虽然文件不到128M时不能占用整个空间,但是这个块的元数据依然会在内存中占150个字节
994 | - [不适合小文件的解释](),从内存占用出发:假设存储一个块都是占用1M和都是128M,同样存储1PB数据,如果是以1M的小文件存储,占用的内存空间为1PB/1MB×150Byte=150G的内存。如果是以128MB的文件存储,占用空间为1PB/128M×150Byte=1.17G的内存占用。可以看到小文件存储比大文件存储占用更多内存。
995 |
996 |
997 |
998 | #### 4.4 块的相关参数设置
999 |
1000 | ~~~xml
1001 | # 块大小在默认配置文件hdfs-default.xml中,我们可以在hdfs-default.xml中进行重置
1002 | [root@ben01 hadoop]# ll /usr/local/hadoop/etc/hadoop/hdfs-site.xml
1003 |
1004 | dfs.blocksize
1005 | 134217728
1006 | 默认块大小,以字节为单位。可以使用以下后缀(不区分大小写):k,m,g,t,p,e以重新指定大小(例如128k, 512m, 1g等)
1007 |
1008 |
1009 |
1010 | dfs.namenode.fs-limits.min-block-size
1011 | 1048576
1012 | 以字节为单位的最小块大小,由Namenode在创建时强制执行时间。可以防止意外创建带有小块文件降低性能
1013 |
1014 |
1015 |
1016 | dfs.namenode.fs-limits.max-blocks-per-file
1017 | 1048576
1018 | 每个文件的最大块数,由写入时的Namenode执行。这可以防止创建降低性能的最大文件
1019 |
1020 | ~~~
1021 |
1022 |
1023 |
1024 | #### 4.5 块的存储位置
1025 |
1026 | 在hdfs-site.xml中配置过下面这个属性,这个属性的值就是块在Linux系统上的存储位置
1027 |
1028 | ~~~xml
1029 |
1030 |
1031 | dfs.datanode.data.dir
1032 | file://${hadoop.tmp.dir}/dfs/data
1033 |
1034 | ~~~
1035 |
1036 | 
1037 |
1038 |
1039 |
1040 | #### 4.6 HDFS的优点
1041 |
1042 | ~~~sh
1043 | 1. 高容错性(硬件故障是常态):数据自动保存多个副本,副本丢失后,会自动恢复。
1044 | 2. 适合大数据集:GB、TB、甚至PB级数据、千万规模以上的文件数量,1000以上节点规模。
1045 | 3. 数据访问:一次性写入,多次读取;保证数据一致性,安全性。
1046 | 4. 构建成本低:可以构建在廉价机器上。
1047 | 5. 多种软硬件平台中的可移植性。
1048 | 6. 高效性:Hadoop能够在节点之间动态的移动数据,并保证各个节点的动态平衡,因此处理速度非常快。
1049 | 7. 高可靠性:Hadoop的存储和处理数据的能力值得人们信赖。
1050 | ~~~
1051 |
1052 |
1053 |
1054 | #### 4.7 HDFS的缺点
1055 |
1056 | ~~~
1057 | 1. 不适合做低延迟数据访问:
1058 | HDFS的设计目标是处理大型数据集,高吞吐率。这一点势必以高延迟为代价,因此HDFS不适合处理毫秒级低延迟应用的请求。
1059 | 2. 不适合小文件存取:
1060 | 一个是大量小文件寻址时间浪费以及元数据浪费。
1061 | 3. 不适合并发写入,文件随机修改:
1062 | HDFS上的文件只能拥有一个写着,仅支持append操作,不支持多用户对同一文件的写操作,以及在文件任意位置进行修改。
1063 | ~~~
1064 |
1065 |
1066 |
1067 | ### 五 HDFS的体系结构
1068 |
1069 | #### 5.1 体系结构解析
1070 |
1071 | ~~~
1072 | HDFS采用的是master/slaves这种主从的结构模型来管理数据,主要由四个部分组成,分别是Client(客户端)、NameNode(名称节点)、DataNode(数据节点)和SecondaryNameNode。
1073 |
1074 | 一般HDFS集群包括一个NameNode和若干个DataNode。
1075 |
1076 | NameNode是一个中心服务器,负责管理文件系统的命令空间(Namespace),它在内存中维护着命名空间的最新状态,同时对持久性文件(fsimage和edit)进行备份,防止宕机后数据丢失。NameNode还负责管理客户端对文件的访问,如权限验证等。
1077 |
1078 | 集群中的DataNode一般是一个节点运行一个DataNode进程,真正负责管理客户端的读写请求,在NameNode的统一调度下进行数据块的创建、删除和复制等操作。数据块实际上都是保存在DataNode本地的Linux文件系统中。每个DataNode会定期的向NameNode发送数据,报告自己的状态(我们称之为心跳机制)。没有按时发送心跳信息的DataNode会被NameNode标记为“宕机”,不会在给它分配任何I/O请求。
1079 |
1080 | 用户在使用Client进行I/O操作时,依然可以像使用普通文件系统那样,使用文件名去存储和访问文件,只不过在HDFS内部,一个文件会被切分成若干个数据块,并分布存储到若干个DataNode上。
1081 |
1082 | 通过Client访问文件流程如下:
1083 | 客户端把文件名发送给NameNode——>NameNode根据文件名找到对应数据块信息及每个数据块所在DataNode位置——>把这些信息发送给客户端——>客户端直接与DataNode进行通信,获取数据。
1084 | 这种设计的好处在于实现并发访问,大大提高了数据的访问速度。
1085 |
1086 | HDFS集群中只有唯一的NameNode负责所有元数据的管理工作,这种方式保证了DataNode不会脱离NameNode的控制,同时用户数据也永远不会经过NameNode,大大减轻了NameNode的工作负担,使之更方便管理工作。通常在集群中,要选择一套性能较好的机器作为NameNode。当然一台机器可以运行多个DataNode或NameNode和DataNode放在同一机器,只不过实际部署中通常不会这么做。
1087 | ~~~
1088 |
1089 | 
1090 |
1091 |
1092 |
1093 | #### 5.2 开机启动HDFS的过程
1094 |
1095 | 5.2.1 非第一次启动集群
1096 |
1097 | 我们应该知道,在启动NameNode之前,内存里没有任何有关于元数据的信息的。那么启动集群的过程是怎样的呢?如下:
1098 |
1099 | ~~~
1100 | 第一步:
1101 | NameNode启动时,会先加载name目录下最近的fsimage文件。
1102 | 将fsimage里保存的元数据加载到内存当中,这样内存里就有之前检查点存储的所有元数据。但还少了从最近一次检查时间点到关闭系统时的部分数据,也就是edit日志文件里存储的数据。
1103 |
1104 | 第二步:
1105 | 加载剩下的edit日志文件
1106 | 将从最近一次检查点到目前为止的所有日志文件加载到内存里,重演一次客户端操作,这样内存里就是最新的文件系统的所有元数据了。
1107 |
1108 | 第三步:
1109 | 进行检查点设置(满足条件会进行)
1110 | namenode会终止之前正在使用的edit文件,创建一个空的edit日志文件。将所有的未合并过的edit日志文件和fsimage文件进行合并,产生一个新的fsimage。
1111 |
1112 | 第四步:
1113 | 处于安全模式下,等待datanode节点的心跳反馈,当收到99.9%的块至少一个副本后,退出安全模式,开始转为正常状态。
1114 | ~~~
1115 |
1116 |
1117 |
1118 | 5.2.2 非首次启动集群
1119 |
1120 | 
1121 |
1122 | > 作者重启过机器,公网IP有所变化。
1123 | >
1124 | > 首次启动的Loading edits和Saving checkpoint都是空的
1125 |
1126 |
1127 |
1128 | #### 5.3 Secondary NameNode 工作机制
1129 |
1130 | Secondary NameNode 是HDFS集群中的重要组成部分,它可以辅助namenode进行fsimage和editlog的合并工作,减少editlog文件大小,以便缩短下次namenode的重启时间,能尽快退出安全模式。
1131 |
1132 | 两个文件的合并周期,称之为检查点机制(checkpoint),是可以通过hdfs-default.xml配置文件进行修改的:
1133 |
1134 | ~~~xml
1135 |
1136 | dfs.namenode.checkpoint.period
1137 | 3600
1138 | 两次检查点间隔的秒数,默认是1个小时
1139 |
1140 |
1141 | dfs.namenode.checkpoint.txns
1142 | 1000000
1143 | txid执行的次数达到100w次,也执行checkpoint
1144 |
1145 |
1146 | dfs.namenode.checkpoint.check.period
1147 | 60
1148 | 60秒一次检查txid的执行次数
1149 |
1150 | ~~~
1151 |
1152 | 
1153 |
1154 | 通过上图,可以总结如下:
1155 |
1156 | ~~~
1157 | 1. SecondaryNameNode请求namenode停止使用正在编辑的editlog文件,namenode会创建新的editlog文件,同时更新seed_tixd文件。
1158 | 2. SecondaryNameNode通过HTTP协议获得namenode上的fsimage和editlog文件。
1159 | 3. SecondaryNameNode将fsimage读进内存当中,并逐步分析editlog文件里的数据,进行合并操作,然后写入新文件fsimage_x.ckpt文件中。
1160 | 4. SecondaryNameNode将新文件fsimage_x.ckpt通过HTTP协议发送回namenode。
1161 | 5. namenode再进行更名操作。
1162 | ~~~
1163 |
1164 |
1165 |
1166 | ### 六 HDFS的读写流程
1167 |
1168 | #### 6.1 读流程详解
1169 |
1170 | ~~~
1171 | 读操作:
1172 | - hdfs dfs -get /file2 ./file2
1173 | - hdfs dfs -copyToLocal /file2 ./file2
1174 | - FSDataInputStream fsis = fs.open("/input/a.txt");
1175 | - fsis.read(byte[] a)
1176 | - fs.copyToLocal(path1,path2)
1177 | ~~~
1178 |
1179 | 
1180 |
1181 | ~~~
1182 | 1. 客户端通过调用FileSystem对象的open()方法来打开希望读取的文件,对于HDFS来说,这个对象是DistributedFileSystem,它通过使用远程过程调用(RPC)来调用namenode,以确定文件起始块的位置。
1183 |
1184 | 2. 对于每个块,namenode返回有该块副本的datanode地址,并根据距离客户端的远近来排序。
1185 |
1186 | 3. DistributedFileSystem实例会返回一个FSDataInputStream对象(支持文件定位功能)给客户端以便读取数据,接着客户端对这个输入流调用read() 方法。
1187 |
1188 | 4. FSDataInputStream随即连接距离最近的文件中第一个块所在的datanode,通过对数据流反复调用read()方法,将数据从datanode传输到客户端。
1189 |
1190 | 5. 当读取到块的末端时,FSInputStream关闭与该datanode的连接,然后寻找下一块最佳datanode
1191 |
1192 | 6. 客户端从流中读取数据时,块是按照打开FSInputStream与datanode的新建连接的顺序读取。它也会根据需要询问namenode来检索下一批数据块的datanode位置。一旦客户端完成读取,就对FSInputStream调用close方法。
1193 |
1194 | 注意:在读取数据的时候,如果FSInputStream与datanode通信时遇到错误,会尝试从这个块的最近的datanode读取数据,并且记住那个故障的datanode,保证后续不会反复读取该节点上后续的块。FSInputStream也会通过校验和确认从datanode发来的数据是否完整。如果发现有损坏的块,FSInputStream会从其它的块读取副本,并且将损坏的块通知给namenode。
1195 | ~~~
1196 |
1197 |
1198 |
1199 | #### 6.2 写流程详解
1200 |
1201 | ~~~
1202 | 写操作:
1203 | - hdfs dfs -put /file2 ./file2
1204 | - hdfs dfs -copyToLocal /file2 ./file2
1205 | - FSDataOutputStream fsout = fs.create(path); fsout.write(byte[])
1206 | - fs.copyFromLocal(path1,path2)
1207 | ~~~
1208 |
1209 | 
1210 |
1211 | ~~~
1212 | 1. 客户端通过对DistributedFileSystem对象调用create()方法来新建文件
1213 |
1214 | 2. DistributedFileSystem对namenode创建一个RPC调用,在文件系统的命名空间中新建一个文件,此时该文件中还没有相应的数据块。
1215 |
1216 | 3. namenode执行各种不同的检查,以确保这个文件不存在以及客户端有新建该文件的权限。如果检查通过,namenode就会为创建新文件记录一条事务记录(否则,文件创建失败并向客户端跑出一个IOException异常)。DistributedFileSystem向客户端返回一个FSDataOuputStream对象,由此客户端可以写入数据。
1217 |
1218 | 4. 在客户端写入数据时,FSDataOuputStream将它分成一个个的数据包(packet),并写入一个内部队列,这个队列称为“数据队列”(data queue)。DataStreamer线程负责处理数据队列,它的责任是挑选合适存储数据复本的一组datanode,并以此来要去namenode分配新的数据块。这一组datanode将构成一个管道,以默认复本3个为例,所以该管道中有3个节点。DataStreamer将数据包流式传输到管道中第一个datanode,该datanode存储数据包,并将它发送到管道中的第2个datanode。同样,第2个datanode存储该数据包,并发送给管道中的第三个datanode。DataStreamer再将一个个packet流式传到第一个datanode节点后,还会将此packet从数据队列移动到另一个队列 确认队列(ask queue)中。
1219 |
1220 | 5. datanode写入数据成功之后,会为ResponseProcessor线程发送一个写入成功的信息回执,当收到管道中所有的datanode确认信息后,ResponseProcessor线程会将该数据包从确认队列中删除。
1221 | ~~~
1222 |
1223 | 如果任何datanode在写入期间发生故障,则执行以下操作:
1224 |
1225 | ~~~~
1226 | 1. 首先关闭管道,把确认队列中的所有数据包添加回数据队列的最前端,以确保故障节点下游的datanode不会漏掉任何一个数据包。
1227 |
1228 | 2. 为存储在另一个正常datanode的当前数据块制定一个新标识,并将该标识传送给namenode,以便故障datanode在恢复后可以删除存储的部分数据块。
1229 |
1230 | 3. 从管道中删除故障datanode,基于两个正常datanode构建一条新管道,余下数据块写入管道中正常的datanode。
1231 |
1232 | 4. namenode注意到块复本不足时,会在一个新的datanode节点上创建一个新的复本。
1233 |
1234 | 注意:一个块被写入期间可能会有多个datanode同时发生故障,但概率非常低。只要写入了。
1235 | dfs.namenode.replication.min的复本数(默认1),写操作就会成功,并且这个块可以在集群中异步复制,直到达到其目标复本数dfs.replication的数量(默认3)
1236 | ~~~~
1237 |
1238 |
1239 |
1240 | ### 七. Zookeeper的概述
1241 |
1242 | #### 7.1 Zookeeper是什么
1243 |
1244 | 1. Zookeeper是一个分布式应用程序提供的一个分布式开源协调服务框架。是Hadoop和Hbase的重要组件,主要用于解决分布式集群中应用系统的一致性问题。
1245 | 2. 提供了基于Unix系统的目录节点树方式的数据存储。
1246 | 3. 可用于维护和监控存储的数据的变化,通过监控这些数据状态的变化,从而达到基于数据的集群管理
1247 | 4. 提供了一组原语(机器指令),提供Java和C语言的接口。
1248 |
1249 |
1250 |
1251 | #### 7.2 Zookeeper的特点
1252 |
1253 | 1. 一个分布式集群,一个领导者(leader),多个跟随着(follower)。
1254 | 2. 集群中只要有半数以上的节点存活,Zookeeper集群就能正常服务。
1255 | 3. 全局数据一致性:每个server保存一份相同的数据副本,client无论连接到哪个server,数据都是一致的。
1256 | 4. 更新请求按顺序进行:来自同一个client的更新请求按其发送顺序依次执行。
1257 | 5. 数据更新的原子性:一次数据的更新要么成功,要么失败。
1258 | 6. 数据的实用性:在一定时间范围内,client能读到最新数据。
1259 |
1260 | 
1261 |
1262 |
1263 |
1264 | #### 7.3 Zookeeper的数据模型
1265 |
1266 | Zookeeper的数据模型采用的与Unix文件系统类似的层次化的树形结构。我们可以将其理解为一个具有高可用特征的文件系统。这个文件系统中没有文件和目录,而是统一使用“节点”(node)的概念,称之为znode。znode既可以作为保存数据的容器(如同文件),也可以作为保存其它znode的容器(如同目录)。所有的znode构成了一个层次化的命名空间。
1267 |
1268 | 
1269 |
1270 | > - Zookeeper 被设计用来实现协调服务(这类服务通常使用小数据文件),而不是用于大容量数据存储,因此一个znode能存储的数据被限制在1MB以内。
1271 | > - 每个znode都可以通过其路径唯一标识。
1272 |
1273 |
1274 |
1275 | #### 7.4 Zookeeper的应用场景
1276 |
1277 | 1. 统一配置管理
1278 | 2. 统一集群管理
1279 | 3. 服务器节点动态上下线感知
1280 | 4. 软负载均衡
1281 | 5. 分布式锁
1282 | 6. 分布式队列
1283 |
1284 |
1285 |
1286 | ### 八. Zookeeper的安装
1287 |
1288 | #### 8.1 安装与环境变量的配置
1289 |
1290 | ~~~sh
1291 | # 1. 将Zookeeper-3.4.10.tar.gz上传到/root中
1292 | # 2. 解压
1293 | [root@ben01 softwares]# tar -zxvf zookeeper-3.4.10.tar.gz -C /usr/local
1294 | # 3. 更名
1295 | [root@ben01 ~]# cd /usr/local
1296 | [root@ben01 local]# mv zookeeper-3.4.10 zookeeper
1297 | # 4. 配置环境变量
1298 | [root@ben01 local]# vi /etc/profile
1299 | ......
1300 | #zk environment
1301 | export ZOOKEEPER_HOME=/usr/local/zookeeper
1302 | export PATH=$ZOOKEEPER_HOME/bin:$PATH
1303 | # 5. 使得当前会话生效
1304 | [root@ben01 local]# source /etc/profile
1305 | # 6. 检测是否生效
1306 | # 配置成功后,tab键可补全zk,有zk相关脚本提示即可。
1307 | ~~~
1308 |
1309 |
1310 |
1311 | #### 8.2 集群模式的配置
1312 |
1313 | 8.2.1 Zookeeper的服务进程布局
1314 |
1315 | ~~~
1316 | ben01 QuorumPeerMain
1317 | ben02 QuorumPeerMain
1318 | ben03 QuorumPeerMain
1319 | ~~~
1320 |
1321 | 8.2.2 修改zoo.cfg文件
1322 |
1323 | ~~~sh
1324 | [root@ben01 local]# cd ./zookeeper/conf/
1325 | [root@ben01 conf]# cp zoo_sample.cfg zoo.cfg
1326 | # 修改存储路径(需要创建)并添加三个服务节点
1327 | [root@ben01 conf]# vim zoo.cfg
1328 | tickTime=2000 # 定义时间单元(单位毫秒),下面的两个值都是tickTime的倍数
1329 | initLimit=10 # follower连接并同步leader的初始化事件
1330 | syncLimit=5 # 心跳机制的时间(正常情况下的请求和应答的时间)
1331 | dataDir=/usr/local/zookeeper/zkData # 修改zk的存储路径,需要创建zkData目录
1332 | clientPort=2181 # 客户端连接服务器的port
1333 | server.1=ben01:2888:3888 # 添加三个服务器节点
1334 | server.2=ben02:2888:3888
1335 | server.3=ben03:2888:3888
1336 |
1337 | # 保存并退出
1338 | [root@ben01 conf]# mkdir ../zkData
1339 | [root@ben01 conf]# ll /usr/local/zookeeper/zkData
1340 | 总用量 0
1341 |
1342 | # 解析Server.id=ip:port1:port2
1343 | id:服务器的id号,对应zkData/myid文件内的数字
1344 | ip:服务器的ip地址
1345 | port1:follower与leader交互的port
1346 | port2:选举期间使用的port
1347 | ~~~
1348 |
1349 | > ~~~sh
1350 | > [root@ben01 conf]# cat zoo.cfg
1351 | > tickTime=2000
1352 | > initLimit=10
1353 | > syncLimit=5
1354 | > dataDir=/usr/local/zookeeper/zkData
1355 | > clientPort=2181
1356 | > server.1=ben01:2888:3888
1357 | > server.2=ben02:2888:3888
1358 | > server.3=ben03:2888:3888
1359 | >
1360 | > ~~~
1361 |
1362 | 8.2.3 添加myid
1363 |
1364 | ~~~sh
1365 | # $ZOOKEEPER_HOME/zkData目录下添加myid文件,内容为server的id号
1366 | [root@ben01 zookeeper]# cd /usr/local/zookeeper/zkData
1367 | [root@ben01 zkData]# echo "1" >> myid
1368 | ~~~
1369 |
1370 | 8.2.4 搭建其它两台server节点的环境
1371 |
1372 | 1)使用scp命令将zk环境复制到ben02、ben03中
1373 |
1374 | ~~~sh
1375 | [root@ben01 zkData]# cd /usr/local/
1376 | [root@ben01 local]# scp -r zookeeper ben02:/usr/local/
1377 | [root@ben01 local]# scp -r zookeeper ben03:/usr/local/
1378 | ~~~
1379 |
1380 | 2)使用scp命令拷贝/etc/profile到两台机器上
1381 |
1382 | ~~~sh
1383 | [root@ben01 local]# scp /etc/profile ben02:/etc/
1384 | [root@ben01 local]# scp /etc/profile ben03:/etc/
1385 | ~~~
1386 |
1387 | 3)source,并修改ben02的myid文件为2
1388 |
1389 | ~~~sh
1390 | [root@ben02 ~]# source /etc/profile
1391 | [root@ben02 ~]# echo "2" > /usr/local/zookeeper/zkData/myid
1392 | ~~~
1393 |
1394 | 4)source,并修改ben02的myid文件为3
1395 |
1396 | ~~~sh
1397 | [root@ben03 ~]# source /etc/profile
1398 | [root@ben03 ~]# echo "3" > /usr/local/zookeeper/zkData/myid
1399 | ~~~
1400 |
1401 | 8.2.5 启动zk
1402 |
1403 | 1)三台机器上都启动zk(防火墙需是关闭状态)
1404 |
1405 | ~~~sh
1406 | [root@ben01 local]# zkServer.sh start
1407 | ZooKeeper JMX enabled by default
1408 | Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
1409 | Starting zookeeper ... STARTED
1410 | # 此时查看状态提示还没运行起来
1411 | [root@ben01 local]# zkServer.sh status
1412 | ZooKeeper JMX enabled by default
1413 | Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
1414 | Error contacting service. It is probably not running.
1415 | # 查看jps,已经能看到QuorumPeerMain了,说明启动了,但由于zk是三节点,最低要求有半数以上的节点可用,所以还需启动ben02上的zk
1416 | [root@ben01 local]# jps
1417 | 801 Jps
1418 | 22898 ResourceManager
1419 | 22281 DataNode
1420 | 23100 NodeManager
1421 | 22078 NameNode
1422 | 719 QuorumPeerMain
1423 |
1424 | [root@ben02 ~]# zkServer.sh start
1425 | ZooKeeper JMX enabled by default
1426 | Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
1427 | Starting zookeeper ... STARTED
1428 | # 可以看到状态是已经成功的
1429 | [root@ben02 ~]# zkServer.sh status
1430 | ZooKeeper JMX enabled by default
1431 | Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
1432 | Mode: leader
1433 | [root@ben02 ~]# jps
1434 | 21762 NodeManager
1435 | 21330 DataNode
1436 | 2565 Jps
1437 | 1958 QuorumPeerMain
1438 | 21535 SecondaryNameNode
1439 |
1440 |
1441 | [root@ben03 ~]# zkServer.sh start
1442 | ZooKeeper JMX enabled by default
1443 | Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
1444 | Starting zookeeper ... STARTED
1445 | [root@ben03 ~]# zkServer.sh status
1446 | ZooKeeper JMX enabled by default
1447 | Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
1448 | Mode: follower
1449 | [root@ben03 ~]# jps
1450 | 19713 Jps
1451 | 21619 DataNode
1452 | 21929 NodeManager
1453 | 2319 QuorumPeerMain
1454 | ~~~
1455 |
1456 | 2)启动客户端(ben01)
1457 |
1458 | ~~~sh
1459 | [root@ben01 local]# zkCli.sh
1460 | [zk: localhost:2181(CONNECTED) 0] ls
1461 | [zk: localhost:2181(CONNECTED) 1] quit
1462 | # 退出,连到指定的zk机器
1463 | [root@ben01 local]# zkCli.sh -server ben02:2181
1464 | [zk: ben02:2181(CONNECTED) 0] quit
1465 | ~~~
1466 |
1467 |
1468 |
1469 | ### 九. Zookeeper的shell操作
1470 |
1471 | | 命令 | 描述 | 示例 |
1472 | | ------ | ------------------------------------------------------------ | ----------------------------------------------- |
1473 | | ls | 查看某个目录包含的所有文件 | ls / |
1474 | | ls2 | 查看某个目录包含的所有文件,与ls不同的是它查看到time、version等信息 | ls2 / |
1475 | | create | 修改znode,并需要设置初始内容 | create /test "test"
create -e /test "test" |
1476 | | get | 获取znode的数据 | get /test |
1477 | | set | 修改znode的内容 | set /test "test2" |
1478 | | delete | 删除znode | delete /test |
1479 | | quit | 退出客户端 | |
1480 | | help | 帮助命令 | |
1481 |
1482 |
1483 |
1484 | ### 十. YARN的概述
1485 |
1486 | 为了克服Hadoop 1.x中HDFS和MapReduce存在的各种问题而提出的,针对Hadoop1.x中的MapReduce在扩展性和多框架支持方面的不足,提出了全选的资源管理框架YARN。
1487 |
1488 | Apache YARN是Hadoop集群的资源管理系统,负责为计算程序提供服务器计算资源,相当于一个分布式的操作系统平台,而MapReduce等计算程序则相当于操作系统之上的应用程序。
1489 |
1490 | YARN被引入Hadoop2,最初是为了改善MapReduce的实现,但因其足够的通用性,又支持其他的分布式计算模式,如Spark、Tez等结算框架,逐渐取代原有MapReduce的位置,MapReduce进行了完全重构,发生了根本上的改变,是运行在YARN之上的分布式应用程序。
1491 |
1492 | 
1493 |
1494 |
1495 |
1496 | ### 十一. YARN的架构及组件
1497 |
1498 | #### 11.1 MapReduce 1.x的简介
1499 |
1500 | 第一代Hadoop,由分布式存储系统hdfs和分布式计算框架MapReduce组成,其中,hdfs由一个NameNode和多个DataNode组成,MapReduce由一个JobTracker和多个TaskTracker组成,对应Hadoop版本为Hadoop 1.x和0.21.x,0.22.x。
1501 |
1502 | 1)MapReduce 1 的角色
1503 |
1504 | ~~~
1505 | -- 1.Client:作业提交发起者
1506 | -- 2.JobTracker:初始化作业,分配作业,与TackTracker通信,协调整个作业。
1507 | -- 3.TaskTracker:保持JobTracker通信,在分配的数据片段上执行MapReduce任务。
1508 | ~~~
1509 |
1510 | 
1511 |
1512 | 2)MapReduce执行流程
1513 |
1514 | 
1515 |
1516 | > ~~~
1517 | > 步骤1 提交作业
1518 | > 编写MapReduce程序代码,创建job对象,并进行配置,比如输入和输出路径,压缩格式等,然后通过JobClient来提交作业。
1519 | >
1520 | > 步骤2 作业的初始化
1521 | > 客户提交完成后,JobTracker会将作业加入队列,然后进行调度,默认的调度方法是FIFO调试方法。
1522 | >
1523 | > 步骤3 任务的分配
1524 | > TaskTracker和JobTracker之间的通信与任务的分配是通过心跳机制完成的。
1525 | > TaskTracker会主动向JobTracker询问是否有作业要做,如果自己可以做,那么就会申请到作业任务,这个任务可以是MapTask也可以是ReduceTask。
1526 | >
1527 | > 步骤4 任务的执行
1528 | > 申请到后,TaskTracker会做如下事情:
1529 | > - 1.拷贝代码到本地
1530 | > - 2.拷贝任务的信息到本地
1531 | > - 3.启动JVM运行任务
1532 | >
1533 | > 步骤5 状态与任务的更新
1534 | > 任务在运行过程中,首先会将自己的状态汇报给TaskTracker,然后由TaskTracker汇总给JobTracker。任务进度是通过计数器来实现的。
1535 | >
1536 | > 步骤6 作业的完成
1537 | > JobTracker是在接受到最后一个任务运行完成后,才会将任务标记为成功,此时会做删除中间结果等善后工作。
1538 | > ~~~
1539 |
1540 |
1541 |
1542 | #### 11.2 YARN的设计思想
1543 |
1544 | YARN的基本思想是将资源管理和作业调度/监控功能划分为单独的守护进程。其思想是拥有一个全局ResourceManager(RM),以及每个应用程序拥有一个ApplicationMaster(AM)。应用程序可以是单个作业,也可以是一组作业
1545 |
1546 | 
1547 |
1548 | 一个ResourceManager和多个NodeManager构成了yarn资源管理框架。他们是yarn启动后长期运行的守护进程,来提供核心服务。
1549 |
1550 | ~~~
1551 | ResourceManager 是在系统中的所有应用程序之间仲裁资源的最终权威,即管理整个集群上的所有资源分配,内部含有一个Scheduler(资源调度器)
1552 |
1553 | NodeManager 是每台机器的资源管理器,也就是单个节点的管理者,负责启动和监视容器(container)资源使用情况,并向ResourceManager及其Scheduler报告使用情况
1554 |
1555 | Container 是集群上的可用资源,包含cpu、内存、磁盘、网络等
1556 |
1557 | ApplicationMaster(AM) 实际上是框架的特定库,每启动一个应用程序,都会启动一个AM,它的任务是与ResourceManager协商资源,并与NodeManager一起执行和监视任务。
1558 | ~~~
1559 |
1560 | 扩展)YARN与MapReduce 1的比较
1561 |
1562 | | MapReduce 1 | YARN |
1563 | | ----------- | ----------------------------------------------------- |
1564 | | Jobtracker | Resource manager, application master, timeline server |
1565 | | Tasktracker | Node manager |
1566 | | Slot | Container |
1567 |
1568 |
1569 |
1570 | #### 11.3 YARN的配置
1571 |
1572 | YARN属于Hadoop的一个组件,不需要再单独安装程序,Hadoop中已存在配置文件的设置,本身就是一个集群,有主节点和从节点。
1573 |
1574 | ~~~
1575 | 注意之间的值不能有空格
1576 | ~~~
1577 |
1578 | 在mapred-site.xml中的配置如下:
1579 |
1580 | > 无需再配
1581 |
1582 | ~~~xml
1583 |
1584 |
1585 |
1586 | mapreduce.framework.name
1587 | yarn
1588 |
1589 |
1590 |
1591 | mapreduce.jobhistory.address
1592 | ben01:10020
1593 |
1594 |
1595 |
1596 | mapreduce.jobhistory.webapp.address
1597 | ben01:19888
1598 |
1599 |
1600 |
1601 | yarn.app.mapreduce.am.env
1602 | HADOOP_MAPRED_HOME=/usr/local/hadoop
1603 |
1604 |
1605 | mapreduce.map.env
1606 | HADOOP_MAPRED_HOME=/usr/local/hadoop
1607 |
1608 |
1609 | mapreduce.reduce.env
1610 | HADOOP_MAPRED_HOME=/usr/local/hadoop
1611 |
1612 |
1613 | ~~~
1614 |
1615 | 在yarn-site.xml中的配置如下:
1616 |
1617 | > 无需再配
1618 |
1619 | ~~~xml
1620 |
1621 |
1622 |
1623 |
1624 |
1625 | yarn.nodemanager.aux-services
1626 | mapreduce_shuffle
1627 |
1628 |
1629 |
1630 | yarn.resourcemanager.hostname
1631 | ben01
1632 |
1633 |
1634 |
1635 | yarn.nodemanager.aux-services.mapreduce_shuffle.class
1636 | org.apache.hadoop.mapred.ShuffleHandler
1637 |
1638 |
1639 |
1640 |
1641 | yarn.resourcemanager.address
1642 | ben01:8032
1643 |
1644 |
1645 |
1646 | yarn.resourcemanager.scheduler.address
1647 | ben01:8030
1648 |
1649 |
1650 |
1651 | yarn.resourcemanager.resource-tracker.address
1652 | ben01:8031
1653 |
1654 |
1655 |
1656 | yarn.resourcemanager.admin.address
1657 | ben01:8033
1658 |
1659 |
1660 |
1661 | yarn.resourcemanager.webapp.address
1662 | ben01:8088
1663 |
1664 |
1665 | ~~~
1666 |
1667 | 1)日志位置
1668 |
1669 | ~~~sh
1670 | jps:当启动进程时出错后的解决步骤
1671 |
1672 | 如果是hdfs上的问题,则查看对应的日志
1673 | less 或 tail -1000 $HADOOP_HOME/logs/hadoop-{user.name}-{jobname}-{hostname}.log
1674 | 如果是yarn则查看
1675 | less 或 tail -1000 $HADOOP_HOME/logs/yarn-{user.name}-{jobname}-{hostname}.log
1676 | ~~~
1677 |
1678 | 2)历史服务
1679 |
1680 | ~~~sh
1681 | 如果需要查看YARN作业历史,需要打开历史服务:
1682 | # 1.先停止当前YARN进程
1683 | [root@ben01 ~]# jps
1684 | 22898 ResourceManager
1685 | 20324 Jps
1686 | 22281 DataNode
1687 | 23100 NodeManager
1688 | 22078 NameNode
1689 | 719 QuorumPeerMain
1690 |
1691 | [root@ben01 ~]# stop-yarn.sh
1692 | [root@ben01 ~]# jps
1693 | 26176 Jps
1694 | 22281 DataNode
1695 | 22078 NameNode
1696 | 719 QuorumPeerMain
1697 | # 可以看到有关yarn的服务都停了,如Nodemanager、ResourceManager,另外两台机器也可以看到Nodemanager已关闭。
1698 |
1699 | # 2.打开并添加配置
1700 | [root@ben01 ~]# cd /usr/local/hadoop/etc/hadoop/
1701 | [root@ben01 hadoop]# vim yarn-site.xml
1702 |
1703 | ......
1704 |
1705 |
1706 | yarn.log-aggregation-enable
1707 | true
1708 |
1709 |
1710 |
1711 | yarn.log-aggregation.retain-seconds
1712 | 640800
1713 |
1714 |
1715 |
1716 | # 3.分发到其它节点
1717 | [root@ben01 hadoop]# scp yarn-site.xml ben02:$PWD
1718 | [root@ben01 hadoop]# scp yarn-site.xml ben03:$PWD
1719 |
1720 | # 4.启动YARN进程
1721 | [root@ben01 hadoop]# start-yarn.sh
1722 | [root@ben01 hadoop]# jps
1723 | 20336 ResourceManager
1724 | 20547 NodeManager
1725 | 22772 Jps
1726 | 22281 DataNode
1727 | 22078 NameNode
1728 | 719 QuorumPeerMain
1729 |
1730 | # 5.开启历史服务
1731 | [root@ben01 hadoop]# mapred --daemon start historyserver
1732 | [root@ben01 hadoop]# jps
1733 | 20336 ResourceManager
1734 | 32672 Jps
1735 | 20547 NodeManager
1736 | 22281 DataNode
1737 | 26381 JobHistoryServer
1738 | 22078 NameNode
1739 | 719 QuorumPeerMain
1740 | # 可以看到JobHistoryServer已经有了
1741 | ~~~
1742 |
1743 |
1744 |
1745 | ### 阶段关机需要做的操作
1746 |
1747 | ~~~sh
1748 | # 关机前需要关闭的服务,zk(zk需要三台机器都操作)、hadoop、作业任务
1749 | [root@ben01 ~]# zkServer.sh stop
1750 | [root@ben02 ~]# zkServer.sh stop
1751 | [root@ben03 ~]# zkServer.sh stop
1752 | [root@ben01 ~]# mapred --daemon stop historyserver
1753 | [root@ben01 ~]# stop-all.sh
1754 | [root@ben01 ~]# jps
1755 | 32672 Jps
1756 |
1757 | # 开机需要开启的服务
1758 | [root@ben01 ~]# start-all.sh
1759 | # 此时查看三台机器的jps可以看到服务启动的内容
1760 | [root@ben01 ~]# zkServer.sh start
1761 | [root@ben02 ~]# zkServer.sh start
1762 | [root@ben03 ~]# zkServer.sh start
1763 | [root@ben01 ~]# mapred --daemon start historyserver
1764 | [root@ben01 ~]# jps
1765 | 19507 Jps
1766 | 13193 DataNode
1767 | 14012 NodeManager
1768 | 15724 QuorumPeerMain
1769 | 12972 NameNode
1770 | 13821 ResourceManager
1771 | 19343 JobHistoryServer
1772 | ~~~
1773 |
1774 |
1775 |
1776 | ### 十二. YARN的执行原理
1777 |
1778 | 在MR程序运行时,有五个独立的进程:
1779 |
1780 | ~~~sh
1781 | -- YarnRunner:用于提交作业的客户端程序
1782 | -- ResourceManager:yarn资源管理器,负责协调集群上计算机资源的分配
1783 | -- NodeManager:yarn节点管理器,负责启动和监视集群中机器上的计算容器(container)
1784 | -- Application Master:负责协调运行MapReduce作业的任务,它和任务都在容器中运行,这些容器由资源管理器分配并由节点管理器进行管理。
1785 | -- HDFS:用于共享作业所需文件。
1786 | ~~~
1787 |
1788 | 整个过程如下图描述:
1789 |
1790 | 
1791 |
1792 | > ~~~sh
1793 | > 1. 调用waitForCompletion方法每秒轮询作业的进度,内部封装了submit()方法,用于创建JobCommiter实例,并且调用其的submitJobInternal方法。提交成功后,如果有状态改变,就会把进度报告到控制台。错误也会报告到控制台。
1794 | > 2. JobCommiter实例会向ResourceManager申请一个新应用ID,用于MapReduce作业ID。这期间JobCommiter也会进行检查输出路径的情况,以及计算输入分片。
1795 | > 3. 如果成功申请到ID,就会将运行作业所需要的资源(包括作业jar文件,配置文件和计算所得的输入分片元数据文件)上传到一个用ID命令目录下HDFS上。此时副本个数是10。
1796 | > 4. 准备工作已做好,再通知ResourceManager调用submitApplication方法提交作业。
1797 | > 5. ResourceManager调用submitApplication方法后,会通知Yarn调度器(Scheduler),调度器分配一个容器,在节点管理器的管理下在容器中启动 application master进程。
1798 | > 6. application master的主类是MRAppMaster,其主要作用是初始化任务,并接受来来自任务的进度和完成报告。
1799 | > 7. 然后从HDFS上接受资源,主要是split。然后为每一个split创建MapTask以及参数指定的ReduceTask,任务ID在此时分配。
1800 | > 8. 然后Application Master会向资源管理器请求容器,首先为MapTask申请容器,然后再为ReduceTask申请容器。(5%)
1801 | > 9. 一旦ResourceManager中的调度器(Scheduler),为Task分配了一个特定节点上的容器,Application Master就会与NodeManager进行通信来启动容器。
1802 | > 10. 运行任务是由YarnChild来执行的,运行任务前,先将资源本地化(jar文件,配置文件,缓存文件)
1803 | > 11. 然后开始运行MapTask或ReduceTask。
1804 | > 12. 当收到最后一个任务已经完成通知后,application master会把作业状态设置为success。然后Job轮询时,知道成功完成,就会通知客户端,并把统计信息输出到控制台。
1805 | > ~~~
1806 | >
1807 | >
1808 |
1809 | ### 十三. YARN的案例测试
1810 |
1811 | 使用官方提供的
1812 |
1813 | ~~~sh
1814 | # 1.创建一个内容
1815 | [root@ben01 ~]# ls
1816 | e1 empty file file1 file2 file3 softwares test.txt
1817 | [root@ben01 ~]# mkdir input
1818 | [root@ben01 ~]# mv file* input/
1819 | [root@ben01 ~]# cat input/*
1820 | Hello ben01
1821 | Hello ben02
1822 | Hello ben03
1823 | Hello ben01
1824 | Hello ben02
1825 | Hello ben03
1826 |
1827 | # 2.将文件夹put到hdfs上
1828 | [root@ben01 ~]# hdfs dfs -put input/ /
1829 | [root@ben01 ~]# hdfs dfs -cat /input/*
1830 |
1831 | # 3.官方案例
1832 | [root@ben01 ~]# cd /usr/local/hadoop/share/hadoop/mapreduce
1833 |
1834 | # 4.运行
1835 | [root@ben01 mapreduce]# hadoop jar hadoop-mapreduce-examples-3.3.1.jar wordcount /input /output
1836 | ~~~
1837 |
1838 | > 输入内容如下:
1839 | >
1840 | > ~~~sh
1841 | > [root@ben01 mapreduce]# hadoop jar hadoop-mapreduce-examples-3.3.1.jar wordcount /input /output
1842 | > 2022-07-27 09:38:32,446 INFO client.DefaultNoHARMFailoverProxyProvider: Connecting to ResourceManager at ben01/10.206.0.10:8032
1843 | > 2022-07-27 09:38:32,917 INFO mapreduce.JobResourceUploader: Disabling Erasure Coding for path: /tmp/hadoop-yarn/staging/root/.staging/job_1658885211305_0002
1844 | > 2022-07-27 09:38:33,181 INFO input.FileInputFormat: Total input files to process : 4
1845 | > 2022-07-27 09:38:33,267 INFO mapreduce.JobSubmitter: number of splits:4
1846 | > 2022-07-27 09:38:33,832 INFO mapreduce.JobSubmitter: Submitting tokens for job: job_1658885211305_0002
1847 | > 2022-07-27 09:38:33,832 INFO mapreduce.JobSubmitter: Executing with tokens: []
1848 | > 2022-07-27 09:38:34,002 INFO conf.Configuration: resource-types.xml not found
1849 | > 2022-07-27 09:38:34,002 INFO resource.ResourceUtils: Unable to find 'resource-types.xml'.
1850 | > 2022-07-27 09:38:34,055 INFO impl.YarnClientImpl: Submitted application application_1658885211305_0002
1851 | > 2022-07-27 09:38:34,086 INFO mapreduce.Job: The url to track the job: http://ben01:8088/proxy/application_1658885211305_0002/
1852 | > 2022-07-27 09:38:34,087 INFO mapreduce.Job: Running job: job_1658885211305_0002
1853 | > 2022-07-27 09:38:41,181 INFO mapreduce.Job: Job job_1658885211305_0002 running in uber mode : false
1854 | > 2022-07-27 09:38:41,182 INFO mapreduce.Job: map 0% reduce 0%
1855 | > 2022-07-27 09:38:52,312 INFO mapreduce.Job: map 25% reduce 0%
1856 | > 2022-07-27 09:38:53,322 INFO mapreduce.Job: map 100% reduce 0%
1857 | > 2022-07-27 09:38:57,359 INFO mapreduce.Job: map 100% reduce 100%
1858 | > 2022-07-27 09:38:57,375 INFO mapreduce.Job: Job job_1658885211305_0002 completed successfully
1859 | > 2022-07-27 09:38:57,459 INFO mapreduce.Job: Counters: 54
1860 | > File System Counters
1861 | > FILE: Number of bytes read=126
1862 | > FILE: Number of bytes written=1363365
1863 | > FILE: Number of read operations=0
1864 | > FILE: Number of large read operations=0
1865 | > FILE: Number of write operations=0
1866 | > HDFS: Number of bytes read=447
1867 | > HDFS: Number of bytes written=32
1868 | > HDFS: Number of read operations=17
1869 | > HDFS: Number of large read operations=0
1870 | > HDFS: Number of write operations=2
1871 | > HDFS: Number of bytes read erasure-coded=0
1872 | > Job Counters
1873 | > Launched map tasks=4
1874 | > Launched reduce tasks=1
1875 | > Data-local map tasks=4
1876 | > Total time spent by all maps in occupied slots (ms)=40246
1877 | > Total time spent by all reduces in occupied slots (ms)=2673
1878 | > Total time spent by all map tasks (ms)=40246
1879 | > Total time spent by all reduce tasks (ms)=2673
1880 | > Total vcore-milliseconds taken by all map tasks=40246
1881 | > Total vcore-milliseconds taken by all reduce tasks=2673
1882 | > Total megabyte-milliseconds taken by all map tasks=41211904
1883 | > Total megabyte-milliseconds taken by all reduce tasks=2737152
1884 | > Map-Reduce Framework
1885 | > Map input records=6
1886 | > Map output records=12
1887 | > Map output bytes=120
1888 | > Map output materialized bytes=144
1889 | > Input split bytes=375
1890 | > Combine input records=12
1891 | > Combine output records=10
1892 | > Reduce input groups=4
1893 | > Reduce shuffle bytes=144
1894 | > Reduce input records=10
1895 | > Reduce output records=4
1896 | > Spilled Records=20
1897 | > Shuffled Maps =4
1898 | > Failed Shuffles=0
1899 | > Merged Map outputs=4
1900 | > GC time elapsed (ms)=1627
1901 | > CPU time spent (ms)=2840
1902 | > Physical memory (bytes) snapshot=1411006464
1903 | > Virtual memory (bytes) snapshot=13943873536
1904 | > Total committed heap usage (bytes)=1187512320
1905 | > Peak Map Physical memory (bytes)=309903360
1906 | > Peak Map Virtual memory (bytes)=2796343296
1907 | > Peak Reduce Physical memory (bytes)=201601024
1908 | > Peak Reduce Virtual memory (bytes)=2792300544
1909 | > Shuffle Errors
1910 | > BAD_ID=0
1911 | > CONNECTION=0
1912 | > IO_ERROR=0
1913 | > WRONG_LENGTH=0
1914 | > WRONG_MAP=0
1915 | > WRONG_REDUCE=0
1916 | > File Input Format Counters
1917 | > Bytes Read=72
1918 | > File Output Format Counters
1919 | > Bytes Written=32
1920 | > ~~~
1921 | >
1922 | >
1923 |
1924 |
1925 |
1926 | ### 十五. YARN的Web UI查看
1927 |
1928 | 外网加上8088端口访问
1929 |
1930 | 
1931 |
1932 |
1933 |
1934 | **Hadoop基础部分已完成🎉🎉🎉**
--------------------------------------------------------------------------------
/Hadoop生态/第二章——Hive.md:
--------------------------------------------------------------------------------
1 | # 第二章——Hive
2 |
3 | ### 一. Hive开篇
4 |
5 | #### 1.1 hive简介
6 |
7 | ##### 1.1.1 hive出现的原因
8 |
9 | 视频网站每天产生海量的结构化日志数据,为了对这些数据进行管理,且因机器学习的需求,产生了hive这门技术,并继续发展成为一个成功的Apache项目。
10 |
11 |
12 |
13 | ##### 1.1.2 什么是hive
14 |
15 | hive是构建在Hadoop上的数据仓库工具(框架),可以将结构化的数据文件映射成一张数据表,并可以使用类sql的方式来对这样的数据文件进行读、写以及管理(包括元数据)。这套HIVE SQL简称HQL。hive的执行引擎可以是MR、Spark以及Tez。
16 |
17 | 如果执行引擎是MapReduce的话,hive会将HQL翻译成MR进行数据的计算。用户可以使用命令行工具或JDBC驱动程序来连接到hive。
18 |
19 |
20 |
21 | ##### 1.1.3 为什么使用hive
22 |
23 | 因为直接使用MapReduce需要面临以下问题:
24 |
25 | - 人员学习成本高
26 | - 项目周期要求太短
27 | - MapReduce实现复杂查询逻辑开发难度大
28 |
29 |
30 |
31 | ##### 1.1.4 hive的优缺点
32 |
33 | 1)hive的优点
34 |
35 | - 学习成本低
36 | - 提供类SQL查询语法HQL(简单、容易上手),避免直接写MapReduce(适合java语音不好的,sql熟练的人)
37 | - 可扩展性好
38 | - 为超大数据集设计了计算/扩展能力(MR作为计算引擎,HDFS作为而存储系统),Hive可以自由的扩展集群的规模,一般情况下不需要重启服务。
39 | - 适合做离线分析处理(OLAP)
40 | - Hive的执行延迟比较高,因此Hive常用于数据分析,对实时性要求不高的场合。
41 | - 延展性好
42 | - Hive支持用户自定义函数,用户可以根据自己的需求来实现自己的函数。
43 | - 良好的容错性
44 | - 某个数据节点出现问题HQL仍可完成执行。
45 | - 统计管理
46 | - 提供了统一的元数据管理
47 |
48 | 2)hive的缺点
49 |
50 | - hive的HQL表达能力有限
51 | - 迭代式算法无法表达,比如PageRank(网页排名)
52 | - 数据挖掘方面,比如KMeans(k均值聚类算法)
53 | - hive的效率比较低
54 | - hive自动生成的MapReduce作业,通常情况下不够智能化
55 | - hive调优比较苦难,粒度较粗
56 |
57 |
58 |
59 | #### 1.2 Hive架构和原理
60 |
61 | ##### 1.2.1 hive的架构简介
62 |
63 | 
64 |
65 | 从上图可以看出,Hive的体系结构分位以下几部分:
66 |
67 | ~~~
68 | 1. 用户连接接口
69 | CLI:指Shell命令行。
70 | JDBC/ODBC:指Hive的java实现,与传统数据库JDBC类似。
71 | WebUI:指可通过浏览器访问Hive。
72 | 2. Thrift Server:
73 | hive的可选组件,是一个软件框架服务,允许客户端使用包括Java、C++、Ruby和其它多种语言,通过编程的方式远程访问Hive。
74 | 3. Meta Store
75 | Hive将元数据存储在数据库中,如mysql、derby。hive中的元数据包括(表名、表所属的数据库表名、表的拥有者、列/分区字段、表的类型(是否是外部表)、表的数据所在目录等)
76 | 4. 驱动器(Driver)
77 | - 解析器(SQLParser):
78 | 将HQL字符串转换成抽象语法树AST,这一步一般都用第三方工具库完成,比如antlr;对AST进行语法分析,比如表是否存在、字段是否存在、SQL语义是否有误。
79 | - 编译器(Compiler):
80 | 对hql语句进行词法、语法、语义的编译(需要跟元数据关联),编译完成后会生成一个执行计划。hive上就是编译成MapReduce的job。
81 | - 优化器(Optimizer):
82 | 将执行计划进行优化,减少不必要的列、使用分区、使用索引等,优化job。
83 | - 执行器(Executer):
84 | 将优化后的执行计划提交给hadoop的yarn上执行。提交job。
85 | 5.hadoop
86 | Jobtracker是hadoop1.x中的组件,它的功能相当于:Resourcemanager+AppMaster
87 | TaskTracker相当于:Nodemanager+yarnchild
88 |
89 | Hive的数据存储在HDFS中,大部分的插叙、计算由MapReduce完成
90 | ~~~
91 |
92 | 注意:
93 |
94 | ~~~~
95 | - 包含*的全表查询,比如select * from table 不会生成MapReduce任务
96 | - 包含*的limit查询,比如select * from table limit 10 不会生成MapReduce任务
97 | ~~~~
98 |
99 |
100 |
101 | ##### 1.2.2 hive的工作原理
102 |
103 | 
104 |
105 | ~~~
106 | 1. 用户提交查询等任务给Driver。(可以选择CLI/JDBC/Web UI)
107 | 2. 驱动程序将Hql发送编译器,检查语法和生成查询计划。
108 | 3. 编译器Compiler根据用户任务去MetaStore中获取需要的Hive的元数据信息。
109 | 4. 编译器Compiler得到元数据信息,对任务进行编译,先将HiveQL转换为抽象语法树,然后抽象语法树转换成查询块,将查询块转化为逻辑的查询计划,重写逻辑查询计划,将逻辑计划转化为物理的计划(MapReduce),最后选择最佳的策略。
110 | 5. 将最终的计划提交给Driver。到此为止,查询解析和编译完成。
111 | 6. Driver将计划Plan转交给ExecutionEngine去执行。
112 | 7.1 在内部,执行作业的过程是一个MapReduce工作。执行引擎发送作业给JobTracker,在名称节点并把它分配作业到TaskTracker,这是在数据节点。在这里,查询执行MapReduce工作。
113 | 7.2 与此同时,执行时的执行引擎可以通过MetaStore执行元数据操作。
114 | 8. 执行引擎接收来自数据节点的结果。
115 | 9. 执行引擎发送这些结果值给驱动程序。
116 | 10. 驱动程序将结果发送给Hive接口。
117 | ~~~
118 |
119 |
120 |
121 | ##### 1.2.3 Hive和Hadoop的关系
122 |
123 | ~~~
124 | - hive本身没有多少功能,hive相当于在hadoop上包了一个壳子,就是对hadoop进行了一次封装。
125 | - hive的存储是基于hdfs/hbase的,hive的计算是基于mapreduce。
126 | 参考下图:
127 | ~~~
128 |
129 | 
130 |
131 |
132 |
133 | #### 1.3 hive与传统数据库的区别
134 |
135 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 ben.guo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Practical-projects-of-BigData-and-AI
2 |
3 |
4 |
5 | ## 学习路径:
6 |
7 | - Hadoop生态——对应项目《采集与监控平台》
8 | - Hadoop基础
9 |
10 |
--------------------------------------------------------------------------------
/soft/README.md:
--------------------------------------------------------------------------------
1 | # README
2 |
3 | 这个软件包只有部分文件,请到百度云下载链接:https://pan.baidu.com/s/1UZABuNRtoPzjrrc73r26Yw
4 | 提取码:d0ni
5 |
6 | 有需要补充的可以发邮箱给我909336740@qq.com
7 |
8 |
--------------------------------------------------------------------------------
/soft/frpc_0.33.linux_adm64.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/soft/frpc_0.33.linux_adm64.tgz
--------------------------------------------------------------------------------
/准实时数仓项目/README.md:
--------------------------------------------------------------------------------
1 | # README
2 |
3 | 准实时数仓项目
4 |
5 | ### 一.项目背景
6 |
7 | #### 1.简介
8 |
9 | **WAHT:**基于数据采集与监控。其中用户行为数据、内容数据、业务数据收集到HDFS。我们着重普及事件分析、漏斗分析、留存分析在用户模型分析在用户模型分析中最常见的3个分析模型。
10 |
11 | **WHY:**搭建数仓的最终目的是为了使得数据更为高效的令其产生价值。数据仓库的手段不一,但最终目的是一致的。
12 |
13 | **HOW:**如经典模型:DAU[日常活跃用户统计:Daily Active User]预测未来一段时间的日活跃用户,根据活跃用户可以指定策略等。如生鲜市场备货等,更有效的减少损耗,提高资源利用率。
14 |
15 | #### 2.行为分析模型
16 |
17 | 在分析模型中经常会出现两个名词:指标和维度。
18 |
19 | - 指标:具体的数值。新闻浏览量、新闻点击量。指标一般都要结合维度才能产生更大的价值。
20 | - 维度:看数据的角度,描述一个事物本身具备的特征或属性。在数据分析领域,维度通常用来描述分析指标(如上面的新闻分成实事、娱乐的维度,亦或者分为国内、国外的维度)。
21 |
22 | #### 2.1.事件分析
23 |
24 | 用来追踪用户行为的。在行为日志中有个event:AppClick
25 |
26 | - 所谓事件分析,就是基于事件的指标统计。
27 |
28 | - 最近一周哪个渠道的新增用户最多,以及变化趋势。
29 |
30 | > 其中“最近一周”和“哪个渠道”都是维度,“新增用户”量和“变化趋势”则是指标
31 |
32 | - 每天每个地域的活跃用户数。
33 |
34 | #### 2.2.留存分析
35 |
36 | **WHAT:**一种用来分析用户参与情况/活跃程度的分析模型,考察进行初始行为的用户中,有多少人会进行后续行为。
37 |
38 | > 在互联网行业里,通常我们会通过拉新把客户引过来,但是经过一段时间可能就会有一部分客户逐渐流失了。那些留下来的人或者是经常回访我们公司网站 / App 的人就称为留存。
39 |
40 | **WHY:**用来衡量产品对用户价值高低的重要方法。
41 |
42 | > 如某个社交产品改进了新注册用户的引导留存,期待改善用户注册后的参与程度,这样用留存分析就可以验证改进的效果。亦或者是判断某项产品改动是否奏效,如新增了一个邀请好友的功能,观察是否有人因新增功能而多使用产品几个月。
43 |
44 | 特点与价值
45 |
46 | - 留存率是判断产品价值最重要的标准,揭示了产品保留用户的能力
47 | - 宏观上把握用户生命周期长度以及定位产品可改善至之处
48 |
49 | #### 2.3.漏斗分析
50 |
51 | **WHAT:**一套流程式数据分析,它能够科学反映用户行为状态以及从起点到终点各阶段用户转化率情况的重要分析模型。
52 |
53 | > 漏斗的步骤、漏斗的时间范围、漏斗的窗口期。
54 | >
55 | > 漏斗分析模型已经广泛应用于流量监控、产品目标转化等日常数据运营与数据分析的工作中。例如在一款产品服务平台中,直播用户从激活APP开始到花费,一般的用户购物路径为激活APP、注册账号、进入直播间、互动行为、礼物花费五大阶段,漏斗能够展现出各个阶段的转化率,通过漏斗各环节相关数据的比较,能够直观地发现和说明问题所在,从而找到优化方向。
56 |
57 | 特点与价值
58 |
59 | - 企业可以监控用户在各个层级的转化情况,聚焦用户选购全流程中最有效转化路径;同时找到可优化的短板,提升用户体验。
60 | - 多维度切分与呈现用户转化情况,成单瓶颈无处遁形。
61 | - 不同属性的用户群体漏斗比较,从差异角度窥视优化思路。
62 |
63 | #### 2.3.1.DAU预测
64 |
65 | 日活跃用户数量。
66 |
67 | > 一般用于反映网站、互联网应用等运营情况。结合MAU(月活跃用户数量)一起使用,用来衡量服务的用户粘性以及服务的衰退周期。
68 |
69 | 
70 |
71 | > $$
72 | > A_N:表示第N日的新增用户\\
73 | > R_i:第i天的留存率
74 | > \sum_{i=1}^N=A_{N-i}*R_i:第N-i天的新增和第i天的留存率乘积之和
75 | > $$
76 | >
77 | > 整个公式:`当天的日活`=`当天的新增用户`+`之前每天新增用户到当日的用户之和`
78 |
79 | ### 二.项目需求
80 |
81 | #### 1.数据
82 |
83 | #### 1.1.数据表
84 |
85 | | 数据库 | 数据表 | 描述 |
86 | | -------- | ------------ | --------------------------- |
87 | | ods_news | news | 原始行为数据 |
88 | | ods_news | news_parquet | 原始数据,json转换为parquet |
89 | | ods_news | news_article | 通过HTTP请求过来的内容日志 |
90 | | ods_news | ad_info | 从MySQL过来的广告信息 |
91 | | ods_news | meta | 从MySQL过来的元信息 |
92 |
93 | #### 1.2.行为数据解释
94 |
95 |
--------------------------------------------------------------------------------
/准实时数仓项目/assets/DAU公式.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/准实时数仓项目/assets/DAU公式.png
--------------------------------------------------------------------------------
/用户画像项目实践/README.md:
--------------------------------------------------------------------------------
1 | # README
2 |
3 | # 用户画像
4 |
5 | ### 一.项目背景
6 |
7 | > 你将掌握的技术:标签存储、中文分词、标签向量化、企业工程实施、向量搜索,以解决企业人群圈定、相似用户搜索。
8 | >
9 | > **WHAT:**用户画像是指根据用户的属性、用户偏好、生活习惯、用户行为等信息而抽象出来的标签化用户模型。 通俗说就是给用户打标签,而标签是通过对用户信息分析而来的高度精炼的特征标识。
10 | >
11 | > **WHY:**电商领域中,有上百万的商品和用户,哪些商品应该推送给哪些客户,这就要知道商品画像和用户画像,与之一一对应就能推送到有效的商品促成购买。
12 | >
13 | > 商品画像往往可以在商家上架时要求商家添加各种属性,而且他们也很乐意这么做,但是让用户一开始就填自己的画像显然不现实,一是非常麻烦二是用户需求会一直变,今天想要这个明天就想要那个了。
14 | >
15 | > 而这个项目就是为了解决这一大痛点,利用用户的看的内容建设画像,并随时更新,且基于用户之间相似性去推荐商品。
16 | >
17 | > **HOW:** 如何得到不同用户的画像(或物品的画像),基于TF-IDF和Word2Vec技术,把用户看的内容分类分向量,以此来得到用户的画像,且计算用户之间的相似性。
18 |
19 | #### 1.介绍
20 |
21 | 想要做用户画像必须以数据仓库作为基础(数据仓库-OLAP引擎)。一般在电商平台或者头条等会有广告投放业务,我们需要将广告展示给不同的用户,不同用户一定是对不同广告感兴趣的,如何才能给到用户想看的?我们需要不停的给用户贴标签(根据用户行为将他/她感兴趣的记录下来)。标签在用户画像里是给机器看的(向量)。
22 |
23 | > 标签包含基本属性、行为等,每个标签也要有排名之分,因为有时候用户是不小心点了那个内容,而有时候看一个内容看了很久。
24 |
25 | #### 2.OLAP对比OTIP
26 |
27 | OLTP(Online Transcation Processing, 联机式事务处理): Java开发,强调对事务的管理,如几个业务之间的一致性等。
28 |
29 | OLAP(Online Analytical Processing, 联机式分析处理): 数据分析,强调分析数据以得出业务价值,增删改查会比较多。
30 |
31 | #### 3.画像&向量(标签)
32 |
33 | 这里的标签同时也指向量
34 |
35 | > 基础标签:性别、年龄、手机号码等
36 | >
37 | > 高级标签:收入、浏览喜好(如游戏的王者荣耀、欧洲足球等)
38 |
39 | **向量:**(也称为欧几里得*向量*、几何*向量*、矢量),指具有大小(magnitude)和方向的量。它可以形象化地表示为带箭头的线段。可以简单理解为它就代表这个用户的画像信息,以**数组**的形式。
40 |
41 | > **数组:**有一维数组[12, 52, 11],也有这样的多维数组
42 | >
43 | > [52, 0.2]
44 | >
45 | > [-0.123, 2]
46 |
47 | **HOW:**如何构建用户画像(标签/向量)
48 |
49 | 1. 对原始数据进行基础ETL就可以得出标签
50 |
51 | 2. 物品打标签,然后获取用户在这些标签上的历史行为
52 |
53 | 3. 利用算法把词变成向量
54 |
55 | > TF-IDF、Word2Vec等
56 |
57 | 4. 把标签存储起来
58 |
59 | > BitMap。
60 | >
61 | > **WHAT:**位图或者说基于位的隐射,用bit位来标记某个元素对应的Value,而key即是该元素,即要表达{1, 2, 4, 6}只需要这么存即可
62 | >
63 | > | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 |
64 | > | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
65 | > | 7 | "6" | 5 | "4" | 3 | "2" | "1" | 0 |
66 | >
67 | > **而这仅仅是1 bit**,如何查找等更多细节参考https://www.cnblogs.com/cjsblog/p/11613708.html
68 | >
69 | > **WHY:**在20亿个随机数中找出某个数,判断它是否在其中。那么量就是20亿个int,int占4个字节,1字节=8位(1 byte=8 bit)。
70 | >
71 | > 即=20亿×4/1024/1024≈7.6G。
72 | >
73 | > 而用BitMap则=2000000000/8/1024/1024/1024)≈0.233G。
74 |
75 | #### 4.表
76 |
77 | ##### 4.1涉及的表
78 |
79 | | 项目 | 数据库 | 表 | 描述 | 本项目是否使用 |
80 | | ---- | -------- | ------------ | ------------- | -------------- |
81 | | 采集 | ods_news | news | 行为数据 | 否 |
82 | | 采集 | ods_news | news_article | 内容数据 | 是 |
83 | | 采集 | ods_news | news_parquet | 行为数据 | 否 |
84 | | 采集 | ods_news | meta | mysql元信息 | 否 |
85 | | 采集 | ods_news | ad_info | mysql广告信息 | 否 |
86 | | dw | ods_news | user | 用户表 | 是 |
87 | | dw | ods_news | event | 行为时间表 | 是 |
88 | | dw | dwb_news | rsu | 留存率加速表 | 否 |
89 |
90 | > 目前数仓有的表
91 |
92 | ##### 4.2人群圈定、相似用户搜索
93 |
94 | ### 二.架构
95 |
96 | #### 1.架构图
97 |
98 | 
99 |
100 | 从HDFS中提取用户和文章的信息,并开放出API接口供调用。
101 |
102 | > 以下内容后续会展开。
103 | >
104 | > 注(1) Hive、Hadoop、Presto:数仓存储数据的,也是HDFS。
105 | >
106 | > 注(2) Log:用户的行为数据。
107 | >
108 | > 注(3) Symbolize features:拿到象征/统计类的标签(通过SQL获取,如近期点击次数)。
109 | >
110 | > 注(4) Base features:拿到基础的标签(通过SQL获取,如性别)。
111 | >
112 | > 注(5) BitMap:利用位图,提取特征里的内容。
113 | >
114 | > 注(6) ClickHouse:存储标签的库。
115 | >
116 | > User content:用户浏览的内容。
117 | >
118 | > 注(7) User content features:利用Spark的技术结合用户浏览内容和关键词,拿到用户的内容相关的特征。
119 | >
120 | > 注(8) Content:相关文章。
121 | >
122 | > Spark:包含Spark MLlib,spark MLlib 是spark中可以扩展的机器学习库,它有一系列的机器学习算法和实用程序组成。包括分类、回归、聚类、协同过滤等。
123 | >
124 | > 注(9) HanLP:用以提取实体词技术(非标点符号等)。
125 | >
126 | > 注(10) word:提取到的实体词(非标点符号等)。
127 | >
128 | > 注(11) TF-IDF:用以提取关键字的技术。
129 | >
130 | > 注(12) Key Words:提取到关键字(代表某个文章的关键字)。
131 | >
132 | > 注(13) Word2Vec:用以出标签向量的技术。
133 | >
134 | > 注(14) Word embedding:提取到标签向量。
135 | >
136 | > 注(15) User content embedding:标签向量结合用户内容特征,形成用户的画像,即不同的用户都喜欢哪些文章哪些关键信息。
137 | >
138 | > 注(16) milvus:存到快速的向量搜索引擎。
139 | >
140 | > 注(17) Springboot:写个代码打通查询的接口。
141 | >
142 | > 注(18) 开放API:提供查询接口。
143 |
144 |
145 |
146 | #### 2.算法
147 |
148 | ~~~python
149 | Word2vec: 获取词向量
150 | navie bayes: 文本分类
151 | TF-IDF: 关键字提取
152 | HanLP/JieBa:中文分词工具
153 | ~~~
154 |
155 |
156 |
157 | ### 三.部署实施
158 |
159 | #### 1.Base feature
160 |
161 | 从用户行为日志中提取的数据(ods_news.event_ro和ods_news.user_ro):[性别、年龄、电话归属地、邮箱、手机号码],通过spark sql将其最终写入到clickhouse
162 |
163 | 我们的数据是直接写入到ClickHouse中的,所以hdfs中并没有数据产生。如果想要使用presto或者hive这样的工具查询数据是没有的,只能通过clickhouse。所以我们应该在保存到clickhouse的同时也想hdfs存放一份数据,通过两种做法,如1.通过spark sql向clickhouse写的时候同时保存一份到hdfs;2.通过presto直接执行相同的逻辑。这里我们使用的第一种方法。
164 |
165 |
--------------------------------------------------------------------------------
/用户画像项目实践/assets/用户画像架构图.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/用户画像项目实践/assets/用户画像架构图.png
--------------------------------------------------------------------------------
/采集与监控平台项目/README.md:
--------------------------------------------------------------------------------
1 | # README
2 |
3 | # 采集与监控平台-头条新闻资讯类APP
4 |
5 | ### 一. 项目背景
6 |
7 | #### 1. 简介
8 |
9 | **WHAT:**学习大数据课程,首先要知道数据从何而来(产生)。
10 |
11 | **WHY:**针对不同场景和不同类型的数据应该使用什么样的技术去收集,当有了数据之后才能在这些数据集上探索利用它们,从中挖掘价值。我们还要对数据采集过程的重要指标进行监控,当数据采集发生故障时我们能够及时收到报警,同时当我们遇到性能问题时,通过监控系统我们能更有针对性的优化采集流程。
12 |
13 | #### 2. 基本概念
14 |
15 | 讨论一件事情一般要有上下文的限定,这里我们限定我们的讨论范围是做C端产品(比如`头条`、`快手`、`微信`、`美团`等移动端可以下载的APP)公司的日志。这些公司不仅有APP,可能还有自己的网站,小程序等,这些应用产生的日志都在我们的讨论范围之内。
16 |
17 | #### 2.1.行为日志
18 |
19 | **WHAT:**我们先引入第一个概念`用户行为日志`,用户行为日志是指用户和应用之间进行交互时,由应用本身记录下来的具有一定格式的文本数据,这个文本数据就是用户行为日志。
20 |
21 | **HOW:**比如当我们在手机上打开美团,此时美团APP就会在后台生成一条日志,记录这个设备进行了启动操作,比如
22 |
23 | ~~~
24 | {
25 | "client_time": 1587523571,
26 | "event_name": "StartAPP",
27 | "properties": {
28 | "os": "android",
29 | "imei": "868034031518269",
30 | "model": "HUAWEI MT7-L09",
31 | "wifi": true
32 | ...
33 | },
34 | ...
35 | }
36 | ~~~
37 |
38 | 这条json数据就是一条行为数据了,不同的应用会有自己的格式定义,但是核心思想不变,就是要记录一个用户什么时间在什么位置做了什么样的操作。上面说的是APP日志,还有网站、小程序等需要和用户交互的都可以产生此类日志。因为这些数据都由客户端应用上报的,我们也叫客户端日志。
39 |
40 | #### 2.2. 内容日志
41 |
42 | 不同的公司根据其业务场景不同,会有不同的`内容日志`,比如头条是做资讯的,那么每条新闻咨询就是一条内容日志,包含了文本信息和图片信息。淘宝是做电商的,每条商品信息就是一条内容日志,这些日志也可以用一个json描述出来。这些日志的来源可能是UGC(用户产生的内容),也可能是PGC(专业生产内容的团队)。
43 |
44 | #### 2.3. 业务日志
45 |
46 | **WHAT:**这里的`业务日志`是指由公司服务端业务系统产生的数据,这样数据往往存储在服务端的数据库中,内容日志也往往存储在服务端的数据库中,但是和内容日志不同的是,业务日志是应用逻辑的支撑,我们叫业务日志,在服务端看来就是数据库中的表。这里的数据库可能是关系型也可能是非关系型。
47 |
48 | > 我们在互联网大数据领域研究的数据,基本就是上述三种类型的数据。
49 |
50 | #### 2.4. 元数据
51 |
52 | **WHAT:**元数据用来`描述数据`的数据。如,我们再MySQL创建了一张表,我们创建这张表的同时MySQL会记录下我们创建的表的名字,创建的列的字段信息(列名、数据类型等),索引信息等,这些信息就叫做这张表的元信息,它不是表中的数据,而是描述这些数据用的,这些信息存储在MySQL的`information_shcema`库中。只要是描述数据而用的,都可以叫元数据。
53 |
54 | #### 3. 场景
55 |
56 | 在企业中我们要构建数据平台,第一步先要设计我们的数据采集系统,先把数据从各个源头收集过来。此项目我们以信息流产品类型的企业数据来讲解如何构建数据数据采集及监控系统。对于信息流产品,如`今日头条`、`抖音`、`快手`等都是信息流产品,有的除了图文还有视频,本项目以图文信息流产品为例。
57 |
58 |
59 |
60 | ### 二. 项目需求
61 |
62 | #### 1. 数据源
63 |
64 | 在项目背景中我们已经知道,需要采集的是信息流产品的数据信息。在这里我们限定我们需要采集的数据是新闻资讯类的产品,类似头条。需要收集的日志如下:
65 |
66 | - 用户行为数据。包含用户启动APP,各页面的浏览、点击、分享、收藏广告的点击等行为日志,这些日志会在`客户端埋点`,将数据通过HTTP请求发送到我们设计的数据接口。
67 |
68 | > 如何埋点,写一段Android或IOS代码,监听用户在页面的操作,并按照一定的数据格式记录下俩,之后发送HTTP请求即可。可以自己写代码,也可以选择开源的埋点SDK,比如`sa[sensors data]`,`argo`等。有了客户端发过来的数据,那么我们再设计HTTP数据接口就能让这些数据实时发送到接口中。
69 |
70 | - 资讯数据,这些数据有专门的数据公司提供,一般会有两种方式让我们获取数据。
71 |
72 | - 第一种,拉模式,他们提供HTTP接口,我们通过接口拉取数据。
73 |
74 | > 一般选这种,方便可控。
75 |
76 | - 第二种,推模式,我们提供HTTP接口给他们,他们往接口推数据。
77 |
78 | 数据会先被服务端拉取(前面说的第一种模式),供线上业务使用,如果数据部分需要这部分数据,服务端可以选择在拉取到数据后给数据部分推一份过去。数据端也可以主动去对应的业务库拉数据。在这里我们选择提供HTTP接口,服务端先去拉数据,再把数据推给我们。
79 |
80 | - 业务数据,由于服务端业务表非常多,我们只抽取广告信息表的数据做案例,此表在MySQL中,存储着每条广告的基本信息和对应的广告主。需要我们同步此表的数据到HDFS中。
81 |
82 | #### 2. 目标
83 |
84 | - 数据采集到HDFS。
85 |
86 | > 针对现有的三种数据源设计数据收集方案,将数据采集到HDFS中,建立相对应的Hive表,以供后续进行数据的联合分析。
87 |
88 | - 数据时间自适应。
89 |
90 | > 用户行为和内容数据落到HDFS上均自动按天进行分目录,分目录的时间应当以每条`日志中的时间字段`为准。不能以当下时间为准,这样保证数据有延迟时,每条日志依然归属到其本身对应的时间日期中,业务数据直接全量同步。
91 |
92 | - 数据自校验。
93 |
94 | > 数据落地后,简历Hive外部表,并通过Azkaban调度Shell作业,自动检测当日数据目录是否已存在,并自动添加分区。
95 | >
96 | > Hive是读时模式,即便当日数据目录不存在,一样可以成功添加分区,这里之所以要检测目录是为了后续项目做准备。
97 |
98 | - 监控告警。
99 |
100 | > 基于`Prometheus`、`Grafana`搭建监控系统,监控采集流量的重要指标,需要监控的指标有HTTP数据接口的`QPS`、`99延时`、Flume的`channel填充量`、`发送Event速度`。当指标异常是发出报警,我们选择奖报警消息发送到钉钉或企业微信。
101 |
102 | #### 3. 二次开发
103 |
104 | ##### 3.1. Azkaban Alert 二次开发
105 |
106 | 需要对Azkaban的`报警进行二次开发`,使其支持将调度失败的信息发送至我们自有的报警中心,在这里我们直接开发使其支持将失败消息发送至钉钉。
107 |
108 | ##### 3.2. Flume Interceptor 二次开发
109 |
110 | 因为我们的行为数据是经过BASE64编码的(下方架构会有说明),但落地到HDFS时如果我们需要直接用日志中的某个字段值作为我们HDFS上的目录划分,就必须先对BASE64进行编码,然后取出字段值放到Flume的头信息中,在Sink端配置使用。
111 |
112 |
113 |
114 | ### 三. 架构及阐述——核心
115 |
116 | - 采集`架构一`采用LVS+Nginx(OpenResty)+Lua+Flume+Sqoop+Hadoop+Hive+Azkaban+SpringBoot
117 | - 采集`架构二`采用LVS+Nginx(OpenResty)+Lua+Kafka+Sqoop+DataX+Hadoop+Hive+Azkaban+SpringBoot
118 | - 整体监控架构采用`Prometheus`+`Grafana`+`IM`方案
119 | - 本项目中的组件监控`ngingx-lua-prometheus`+`flume-expoter`+`Prometheus`+`Grafana`+`Supervisor`+`IM`
120 | - 元数据管理及实施数据接收`FRP(Ngrok)`+`Supervisor`+`Metaserver`+`qfapp`
121 |
122 | #### 1. 架构图
123 |
124 | 设计两个架构图,架构一不涉及`Kafka`、`Spark`、`Flink`。架构二作为参考架构。后面讲完`Kafka`、`Spark`、`Flink`等组件后由大家自己思考
125 |
126 | 
127 |
128 | > 数据采集与监控架构一,也是我们这次实战使用的架构,架构阐述在三.1.3架构阐述会进行阐述
129 |
130 | 
131 |
132 | > 数据采集与监控架构二
133 |
134 | #### 1.2. 项目组件
135 |
136 | 本项目中我们的操作系统信息如下:
137 |
138 | ~~~
139 | * CentOS Linux release 7.6.1810 (Core) # 操作系统选择Centos7即可
140 | * JDK 1.8
141 | ~~~
142 |
143 |
144 |
145 | | 编号 | 组件 | 基础课 | 版本 | 备注 |
146 | | ---- | ---------- | ------ | -------- | ------------------------------ |
147 | | 1 | OpenResty | | 1.15.8.3 | based on the nginx 1.15.8 core |
148 | | 2 | Flume | | 1.7.0 | |
149 | | 3 | Sqoop | | 1.4.7 | |
150 | | 4 | DataX | | 3.0 | |
151 | | 5 | LVS | | | |
152 | | 6 | Prometheus | | 2.17.1 | |
153 | | 7 | Grafana | | 6.7.3 | |
154 | | 8 | Hadoop | | 2.7.6 | |
155 | | 9 | Hive | | 2.1.1 | |
156 | | 10 | Azkaban | | 3.82.0 | |
157 | | 11 | SpringBoot | | 2.2.0 | |
158 |
159 | #### 1.3. 架构阐述
160 |
161 | 本阶段的项目使用上图的架构一进行实施,接下来是对其设计进行阐述,会简单讲解其中的基本概念和在架构中的作用,后续在实施时会进行具体讲解。
162 |
163 | ##### 1.3.1. 客户端日志收集
164 |
165 | 之前讲到我们要开发一个HTTP服务,接收客户端上报给来的用户行为日志(HTTP请求),除此之外还要将数据落到磁盘上,这样数据就从客户端保存到服务器的磁盘上,也就是下图的部分:
166 |
167 | 
168 |
169 | 从图中可以看到,我们通过开源的SDK进行埋点(可自行研发)将APP、网页(H5)等客户端的用户日志,通过HTTP请求发送到Nginx集群,这里有几点需求说明:
170 |
171 | - 可以看到通过HTTP发送的数据并非直接的明文数据,而是做了`base64`编码,对于`base64`编码
172 |
173 | - 第一,其并非加密算法,而是一种编码方式,所以并不能保证数据的安全,如果想要加密数据可以使用对称加密或非对称加密算法,但这样也就涉及解密,这一过程需要耗费大量计算资源,尤其是海量数据的情况下。现在基本APP数据上报都是`base64`编码,有的APP会使用https相对更安全些。
174 | - 第二,客户端并不会在用户每次产生行为数据就立刻上报,而是有两个条件控制`时间`和`日志条数`,比如`30s, 100条`这两个条件任何一个达到才触发请求,请求数据时为了减少网络流量我们会将这些数据压缩为二进制,然后转换为`base64`后传输。
175 |
176 | - 使用Nginx作为HTTP接口服务,Nginx是一个高性能的web服务器,在环境搭建中也会做一个Nginx的文档。请求通过Nginx的Access日志直接落盘。
177 |
178 | - Nginx可以线性扩展,以应对高并发请求,一般来讲,一台`4C8G的机器`,可以承受`8000-11000的QPS`。多台Nginx我们通过`lvs`来做负载均衡或者云缠上提供的`slb`服务,lvs是为了将流量合理的分配给多台Nginx,分配算法一般是Round Robin(RR)轮询算法,Least Connections(LC)最小连接调度。
179 |
180 | > lvs:整体相比Nginx/HAProxy要更稳定,转发效率也更高。将请求(流量)合理分配给服务器(这里指Nginx),其包含十种调度算法和三种工作模式,供使用者选择。其中常用且容易理解的就是“加权最小连接调度”,在调度新连接时尽可能使服务器的已建立连接数和其权值成比例。
181 | >
182 | > slb:商业的(花钱搞定一切),解决lvs的一些痛点,如:跨vlan通讯问题、DDOS攻击防御功能、性能部署扩容等。当然这些也可以在lvs的基础上搞,只是花时间而且没有人兜底。`兜底`≈出问题有人背锅。
183 |
184 | - 对于落盘后的数据我们可以通过Flume监听磁盘数据目录,实时收集数据发送到hdfs指定的目录上。我们会做一个定时任务切分Nginx Access日志到指定的磁盘数据目录。Flume监听这个数据目录中的文件,发送完一个文件就删除一个文件。
185 |
186 | ##### 1.3.2. 新闻&业务数据收集
187 |
188 | 
189 |
190 | 前面讲到,新闻资讯服务端先拉专门数据公司的数据,再通过推的方式给我们,我们可以通过`Flume Http Source`提供HTTP接口给服务端,让他们把数据推给我们,之后直接投递到hdfs上。我们的业务数据在MySQL(RDB关系型数据库)中,只收集广告数据表中的数据,这个数据通过Sqoop或者DataX也可以直接收集,相关MySQL连接信息后面会给出。
191 |
192 | ##### 1.3.3. 元数据&调度
193 |
194 | 
195 |
196 | 从图中可以看出,我们的数据经过`Flume、Sqoop`发送到hdfs,通过Azkaban调度建立HIVE分区表,注意此表中是json数据,再调度一个json数据转换的宽表,之后我们会有一个MR根据`元数`据信息和我们的数据信息结合做一个数据校验控制。
197 |
198 | > 元数据(Metadata):又称中介数据、中继数据,为描述数据的数据(data about data),主要是描述数据属性(property)的信息,用来支持如指示存储位置、历史数据、资源查找、文件记录等功能。
199 | >
200 | > 这里可以简单理解为对于我们埋点数据json中的字段进行管理,如json中有哪些字段,字段都是什么类型等。实际开发过程中,每埋一个点,都需要在埋点数据管理平台(上图中的web),提交一个申请,比如我要增加一个点击新闻的埋点,这个埋点需要添加什么字段,是什么类型等都要写清楚,经过审核后,客户端伙伴才会开发埋点。这样我们上报的json数据才会有这个字段。
201 | >
202 | > 不用关注埋点数据管理平台如何实现,了解其作用即可。
203 |
204 | 这样通过元数据和我们上报的数据可以做一个检验,过滤掉客户端再实际操作过程中出现的错误数据,同时对于埋点,我们通过和元数据结合,也可以做到快速下线数据。在本项目中,我们会把元数据也同步到HIVE表,通过SQL Join的方式去做数据校验,而不是写MR。DophonScheduler也是作业流调度系统。
205 |
206 | ### 四.部署实施
207 |
208 | #### 4.1. 数据格式
209 |
210 | ##### 4.1.1. 行为数据格式
211 |
212 | 之前我们说过,对于用户行为数据的Scheme我们是有埋点平台的,通过元数据管理控制,在这里我们为了简化,不再去开发元数据管理的WEB平台,而是直接给出元数据的表结构信息,这个表在MySQL中。我们通过已经埋过的点的数据结构,只要开发好HTTP接口,将客户端上报数据的地址,指向开发好的HTTP接口地址,就可以实时收到用户行为数据。
213 |
214 | - 配置客户端上报数据的地址,这里的地址就是开发好的HTPP接口地址,使用方式如下
215 |
216 | ~~~shell
217 | # 例如我们开发好HTTP接口地址为 http://xxx.xxx.xxx/data/v1, 我们需要执行如下命令,将我们开发好的地址配置到管理中心,只要有数据发送到我们的接口中,我们可以在request_body中获取到发送的base64编码数据,在后面的部署中会详细说明
218 | curl -X POST \
219 | http://meta.frp.qfbigdata.com:8112/ \
220 | -F data_url=http://xxx.xxx.xxx/data/v1
221 | ~~~
222 |
223 |
224 |
225 | - 上报数据格式
226 |
227 | ~~~json
228 | # 原始base64
229 | eyJjb25ldG50Ijp7ImRpc3RpbmN0X2lkIjoiNjkyNDA3MiIsInByb3BlcnRpZXMiOnsibW9kZWwiOiJIUlktQUwwMGEiLCJuZXR3b3JrX3R5cGUiOiJXSUZJIiwiaXNfY2hhcmdpbmciOiIyIiwiYXBwX3ZlcnNpb24iOiI0LjQuNSIsImVsZW1lbnRfbmFtZSI6IuaIkeeahOmSseWMhemhtSIsImVsZW1lbnRfcGFnZSI6IummlumhtSIsImNhcnJpZXIiOiLkuK3lm73np7vliqgiLCJvcyI6ImFuZHJvaWQiLCJpbWVpIjoiOTM4ZDczMWY0MTg3NGRhMCIsImJhdHRlcnlfbGV2ZWwiOiI2OSIsInNjcmVlbl93aWR0aCI6IjEwODAiLCJkZXZpY2VfaWQiOiJlZDcxZDdkZi0yZjVjLTY2ZDMtY2JmYi01M2Y1NWJjNzg5OTkiLCJjbGllbnRfdGltZSI6IjIwMjAtMDQtMjUwNzo1OTo1MCIsImlwIjoiMTIxLjU2Ljc5LjQiLCJ3aWZpIjoiMSIsIm1hbnVmYWN0dXJlciI6IkhVQVdFSSIsInNjcmVlbl9oZWlnaHQiOiIyMzQwIn0sImV2ZW50IjoiQXBwQ2xpY2sifSwicHJvamVjdCI6Im5ld3MiLCJjdGltZSI6IjE1ODc3NzU3NDUifQo=
230 | # decode之后的json
231 | {
232 | "conetnt": {
233 | "distinct_id": "6924072", # 用户ID
234 | "properties": {
235 | "model": "HRY-AL00a", #机型
236 | "network_type": "WIFI", #用户网络类型
237 | "is_charging": "2", #是否充电中
238 | "app_version": "4.4.5", #app版本
239 | "element_name": "我的钱包页", #元素名称
240 | "element_page": "首页", #元素所在页面
241 | "carrier": "中国移动", #运营商
242 | "os": "android", #操作系统
243 | "imei": "938d731f41874da0", #手机IMEI号
244 | "battery_level": "69", #手机电量
245 | "screen_width": "1080", #屏幕宽度
246 | "device_id": "ed71d7df-2f5c-66d3-cbfb-53f55bc78999", #设备ID
247 | "client_time": "2020-04-25 07:59:50",#客户端上报此条日志时间
248 | "ip": "121.56.79.4", #客户端IP地址
249 | "manufacturer": "HUAWEI", #制造商
250 | "screen_height": "2340", #屏幕高度
251 | "client_time":"1587771745000" # 客户端上报日志时间
252 | },
253 | "event": "AppClick" # 事件名称
254 | },
255 | "project": "news", #产品名称
256 | "ctime": "1587775745000" #服务器接收到日志时间
257 | }
258 | ~~~
259 |
260 | ##### 4.1.2. 业务数据格式
261 |
262 | 业务数据在MySQL中共两张表,一张是行为数据的元数据表,一张表是广告信息表,结构信息如下
263 |
264 | ~~~sql
265 | -- 元信息表,已经过简化
266 | CREATE TABLE `meta` (
267 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID,主键',
268 | `field` varchar(50) NOT NULL DEFAULT '' COMMENT '字段名称',
269 | `filed_type` varchar(20) NOT NULL DEFAULT '' COMMENT '字段类型',
270 | `field_desc` varchar(255) DEFAULT NULL COMMENT '字段说明',
271 | `app_version` varchar(10) NOT NULL DEFAULT '' COMMENT '上线版本号',
272 | `status` tinyint(4) DEFAULT '0' COMMENT '字段状态,0 下线 1 上线',
273 | `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
274 | `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
275 | PRIMARY KEY (`id`)
276 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
277 | -- 广告信息表,已经过简化
278 | CREATE TABLE `ad_info` (
279 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID,主键',
280 | `ad_id` int(11) DEFAULT NULL COMMENT '广告ID',
281 | `advertiser_id` int(11) DEFAULT NULL COMMENT '广告商ID,一个广告商会投放多个广告',
282 | `advertiser_name` varchar(255) DEFAULT NULL COMMENT '广告商名称',
283 | `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
284 | `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
285 | PRIMARY KEY (`id`)
286 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
287 |
288 | ~~~
289 |
290 | ##### 4.1.3. 新闻资讯数据格式
291 |
292 | 新闻资讯数据服务端会通过我们提供的`Flume HTTP Source`推给我们,格式如下
293 |
294 | ~~~json
295 | {
296 | "article_id": 487186016, # 文章ID,新闻的唯一标识
297 | "type_name": " 科技", #新闻类型
298 | "pub_time": "2020-04-20 19:45:36.919", # 新闻发布时间
299 | "title": "小米10pro 30w无线充电对比华为40w有线充电", # 新闻标题
300 | "content": "之前做了一个小米10pro 30w无线充电对比华为40w有线充电,米10pro无线充电充满只需要55分钟。 这次oppoACE2,看了其他媒体测试的40w快充,前半小时还比不上米10pro,总时间才快了5分钟。然而一 个40w,一个30w,一个4000毫安,一个4500毫安。。。这40w真的阳痿

", # 新闻内容html格式
301 | "source_name": "今日头条移动端-数码", # 新闻来源
302 | "tags": "pro,充电,小时,测试,充满,比不上,需要" # 内容标签
303 | }
304 | ~~~
305 |
306 | #### 4.2. 采集架构实施
307 |
308 | ##### 4.2.1. LVS负载均衡
309 |
310 | 对于负载均衡LVS的环境,因为这部分的内容偏运维,并不是我们大数据课程要掌握的内容,所以在这里我们不搭建这个环节,大家只要知晓工程中我们在高并发场景下,可以使用LVS做负载均衡就可以了。
311 |
312 | > 例如:我们用一天机器作为数据收集的机器,但是单台的机器性能是有限的,比如4C8G单台机器只能抗住我们`10000 QPS`,但我们的流量高峰有`15000 QPS`,那我们再加一台机器就可以了。但是这两台机器如何同一对外提供服务呢,我们的目的简单来讲有两点,第一,对外是一个IP提供服务。第二,两台机器要相对均衡分配,每天机器都分到`7500 QPS`。那LVS就可以实现我们的目的,那我们再说一个LVS最简单的流量调度算法,Round Robin Scheduling (RR)轮询算法,就是一次把请求分配给RS(real server),就是我们的两台机器。LVS有8种调度算法,来满足不同场景的需求,在大数据这门课程中,并不需要我们掌握。另外对于负载均衡(SLB),LVS只是负载均衡的一种实现方式,不要片面的认为LVS就是SLB。
313 |
314 | ##### 4.2.2. Nginx & Lua 数据埋点接口
315 |
316 | > 写在前面的话:我们在本节中使用Lua,并没用使用Lua直接写入Kafka的模型,因为当前Kafka我们还没有学习,这里通过Lua写access日志,对于写Kafka的模式对应前文的架构图而,等学完Kafka后,我们将采用写Kafka的方式
317 |
318 | 前文框架阐述中我们已经说过,将使用Nginx作为数据采集的HTTP服务,词小节我们会对Nginx及其部署做说明。在LVS小节中我们说过并不搭建LVS环境,因此将直接使用单台Nginx构建我们的数据采集接口。以下是引用[官网]([Welcome to NGINX Wiki! | NGINX](https://www.nginx.com/resources/wiki/))对Nginx的描述
319 |
320 | > NGINX is a free, open-source, high-performance HTTP server and reverse proxy, as well as an
321 | > IMAP/POP3 proxy server. NGINX is known for its high performance, stability, rich feature set, simple
322 | > configuration, and low resource consumption.
323 |
324 | 我们私用时并没有使用官方的原生Nginx,而是使用[OpenResty](https://openresty.org/cn/),它是就Nginx和LuaJit开发的开源软件,可以让我们在Nginx中直接嵌入Lua代码,使我们可以更加灵活的操控数据。对于Lua在本届中,我们也不会单独讲解,在后面搭建环境时,你可以从代码中看到Lua在其中起到的作用就足够了。
325 |
326 | OpenResty安装(**实操**)
327 |
328 | > 使用的是4C8G的虚拟机7.6版本,我在腾讯云开的
329 |
330 | 1. 选择yum安装方式,yum默认会安装openresty源的最新版本,当前版本是1.19.9.1
331 |
332 | ~~~shell
333 | sudo yum install -y yum-utils
334 | # 添加openresty yum 源
335 | sudo yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
336 | # 安装openresty
337 | sudo yum install -y openresty
338 | ~~~
339 |
340 | > 使用open+tab键看是否补全,或`whereis openresty`查看是否安装到/usr/bin/下。
341 | >
342 | > 
343 | >
344 | > 如果是root用户,可以不用sudo,如果非root用户且具有sudo权限,并准备用root权限创建应用,就用sudo。如果责备用个人用户创建,请确保具有相关目录的权限,这里我们使用sudo的权限。
345 |
346 | 2. yum方式安装后,安装目录在/usr/local/openresty 同时可执行文件在/usr/bin/openresty, 它只是一个软链接如果你的/usr/bin 目录不在系统PATH中,是无法直接执行 openresty 命令的。你需要把/usr/bin 添加到系统环境变量PATH中,[关于OpenResty RPM包的更多介绍](https://openresty.org/cn/rpm-packages.html),如果指定安装也是可以的,查看源中可用的版本,之后指定一个版本安装即可,这里我们使用默认最新版本
347 |
348 | ~~~shell
349 | # 查看可用版本
350 | sudo yum list openresty --showduplicates
351 | ~~~
352 |
353 |
354 |
355 | 3. 为我们的采集服务创建一个OpenResty应用,其实就是创建一个目录,应用相关配置文件都放在这个目录里,方便管理。
356 |
357 | ~~~shell
358 | # 创建相关目录
359 | sudo mkdir -p /opt/app/collect-app/
360 | sudo mkdir -p /opt/app/collect-app/conf/
361 | sudo mkdir -p /opt/app/collect-app/logs/
362 | sudo mkdir -p /opt/app/collect-app/conf/vhost/
363 | sudo cp /usr/local/openresty/nginx/conf/mime.types /opt/app/collect-app/conf/
364 | ~~~
365 |
366 | > conf目录存储主配置文件
367 | >
368 | > logs存储程序运行过程中的日志,用户行为日志
369 | >
370 | > vhost放nginx的副配置文件
371 | >
372 | > 
373 |
374 | 4. 编写nginx配置文件,cd到/opt/app/collect-app/conf/ 目录下,vi/vim命名nginx.conf添加如下内容
375 |
376 | ~~~shell
377 | # nginx.conf
378 | # nginx 用户和组
379 | user root root;
380 | # work进程数,
381 | worker_processes 4;
382 | # 错误日志路径,和日志级别
383 | error_log logs/nginx_error.log error;
384 | # nginx pid文件
385 | pid logs/nginx.pid;
386 | # 单个worker最大打开的文件描述符个数
387 | worker_rlimit_nofile 65535;
388 | events
389 | {
390 | #使用epoll模型
391 | use epoll;
392 | # 单个worker进程允许的最多连接数
393 | worker_connections 65535;
394 | }
395 | http
396 | {
397 | include mime.types;
398 | default_type application/octet-stream;
399 | gzip on;
400 | gzip_min_length 1k;
401 | gzip_buffers 4 16k;
402 | gzip_http_version 1.0;
403 | gzip_comp_level 2;
404 | gzip_types text/plain application/x-javascript text/css application/xml;
405 | gzip_vary on;
406 | underscores_in_headers on;
407 | log_format main
408 | '$remote_addr - $remote_user [$time_local] '
409 | '$request_length '
410 | '"$request" $status $bytes_sent $body_bytes_sent '
411 | '"$http_referer" "$http_user_agent" '
412 | '"$gzip_ratio" "$request_time" '
413 | '"$upstream_addr" "$upstream_status" "$upstream_response_time"';
414 | # 定义我们数据采集的 access 日志格式
415 | log_format collect-app '$cad';
416 | open_log_file_cache max=1000 inactive=60s;
417 | keepalive_timeout 0;
418 | client_max_body_size 20m;
419 | include /opt/app/collect-app/conf/vhost/*.conf;
420 | }
421 | ~~~
422 |
423 |
424 |
425 | 5. cd到/opt/app/collect-app/conf/vhost/下,编写我们的APP的nginx配置,命名为collect-app.conf。这里说明一下,企业工程中我们一般会把多个应用配置和nginx主配置文件分开,然后在主配置文件中通过include命令包含我们的应用配置,在上方配置最后一行你可以看到include命令。这样做的目的是方便我们管理配置。
426 |
427 | ~~~shell
428 | #collect-app.conf
429 | server {
430 | listen 8802 default_server;
431 |
432 | # lua_need_request_body on;
433 | client_max_body_size 5M;
434 | client_body_buffer_size 5M;
435 | location /data/v1 {
436 | set $cad '';
437 | content_by_lua_block {
438 | -- cjson模块
439 | local cjson = require "cjson"
440 | -- 读取请求体信息
441 | ngx.req.read_body()
442 | -- 请求体信息存放到 body_data变量中
443 | local body_data = ngx.req.get_body_data()
444 | -- 如果请求体为空,返回错误
445 | if body_data == nil then
446 | ngx.say([[{"code":500,"msg":"req body nil"}]])
447 | return
448 | end
449 | -- 定义当前时间
450 | local current_time = ngx.now()*1000
451 | -- 请求的URL project参数中获取其值
452 | local project = ngx.var.arg_project
453 | -- 定义一个字典,存放有当前服务为日志增加的信息,如ctime表示接受到请求的时间,ip地址等
454 | local data={}
455 | data["project"] = project
456 | data["ctime"] = current_time
457 | if ngx.var.http_x_forwarded_for == nil then
458 | data["ip"] = ngx.var.remote_addr;
459 | else
460 | data["ip"] = ngx.var.http_x_forwarded_for
461 | end
462 | -- 将增加的信息编码为json
463 | local meta = cjson.encode(data)
464 | -- 将编码的json信息做base64 和 body_data拼接
465 | local res = ngx.encode_base64(meta) .. "-" .. ngx.unescape_uri(body_data)
466 | -- 将数据赋值给我们定义的nginx变量cad中,我们定义的log_format就使用这个变量的值
467 | ngx.var.cad = res
468 | ngx.say([[{"code":200,"msg":"ok"}]])
469 | }
470 | access_log logs/collect-app.access.log collect-app;
471 | }
472 | }
473 | ~~~
474 |
475 | > 上面的`ccess_log logs/collect-app.access.log collect-app`指定的是相对位置,下面我们再指定前缀
476 |
477 | 6. 将上述第4,5步中的配置文件,放到对应的目录下后,执行以下命令测试配置文件是否正确,并启动服务
478 |
479 | ~~~shell
480 | # 测试配置文件格式是否正确,-p指定上面需要的前缀
481 | sudo openresty -p /opt/app/collect-app/ -t
482 | # 后台启动
483 | sudo openresty -p /opt/app/collect-app/
484 | #如果没有任何输出,代表我们已经成功启动nginx,后面讲解报警服务时,我们会用supervisor来管理nginx服务。
485 | # 执行下面命令,可以查看到我们的nginx的进程
486 | ps axu|grep nginx|grep -v grep
487 | # 也可以通过我们监听的端口号查看到nginx master
488 | netstat -antp |grep 8802
489 | ~~~
490 |
491 | > 
492 |
493 | 7. Nginx启动后,安装我们的配置,已经在`8802`端口监听请求了,我们数据服务配置的路径是/data/v1,同时我们会解析请求中url的参数project的参数值。接下来我们就测试一下我们接口是否能正常接收数据。我们再来认识一下我们的接口。首先,我们的服务监听`8802`端口,请求的路径是/data/v1,如果客户端有数据请求过来,我们会解析HTTP请求`request_body`中的数据,接收到数据之后和我们自己定义的数据做拼接,日志写入到`collect-app.access.log`文件中,返回给客户端`{"code":200,"msg":"ok"}`。如果一个客户请求的`request_body`为空,我们将在日志中记录空行,返回客户端`{"code":500,"msg":"req body nil"}`。我们接下来通过测试验证我们的接口是否正常。
494 |
495 | ~~~shell
496 | # 发送一个带request_body的请求,值可以是任意值。 project参数的值,我们定成news,因为我们是新闻的行为数据
497 | curl localhost:8802/data/v1?project=news -d test_data
498 | # > return: {"code":200,"msg":"ok"}
499 |
500 | # 查看我们记录到的日志
501 | tail -n 1 /opt/app/collect-app/logs/collect-app.access.log
502 | # > return: eyJjdGltZSI6MTY0Mzg2MTc3ODg3NiwicHJvamVjdCI6Im5ld3MiLCJpcCI6IjEyNy4wLjAuMSJ9-test_data
503 | #可以看到我们的数据通过我们定义的"-"做了分隔,第一部分是我们base64后的自定义信息,每个人看到的可能是不一样的,第二部分是刚才HTTP请求体的数据test_data。这些在collect-app.conf文件里local res行定义了
504 |
505 | # 我们之前说过客户端发过来的数据也是做了base64的,我们也模拟一下
506 | echo "test_data"|base64 |xargs -I {} curl localhost:8802/data/v1?project=news -d {}
507 | # > return: {"code":200,"msg":"ok"}
508 | tail -n 1 /opt/app/collect-app/logs/collect-app.access.log
509 | # > return: eyJjdGltZSI6MTY0Mzg2MjA0ODAwNiwicHJvamVjdCI6Im5ld3MiLCJpcCI6IjEyNy4wLjAuMSJ9-dGVzdF9kYXRhCg==
510 |
511 | # 可以看到,我们得到了两部分base64数据,他们中间依然以"-"分隔,我们解析出来这两部分数据,验证结果
512 | # 第一部分
513 | echo "eyJjdGltZSI6MTY0Mzg2MjA0ODAwNiwicHJvamVjdCI6Im5ld3MiLCJpcCI6IjEyNy4wLjAuMSJ9"|base64 -d
514 | # > return: {"ctime":1643862048006,"project":"news","ip":"127.0.0.1"}
515 | # 可以看到,解出来的是我们在nginx lua中自定义的json信息。
516 | # 第二部分
517 | echo "dGVzdF9kYXRhCg=="|base64 -d
518 | # > return test_data
519 | # 可以看到解析出来是我们的reqest_body中的数据
520 |
521 | # 我们再发送一个不带 request_body 的数据
522 | curl localhost:8802/data/v1?project=news
523 | # > return {"code":500,"msg":"req body nil"}
524 | # 可以看到,如果客户端不带请求体发送请求,我们会给客户端返回错误信息
525 | ~~~
526 |
527 | > 
528 |
529 | 8. 配置接口到管理中心。经过上述步骤,我们的接口就开发完毕了。接下来需要把我们的接口配置到管理中心,这样就可以接收到真实客户端上报的数据了。但由于大家的机器是内网,所以需要先把本机的服务端口,通过内网穿透暴露公网地址,之后配置这个公网地址到管理中心,管理中心通过这个公网给你地址发送数据。这部分内容仅仅是为了让你能够实时的接收到数据,按照下面的方法执行就可以了
530 |
531 | ~~~shell
532 | # 下载frp 客户端,这个frp是我们自己编译的,已经修改了源码中的默认地址,并做了相关优化,你下载完直接用即可
533 | mkdir -p /opt/soft/frp/
534 | cd /opt/soft/frp/
535 | wget https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/main/soft/frpc_0.33.linux_adm64.tgz
536 | # 可能需要改名
537 | mv frpc_0.33.linux_adm64.tgz\?raw\=true frpc_0.33.linux_adm64.tgz
538 |
539 | tar -xvzf frpc_0.33.linux_adm64.tgz
540 | /opt/soft/frp/frpc http --sd name -l 8802 -s frp.qfbigdata.com:7001 -u name # 这两个name替换成自己的名字拼音就可以 8802是我们nginx配置的监听端口,注意,name一定要改
541 | ~~~
542 |
543 | > 
544 | >
545 | > 
546 | >
547 | > 这个打开后不能停,不小心停了再打开
548 |
549 | ~~~shell
550 | #运行上述命令后,会看到如上图日志
551 | # 只需要将我们开发的接口主机地址,替换为上述红色圈子中的http连接地址即可,比如上图:
552 | # 我们开发的接口地址是:localhost:8802/data/v1?project=news
553 | # 替换后的地址是(注意非https) http://name.frp.frp.qfbigdata.com:8002/data/v1?project=news
554 | # 同样可以测试一下新地址是否正常,注意name替换为自己写的名字拼音全拼
555 | curl http://name.frp.qfbigdata.com:8002/data/v1?project=news -d test_data
556 | #> 返回结果{"code":200,"data":true}
557 | ~~~
558 |
559 | > 新打开一个窗口
560 | >
561 | > 
562 |
563 | ~~~shell
564 | # 接下来把次地址通过命令,配置到管理中心,你只需要把下方命令中 data_url=后面的地址替换成你的地址, name参数的值换成刚刚改的名字
565 | curl -X POST \
566 | http://meta.frp.qfbigdata.com:8112/api/v1/meta/register \
567 | -F data_url=http://name.frp.qfbigdata.com:8002/data/v1?project=news \
568 | -F type=1 \
569 | -F name=name
570 | # 请求成功后会有如下图返回值
571 | ~~~
572 |
573 | > 
574 | >
575 | > 这时候内网穿透已经完成了
576 |
577 | ~~~shell
578 | # 如果一切OK,可以去看我们的access日志了,会发现数据正在实时写入到日志文件
579 | tail -f /opt/app/collect-app/logs/collect-app.access.log
580 |
581 | # 最后注意,你的frp启动后,如果想要一直接收数据,是不能停止的,一旦停止,就接收不到数据了,再启动就又可以接收到了
582 | ~~~
583 |
584 | > 文件一直在增大
585 | >
586 | > 
587 | >
588 | > [数据管理中心代码](http://qianfeng.qfjava.cn:8087/bigdata/dmc), 这个是GoLang开发的,有兴趣可以看一下
589 |
590 | ##### 4.2.3. Flume行为数据收集
591 |
592 | 我们的日志已经实时的写入到文件了,为了能够通过Hadoop分析这些数据,我们首先要做的就是将数据投递到HDFS上。我们很容易想到Flume就是帮我们做这个事情的。我们需要思考,使用Flume时,`source channel sink`应该如何选择。
593 |
594 | 1. 首先对于`sink` 因为我们要将数据投递到HDFS上,毋庸置疑我们选择`hdfs sink`。
595 | 2. 对于`channel`, 我们有多种选择,如果速度是第一优先级,同时允许数据再Flume挂掉后有丢失,我们可以选择`Memory Channel`,如果不允许数据丢失,我们可以选择其他可以持久化数据的Channel,比如`File Channel,Kafka Channel` 。对于生成环境中,不同的公司根据其业务会有不同的选择,比如对于用户行为日志,偶尔的丢失数据是可以接受的,因为丢失的数据相对于总体的数据可用忽略不计,也不会因为少量的数据丢失而影响到分析结果,而数据采集的速度和性能我们是比较关注的,因此我们会选择`Memory Channel`。当然很多些企业也会选择`File Channel`,这是个仁者见仁智者见智的事情,`Kafka Channel`我们还没有学习Kakfa,这里先不提。
596 | 3. 对于`Source`,因为我们的数据是` access `日志文件,因此我们的Source可以选择`Spooling Directory Source` 和 `Exec Source` 。 我们为什么不使用`Exec Source`,请[参看官网对其解释](https://flume.apache.org/FlumeUserGuide.html#exec-source), 我们使用Spooling Directory Source,先要对我们的access日志做切分处理。接下来我们就开始自己写脚本,切分日志。
597 |
598 | 先来看一个现象,我们把名字改了
599 |
600 | 
601 |
602 | 可以看到改了名字后依然在变大,这说明nginx监控到的数据依然在往这里面放,因为nginx追踪的并不是文件名,而是inode唯一标识。即使移动文件也依然在增长。
603 |
604 | 
605 |
606 | 如果想做切分,但数据还是会往相同的inode里写入,所以还需要做一些操作,怎么让它停止对文件的追踪开发新的文件写新的日志,如下操作
607 |
608 | 
609 |
610 | `kill -USR1 2095`kill 掉掉这个id,让nginx产生一个新的文件
611 |
612 | 
613 |
614 | 可以看到旧的abc.log不再增加
615 |
616 | > 注意,这里我重启了机器,所以文件变小了。重启机器只需要跑这两个命令即可`openresty -p /opt/app/collect-app/`,`/opt/soft/frp/frpc http --sd name -l 8802 -s frp.qfbigdata.com:7001 -u name`注意name要改成之前改的名字
617 |
618 | 接下来定时执行我们的切分脚本,根据数据量级和对数据实时性的要求,选择切分周期,这里我们选择1分钟切分一次。
619 |
620 | ~~~shell
621 | # 先创建我们切分后的数据目录
622 | mkdir -p /opt/app/collect-app/logs/data/
623 | # 建立一个目录用来存放我们以后的脚本文件
624 | mkdir -p /opt/scripts/
625 | ~~~
626 |
627 | cd到/opt/scripts/下,创建split-access-log.sh脚本文件,并添加如下内容
628 |
629 | ~~~shell
630 | #!/bin/sh
631 | # filename: split-access-log.sh
632 | # desc: 此脚本用于切割Nginx Access日志到指定的目录下,供Flume采集使用
633 | # date: 2020-04-28
634 |
635 | # 帮助
636 | usage() {
637 | echo "Usage:"
638 | echo " split-access-log.sh [-f log_file] [-d data_dir] [-p pid_file]"
639 | echo "Description:"
640 | echo " log_file: nginx access file absolute path"
641 | echo " data_dir: split data dir"
642 | echo " pid_file: nginx pid file absolute path"
643 | echo "Warning: if no parmas, use default value"
644 | exit -1
645 | }
646 | default(){
647 | echo "user default value:"
648 | echo " log_file=/opt/app/collect-app/logs/collect-app.access.log"
649 | echo " data_dir=/opt/app/collect-app/logs/data/"
650 | echo " pid_file=/opt/app/collect-app/logs/nginx.pid"
651 | # 我们的access日志文件
652 | log_file="/opt/app/collect-app/logs/collect-app.access.log"
653 | # 切分后文件所放置的目录
654 | data_dir="/opt/app/collect-app/logs/data/"
655 | # Nginx pid 文件
656 | pid_file="/opt/app/collect-app/logs/nginx.pid"
657 | }
658 |
659 | while getopts 'f:d:p:h' OPT; do
660 | case $OPT in
661 | f) log_file="$OPTARG";;
662 | d) data_dir="$OPTARG";;
663 | p) pid_file="$OPTARG";;
664 | h) usage;;
665 | ?) usage;;
666 | *) usage;;
667 | esac
668 | done
669 |
670 | # 当没有参数传入时
671 | if [ $# -eq 0 ];then
672 | default
673 | fi
674 |
675 | # 重命名access, 注意mv 的过程日志是不会丢失的,因为nginx是以inode来表示数据文件的,而不是文件名,这里mv的操作不会改变inode
676 | if [ ! "${log_file}" ] || [ ! "${data_dir}" ] || [ ! ${pid_file} ]; then
677 | echo "some parmas is empty,please user "
678 | exit -1
679 | fi
680 | # 切分之前,先判断日志文件是否有数据,如果有数据再切分,防止切分出来很多空文件
681 | line=`tail -n 1 ${log_file}`
682 | if [ ! "$line" ];then
683 | echo "Warning: access log file no data, do not split!"
684 | exit 0
685 | fi
686 | mv ${log_file} ${data_dir}collect-app.access.$(date +"%s").log
687 | # 向nginx 发送 USR1信号,让其重新打开一个新的日志文件
688 | kill -USR1 `cat ${pid_file}`
689 | echo "finish!"
690 | ~~~
691 |
692 | > 
693 | >
694 | > 测试下效果
695 | >
696 | > ~~~shell
697 | > sh split-access-log.sh
698 | > ~~~
699 | >
700 | > 
701 | >
702 | > 可以看到上面的红框内容已经变小,被切分到下面的红框内了。
703 |
704 | ~~~shell
705 | # 建立一个目录用来存放我们以后的脚本执行过程中产生的日志
706 | mkdir -p /opt/scripts/logs
707 | # 写个crontab定时执行文件,一分钟切一次
708 | echo "*/1 * * * * sh /opt/scripts/split-access-log.sh >> /opt/scripts/logs/split-access-log.log 2>&1 " > /opt/scripts/collect-app-log.cron
709 | # 添加到定时任务,执行下面命令,linux其实是将你的定时脚本中的内容写到了 /var/spool/cron/root 文件中,这个root就是当前的用户名,因为我这边是以root执行的
710 | crontab /opt/scripts/collect-app-log.cron
711 | # 查看生成的定时任务
712 | crontab -l
713 | # 1分钟后可以查看我们脚本的执行日志
714 | more /opt/scripts/logs/split-access-log.log
715 | ~~~
716 |
717 | > 
718 | >
719 | > 可以看到红框内的log已经变成0了,因为到一分钟了。
720 |
721 | 至此我们切分日志的工作就做完了,接下来我们要做就是编写Flume的配置文件,将数据投递到HDFS上了,根据我们前文对于`Source,Channel,Sink` 的选择,在/opt/scripts/下创建collect-app-agent.conf,并添加配置内容
722 |
723 | ~~~shell
724 | # filename: collect-app-agent.conf
725 | # 定义一个名字为 a1001 的agent
726 | # 定义channel
727 | a1001.channels = ch-1
728 | # 定义source
729 | a1001.sources = src-1
730 | # 定义sink
731 | a1001.sinks = k1
732 |
733 | # sink 接到 channel 上
734 | a1001.sinks.k1.channel = ch-1
735 | # source 接到 channel 上
736 | a1001.sources.src-1.channels = ch-1
737 | a1001.sources.src-1.type = spooldir
738 | # 数据文件目录
739 | a1001.sources.src-1.spoolDir = /opt/app/collect-app/logs/data/
740 | # 正则匹配我们需要的数据文件
741 | a1001.sources.src-1.includePattern = ^collect-app.*.log
742 | # 如果想在header信息中加入你传输的文件的文件名,设置下面参数为true,同时设置文件header的key,我们这里设置成fileName,之后你就可以在sink端通过 %{fileName}, 取出header中的fileName变量中的值,这个值就是文件名
743 | # a1001.sources.src-1.basenameHeader = true
744 | # a1001.sources.src-1.basenameHeaderKey = fileName
745 |
746 | # 积累多少个event后,一起发到channel, 这个值在生成环境中我们需要根据数据量配置batchSize大的下,通常来讲们的batchSize越大,吞吐就高,但是也要受到 channel 的capacity,transactionCapacity的限制,不能大于channel的transactionCapacity值。 关于这三个参数的区别及说明参看 [官方wiki](https://cwiki.apache.org/confluence/display/FLUME/BatchSize%2C+ChannelCapacity+and+ChannelTransactionCapacity+Properties)
747 | a1001.sources.src-1.batchSize = 100
748 |
749 | a1001.sinks.k1.type = hdfs
750 | a1001.sinks.k1.hdfs.path = hdfs://qianfeng01:8020/sources/news/%Y%m%d
751 | a1001.sinks.k1.hdfs.filePrefix = news-%Y%m%d_%H
752 | a1001.sinks.k1.hdfs.fileSuffix = .gz
753 | a1001.sinks.k1.hdfs.codeC = gzip
754 | a1001.sinks.k1.hdfs.useLocalTimeStamp = true
755 | a1001.sinks.k1.hdfs.writeFormat = Text
756 | a1001.sinks.k1.hdfs.fileType = CompressedStream
757 | # 禁用安装event条数来滚动生成文件
758 | a1001.sinks.k1.hdfs.rollCount = 0
759 | # 如果一个文件达到10M滚动
760 | a1001.sinks.k1.hdfs.rollSize = 10485760
761 | # 5分钟滚动生成新文件,和文件大小的滚动一起,那个先达到,执行那个
762 | a1001.sinks.k1.hdfs.rollInterval = 600
763 | # 参加上边连接官网说明,理论上batchSize 越大,吞吐越高。 但是HDFS Sink 调用 Hadoop RPC(包括 open、flush、close ..)超时会抛出异常,如果发生在 flush 数据阶段,部分 event 可能已写入 HDFS,事务回滚后当前 BatchSize 的 event 还会再次写入造成数据重复。 batchSize越大可能重复的数据就越多. 同时batchSize值,不能大于channel的transactionCapacity值
764 | a1001.sinks.k1.hdfs.batchSize = 100
765 | # 每个HDFS SINK 开启多少线程来写文件
766 | a1001.sinks.k1.hdfs.threadsPoolSize = 10
767 | # 如果一个文件超过多长时间没有写入,就自动关闭文件,时间单位是秒
768 | a1001.sinks.k1.hdfs.idleTimeout = 60
769 |
770 | a1001.channels.ch-1.type = memory
771 | a1001.channels.ch-1.capacity = 10000
772 | a1001.channels.ch-1.transactionCapacity = 100
773 | ~~~
774 |
775 | 编写一个脚本启动Flume Agent 开始采集数据,在/opt/scripts/下创建start-flume-agent.sh
776 |
777 | ~~~shell
778 | #!/bin/sh
779 | # filename: start-flume-agent.sh
780 | # desc: 启动采集数据的flume agent,agent 名字为 a1001
781 | # date: 2020-04-28
782 | # 请写你安装的FLUME的路径
783 | FLUME_HOME=/opt/local/flume
784 |
785 | ${FLUME_HOME}/bin/flume-ng agent -c ${FLUME_HOME}/conf -f /opt/scripts/conf/collect-app-agent.conf -n a1001 -Dflume.root.logger=INFO,console -Dflume.monitoring.type=http -Dflume.monitoring.port=31001
786 | ~~~
787 |
788 | 启动
789 |
790 | ~~~shell
791 | # 依然把你的脚本放到你之前创建的 /opt/scripts/ 目录下
792 | # 直接执行,程序运行在前台
793 | sh /opt/scripts/start-flume-agent.sh
794 | # 可以后台执行
795 | nohup sh /opt/scripts/start-flume-agent.sh >> /opt/scripts/a1001-flume-agent.log 2>&1 &
796 | # 终止进程
797 | ps axu|grep flume|grep collect-app-agent.conf|gawk '{print $2}'|xargs -n1 kill -15
798 | ~~~
799 |
800 |
801 |
802 |
803 |
804 |
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1642557741872.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1642557741872.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1642563394249.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1642563394249.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1642564130136.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1642564130136.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643860152216.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643860152216.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643860621257.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643860621257.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643861562371.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643861562371.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643862270997.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643862270997.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643866651880.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643866651880.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643869444450.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643869444450.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643869498980.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643869498980.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643869508047.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643869508047.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643870788661.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643870788661.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643873695928.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643873695928.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643873897636.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643873897636.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643890185710.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643890185710.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643890246493.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643890246493.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643890458958.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643890458958.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643890762076.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643890762076.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643890853267.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643890853267.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643890899399.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643890899399.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/1643891023827.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/1643891023827.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/采集架构一.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/采集架构一.png
--------------------------------------------------------------------------------
/采集与监控平台项目/assets/采集架构二.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ben1234560/Practical-projects-of-BigData-and-AI/65c5a4bdb9f0ff2a57b1be3116924e9aad5beeda/采集与监控平台项目/assets/采集架构二.png
--------------------------------------------------------------------------------