当前位置: 首页 > news >正文

离线数仓 (四) --------- 用户行为数据采集模块

目录

  • 一、集群所有进程查看脚本
  • 二、Hadoop安装
    • 1. HDFS存储多目录
    • 2. 集群数据均衡
    • 3. 支持 LZO 压缩配置
    • 4. LZO 创建索引
    • 5. 基准测试
      • ① 测试HDFS写性能
      • ② 测试HDFS读性能
      • ③ 使用 Sort 程序评测 MapReduce
    • 6. Hadoop 参数调优
  • 三、Zookeeper 安装
    • 1. 安装 ZK
    • 2. ZK集群启动停止脚本
  • 四、Kafka 安装
    • 1. Kafka 集群安装
    • 2. Kafka 集群启动停止脚本
    • 3. Kafka 常用命令
    • 4. Kafka 机器数量计算
    • 5. Kafka压力测试
    • 6. Kafka分区数计算
  • 五、采集日志 Flume
    • 1. 日志采集Flume安装
    • 2. Flume组件选型
    • 3. 日志采集 Flume 配置
    • 4. Flume 拦截器
    • 5. 测试 Flume-Kafka 通道
    • 6. Flume 启动停止脚本
  • 六、消费 Kafka 数据 Flume
    • 1. Flume 组件选型
    • 2. 消费者 Flume 配置
    • 3. Flume 时间戳拦截器
    • 4. 消费者 Flume 启动停止脚本
    • 5. Flume 内存优化
    • 6. 采集通道启动/停止脚本
  • 七、常见问题及解决方案
    • 1. 2NN页面不能显示完整信息


一、集群所有进程查看脚本

A、在 /home/fancy/bin 目录下创建脚本 xcall.sh

[fancy@node1 bin]$ vim xcall.sh

B、在脚本中编写如下内容

#! /bin/bash
 
for i in node101 node102 node103
do
    echo --------- $i ----------
    ssh $i "$*"
done

C、修改脚本执行权限

[fancy@node101 bin]$ chmod 777 xcall.sh

D、启动脚本

[fancy@node101 bin]$ xcall.sh jps

二、Hadoop安装

具体安装过程详见 Hadoop (四) --------- Hadoop 运行模式 这篇文章。。。

集群规划:

服务器 node101服务器 node102服务器103
HDFSNameNode、DataNodeDataNodeDataNode、SecondaryNameNode
YarnNodeManagerResourcemanager、NodeManagerNodeManager

注意:尽量使用离线方式安装

1. HDFS存储多目录

A、给Linux系统新增加一块硬盘

参考:https://www.cnblogs.com/yujianadu/p/10750698.html

B、生产环境服务器磁盘情况

在这里插入图片描述

C、在hdfs-site.xml文件中配置多目录,注意新挂载磁盘的访问权限问题

HDFS 的 DataNode 节点保存数据的路径由 dfs.datanode.data.dir 参数决定,其默认值为 file://${hadoop.tmp.dir}/dfs/data,若服务器有多个磁盘,必须对该参数进行修改。如服务器磁盘如上图所示,则该参数应修改为如下的值。

<property>
    <name>dfs.datanode.data.dir</name>
	<value>file:///dfs/data1,file:///hd2/dfs/data2,file:///hd3/dfs/data3,file:///hd4/dfs/data4</value>
</property>

注意:因为每台服务器节点的磁盘情况不同,所以这个配置配完之后,不需要分发。

2. 集群数据均衡

A、节点间数据均衡

开启数据均衡命令

start-balancer.sh -threshold 10

对于参数10,代表的是集群中各个节点的磁盘空间利用率相差不超过10%,可根据实际情况进行调整。

停止数据均衡命令

stop-balancer.sh

注意:于HDFS需要启动单独的 Rebalance Server 来执行 Rebalance 操作,所以尽量不要在 NameNode 上执行 start-balancer.sh,而是找一台比较空闲的机器。

B、磁盘间数据均衡

生成均衡计划 (我们只有一块磁盘,不会生成计划)

hdfs diskbalancer -plan node102

执行均衡计划

hdfs diskbalancer -execute node102.plan.json

查看当前均衡任务的执行情况

hdfs diskbalancer -query node102

取消均衡任务

hdfs diskbalancer -cancel node102.plan.json

3. 支持 LZO 压缩配置

A、hadoop-lzo编译

hadoop 本身并不支持 lzo 压缩,故需要使用 twitter 提供的 hadoop-lzo 开源组件。hadoop-lzo 需依赖
hadoop 和 lzo 进行编译,编译步骤如下。

0. 环境准备
maven (下载安装,配置环境变量,修改sitting.xml加阿里云镜像)
gcc-c++
zlib-devel
autoconf
automake
libtool
通过yum安装即可, yum -y install gcc-c++ lzo-devel zlib-devel autoconf automake libtool

1. 下载、安装并编译LZO

wget http://www.oberhumer.com/opensource/lzo/download/lzo-2.10.tar.gz

tar -zxvf lzo-2.10.tar.gz

cd lzo-2.10

./configure -prefix=/usr/local/hadoop/lzo/

make

make install

2. 编译hadoop-lzo源码

2.1 下载hadoop-lzo的源码,下载地址:https://github.com/twitter/hadoop-lzo/archive/master.zip
2.2 解压之后,修改pom.xml
     <hadoop.current.version>3.1.3</hadoop.current.version>
2.3 声明两个临时环境变量
     export C_INCLUDE_PATH=/usr/local/hadoop/lzo/include
     export LIBRARY_PATH=/usr/local/hadoop/lzo/lib 
2.4 编译
     进入hadoop-lzo-master,执行maven编译命令
     mvn package -Dmaven.test.skip=true
2.5 进入target,hadoop-lzo-0.4.21-SNAPSHOT.jar 即编译成功的hadoop-lzo组件

B、将编译好后的 hadoop-lzo-0.4.20.jar 放入 hadoop-3.1.3/share/hadoop/common/

[fancy@node101 common]$ pwd
/opt/module/hadoop-3.1.3/share/hadoop/common
[fancy@node101 common]$ ls
hadoop-lzo-0.4.20.jar

