Apache Hadoop学习

Hadoop诞生前景

大数据(Big Data)

随着信息化互联网|物联网发展要求,万物互联趋势势在必行。随之引发架构的演变由单一架构向高并发分布式架构演变。数据的存储也开始由原始的单机存储演变为分布式存储。

  • 高并发互联网设计 LNMP、数据库主从RDBMS|NoSQL、Spring Cloud、Dubbo – 应用架构
  • 海量数据存储|数据分析 :存储方案(HDFS)、计算方案(Map Reduce、Storm、Spark、Flink)

面临问题

分布式:服务间出现跨机器、跨进程通信同一称为分布式

  • 存储

    • 单机存储-瓶颈:容量限制、扩展性差、数据灾备问题
    • 分布式存储:使用存储的集群实现对海量数据的并行化的读写,提升系统写的吞吐能力。目前针对传统业务领域的分布式文件存储方案有:FastDFS/GlusterFS/GridFS、大文本日志存储解决方案:HDFS
  • 分析

    • 单机分析-计算:慢,受限于单机存储的内存、CPU、网络限制。
    • 分布式计算:将计算任务交给专门的计算集群负责任务的计算。打破单机计算的瓶颈,实现并行计算,模拟多核CPU的计算能力。可以实现在一定的时间内达到对数据的有效分析。

Hadoop诞生

2003-2004年,Google公布了部分GFS和MapReduce思想的细节,受此启发的Doug Cutting等人用2年的业余时间实现了DFS和MapReduce机制,使Nutch性能飙升。然后Yahoo招安Doug Gutting及其项目。
2005年:Hadoop作为Lucene的子项目Nutch的一部分正式引入Apache基金会。
2006年2月:yahoo加入Nutch工程尝试将Nutch存储和计算分离出来,成为一套完整独立的软件并起名为Hadoop 。

人称Hadoop之父的Doug Cutting,Apache软件基金会主席,是Lucene、Nutch 、Hadoop等项目的发起人。最开始Hadoop只是Apache Lucene的子项目Nutch的一部分。Lucene 是全球第一个开源的全文检索引擎工具包, Nutch基于Lucene,并具有网页抓取和解析的功能,可以实现一个搜索引擎的开发,但是如果投入使用的话就必须在极短时间内做出反应,并且能够实现短时间内对亿级数量的网页进行分析处理,这就需要考虑分布式任务处理、故障恢复、负载均衡这些问题。后来Doug Cutting 借鉴谷歌的Google File System和MapReduce:Simplified Data Processing On Large Clusters两篇论文,移植了其中的技术,并将其命名为:Hadoop。

Hadoop由HDFS和Map Reduce两部分组成

HDFS: Hadoop 分布式文件存储系统,解决海量数据的存储问题(非常重要)

Map Reduce: Hadoop项目分布式计算框架(老),已经成为大数据计算的标杆。是早期分布式计算解决方案。期间该方案在2010年又被Yahoo团队做了一次升级,主要解决的是MapReduce在大规模计算集群的扩展性问题,但是并没有本质改变MapReduce计算本质。因为MapReduce计算的是通过对数据做磁盘迭代计算。导致计算速度不算太快。2013年下半年出现了Spark是一款基于内存的分布式计算框架,用于替代Hadoop的MapReduce,被人们称为第二代大数据计算引擎。

HDFS基本概念

是一个基于分布式存储通用的文件系统,该系统特点容易部署、对系统硬件要求低,搭建成本可控。可以使得数据存储大小和集群过程呈现一种线性关系 (目前最大已知规模2000台左右规模,实际在生产环境下集群规模一般在10~100台左右)。HDFS文件系统的架构图:
在这里插入图片描述
NameNode:使用内存存储集群中的元数据(文件命名空间-文件目录结构、数据块到DataNode映射)

DataNode:负责响应客户端对数据块的读写请求,向NameNode汇报自身状态信息。

Block:是HDFS切分文件的尺度,Hadoop2.x/3.x版本默认是128MB,1.x版本默认是64MB,一个文件最多只有 一个不足128MB块

副本因子:HDFS为了防止DataNode宕机导致块的丢失,允许一个块有多个备份,默认备份是3

HDFS缺点

1. 不适合低延时数据访问;比如毫秒级的存储数据,是做不到的
2. 无法高效的对大量小文件记性存储
3. 不支持并发写入、文件随机修改
     文件只能有一个写,不允许多个线程同时写
    仅支持数据append(追加),不支持文件的随机修改

为什么HDFS不擅长存储小文件?

案例NNDN
1文件128MB1条数据块映射元数据128MB磁盘存储*(副本因子)
1000文件总计128MB1000*1条数据块映射元数据128MB磁盘存储*(副本因子)

因为Namenode使用单机的内存存储,因此由于小文件会占用更多的内存空间,导致了Namenode内存浪费。

HDFS的读写流程

在这里插入图片描述

  1. 客户端通过 Distributed FileSystem 模块向 NameNode 请求上传文件,NameNode 检查目标文件是否已存在,父目录是否存在.
  2. NameNode 返回是否可以上传。
  3. 客户端请求第一个 Block 上传到哪几个 DataNode 服务器上。
  4. NameNode 返回 3 个 DataNode 节点,分别为 dn1、dn2、dn3。
  5. 客户端通过 FSDataOutputStream 模块请求 dn1 上传数据,dn1 收到请求会继续调用dn2,然后 dn2 调用 dn3,将这个通信管道建立完成。
  6. dn1、dn2、dn3 逐级应答客户端。
  7. 客户端开始往 dn1 上传第一个 Block(先从磁盘读取数据放到一个本地内存缓存),以 Packet 为单位,dn1 收到一个 Packet 就会传给 dn2,dn2 传给 dn3;dn1 每传一个 packet会放入一个应答队列等待应答。
  8. 当一个 Block 传输完成之后,客户端再次请求 NameNode 上传第二个 Block 的服务器。(重复执行 3-7 步)。