C、同步 hadoop-lzo-0.4.20.jar 到 node102、node103

[fancy@node101 common]$ xsync hadoop-lzo-0.4.20.jar

D、core-site.xml增加配置支持LZO压缩

<configuration>
    <property>
        <name>io.compression.codecs</name>
        <value>
            org.apache.hadoop.io.compress.GzipCodec,
            org.apache.hadoop.io.compress.DefaultCodec,
            org.apache.hadoop.io.compress.BZip2Codec,
            org.apache.hadoop.io.compress.SnappyCodec,
            com.hadoop.compression.lzo.LzoCodec,
            com.hadoop.compression.lzo.LzopCodec
        </value>
    </property>

    <property>
        <name>io.compression.codec.lzo.class</name>
        <value>com.hadoop.compression.lzo.LzoCodec</value>
    </property>
</configuration>

E、同步 core-site.xml 到 node102、node103

[fancy@node101 hadoop]$ xsync core-site.xml

F、启动及查看集群

[fancy@node101 hadoop-3.1.3]$ sbin/start-dfs.sh
[fancy@node102 hadoop-3.1.3]$ sbin/start-yarn.sh

G、测试-数据准备

[fancy@node101 hadoop-3.1.3]$ hadoop fs -mkdir /input
[fancy@node101 hadoop-3.1.3]$ hadoop fs -put README.txt /input

H、测试-压缩

[fancy@node101 hadoop-3.1.3]$ hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount -Dmapreduce.output.fileoutputformat.compress=true -Dmapreduce.output.fileoutputformat.compress.codec=com.hadoop.compression.lzo.LzopCodec  /input /output

4. LZO 创建索引

A、创建LZO文件的索引

LZO 压缩文件的可切片特性依赖于其索引,故我们需要手动为LZO压缩文件创建索引。若无索引,则LZO 文件的切片只有一个。

hadoop jar /path/to/your/hadoop-lzo.jar com.hadoop.compression.lzo.DistributedLzoIndexer big_file.lzo

B、测试

将 bigtable.lzo (200M) 上传到集群的根目录

[fancy@node101 module]$ hadoop fs -mkdir /input
[fancy@node101 module]$ hadoop fs -put bigtable.lzo /input

执行 wordcount 程序

[fancy@hadoop102 module]$ hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount -Dmapreduce.job.inputformat.class=com.hadoop.mapreduce.LzoTextInputFormat /input /output1

在这里插入图片描述
对上传的LZO文件建索引

[fancy@node101 module]$ hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/common/hadoop-lzo-0.4.20.jar  com.hadoop.compression.lzo.DistributedLzoIndexer /input/bigtable.lzo

再次执行WordCount程序

[fancy@node101 module]$ hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount -Dmapreduce.job.inputformat.class=com.hadoop.mapreduce.LzoTextInputFormat /input /output2

在这里插入图片描述

C、注意

如果以上任务,在运行过程中报如下异常

Container [pid=8468,containerID=container_1594198338753_0001_01_000002] 
is running 318740992B beyond the 'VIRTUAL' memory limit. Current usage: 
111.5 MB of 1 GB physical memory used; 2.4 GB of 2.1 GB virtual memory used. Killing container.
Dump of the process-tree for container_1594198338753_0001_01_000002 :

解决办法:在 node101 的 /opt/module/hadoop-3.1.3/etc/hadoop/yarn-site.xml 文件中增加如下配置,然后分发到 node102、node103 服务器上,并重新启动集群。

<!--是否启动一个线程检查每个任务正使用的虚拟内存量,如果任务超出分配值,则直接将其杀掉,默认是true -->
<property>
   <name>yarn.nodemanager.vmem-check-enabled</name>
   <value>false</value>
</property>

5. 基准测试

在企业中非常关心每天从 Java 后台拉取过来的数据,需要多久能上传到集群?消费者关心多久能从HDFS 上拉取需要的数据?

为了搞清楚 HDFS 的读写性能,生产环境上非常需要对集群进行压测。

HDFS 的读写性能主要受网络和磁盘影响比较大。为了方便测试,将 node101、node102、node103 虚拟机网络都设置为 100mbps。

100Mbps单位是bit;10M/s单位是byte ; 1byte=8bit,100Mbps/8=12.5M/s。

在这里插入图片描述

测试网速:

A、来到 node101 的 /opt/module 目录,创建一个

[fancy@node101 software]$ python -m SimpleHTTPServer

B、在 Web 页面上访问

node101:8000

① 测试HDFS写性能

写测试底层原理

在这里插入图片描述

测试内容: 向HDFS集群写10个128M的文件

[fancy@node101 mapreduce]$ hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-3.1.3-tests.jar TestDFSIO -write -nrFiles 10 -fileSize 128MB
2021-02-09 10:43:16,853 INFO fs.TestDFSIO: ----- TestDFSIO ----- : write
2021-02-09 10:43:16,854 INFO fs.TestDFSIO:             Date & time: Tue Feb 09 10:43:16 CST 2021
2021-02-09 10:43:16,854 INFO fs.TestDFSIO:         Number of files: 10
2021-02-09 10:43:16,854 INFO fs.TestDFSIO:  Total MBytes processed: 1280
2021-02-09 10:43:16,854 INFO fs.TestDFSIO:       Throughput mb/sec: 1.61
2021-02-09 10:43:16,854 INFO fs.TestDFSIO:  Average IO rate mb/sec: 1.9
2021-02-09 10:43:16,854 INFO fs.TestDFSIO:   IO rate std deviation: 0.76
2021-02-09 10:43:16,854 INFO fs.TestDFSIO:      Test exec time sec: 133.05
2021-02-09 10:43:16,854 INFO fs.TestDFSIO:

注意:nrFiles n 为生成 mapTask 的数量,生产环境一般可通过 node102:8088 查看CPU核数,设置为(CPU核数 - 1)

  • Number of files :生成 mapTask 数量,一般是集群中 (CPU核数 - 1),我们测试虚拟机就按照实际的物理内存 -1 分配即可。(目标,让每个节点都参与测试)

  • Total MBytes processed :单个map处理的文件大小

  • Throughput mb/sec : 单个mapTak的吞吐量
    计算方式:处理的总文件大小/每一个mapTask写数据的时间累加
    集群整体吞吐量:生成mapTask数量*单个mapTak的吞吐量

  • Average IO rate mb/sec 平均mapTak的吞吐量
    计算方式:每个mapTask处理文件大小/每一个mapTask写数据的时间,全部相加除以task数量

  • IO rate std deviation:方差、反映各个 mapTask 处理的差值,越小越均衡

注意: 如果测试过程中,出现异常

① 可以在 yarn-site.xml 中设置虚拟内存检测为 false

<!--是否启动一个线程检查每个任务正使用的虚拟内存量,如果任务超出分配值,则直接将其杀掉,默认是true -->
<property>
     <name>yarn.nodemanager.vmem-check-enabled</name>
     <value>false</value>
</property>

②分发配置并重启Yarn集群

测试结果分析

①由于副本1就在本地,所以该副本不参与测试

在这里插入图片描述

一共参与测试的文件:10个文件 * 2个副本 = 20个
压测后的速度:1.61
实测速度:1.61M/s * 20个文件 ≈ 32M/s
三台服务器的带宽:12.5 + 12.5 + 12.5 ≈ 30m/s

所有网络资源都已经用满。

如果实测速度远远小于网络,并且实测速度不能满足工作需求,可以考虑采用固态硬盘或者增加磁盘个数。

② 如果客户端不在集群节点,那就三个副本都参与计算

在这里插入图片描述

② 测试HDFS读性能

测试内容: 读取 HDFS 集群 10 个 128M 的文件

[fancy@hadoop102 mapreduce]$ hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-3.1.3-tests.jar TestDFSIO -read -nrFiles 10 -fileSize 128MB
2021-02-09 11:34:15,847 INFO fs.TestDFSIO: ----- TestDFSIO ----- : read
2021-02-09 11:34:15,847 INFO fs.TestDFSIO:             Date & time: Tue Feb 09 11:34:15 CST 2021
2021-02-09 11:34:15,847 INFO fs.TestDFSIO:         Number of files: 10
2021-02-09 11:34:15,847 INFO fs.TestDFSIO:  Total MBytes processed: 1280
2021-02-09 11:34:15,848 INFO fs.TestDFSIO:       Throughput mb/sec: 200.28
2021-02-09 11:34:15,848 INFO fs.TestDFSIO:  Average IO rate mb/sec: 266.74
2021-02-09 11:34:15,848 INFO fs.TestDFSIO:   IO rate std deviation: 143.12
2021-02-09 11:34:15,848 INFO fs.TestDFSIO:      Test exec time sec: 20.83

删除测试生成数据

[fancy@hadoop102 mapreduce]$ hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-3.1.3-tests.jar TestDFSIO -clean

测试结果分析:

为什么读取文件速度大于网络带宽?由于目前只有三台服务器,且有三个副本,数据读取就近原则,相当于都是读取的本地磁盘数据,没有走网络。

在这里插入图片描述

③ 使用 Sort 程序评测 MapReduce

使用 RandomWriter 来产生随机数,每个节点运行 10 个 Map 任务,每个 Map 产生大约 1G 大小的二进制随机数

[fancy@node101 mapreduce]$ hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar randomwriter random-data

执行Sort程序

[fancy@node101 mapreduce]$ hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar sort random-data sorted-data

验证数据是否真正排好序了

[fancy@node101 mapreduce]$ hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-3.1.3-tests.jar testmapredsort -sortInput random-data -sortOutput sorted-data

6. Hadoop 参数调优

A、HDFS参数调优hdfs-site.xml

The number of Namenode RPC server threads that listen to requests from clients. If dfs.namenode.servicerpc-address is not configured then Namenode RPC server threads listen to requests from all nodes.
NameNode有一个工作线程池,用来处理不同 DataNode 的并发心跳以及客户端并发的元数据操作。
对于大集群或者有大量客户端的集群来说,通常需要增大参数dfs.namenode.handler.count的默认值10。

<property>
    <name>dfs.namenode.handler.count</name>
    <value>10</value>
</property>

dfs.namenode.handler.count=20×〖log〗_e^(Cluster Size),比如集群规模为 8 台时,此参数设置为 41。可通过简单的 python 代码计算该值,代码如下。

[fancy@node101 ~]$ python
Python 2.7.5 (default, Apr 11 2018, 07:36:10) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> print int(20*math.log(8))
41
>>> quit()

B、YARN参数调优yarn-site.xml

情景描述:总共7台机器,每天几亿条数据,数据源->Flume->Kafka->HDFS->Hive
面临问题:数据统计主要用HiveSQL,没有数据倾斜,小文件已经做了合并处理,开启的JVM重用,而且IO没有阻塞,内存用了不到50%。但是还是跑的非常慢,而且数据量洪峰过来时,整个集群都会宕掉。基于这种情况有没有优化方案。

解决办法:

NodeManager 内存和服务器实际内存配置尽量接近,如服务器有 128g 内存,但是 NodeManager 默认内存 8G,不修改该参数最多只能用 8G 内存。NodeManager 使用的 CPU 核数和服务器 CPU 核数尽量接近。
yarn.nodemanager.resource.memory-mb NodeManager 使用内存数
yarn.nodemanager.resource.cpu-vcores NodeManager 使用CPU核数

三、Zookeeper 安装

1. 安装 ZK

详见 Zookeeper (二) --------- Zookeeper 本地安装 这一篇文章

集群规划

服务器 node101服务器 node102服务器 node103
ZookeeperZookeeperZookeeperZookeeper

2. ZK集群启动停止脚本

A、在 node101 的 /home/fancy/bin 目录下创建脚本

[fancy@node101 bin]$ vim zk.sh

在脚本中编写如下内容

#!/bin/bash