在这里插入图片描述

  1. 客户端通过 DistributedFileSystem 向 NameNode 请求下载文件,NameNode 通过查询元数据,找到文件块所在的 DataNode 地址。
  2. 挑选一台 DataNode(就近原则,然后随机)服务器,请求读取数据。
  3. DataNode 开始传输数据给客户端(从磁盘里面读取数据输入流,以 Packet 为单位来做校验)。
  4. 客户端以 Packet 为单位接收,先在本地缓存,然后写入目标文件。

HDFS环境搭建(单机|伪分布式)

  • 安装虚拟器并且安装CentOS-6.5 64位
  • 安装jdk-8u171-linux-x64.rpm配置JAVA_HOME环境变量(~/.bashrc)
[root@CentOS ~]# rpm -ivh jdk-8u171-linux-x64.rpm 
[root@CentOS ~]# ls -l /usr/java/
total 4
lrwxrwxrwx. 1 root root   16 Mar 26 00:56 default -> /usr/java/latest
drwxr-xr-x. 9 root root 4096 Mar 26 00:56 jdk1.8.0_171-amd64
lrwxrwxrwx. 1 root root   28 Mar 26 00:56 latest -> /usr/java/jdk1.8.0_171-amd64
[root@CentOS ~]# vi .bashrc 
JAVA_HOME=/usr/java/latest
PATH=$PATH:$JAVA_HOME/bin
CLASSPATH=.
export JAVA_HOME
export PATH
export CLASSPATH
[root@CentOS ~]# source ~/.bashrc # 加载环境变量
  • 配置主机名和IP映射关系
[root@CentOS ~]# ifconfig  #查询IP
eth0      Link encap:Ethernet  HWaddr 00:0C:29:37:20:59  
          inet addr:`192.168.40.128`  Bcast:192.168.40.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe37:2059/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:246948 errors:0 dropped:0 overruns:0 frame:0
          TX packets:22719 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:370063776 (352.9 MiB)  TX bytes:2150553 (2.0 MiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)
[root@CentOS ~]# vi /etc/hosts # 一定是自己的IP
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.40.128 CentOS		#IP和主机映射      
  • 配置Linux免密码登陆(Linux系统间登陆方式)
    基于口令登陆需要输入用户名和密码
    基于秘钥登陆方式
[root@CentOS ~]# ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
4b:29:93:1c:7f:06:93:67:fc:c5:ed:27:9b:83:26:c0 root@CentOS
The key's randomart image is:
+--[ RSA 2048]----+
|                 |
|         o   . . |
|      . + +   o .|
|     . = * . . . |
|      = E o . . o|
|       + =   . +.|
|        . . o +  |
|           o   . |
|                 |
+-----------------+
[root@CentOS ~]# ssh-copy-id CentOS
The authenticity of host 'centos (192.168.40.128)' can't be established.
RSA key fingerprint is 3f:86:41:46:f2:05:33:31:5d:b6:11:45:9c:64:12:8e.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'centos,192.168.40.128' (RSA) to the list of known hosts.
root@centos's password: 
Now try logging into the machine, with "ssh 'CentOS'", and check in:

  .ssh/authorized_keys

to make sure we haven't added extra keys that you weren't expecting.
[root@CentOS ~]# ssh root@CentOS
Last login: Tue Mar 26 01:03:52 2019 from 192.168.40.1
[root@CentOS ~]# exit
logout
Connection to CentOS closed.
  • 关闭防火墙
# 临时关闭服务
[root@CentOS ~]# service iptables stop
iptables: Setting chains to policy ACCEPT: filter [  OK  ]
iptables: Flushing firewall rules: [  OK  ]
iptables: Unloading modules: [  OK  ]
[root@CentOS ~]# service iptables status
iptables: Firewall is not running.
# 关闭开机自动启动
[root@CentOS ~]# chkconfig iptables off
[root@CentOS ~]# chkconfig --list | grep iptables
iptables        0:off   1:off   2:off   3:off   4:off   5:off   6:off
  • 安装配置Hadoop
    解压并配置环境变量
[root@CentOS ~]# tar -zxf hadoop-2.6.0_x64.tar.gz -C /usr/
[root@CentOS ~]# ls /usr/hadoop-2.6.0/
bin  etc  include  lib  libexec  LICENSE.txt  NOTICE.txt  README.txt  sbin  share
[root@CentOS ~]# vi ~/.bashrc 
HADOOP_HOME=/usr/hadoop-2.6.0
JAVA_HOME=/usr/java/latest
PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
CLASSPATH=.
export JAVA_HOME
export PATH
export CLASSPATH
export HADOOP_HOME
[root@CentOS ~]# source  ~/.bashrc 
[root@CentOS ~]# hadoop version
Hadoop 2.6.0
Subversion Unknown -r Unknown
Compiled by root on 2016-08-01T20:48Z
Compiled with protoc 2.5.0
From source with checksum 18e43357c8f927c0695f1e9522859d6a
This command was run using /usr/hadoop-2.6.0/share/hadoop/common/hadoop-common-2.6.0.jar

hadoop解压后的文件目录:
在这里插入图片描述

配置hadoop配置文件etc/hadoop/

  • core-site.xml(配置的是NameNode访问入口,以及服务存储的根目录)
[root@CentOS ~]# vi /usr/hadoop-2.6.0/etc/hadoop/core-site.xml 
<!--用于设置namenode并且作为Java程序的访问入口--->
<property>
    <name>fs.defaultFS</name>
    <value>hdfs://CentOS:9000</value>
</property>
<!--hdfs工作基础目录-->
<!--存储NameNode持久化的数据,DataNode块数据——>
<!--手工创建$HADOOP_HOME/data/tmp-->
<property>
    <name>hadoop.tmp.dir</name>
    <value>/usr/hadoop-2.6.0/data/tmp</value>
</property>

hdfs-site.xml(副本数量和Sencondary namenode)

[root@CentOS ~]# vi /usr/hadoop-2.6.0/etc/hadoop/hdfs-site.xml 
<!--block副本因子 默认是3 但是单节点测试,改成1-->
<property>
    <name>dfs.replication</name>
    <value>1</value>
</property>
<!--配置Sencondary namenode所在物理主机 这里可以不配 下面会提到Sencondary namenode-->
<property>
    <name>dfs.namenode.secondary.http-address</name>
    <value>CentOS:50090</value>