case $1 in
"start"){
	for i in hadoop102 hadoop103 hadoop104
	do
        echo ---------- zookeeper $i 启动 ------------
		ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh start"
	done
};;
"stop"){
	for i in hadoop102 hadoop103 hadoop104
	do
        echo ---------- zookeeper $i 停止 ------------    
		ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh stop"
	done
};;
"status"){
	for i in hadoop102 hadoop103 hadoop104
	do
        echo ---------- zookeeper $i 状态 ------------    
		ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh status"
	done
};;
esac

B、增加脚本执行权限

[fancy@node101 bin]$ chmod u+x zk.sh

C、Zookeeper集群启动脚本

[fancy@node101 module]$ zk.sh start

D、Zookeeper集群停止脚本

[fancy@node101 module]$ zk.sh stop

四、Kafka 安装

在这里插入图片描述

1. Kafka 集群安装

具体安装详见 Kafka (二) ---------- Kafka 快速入门 这篇文章

集群规划:

服务器hadoop102服务器hadoop103服务器hadoop104
KafkaKafkaKafkaKafka

2. Kafka 集群启动停止脚本

A、在 /home/fancy/bin 目录下创建脚本kf.sh

[fancy@node101 bin]$ vim kf.sh

在脚本中填写如下内容

#! /bin/bash
case $1 in
"start"){
    for i in hadoop102 hadoop103 hadoop104
    do
        echo " --------启动 $i Kafka-------"
        ssh $i "/opt/module/kafka/bin/kafka-server-start.sh -daemon /opt/module/kafka/config/server.properties"
    done
};;
"stop"){
    for i in hadoop102 hadoop103 hadoop104
    do
        echo " --------停止 $i Kafka-------"
        ssh $i "/opt/module/kafka/bin/kafka-server-stop.sh stop"
    done
};;
esac

B、增加脚本执行权限

[fancy@node101 bin]$ chmod u+x kf.sh

C、kf 集群启动脚本

[fancy@node101 module]$ kf.sh start

D、kf集群停止脚本

[fancy@node101 module]$ kf.sh stop

3. Kafka 常用命令

A、查看Kafka Topic列表

[fancy@node101 kafka]$ bin/kafka-topics.sh --zookeeper hadoop102:2181/kafka --list

B、创建Kafka Topic

进入到 /opt/module/kafka/ 目录下创建日志主题

[fancy@node101 kafka]$ bin/kafka-topics.sh --zookeeper node101:2181,node102:2181,node103:2181/kafka  --create --replication-factor 1 --partitions 1 --topic topic_log

C、删除Kafka Topic

[fancy@node101 kafka]$ bin/kafka-topics.sh --delete --zookeeper node101:2181,node102:2181,node103:2181/kafka --topic topic_log

D、Kafka生产消息

[fancy@node101 kafka]$ bin/kafka-console-producer.sh \
--broker-list node101:9092 --topic topic_log
>hello world
>fancy fancy

E、Kafka消费消息

[fancy@hadoop102 kafka]$ bin/kafka-console-consumer.sh \
--bootstrap-server hadoop102:9092 --from-beginning --topic topic_log

--from-beginning: 会把主题中以往所有的数据都读取出来。根据业务场景选择是否增加该配置。

F、查看 Kafka Topic 详情

[fancy@node101 kafka]$ bin/kafka-topics.sh --zookeeper hadoop102:2181/kafka \
--describe --topic topic_log

4. Kafka 机器数量计算

Kafka机器数量(经验公式) = 2 *(峰值生产速度 * 副本数 / 100)+ 1

先拿到峰值生产速度,再根据设定的副本数,就能预估出需要部署Kafka的数量。

A、峰值生产速度

峰值生产速度可以压测得到。

B、副本数

副本数默认是1个,在企业里面2-3个都有,2个居多。
副本多可以提高可靠性,但是会降低网络传输效率。
比如我们的峰值生产速度是50M/s。副本数为2。

Kafka机器数量 = 2 *(50 * 2 / 100)+ 1 = 3

5. Kafka压力测试

A、Kafka压测

用Kafka官方自带的脚本,对Kafka进行压测。

kafka-consumer-perf-test.sh
kafka-producer-perf-test.sh

Kafka压测时,在硬盘读写速度一定的情况下,可以查看到哪些地方出现了瓶颈(CPU,内存,网络IO)。一般都是网络IO达到瓶颈。

B、Kafka Producer压力测试

在这里插入图片描述

Ⅰ、压测环境准备:

① node101、node102、node103 的网络带宽都设置为 100mbps 。
② 关闭 node101 主机,并根据 node101 克隆出node104 (修改IP和主机名称)
③ node104 的带宽不设限
④ 创建一个test topic,设置为 3 个分区 2 个副本

[fancy@node101 kafka]$ bin/kafka-topics.sh --zookeeper node101:2181,node102:2181,node103:2181/kafka --create --replication-factor 2 --partitions 3 --topic test

/opt/module/kafka/bin 目录下面有这两个文件。我们来测试一下

[fancy@node101 kafka]$ bin/kafka-producer-perf-test.sh  --topic test --record-size 100 --num-records 10000000 --throughput -1 --producer-props bootstrap.servers=node101:9092,node102:9092,node103:9092

说明:

  • record-size是一条信息有多大,单位是字节。
  • num-records是总共发送多少条信息。
  • throughput 是每秒多少条信息,设成-1,表示不限流,尽可能快的生产数据,可测出生产者最大吞吐量。

Ⅱ、Kafka 会打印下面的信息

699884 records sent, 139976.8 records/sec (13.35 MB/sec), 1345.6 ms avg latency, 2210.0 ms max latency.
713247 records sent, 141545.3 records/sec (13.50 MB/sec), 1577.4 ms avg latency, 3596.0 ms max latency.
773619 records sent, 153862.2 records/sec (14.67 MB/sec), 2326.8 ms avg latency, 4051.0 ms max latency.
773961 records sent, 154206.2 records/sec (15.71 MB/sec), 1964.1 ms avg latency, 2917.0 ms max latency.
776970 records sent, 154559.4 records/sec (15.74 MB/sec), 1960.2 ms avg latency, 2922.0 ms max latency.
776421 records sent, 154727.2 records/sec (15.76 MB/sec), 1960.4 ms avg latency, 2954.0 ms max latency.

参数解析: Kafka 的吞吐量 15m/s 左右是否符合预期呢?
node101、node102、node103 三台集群的网络总带宽 30m/s 左右,由于是两个副本,所以 Kafka 的吞吐量 30m/s ➗ (2副本) = 15m/s
结论:网络带宽和副本都会影响吞吐量。

Ⅲ、调整batch.size

batch.size默认值是16k。
batch.size较小,会降低吞吐量。比如说,批次大小为0则完全禁用批处理,会一条一条发送消息);
batch.size过大,会增加消息发送延迟。比如说,Batch设置为64k,但是要等待5秒钟Batch才凑满了64k,才能发送出去。那这条消息的延迟就是5秒钟。

[fancy@node101 kafka]$ bin/kafka-producer-perf-test.sh  --topic test --record-size 100 --num-records 10000000 --throughput -1 --producer-props bootstrap.servers=node101:9092,node102:9092,node103:9092 batch.size=500

输出结果

69169 records sent, 13833.8 records/sec (1.32 MB/sec), 2517.6 ms avg latency, 4299.0 ms max latency.
105372 records sent, 21074.4 records/sec (2.01 MB/sec), 6748.4 ms avg latency, 9016.0 ms max latency.
113188 records sent, 22637.6 records/sec (2.16 MB/sec), 11348.0 ms avg latency, 13196.0 ms max latency.
108896 records sent, 21779.2 records/sec (2.08 MB/sec), 12272.6 ms avg latency, 12870.0 ms max latency.

Ⅳ、linger.ms
如果设置 batch size 为64k,但是比如过了10分钟也没有凑够64k,怎么办?
可以设置 linger.ms。比如 linger.ms=5ms,那么就是要发送的数据没有到 64k,5ms 后,数据也会发出去。

Ⅴ、总结

同时设置batch.size和 linger.ms,就是哪个条件先满足就都会将消息发送出去。Kafka需要考虑高吞吐量与延时的平衡。

C、Kafka Consumer压力测试

在这里插入图片描述

Ⅰ、Consumer的测试,如果这四个指标 (IO,CPU,内存,网络) 都不能改变,考虑增加分区数来提升性能。

[fancy@node101 kafka]$ bin/kafka-consumer-perf-test.sh --broker-list node101:9092,node102:9092,node103:9092 --topic test --fetch-size 10000 --messages 10000000 --threads 1

①参数说明:

--broker-list 指定 Kafka 集群地址
--topic 指定 topic 的名称
--fetch-size 指定每次 fetch 的数据的大小
--messages 总共要消费的消息个数

②测试结果说明:

start.time, end.time, data.consumed.in.MB, MB.sec, data.consumed.in.nMsg, nMsg.sec
2021-08-03 21:17:21:778, 2021-08-03 21:18:19:775, 514.7169, 8.8749, 5397198, 93059.9514

开始测试时间,测试结束数据,共消费数据514.7169MB,吞吐量8.8749MB/s

Ⅱ、调整fetch-size

① 增加fetch-size值,观察消费吞吐量。

[fancy@node101 kafka]$ bin/kafka-consumer-perf-test.sh --broker-list node101:9092,node102:9092,node103:9092 --topic test --fetch-size 100000 --messages 10000000 --threads 1

②测试结果说明:
start.time, end.time, data.consumed.in.MB, MB.sec, data.consumed.in.nMsg, nMsg.sec
2021-08-03 21:22:57:671, 2021-08-03 21:23:41:938, 514.7169, 11.6276, 5397198, 121923.7355

Ⅲ、总结
吞吐量受网络带宽和fetch-size的影响

6. Kafka分区数计算

  • 创建一个只有1个分区的 topic
  • 测试这个 topic 的 producer 吞吐量和 consumer 吞吐量。
  • 假设他们的值分别是Tp和Tc,单位可以是MB/s。
  • 然后假设总的目标吞吐量是Tt,那么分区数 = Tt / min(Tp,Tc)

例如:producer吞吐量 = 20m/s;consumer吞吐量 = 50m/s,期望吞吐量100m/s;分区数 = 100 / 20 = 5分区
https://blog.csdn.net/weixin_42641909/article/details/89294698 分区数一般设置为:3-10个

五、采集日志 Flume

在这里插入图片描述

1. 日志采集Flume安装

具体安装步骤详见:https://blog.csdn.net/m0_51111980/article/details/126199070

集群规划:

服务器hadoop102服务器hadoop103服务器hadoop104
Flume(采集日志)FlumeFlume

2. Flume组件选型

A、Source

Taildir Source 相比 Exec Source、Spooling Directory Source 的优势:

  • TailDir Source :断点续传、多目录。Flume1.6 以前需要自己自定义 Source 记录每次读取文件位置,实现断点续传。不会丢数据,但是有可能会导致数据重复。
  • Exec Source :可以实时搜集数据,但是在 Flume 不运行或者 Shell 命令出错的情况下,数据将会丢失。
  • Spooling Directory Source 监控目录,支持断点续传。

batchSize大小如何设置?

答:Event 1K左右时,500-1000合适(默认为100)

B、Channel

采用 Kafka Channel,省去了 Sink,提高了效率。KafkaChannel 数据存储在 Kafka 里面,所以数据是存储在磁盘中。注意在 Flume1.7 以前,Kafka Channel 很少有人使用,因为发现 parseAsFlumeEvent 这个配置起不了作用。也就是无论parseAsFlumeEvent 配置为 true 还是 false,都会转为 Flume Event。这样的话,造成的结果是,会始终都把 Flume 的 headers中的信息混合着内容一起写入Kafka的消息中,这显然不是我所需要的,我只是需要把内容写入即可。

3. 日志采集 Flume 配置

A、Flume配置分析

在这里插入图片描述
Flume直接读log日志的数据,log日志的格式是app.yyyy-mm-dd.log。

B、Flume的具体配置如下

/opt/module/flume/conf 目录下创建 file-flume-kafka.conf 文件

[fancy@node101 conf]$ vim file-flume-kafka.conf

在文件配置如下内容

#为各组件命名
a1.sources = r1
a1.channels = c1