</property>
  • mapred-site.xml(与mapred相关,这里可以不配,因为没用到,后续有用到,这里提前写在这了而已)
<!--yarn 与 MR相关-->
<property>	 	        		
	<name>mapreduce.framework.name</name>
	<value>yarn</value>
</property>
  • yarn-site.xml(与yarn相关,也可以不配)
<property>
	<name>yarn.nodemanager.aux-services</name>
	<value>mapreduce_shuffle</value>
</property>
  • slaves
[root@CentOS ~]# vi /usr/hadoop-2.6.0/etc/hadoop/slaves 
CentOS   #主机名
  • hadoop-env.sh
#指定java的安装目录,使用默认的export JAVA_HOME=$JAVA_HOME可能会报如下错误
#Error:JAVA_HOME is not set and could not be found 
export JAVA_HOME=/usr/java/latest
  • HDFS启动
    如果是第一次初始化启动HDFS服务,需要创建一个空的fsimage文件,以便Namenode在启动的时候加载
[root@CentOS ~]# bin/hdfs namenode -format # 创建初始化所需的fsimage文件
...
19/03/26 01:31:09 INFO namenode.NNConf: Maximum size of an xattr: 16384
19/03/26 01:31:09 INFO namenode.FSImage: Allocated new BlockPoolId: BP-1143348175-192.168.40.128-1553535069443
19/03/26 01:31:09 INFO common.Storage: Storage directory `/usr/hadoop-2.6.0/hadoop-root/dfs/name has been successfully formatted.`
19/03/26 01:31:09 INFO namenode.NNStorageRetentionManager: Going to retain 1 images with txid >= 0
...
[root@CentOS ~]# ls /usr/hadoop-2.6.0/dfs/name/current/
fsimage_0000000000000000000  fsimage_0000000000000000000.md5  seen_txid  VERSION
  • 启动/停止hdfs
#启动命令
sbin/hadoop-daemon.sh start namenode
sbin/hadoop-daemon.sh start datanode

#停止命令
sbin/hadoop-daemon.sh stop namenode
sbin/hadoop-daemon.sh stop datanode
  • 查看是否启动成功
#jps查看
[root@CentOS ~]# jps
2280 Jps
1993 DataNode
1918 NameNode
  • 指定Hadoop的启停脚本
创建hadoop-start.sh/hadoop-stop.sh文件
hadoop-start.sh
	#!/bin/bashshell #脚本第一行,规定好的,自行百度
  	sbin/hadoop-daemon.sh start namenode
 	sbin/hadoop-daemon.sh start datanode
#  	sbin/yarn-daemon.sh start resourcemanager		#yarn相关,这里可以暂时不写,后续用到在加上
#  	sbin/yarn-daemon.sh start nodemanager			#也可以不写
hadoop-stop.sh
	#!/bin/bashshell #脚本第一行,规定好的,自行百度
  	sbin/hadoop-daemon.sh stop namenode
  	sbin/hadoop-daemon.sh stop datanode
#  	sbin/yarn-daemon.sh stop resourcemanager		#也可以不写
#  	sbin/yarn-daemon.sh stop nodemanager			#也可以不写

之后启动运行hadoop-start.sh/hadoop-stop.sh即可启动/停止hadoop

 shell脚本 当前目录执行方式  ./hadoop-start.sh
          绝对路径执行shell 绝对路径/hadoop-start.sh  例如/opt/install/hadoo-2.5.2/hadoop-start.sh
  • 查看启动日志文件
NameNode、DataNode、ResourceManage等系统自带的服务输出来的日志默认是存放在${HADOOP_HOME}/logs目录下,如果启动报错,可以去日志中找错误信息
如下图:

在这里插入图片描述

启动命令说明

sbin/hadoop-daemon.sh		单节点启动命令(平时自己搭建非集群模式使用,
							或者集群时,某个节点dataNode单独挂了,用来单独启动)

start-dfs.sh			hdfs集群启动/停止命令,只启动/停止nameNode和dataNode
stop-dfs.sh

start-yarn.sh			MapReduce集群启动/停止命令,只启动/停止ResourcesManager和nodeManger
stop-yarn.sh

start-all.sh			集群启动/停止命令,除了启动/停止nameNode和dataNode,还包括resourceManager和nodeManager
stop-all.sh

HDFS的一些命令

hdfs全部命令如下图:
在这里插入图片描述

这里只介绍部分命令

查看hdfs都有哪些命令:bin/hdfs dfs
		或者bin/hadoop fs,这两个命令作用一样,为了区分学习hdfs和MapReduce,这里都用hdfs dfs命令
		自己习惯哪个可以用哪个
1. 查看目录结构
   bin/hdfs dfs -ls 路径
   bin/hdfs dfs -ls /
2. 创建文件夹
   bin/hdfs dfs -mkdir /文件夹名
   bin/hdfs dfs -mkdir -p /liuh/xiaojr
3. 本地上传文件到hdfs中
   bin/hdfs dfs -put /root/hdfs/data /hdfspath
   bin/hdfs dfs -put local_path hdfs_path
4. 查看文件内容
   bin/hdfs dfs -cat /hdfs/data
   bin/hdfs dfs -cat hdfs_path
5. 删除文件
   bin/hdfs dfs -rm /suns/data
6. 删除非空文件夹 
   bin/hdfs dfs -rmr /文件夹路径
7. 从hdfs下载文件到本地
   bin/hdfs dfs -get hdfs_path local_path
8.统计文件夹的大小信息
   bin/hdfs dfs -du -s -h /aa		#查看aa文件夹的大小
   27 81 /aa		#27:文件夹大小,81:文件夹大小*副本数
   
   bin/hdfs dfs -du -h /aa		#会将aa文件夹下的文件在列举出来,比如:
   14 42 /aa/a1.txt
    7 21 /aa/a2.txt
    6 18 /aa/a3.tx
9.设置 HDFS 文件的副本数量
bin/hdfs dfs -setrep 10 /aa/a1.txt
注意:这里设置的副本数只是记录在 NameNode 的元数据中,是否真的会有这么多副本,还得看DataNode的数量。
比如目前只有3台设备,最多也就3个副本,只有节点数的增加到10台时,副本数才能达到10

HDFS 垃圾回收站