#描述source
a1.sources.r1.type = TAILDIR
a1.sources.r1.filegroups = f1
a1.sources.r1.filegroups.f1 = /opt/module/applog/log/app.*
a1.sources.r1.positionFile = /opt/module/flume/taildir_position.json
a1.sources.r1.interceptors =  i1
a1.sources.r1.interceptors.i1.type = com.atguigu.flume.interceptor.ETLInterceptor$Builder

#描述channel
a1.channels.c1.type = org.apache.flume.channel.kafka.KafkaChannel
a1.channels.c1.kafka.bootstrap.servers = hadoop102:9092,hadoop103:9092
a1.channels.c1.kafka.topic = topic_log
a1.channels.c1.parseAsFlumeEvent = false

#绑定source和channel以及sink和channel的关系
a1.sources.r1.channels = c1

注意:com.fancy.flume.interceptor.ETLInterceptor是自定义的拦截器的全类名。需要根据用户自定义的拦截器做相应修改。

4. Flume 拦截器

A、创建Maven工程flume-interceptor

B、创建包名:com.atguigu.flume.interceptor

C、在pom.xml文件中添加如下配置

<dependencies>
    <dependency>
        <groupId>org.apache.flume</groupId>
        <artifactId>flume-ng-core</artifactId>
        <version>1.9.0</version>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.62</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.3.2</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <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>

注意:scope中provided的含义是编译时用该jar包。打包时时不用。因为集群上已经存在flume的jar包。只是本地编译时用一下。

D、在com.fancy.flume.interceptor包下创建JSONUtils类

package com.fancy.flume.interceptor;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;

public class JSONUtils {
    public static boolean isJSONValidate(String log){
        try {
            JSON.parse(log);
            return true;
        }catch (JSONException e){
            return false;
        }
    }
}

E、在com.fancy.flume.interceptor包下创建LogInterceptor类

package com.fancy.flume.interceptor;

import com.alibaba.fastjson.JSON;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;

import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;

public class ETLInterceptor implements Interceptor {

    @Override
    public void initialize() {

    }

    @Override
    public Event intercept(Event event) {

        byte[] body = event.getBody();
        String log = new String(body, StandardCharsets.UTF_8);

        if (JSONUtils.isJSONValidate(log)) {
            return event;
        } else {
            return null;
        }
    }

    @Override
    public List<Event> intercept(List<Event> list) {

        Iterator<Event> iterator = list.iterator();

        while (iterator.hasNext()){
            Event next = iterator.next();
            if(intercept(next)==null){
                iterator.remove();
            }
        }

        return list;
    }

    public static class Builder implements Interceptor.Builder{

        @Override
        public Interceptor build() {
            return new ETLInterceptor();
        }
        @Override
        public void configure(Context context) {

        }

    }

    @Override
    public void close() {

    }
}

F、打包

在这里插入图片描述

G、需要先将打好的包放入到 node101 的 /opt/module/flume/lib 文件夹下面

[fancy@node101 lib]$ ls | grep interceptor
flume-interceptor-1.0-SNAPSHOT-jar-with-dependencies.jar

H、分发 Flume 到 node102、node103

[fancy@node101 module]$ xsync flume/

I、分别在 node101、node102 上启动Flume

[fancy@node101 flume]$ bin/flume-ng agent --name a1 --conf-file conf/file-flume-kafka.conf &

[fancy@node102 flume]$ bin/flume-ng agent --name a1 --conf-file conf/file-flume-kafka.conf &

5. 测试 Flume-Kafka 通道

在这里插入图片描述
A、生成日志

[fancy@node101 ~]$ lg.sh

B、消费Kafka数据,观察控制台是否有数据获取到

[fancy@node101 kafka]$ bin/kafka-console-consumer.sh \
--bootstrap-server node101:9092 --from-beginning --topic topic_log

说明:如果获取不到数据,先检查 Kafka、Flume、Zookeeper 是否都正确启动。再检查 Flume 的拦截器代码是否正常。

6. Flume 启动停止脚本

A、在 /home/fancy/bin 目录下创建脚本 f1.sh

[fancy@node101 bin]$ vim f1.sh

在脚本中填写如下内容

#! /bin/bash

case $1 in
"start"){
        for i in node101 node102
        do
                echo " --------启动 $i 采集flume-------"
                ssh $i "nohup /opt/module/flume/bin/flume-ng agent --conf-file /opt/module/flume/conf/file-flume-kafka.conf --name a1 -Dflume.root.logger=INFO,LOGFILE >/opt/module/flume/log1.txt 2>&1  &"
        done
};;	
"stop"){
        for i in node101 node102
        do
                echo " --------停止 $i 采集flume-------"
                ssh $i "ps -ef | grep file-flume-kafka | grep -v grep |awk  '{print \$2}' | xargs -n1 kill -9 "
        done

};;
esac
  • 说明1:nohup,该命令可以在你退出帐户/关闭终端之后继续运行相应的进程。nohup 就是不挂起的意思,不挂断地运行命令。

  • 说明2:awk 默认分隔符为空格

  • 说明3:$2是在""双引号内部会被解析为脚本的第二个参数,但是这里面想表达的含义是 awk 的第二个值,所以需要将他转义,用\$2表示。

  • 说明4:xargs 表示取出前面命令运行的结果,作为后面命令的输入参数。

B、增加脚本执行权限

[fancy@node101 bin]$ chmod u+x f1.sh

C、f1集群启动脚本

[fancy@node101 module]$ f1.sh start

D、f1集群停止脚本

[fancy@node101 module]$ f1.sh stop

六、消费 Kafka 数据 Flume

在这里插入图片描述

集群规划

服务器hadoop102服务器hadoop103服务器hadoop104
Flume(消费Kafka)Flume

1. Flume 组件选型

A、FileChannel和MemoryChannel区别

MemoryChannel 传输数据速度更快,但因为数据保存在 JVM 的堆内存中,Agent 进程挂掉会导致数据丢失,适用于对数据质量要求不高的需求。

FileChannel 传输速度相对于 Memory 慢,但数据安全保障高,Agent 进程挂掉也可以从失败中恢复数据。

选型:

  • 金融类公司、对钱要求非常准确的公司通常会选择 FileChannel
  • 传输的是普通日志信息(京东内部一天丢100万-200万条,这是非常正常的),通常选择MemoryChannel。

B、FileChannel优化

通过配置 dataDirs 指向多个路径,每个路径对应不同的硬盘,增大 Flume 吞吐量。

官方说明如下:

Comma separated list of directories for storing log files. Using multiple directories on separate disks can improve file channel peformance

checkpointDir 和 backupCheckpointDir 也尽量配置在不同硬盘对应的目录中,保证 checkpoint 坏掉后,可以快速使用backupCheckpointDir 恢复数据。

在这里插入图片描述
C、Sink:HDFS Sink

  • HDFS存入大量小文件,有什么影响?
    元数据层面: 每个小文件都有一份元数据,其中包括文件路径,文件名,所有者,所属组,权限,创建时间等,这些信息都保存在 Namenode 内存中。所以小文件过多,会占用 Namenode 服务器大量内存,影响 Namenode 性能和使用寿命。
    计算层面: 默认情况下 MR 会对每个小文件启用一个 Map 任务计算,非常影响计算性能。同时也影响磁盘寻址时间。

  • HDFS小文件处理
    官方默认的这三个参数配置写入HDFS后会产生小文件,hdfs.rollInterval、hdfs.rollSize、hdfs.rollCount
    基于以上hdfs.rollInterval=3600,hdfs.rollSize=134217728,hdfs.rollCount =0几个参数综合作用,效果如下:
    ①文件在达到128M时会滚动生成新文件
    ②文件创建超3600秒时会滚动生成新文件

2. 消费者 Flume 配置

A、Flume配置分析
在这里插入图片描述
Flume的具体配置如下:

在 node103 的 /opt/module/flume/conf目录下创建 kafka-flume-hdfs.conf 文件

[fancy@node103 conf]$ vim kafka-flume-hdfs.conf

在文件配置如下内容

## 组件
a1.sources=r1
a1.channels=c1
a1.sinks=k1


## source1
a1.sources.r1.type = org.apache.flume.source.kafka.KafkaSource
a1.sources.r1.batchSize = 5000
a1.sources.r1.batchDurationMillis = 2000
a1.sources.r1.kafka.bootstrap.servers = hadoop102:9092,hadoop103:9092,hadoop104:9092
a1.sources.r1.kafka.topics=topic_log
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = com.atguigu.flume.interceptor.TimeStampInterceptor$Builder

## channel1
a1.channels.c1.type = file
a1.channels.c1.checkpointDir = /opt/module/flume/checkpoint/behavior1
a1.channels.c1.dataDirs = /opt/module/flume/data/behavior1/


## sink1
a1.sinks.k1.type = hdfs
a1.sinks.k1.hdfs.path = /origin_data/gmall/log/topic_log/%Y-%m-%d
a1.sinks.k1.hdfs.filePrefix = log-
a1.sinks.k1.hdfs.round = false

#控制生成的小文件
a1.sinks.k1.hdfs.rollInterval = 10
a1.sinks.k1.hdfs.rollSize = 134217728
a1.sinks.k1.hdfs.rollCount = 0

## 控制输出文件是原生文件。
a1.sinks.k1.hdfs.fileType = CompressedStream
a1.sinks.k1.hdfs.codeC = lzop

## 拼装
a1.sources.r1.channels = c1
a1.sinks.k1.channel= c1

3. Flume 时间戳拦截器

由于 Flume 默认会用 Linux 系统时间,作为输出到 HDFS 路径的时间。如果数据是 23:59 分产生的。Flume 消费 Kafka 里面的数据时,有可能已经是第二天了,那么这部门数据会被发往第二天的 HDFS 路径。我们希望的是根据日志里面的实际时间,发往HDFS的路径,所以下面拦截器作用是获取日志中的实际时间。

解决的思路:拦截json日志,通过 fastjson 框架解析 json,获取实际时间ts。将获取的ts时间写入拦截器header头,header的key必须是timestamp,因为Flume框架会根据这个key的值识别为时间,写入到HDFS。

A、在com.fancy.flume.interceptor包下创建TimeStampInterceptor类

package com.fancy.flume.interceptor;
import com.alibaba.fastjson.JSONObject;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class TimeStampInterceptor implements Interceptor {

    private ArrayList<Event> events = new ArrayList<>();

    @Override
    public void initialize() {

    }

    @Override
    public Event intercept(Event event) {

        Map<String, String> headers = event.getHeaders();
        String log = new String(event.getBody(), StandardCharsets.UTF_8);

        JSONObject jsonObject = JSONObject.parseObject(log);

        String ts = jsonObject.getString("ts");
        headers.put("timestamp", ts);

        return event;
    }

    @Override
    public List<Event> intercept(List<Event> list) {
        events.clear();
        for (Event event : list) {
            events.add(intercept(event));
        }

        return events;
    }

    @Override
    public void close() {

    }

    public static class Builder implements Interceptor.Builder {
        @Override
        public Interceptor build() {
            return new TimeStampInterceptor();
        }

        @Override
        public void configure(Context context) {
        }
    }
}

B、重新打包

在这里插入图片描述
C、需要先将打好的包放入到 node101 的 /opt/module/flume/lib 文件夹下面

[fancy@node101 lib]$ ls | grep interceptor
flume-interceptor-1.0-SNAPSHOT-jar-with-dependencies.jar

D、分发 Flume 到 node102、node103

[fancy@node101 module]$ xsync flume/

4. 消费者 Flume 启动停止脚本

A、在 /home/fancy/bin 目录下创建脚本 f2.sh

[fancy@node101 bin]$ vim f2.sh

在脚本中填写如下内容

#! /bin/bash

case $1 in
"start"){
        for i in node103
        do
                echo " --------启动 $i 消费flume-------"
                ssh $i "nohup /opt/module/flume/bin/flume-ng agent --conf-file /opt/module/flume/conf/kafka-flume-hdfs.conf --name a1 -Dflume.root.logger=INFO,LOGFILE >/opt/module/flume/log2.txt   2>&1 &"
        done
};;
"stop"){
        for i in node103
        do
                echo " --------停止 $i 消费flume-------"
                ssh $i "ps -ef | grep kafka-flume-hdfs | grep -v grep |awk '{print \$2}' | xargs -n1 kill"
        done

};;
esac