用户可以通过配置core-site.xml,开启NameNode的垃圾回收。NameNode会根据fs.trash.interval配置配置垃圾回收的频率,默认单位是分钟。

<property>
    <name>fs.trash.interval</name>
    <value>1</value>
</property>

表示1分钟内,如果用户不处理删除文件,系统会自动删除回收站的内容。该种机制就是为了防止用户的误操作。可以使用mv命令将回收站中数据再移动到原先的目录下。

NameNode的持久化

在这里插入图片描述

NameNode在运行时,把重要的元数据放置在内存中,如果内存出现问题,则元数据丢失,为了保证元数据安全,NameNode有对应的持久化机制,把元数据持久化到硬盘存储。

  1. FSImage和EditsLog存储位置

#fsImage默认存储位置 /opt/install/hadoop-2.5.2/data/tmp/dfs/name
dfs.namenode.name.dir
#editslog默认存储位置
dfs.namenode.edits.dir

  1. 指定FSImage和EditsLog的存储位置
hdfs-site.xml
<property>
    <name>dfs.namenode.name.dir</name>
    <value>file:///xxx/xxxx</value>
</property>
<property>
    <name>dfs.namenode.edits.dir</name>
    <value>file:///xxx/xxxx</value>
</property>
  1. NameNode的安全模式

是对HDFS的一种保护机制。

  • 正常情况下当系统在加载fsimage的初期会自动进入安全模式,在该模式下系统不接受外界的任何请求,当加载完数据检查系统完毕,系统会自动离开安全模式。
  • 如果当DataNode/NameNode使用空间不足时,系统会自动进入安全模式。
  • 当系统维护时,管理员也可手动的将NameNode切换成安全模式,维护结束再离开。
  • safe mode相关命令

注意:这里dfs和admin之间 没有空格
安全模式的 进入/离开/状态
bin/hdfs dfsadmin -safemode [enter leave get]

  • HDFS集群启动流程

HDFS集群启动 过程 (安全模式)

  1. 整合 FSImage和EditsLog 生成新的FSImage,并创建新的EditsLog接收用户写操作命令
  2. DataNode都需向NameNode主动汇报健康情况(心跳)3秒
  3. 汇报块列表 通过校验和 检查块是否可用,并定期1小时汇报。

在这里插入图片描述

#DataNode向 NameNode汇报当前解读信息的时间间隔,默认 6 小时;
<property>
	<name>dfs.blockreport.intervalMsec</name>
	<value>21600000</value>
	<description>Determines block reporting interval in milliseconds.</description>
</property>

#DataNode扫描自己节点块信息列表的时间,默认 6 小时
<property>
	<name>dfs.datanode.directoryscan.interval</name>
	<value>21600s</value>
	<description>Interval in seconds for Datanode to scan data
		directories and reconcile the difference between blocks in memory and on the disk.
		Support multiple time unit suffix(case insensitive), as described in dfs.heartbeat.interval.
	</description>
</property>

在这里插入图片描述

#单位毫秒
<property>
	 <name>dfs.namenode.heartbeat.recheck-interval</name>
	 <value>300000</value>
</property>
#单位秒
<property>
	 <name>dfs.heartbeat.interval</name>
	 <value>3s</value>
</property>

Secondary(辅助) NameNode & NameNode关系?

在这里插入图片描述
fsimage:存储在Namenode服务所在物理主机磁盘上的一个二进制文本文件。记录了元数据信息

editsLog:存储在Namenode服务所在物理主机磁盘上的一个二进制文本文件,记录了对元数据修改操作。

当第一次启动Namenode服务的时候,系统会加载fsimage和edits文件进行合并得到最新元数据信息,并且更新fsimage和edits,一旦服务启动成功后,在服务允许期间不再更新fsimage,只是将操作记录在edits中。导致namenode在长期运行之后重启导致namenode启动时间过长,还可能导致edits文件过大。因此Hadoop HDFS引入Secondary Namenode 辅助Namenode在运行期间完成对元数据的备份和整理。

Secondary NameNode 工作原理

  1. secondarynamenode首先会询问namenode是否需要checkpoint(触发checkpoint需要满足两个条件中的任意一个,定时时间到和edits中数据写满了)。直接带回namenode是否检查结果。
  2. secondarynamenode请求执行checkpoint操作
  3. namenode滚动edits并生成一个空的edits.inprogress,滚动edits的目的是给edits打个标记,以后所有新的操作都写入edits.inprogress,
  4. 将滚动前的edits和fsimage会拷贝到secondarynamenode的本地,
  5. Secondary NameNode将拷贝的edits和fsimage加载到内存中进行合并,生成fsimage.chkpoint,
  6. 然后将fsimage.chkpoint拷贝给namenode,重命名为fsimage后替换掉原来的fsimage。

    namenode再启动时就只需要加载之前未合并的新edits和合并后的fsimage即可,因为合并过的edits中的元数据信息已经被记录在fsimage中。

指定secondaryNameNode 启动的节点 hdfs-site.xml

<property>
    <name>dfs.namenode.secondary.http-address</name>
    <value>CentOS:50090</value>
</property>

SecondaryNameNode每隔一小时执行一次。hdfs-site.xml

<property>
  <name>dfs.namenode.checkpoint.period</name>
  <value>3600</value>
</property>

一分钟检查一次操作次数,当操作次数达到1百万时,SecondaryNameNode执行一次。

<property>
  <name>dfs.namenode.checkpoint.txns</name>
  <value>1000000</value>
  <description>操作动作次数</description>
</property>

<property>
  <name>dfs.namenode.checkpoint.check.period</name>
  <value>60</value>
  <description> 1分钟检查一次操作次数</description>
</property >

Hadoop HA(High Availability:高可用)构建

在这里插入图片描述

准备工作

  • 安装三台CentOS-6.5 64 bit操作系统(完成JDK、SSH免密码认证、IP主机名映射、关闭防火墙等工作)

主机和服务启动映射表

主机服务
CentOSANameNode、zkfc、DataNode、JournalNode、Zookeeper、NodeManager
CentOSBNameNode、zkfc、DataNode、JournalNode、Zookeeper、NodeManager、ResourceManager
CentOSCDataNode、JournalNode、Zookeeper、NodeManager、ResourceManager

主机信息

主机名IP信息
CentOSA192.168.40.129
CentOSB192.168.40.130
CentOSC192.168.40.131

JDK安装和配置

[root@CentOSX ~]# rpm -ivh jdk-8u171-linux-x64.rpm
[root@CentOSX ~]# vi .bashrc
JAVA_HOME=/usr/java/latest
PATH=$PATH:$JAVA_HOME/bin
CLASSPATH=.
export JAVA_HOME
export CLASSPATH
export PATH
[root@CentOSX ~]# source .bashrc

IP主机名映射

[root@CentOSX ~]# vi /etc/hosts
192.168.40.129 CentOSA
192.168.40.130 CentOSB
192.168.40.131 CentOSC

关闭防火墙

[root@CentOSX ~]# service iptables stop
iptables: Setting chains to policy ACCEPT: filter          [  OK  ]
iptables: Flushing firewall rules:                         [  OK  ]
iptables: Unloading modules:                               [  OK  ]
[root@CentOSX ~]# chkconfig iptables off

SSH免密码认证

[root@CentOSX ~]# ssh-keygen -t rsa
[root@CentOSX ~]# ssh-copy-id CentOSA
[root@CentOSX ~]# ssh-copy-id CentOSB
[root@CentOSX ~]# ssh-copy-id CentOSC

Zookeeper

[root@CentOSX ~]# tar -zxf zookeeper-3.4.6.tar.gz -C /usr/
#创建data文件夹
[root@CentOSX ~]# mkdir /root/zkdata
#创建myid文件,这里的1,2,3要对应下面的server.1、server.2、server.3,不可乱写
[root@CentOSA ~]# echo 1 >> /root/zkdata/myid
[root@CentOSB ~]# echo 2 >> /root/zkdata/myid
[root@CentOSC ~]# echo 3 >> /root/zkdata/myid
#创建zoo.cfg或者修改zoo_sample.cfg为zoo.cfg也行,这里是创建
[root@CentOSX ~]# touch /usr/zookeeper-3.4.6/conf/zoo.cfg
[root@CentOSX ~]# vi /usr/zookeeper-3.4.6/conf/zoo.cfg
tickTime=2000
dataDir=/root/zkdata		#创建的data文件夹
clientPort=2181
initLimit=5
syncLimit=2
server.1=CentOSA:2887:3887
server.2=CentOSB:2887:3887
server.3=CentOSC:2887:3887

#启动集群  start|stop|restart   启动|关闭|重启
[root@CentOSX ~]# /usr/zookeeper-3.4.6/bin/zkServer.sh start zoo.cfg
#查看集群状态	
[root@CentOSX ~]# /usr/zookeeper-3.4.6/bin/zkServer.sh status zoo.cfg		
JMX enabled by default
Using config: /usr/zookeeper-3.4.6/bin/../conf/zoo.cfg
Mode: `follower|leader`
[root@CentOSX ~]# jps
5879 `QuorumPeerMain`
7423 Jps

搭建Hadoop 集群(HDFS)

解压并配置HADOOP_HOME

[root@CentOSX ~]# tar -zxf hadoop-2.5.2_x64.tar.gz -C /opt/install/
[root@CentOSX ~]# vi .bashrc
HADOOP_HOME=/opt/install/hadoop-2.5.2
JAVA_HOME=/usr/java/latest
PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
CLASSPATH=.
export JAVA_HOME
export CLASSPATH
export PATH
export HADOOP_HOME
[root@CentOSX ~]# source .bashrc
#检查hadoop环境变量是否成功
[root@CentOSX ~]# hadoop
Usage: hadoop [--config confdir] COMMAND
       where COMMAND is one of:
  fs                   run a generic filesystem user client
  version              print the version
  jar <jar>            run a jar file
  checknative [-a|-h]  check native hadoop and compression libraries availability
  distcp <srcurl> <desturl> copy file or directories recursively
  archive -archiveName NAME -p <parent path> <src>* <dest> create a hadoop archive
  classpath            prints the class path needed to get the
  credential           interact with credential providers
                       Hadoop jar and the required libraries
  daemonlog            get/set the log level for each daemon
  trace                view and modify Hadoop tracing settings
 or
  CLASSNAME            run the class named CLASSNAME

Most commands print help when invoked w/o parameters.

配置core-site.xml

<!--配置Namenode服务ID-->
<property>
	<name>fs.defaultFS</name>
	<value>hdfs://ns</value>
</property>
<property>
	<name>hadoop.tmp.dir</name>
	<value>/opt/install/hadoop-2.5.2/data/tmp</value>
</property>
<property>
	<name>ha.zookeeper.quorum</name>
	<value>CentOSA:2181,CentOSB:2181,CentOSC:2181</value>
</property>

配置hdfs-site.xml

<!-- 权限设置,可写可不写,最好写上,免的用java操作时报权限问题-->
<property>
	<name>dfs.permissions.enabled</name>
	<value>false</value>
</property>
<!--开启自动故障转移-->
<property>
	<name>dfs.ha.automatic-failover.enabled</name>
	<value>true</value>
</property>
<!--指定hdfs的nameservice为ns,需要和core-site.xml中的保持一致 -->
<property>
	<name>dfs.nameservices</name>
	<value>ns</value>
</property>
<!-- ns下面有两个NameNode,分别是nn1,nn2 -->
<property>
	<name>dfs.ha.namenodes.ns</name>
	<value>nn1,nn2</value>
</property>
<!-- nn1的RPC通信地址 --><!--第一个NameNode机器 -->
<property>
	<name>dfs.namenode.rpc-address.ns.nn1</name>
	<value>CentOSA:8020</value>    
</property>
<!-- nn1的http通信地址 -->
<property>
	<name>dfs.namenode.http-address.ns.nn1</name>
	<value>CentOSA:50070</value>
</property>
<!-- nn2的RPC通信地址 --><!-- StandByNameNode -->
<property>
	<name>dfs.namenode.rpc-address.ns.nn2</name>
	<value>CentOSB:8020</value>
</property>
<!-- nn2的http通信地址 -->
<property>
	<name>dfs.namenode.http-address.ns.nn2</name>
	<value>CentOSB:50070</value>