B、增加脚本执行权限

[fancy@node101 bin]$ chmod u+x f2.sh

C、f2集群启动脚本

[fancy@node101 module]$ f2.sh start

D、f2集群停止脚本

[fancy@node101 module]$ f2.sh stop

5. Flume 内存优化

A、问题描述:

如果启动消费Flume抛出如下异常

ERROR hdfs.HDFSEventSink: process failed
java.lang.OutOfMemoryError: GC overhead limit exceeded

B、解决方案步骤

在 node101 服务器的 /opt/module/flume/conf/flume-env.sh 文件中增加如下配置

export JAVA_OPTS="-Xms100m -Xmx2000m -Dcom.sun.management.jmxremote"

同步配置到 node102、node103 服务器

[fancy@node101 conf]$ xsync flume-env.sh

C、Flume内存参数设置及优化

JVM heap一般设置为4G或更高
-Xmx-Xms最好设置一致,减少内存抖动带来的性能影响,如果设置不一致容易导致频繁fullgc。
-Xms表示JVM Heap(堆内存) 最小尺寸,初始分配;
-Xmx 表示 JVM Heap(堆内存) 最大允许的尺寸,按需分配。如果不设置一致,容易在初始化时,由于内存不够,频繁触发 fullgc。

6. 采集通道启动/停止脚本

A、在 /home/fancy/bin 目录下创建脚本cluster.sh

[fancy@node101 bin]$ vim cluster.sh

在脚本中填写如下内容

#!/bin/bash

case $1 in
"start"){
        echo ================== 启动 集群 ==================

        #启动 Zookeeper集群
        zk.sh start

        #启动 Hadoop集群
        hdp.sh start

        #启动 Kafka采集集群
        kf.sh start

        #启动 Flume采集集群
        f1.sh start

        #启动 Flume消费集群
        f2.sh start

        };;
"stop"){
        echo ================== 停止 集群 ==================

        #停止 Flume消费集群
        f2.sh stop

        #停止 Flume采集集群
        f1.sh stop

        #停止 Kafka采集集群
        kf.sh stop

        #停止 Hadoop集群
        hdp.sh stop

        #停止 Zookeeper集群
        zk.sh stop

};;
esac

B、增加脚本执行权限

[fancy@node101 bin]$ chmod u+x cluster.sh

C、cluster集群启动脚本

[fancy@node101 module]$ cluster.sh start

D、cluster集群停止脚本

[fancy@node101 module]$ cluster.sh stop

七、常见问题及解决方案

1. 2NN页面不能显示完整信息

A、问题描述

访问 2NN 页面 http://node103:9868,看不到详细信息

B、解决办法

在浏览器上按F12,查看问题原因。定位bug在61行

找到要修改的文件

[fancy@node101 static]$ pwd
/opt/module/hadoop-3.1.3/share/hadoop/hdfs/webapps/static

[fancy@node101 static]$ vim dfs-dust.js
:set nu

修改61行

return new Date(Number(v)).toLocaleString();

C、分发dfs-dust.js

[fancy@node101 static]$ xsync dfs-dust.js

D、在 http://node103:9868/status.html 页面强制刷新

相关文章:

  • HACKTHEBOX——Valentine
  • 哈佛结构和冯诺依曼结构
  • 【黄啊码】MySQL入门—7、这些函数运用得好,高级工程师都直呼内行
  • UnityPackageManager相关
  • 数字图像处理——基本运算
  • Cosmos模块化功能链 走向亿级用户的超级Dapp时代
  • 【黑马程序员名师pink老师讲HTML】HTML很容易忘记?有它我不慌的
  • .NetCore项目nginx发布
  • 主流开源OLAP对比分析
  • 二叉搜索树的基本操作 || TreeMap和TreeSet介绍
  • 超详细的数据结构---顺序表的有关教程
  • Exchange Server 2016 安装部署
  • 【C51单片机】中断系统之单一外中断应用
  • 2.2 Linux系统的目录结构与文件类型
  • jedis:使用事务开启watch监控
  • Akka系列(七):Actor持久化之Akka persistence
  • AzureCon上微软宣布了哪些容器相关的重磅消息
  • HTTP 简介
  • JavaScript设计模式之工厂模式
  • Material Design
  • MaxCompute访问TableStore(OTS) 数据
  • node和express搭建代理服务器(源码)
  • 基于 Babel 的 npm 包最小化设置
  • 精彩代码 vue.js
  • 理解IaaS, PaaS, SaaS等云模型 (Cloud Models)
  • 理清楚Vue的结构
  • 聊聊flink的BlobWriter
  • 买一台 iPhone X,还是创建一家未来的独角兽?
  • 少走弯路,给Java 1~5 年程序员的建议
  • 项目管理碎碎念系列之一:干系人管理
  • 一些css基础学习笔记
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • # include “ “ 和 # include < >两者的区别
  • #define
  • #宝哥教你#查看jquery绑定的事件函数
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (搬运以学习)flask 上下文的实现
  • (附源码)php新闻发布平台 毕业设计 141646
  • (论文阅读32/100)Flowing convnets for human pose estimation in videos
  • (一)VirtualBox安装增强功能
  • (正则)提取页面里的img标签
  • (转)mysql使用Navicat 导出和导入数据库
  • .net mvc部分视图
  • .NET Standard / dotnet-core / net472 —— .NET 究竟应该如何大小写?
  • .NET 材料检测系统崩溃分析
  • .net/c# memcached 获取所有缓存键(keys)
  • .net对接阿里云CSB服务
  • .NET企业级应用架构设计系列之技术选型
  • .net下的富文本编辑器FCKeditor的配置方法
  • ??eclipse的安装配置问题!??
  • @font-face 用字体画图标
  • [ solr入门 ] - 利用solrJ进行检索
  • [C++] 多线程编程-thread::yield()-sleep_for()
  • [codevs 1296] 营业额统计