</property>
<!-- 指定NameNode的元数据在JournalNode上的存放位置 -->
<property>
	<name>dfs.namenode.shared.edits.dir</name>
	<value>qjournal://CentOSA:8485;CentOSB:8485;CentOSC:8485/ns</value>
</property>
<!-- 指定JournalNode在本地磁盘存放数据的位置 -->
<property>
	<name>dfs.journalnode.edits.dir</name>
	<value>/opt/install/hadoop-2.5.2/journal</value>
</property>
<!--实现故障转切换的实现类-->
<property>
	<name>dfs.client.failover.proxy.provider.ns</name>
	<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<!-- 配置隔离机制,如果ssh是默认22端口,value直接写sshfence即可 -->
<property>
	<name>dfs.ha.fencing.methods</name>
	<value>sshfence</value>
</property>
<!-- 使用隔离机制时需要ssh免登陆 -->
<property>
	<name>dfs.ha.fencing.ssh.private-key-files</name>
	<value>/root/.ssh/id_rsa</value>
</property>

hadoop-env.sh添加如下内容

export JAVA_HOME=/usr/java/latest

配置slaves

CentOSA
CentOSB
CentOSC

启动HDFS(集群初始化启动)

#在某一个namenode节点执行如下命令,创建命名空间
[root@CentOSA|B ~]# bin/hdfs zkfc -formatZK
#在每个journalnode节点用如下命令启动journalnode
[root@CentOSX ~]# sbin/hadoop-daemon.sh start journalnode
#在主namenode节点格式化namenode和journalnode目录
[root@CentOSA ~]# bin/hdfs namenode -format
#在主namenode节点启动namenode进程
[root@CentOSA ~]# sbin/hadoop-daemon.sh start namenode
#在备namenode节点执行下行命令,这个是把备namenode节点的目录格式化并把元数据从主namenode节点copy过来,并且这个命令不会把journalnode目录再格式化了
[root@CentOSB ~]# bin/hdfs namenode -bootstrapStandby
#启动备namenode进程
[root@CentOSB ~]# sbin/hadoop-daemon.sh start namenode
#在两个namenode节点都执行以下命令
[root@CentOSA ~]# sbin/hadoop-daemon.sh start zkfc
[root@CentOSB ~]# sbin/hadoop-daemon.sh start zkfc
#在所有datanode节点都执行以下命令启动datanode
[root@CentOSX ~]# sbin/hadoop-daemon.sh start datanode

日常启停命令
sbin/start-dfs.sh
sbin/stop-dfs.sh

其他的配置查询地址(左下角xxx-default.xml)

Map Reduce

概述
Map Reduce是一个 Hadoop 的并行计算框架,借鉴了函数式编程(Scala 编程应用在Spark)思想和矢量编程(任务做阶段化拆分,每个阶段都可以设定并行度)。Hadoop 中是充分利用了存储节点(Data Node)运行所在主机的计算资源(CPU、内存、网络、少许磁盘-存储计算中间结果)完成对任务的并行计算。Map Reduce框架会在所有的DataNode所在的物理主机启动一个计算资源管理者-Node Manager用于管理本地的计算资源,默认系统会将计算资源均分8个等份,每个等份抽象成一个Container。还会再找一些其他的主机启动一个资源管理中心-Resource Manager,用于管理集群的计算资源。

当用户提交一个计算任务给MapReduce框架,框架会将任务拆分成Map阶段和Reduce阶段(矢量编程思想将任务拆分成两个阶段),框架会根据Map/Reduce阶段的任务并行度.在任务提交初期会启动一个任务管理者(每个任务都有自己的任务管理者)-MRAppMaster(该进程会浪费掉1个计算资源)用于管理Map阶段和Reduce阶段任务执行。在任务执行时期,每个阶段会根据阶段任务的并行度分配计算资源(每个计算资源启动一个Yarn Child),由MRAppMaster完成对阶段任务的检测管理。
在这里插入图片描述
ResourceManager:负责任务资源的统一调度,管理NodeManager资源,启动MRAppMaster,整个集群的老大

NodeManager:用于管理本机上的计算资源,默认会将本机的计算资源拆分为8个等份,每个等份抽象成Container

MRAppMaster:任何一个执行的任务都会有一个MRAppMaster负责YarnChild任务的执行和监测。

YarnChild:是具体执行的MapTask或者是ReduceTask的统称。

  1. 调用waitForCompletion方法每秒轮询作业的进度,内部封装了submit()方法,用于创建JobCommiter实例,并且调用其submitJobInternal方法。提交成功后,如果有状态改变,就会把进度报告到控制台,错误也会报告到控制台。
  2. JobCommiter实例会向ResourceManager申请一个新应应用的ID,用于MapReduce作业ID。这期间JobCommiter也会进行检查输出路径的情况,以及计算输入分片。
  3. 如果成功申请到ID,就会将运行作业所需的资源(包括作业jar文件,配置文件和计算所得的输入分片元数据文件)上传到一个用ID命名的目录下的HDFS上。此时副本个数默认是10(不受hdfs副本数影响)
  4. 准备工作已经做好,再通知ResourceManager调用submitApplication方法提交作业
  5. ResourceManager调用submitApplication方法后,会通知Yarn调度器(Scheduler),调度器分配一个容器,在节点管理器的管理下在容器中启动 application master进程
  6. application master的主类是MRAppMaster,其主要作用是初始化任务,并接受来自任务的进度和完成报告
  7. 然后从HDFS上接受资源,主要split,然后为每一个split创建MapTask以及参数指定的ReduceTask,任务ID在此时分配
  8. 然后Application Master会向资源管理器请求容器,首先为MapTask申请容器,然后为ReduceTask申请容器
  9. 一旦ResourceManager中的调度器(Scheduler),为Task分配了一个特定节点上的容器,Application Master就会与NodeManager进行通信来启动容器
  10. 运行任务是由YarnChild来执行的,任务运行前,先将资源本地化(jar包文件,配置文件,缓存文件)
  11. 然后开始运行MapTask或ReduceTask
  12. 当收到最后一个任务已经完成的通知后,Application Master会把作业状态设置为success。然后job轮询时,知道成功完成,就会通知客户端,并把统计信息输出到控制台

任务执行期间系统会启动MRAppmaster和YarnChild负责任务的执行,一旦任务执行结束MRAppMaster和YarnChild会自动退出。

MapReduce伪分布式搭建

  • mapred-site.xml:该文件不存在的话,手动创建
<property>
	<name>mapreduce.framework.name</name>
	<value>yarn</value>
</property>
  • yarn-site.xml
<!--配置MapReduce计算框架的核心实现Shuffle-洗牌-->
<property>
	<name>yarn.nodemanager.aux-services</name>
	<value>mapreduce_shuffle</value>
</property>
<!--指定resourcemanager所对应的节点-->
<property>
	<name>yarn.resourcemanager.hostname</name>
	<value>resourcemanager所在主机名</value>
</property>

启动|关闭命令

伪分布式yarn启动命令
sbin/yarn-daemon.sh start resourcemanager
sbin/yarn-daemon.sh start nodemanager

Resource Manager集群搭建

yarn-site.xml

<property>
    <name>yarn.nodemanager.aux-services</name>
    <value>mapreduce_shuffle</value>
</property>
<property>
    <name>yarn.resourcemanager.ha.enabled</name>
    <value>true</value>
</property>
<property>
    <name>yarn.resourcemanager.cluster-id</name>
    <value>ns</value>
</property>
<property>
    <name>yarn.resourcemanager.ha.rm-ids</name>
    <value>rm1,rm2</value>
</property>
<property>
    <name>yarn.resourcemanager.hostname.rm1</name>
    <value>CentOSB</value>
</property>
<property>
    <name>yarn.resourcemanager.hostname.rm2</name>
    <value>CentOSC</value>
</property>
<property>
    <name>yarn.resourcemanager.zk-address</name>
    <value>CentOSA:2181,CentOSB:2181,CentOSC:2181</value>
</property>

mapred-site.xml

<property>
    <name>mapreduce.framework.name</name>
    <value>yarn</value>
</property>

其他属性配置查阅地址

集群启动|关闭Yarn服务(resourcemanager节点执行)

[root@CentOSB ~]# sbin/start-yarn.sh 
[root@CentOSX ~]# sbin/stop-yarn.sh

日志说明

此时使用hadoop自带的测试jar包统计文件的中字符串出现的个数(在hdfs的input文件夹下先准备好测试数据)

[root@centos yarn]# cd /opt/install/hadoop-2.6.0/share/hadoop/mapreduce/
#注意这里的wordcount是任务的名字,不可改变。。
[root@centos mapreduce]# hadoop jar hadoop-mapreduce-examples-2.6.0.jar wordcount /input/* /ouput
22/09/16 07:19:42 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where appl            icable
22/09/16 07:19:42 INFO client.RMProxy: Connecting to ResourceManager at centos/192.168.45.4:8032
22/09/16 07:19:43 INFO input.FileInputFormat: Total input paths to process : 2
22/09/16 07:19:43 INFO mapreduce.JobSubmitter: number of splits:2
22/09/16 07:19:43 INFO mapreduce.JobSubmitter: Submitting tokens for job: job_1663283090970_0001
22/09/16 07:19:44 INFO impl.YarnClientImpl: Submitted application application_1663283090970_0001
22/09/16 07:19:44 INFO mapreduce.Job: The url to track the job: http://centos:8088/proxy/application_1663283090970_0001/
22/09/16 07:19:44 INFO mapreduce.Job: Running job: job_1663283090970_0001
22/09/16 07:19:52 INFO mapreduce.Job: Job job_1663283090970_0001 running in uber mode : false
22/09/16 07:19:52 INFO mapreduce.Job:  map 0% reduce 0%
22/09/16 07:20:00 INFO mapreduce.Job:  map 100% reduce 0%
22/09/16 07:20:06 INFO mapreduce.Job:  map 100% reduce 100%
22/09/16 07:20:07 INFO mapreduce.Job: Job job_1663283090970_0001 completed successfully
22/09/16 07:20:08 INFO mapreduce.Job: Counters: 49
        File System Counters
                FILE: Number of bytes read=170
                FILE: Number of bytes written=317145
                FILE: Number of read operations=0
                FILE: Number of large read operations=0
                FILE: Number of write operations=0
                HDFS: Number of bytes read=296
                HDFS: Number of bytes written=58
                HDFS: Number of read operations=9
                HDFS: Number of large read operations=0
                HDFS: Number of write operations=2
        Job Counters
                Launched map tasks=2
                Launched reduce tasks=1
                Data-local map tasks=2
                Total time spent by all maps in occupied slots (ms)=13195
                Total time spent by all reduces in occupied slots (ms)=3956
                Total time spent by all map tasks (ms)=13195
                Total time spent by all reduce tasks (ms)=3956
                Total vcore-seconds taken by all map tasks=13195
                Total vcore-seconds taken by all reduce tasks=3956
                Total megabyte-seconds taken by all map tasks=13511680
                Total megabyte-seconds taken by all reduce tasks=4050944
        Map-Reduce Framework
                Map input records=4
                Map output records=12
                Map output bytes=140
                Map output materialized bytes=176
                Input split bytes=202
                Combine input records=12
                Combine output records=12
                Reduce input groups=6
                Reduce shuffle bytes=176
                Reduce input records=12
                Reduce output records=6
                Spilled Records=24
                Shuffled Maps =2
                Failed Shuffles=0
                Merged Map outputs=2
                GC time elapsed (ms)=247
                CPU time spent (ms)=1280
                Physical memory (bytes) snapshot=459591680
                Virtual memory (bytes) snapshot=6169903104
                Total committed heap usage (bytes)=260485120
        Shuffle Errors
                BAD_ID=0
                CONNECTION=0
                IO_ERROR=0
                WRONG_LENGTH=0
                WRONG_MAP=0
                WRONG_REDUCE=0
        File Input Format Counters
                Bytes Read=94
        File Output Format Counters
                Bytes Written=58

可以看到控制台使用log4j打印日志了,但日志比较简单,且通过webUI也可以查看具体的日志,访问ip:8088
点击applicationId,然点击logs即可查看日志(windows下记得做主机ip映射,否则点logs跳转时需要把跳转地址中的主机名换成ip地址)
在这里插入图片描述
在这里插入图片描述

stderr:异常日志文件(如果执行报错了,可以查看此文件找错误原因)
stdout:通过标准输出打印出来的日志
syslog通过log4j打印的日志,通常这个日志中包含的有用信息最多,也是错误调试中最关键的参考日志

注意:hadoop3.X+版本对日志文件名有所变更
此时hadoop安装目录下也产生了任务的日志文件,yarn日志存放目录

yarn-site.xml

yarn.nodemanager.log.retain-seconds		设置webUI上可以查看日志的时间,默认10800秒(3小时)
yarn.nodemanager.log-dirs		默认存放在默认${yarn.log.dir}/userlogs下
其中yarn.log.dir的值需要去etc/hadoop/yarn-env.sh中查看,如下图

在这里插入图片描述
最终在hadoop的安装目录下的/logs/userlogs/中产生日志文件,如下图
在这里插入图片描述
点进去发现,产生的日志文件更多,这是因为每个容器都日志都是一个文件夹,如下:
在这里插入图片描述

所以为了尽量的长久的保存日志和方便查看日志,这里需要开启Yarn 历史日志 ,日志归档
mapred-site.xml 历史服务

<!--指定历史服务器所在位置及端口号-->
<property>
    <name>mapreduce.jobhistory.address</name>
    <value>resourcemanager主机名:10020</value>
</property>
<!--指定历史服务器所在的外部浏览交互端口及机器位置-->
<property>
    <name>mapreduce.jobhistory.webapp.address</name>
    <value>resourcemanager主机名:19888</value>
</property>

yarn-site.xml 日志聚合

<!--开启日志聚合功能-->
<property>
    <name>yarn.log-aggregation-enable</name>
    <value>true</value>
</property>
<!--指定日志保存时间  单位:秒,这里保留7-->
<property>
    <name>yarn.log-aggregation.retain-seconds</name>
    <value>604800</value>
</property>
将mapred-site.xml和yarn-site.xml  scp给每个节点
//在mapreduce.jobhistory.address配置的节点上执行以下命令即可
#sbin/mr-jobhistory-daemon.sh start historyserver   //开启
#sbin/mr-jobhistory-daemon.sh stop historyserver	//关闭

访问主机名:19888,查看历史日志webUI(必须要开启历史日志服务)
在这里插入图片描述
再次运行样例任务,访问ip:8088查看job,此时就不能再点击applicationId->logs查看日志了;
只能点击history查看日志。
在这里插入图片描述
在这里插入图片描述
注意,在运行样例任务时,如果yarn日志存放目录下,还是会短暂生成日志文件的,但随着任务执行结束,此任务产生的日志文件也会随之删除
在这里插入图片描述

但当真正在生产环境中却很少用上面的方法,因为datanode有多个时,map相对应的也会有多个文件,一个一个看太麻烦,所以会用shell脚本打印日志(需要关闭日志聚合,因为开启的话会把日志写到hdfs上,脚本查看的是本地日志)

yarn-env.sh

export YARN_LOG_DIR=~/logs/yarn
export YARN_PID_DIR=~/data/yarn

创建脚本

if [ $# -le 0 ]
then
    echo 缺少参数
    exit 1
fi

logtype=out

if [ $# -ge 1 ]
then
    logtype=${2}
fi 

for n in `cat /opt/install/hadoop-2.5.2/etc/hadoop/slaves`
do
    echo ===========查看节点 $n============
    ssh $n "cat ~/logs/yarn/userlogs/${1}/container_*/*${logtype}|grep com.test"
done
1. 修改脚本权限(文件可能没有执行权限)
2. ./scanMRLog.sh application_1558968514803_0001
	 脚本名		  job的applicationId

Hadoop2.X版本和Hadoop3.X版本的区别

hadoop2.xHadoop3.x
访问HDFS的web页面端口500709870
访问MR执行情况web页面端口80888088
历史服务器1988819888
客户端访问集群端口(API)90008020
配置文件core-site.xml、hdfs-site.xml、mapred-site.xml、yarn-site.xml、slavescore-site.xml、hdfs-site.xml、mapred-site.xml、yarn-site.xml、workers
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于计算机专业的学生而言,参加各类比赛能够带来多方面的益处,具体包括但不限于以下几点: 技能提升: 参与比赛促使学生深入学习和掌握计算机领域的专业知识与技能,如编程语言、算法设计、软件工程、网络安全等。 比赛通常涉及实际问题的解决,有助于将理论知识应用于实践中,增强问题解决能力。 实践经验: 大多数比赛都要求参赛者设计并实现解决方案,这提供了宝贵的动手操作机会,有助于积累项目经验。 实践经验对于计算机专业的学生尤为重要,因为雇主往往更青睐有实际项目背景的候选人。 团队合作: 许多比赛鼓励团队协作,这有助于培养学生的团队精神、沟通技巧和领导能力。 团队合作还能促进学生之间的知识共享和思维碰撞,有助于形成更全面的解决方案。 职业发展: 获奖经历可以显著增强简历的吸引力,为求职或继续深造提供有力支持。 某些比赛可能直接与企业合作,提供实习、工作机会或奖学金,为学生的职业生涯打开更多门路。 网络拓展: 比赛是结识同行业人才的好机会,可以帮助学生建立行业联系,这对于未来的职业发展非常重要。 奖金与荣誉: 许多比赛提供奖金或奖品,这不仅能给予学生经济上的奖励,还能增强其成就感和自信心。 荣誉证书或奖状可以证明学生的成就,对个人品牌建设有积极作用。 创新与研究: 参加比赛可以激发学生的创新思维,推动科研项目的开展,有时甚至能促成学术论文的发表。 个人成长: 在准备和参加比赛的过程中,学生将面临压力与挑战,这有助于培养良好的心理素质和抗压能力。 自我挑战和克服困难的经历对个人成长有着深远的影响。 综上所述,参加计算机领域的比赛对于学生来说是一个全面发展的平台,不仅可以提升专业技能,还能增强团队协作、沟通、解决问题的能力,并为未来的职业生涯奠定坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值