Apache hadoop

目录

一、hadoop架构变迁

二、Hadoop集群搭建

三、Hadoop集群模式安装(cluster mode) 分布式模式安装

1、安装包、源码包下载地址

2、自己按部就班搭建Hadoop集群

3、直接使用虚拟机快照切换至搭建好的环境

四、Hadoop集群启停命令、Web UI

五、Hadoop

六、HDFS分布式文件系统

1、文件系统

2、数据与元数据

3、分布式文件存储

4、HDFS应用场景

5、整体概述

6、HDFS的shell命令操作

七、HDFS工作流程与机制

1.HDFS集群角色与职责

2.HDFS读写数据流程


一、hadoop架构变迁

"""
Hadoop1.0   两层架构
    HDFS    分布式文件存储
    Mapreduce   资源分布和分布式数据管理
Hadoop2.0   三层架构
    HDFS    分布式数据存储
    YARN    集群资源管理和任务调度
    Mapreduce   分布式数据处理
Hadoop3.0   架构无变化,着重于性能优化
"""

二、Hadoop集群搭建

1、Hadoop集群概述

  • Hadoop集群包含两个部分:hdfs集群和yarn集群

    • Hadoop不存在mapresuce集群,mapreduce是计算框架和代码层面的组件,没有集群的说法

  • 两个集群逻辑上分离,通常物理上在一起

    • 逻辑上分离指的是两个集群之间互相没有依赖,互不影响

    • 物理上在一起指的是某些角色进程往往部署在同一台物理服务器上

  • 两个集群都是标准的主从架构集群

"""
hdfs集群:
    主角色:NameNode
    从角色:DataNode
    主角色辅助角色:SecondaryNameNode
"""
"""
yarn集群:
    主角色:ResourceManager
    从角色:NodeManager
"""

HDFS集群:NN,DN,SNN

YARN集群:RM,NM

 

三、Hadoop集群模式安装(cluster mode) 分布式模式安装

1、安装包、源码包下载地址

Index of /dist/hadoop/common

截止到目前最新的版本为Hadoop3.3.4

# 官方源码包      hadoop-3.3.4-src.tar.gz         2022-07-29 18:11   34M 
# 官方编译安装包   hadoop-3.3.4.tar.gz             2022-07-29 18:11  663M  

2、自己按部就班搭建Hadoop集群

正常情况下直接使用官方编译的包就行了,但是由于官方提供的安装包不支持本地库,为了匹配不同的操作系统的本地库环境,因此需要对Hadoop的源码进行重新编译

关于如何编译,Hadoop在源码根目录下的BUILDING.txt中提供了编译需要安装的软件

此外,如果有已经编译好的安装包,也可以使用编译好的安装包进行安装,此处使用编译好的安装包进行安装

2.1集群角色规划

2.1.1角色规划的准则

根据软件工作特性和服务器硬件资源情况合理分配

2.1.2角色规划注意事项

资源上有抢夺冲突的,尽量不要部署在一起

工作上需要互相配合的。尽量部署在一起

  • 主机角色
    node1NN DN RM NM
    node2SNN DN NM
    node3DN NM

至于如何进行规划以及为什么这么规划,需要进一步的了解学习

2.2服务器基础环境准备

2.2.1查看主机名(3台机器)

# 查看每个机器的主机名 
cat /etc/hostname

2.2.2Hosts映射(3台机器)

因为每个主机的IP地址不好记忆,所以在hosts文件中将每个主机的IP映射为主机名

# 查看主机名与IP的映射关系 
vim /etc/hosts

2.2.3防火墙关闭(3台机器)

由于防火墙的开启会导致许多端口关闭,那么分布式软件就有可能通信失败

# 防火墙关闭
firewall-cmd --state    #查看防火墙状态
systemctl stop firewalld.service  #停止firewalld服务
systemctl disable firewalld.service  #开机禁用firewalld服务

2.2.4ssh免密登录(node1执行->node1|node2|node3)

# ssh免密登录(只需要配置node1至node1、node2、node3即可)
​
# node1生成公钥私钥 (一路回车)
ssh-keygen  
    
# node1配置免密登录到node1 node2 node3
ssh-copy-id node1
ssh-copy-id node2
ssh-copy-id node3
​
# 验证是否已经配置好免密登录,只需要用node1分别连接node1,node2,node3即可

2.2.5集群时间同步(3台机器)

# ntpdate ntp5.aliyun.com

2.2.6创建统一工作目录(3台机器)

理论上来说,软件的安装路径等不需要指定目录,但是在实际使用中,为了方便管理,需要指定固定的安装路径等

mkdir -p /export/server/    #软件安装路径
mkdir -p /export/data/      #数据存储路径
mkdir -p /export/software/  #安装包存放路径

2.3上传安装包、解压安装包

2.3.1JDK 1.8安装(3台机器)

按照规范,安装在export的server下,先把jdk的安装包(jdk-8u241-linux-x64.tar.gz)导入到server目录,再cd到相应的目录,解压jdk的安装包

cd /export/server/
tar zxvf jdk-8u241-linux-x64.tar.gz
# rm -rf jdk-8u241-linux-x64.tar.gz 顺手将安装包删除

配置环境变量,使用vim打开配置文件,G,o,在最后一行进行添加

vim /etc/profile
    
export JAVA_HOME=/export/server/jdk1.8.0_241
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
​
# 重新加载环境变量文件
source /etc/profile
​
# 验证是否安装成功
java -version

第一台机器安装成功后可以直接按照同样的步骤在其他两个机器上安装,也可以用scp远程拷贝给其他两台机器

scp -r /export/server/jdk1.8.0_241/ root@node2:/export/server/
scp -r /export/server/jdk1.8.0_241/ root@node3:/export/server/
​
# 拷贝环境变量
scp -r /etc/profile root@node1:/etc/
scp -r /etc/profile root@node2:/etc/
scp -r /etc/profile root@node3:/etc/
​
# 对三台机器重新加载环境变量文件
source /etc/profile

2.3.2上传、解压Hadoop安装包(node1)

先把Hadoop安装包(hadoop-3.3.0-Centos7-64-with-snappy.tar.gz)复制到node1 /export/server,再cd到相应的目录,解压Hadoop的安装包

cd /export/server/
tar zxvf hadoop-3.3.0-Centos7-64-with-snappy.tar.gz
# rm zxvf hadoop-3.3.0-Centos7-64-with-snappy.tar.gz    顺手将安装包删除

2.4Hadoop安装包目录结构

 

其中,bin和sbin目录时日常维护和操控集群常用的目录

配置文件的目录:/export/server/hadoop-3.3.0/etc/hadoop

2.5:编辑Hadoop配置文件

配置文件很有多种类,在配置Hadoop时需要更改三类配置文件:

  • 第一类1个:

    • hadoop-env.sh

cd到配置文件的目录,用vim打开此文件,G,o,在文件最后添加

#文件最后添加
export JAVA_HOME=/export/server/jdk1.8.0_241
​
export HDFS_NAMENODE_USER=root
export HDFS_DATANODE_USER=root
export HDFS_SECONDARYNAMENODE_USER=root
export YARN_RESOURCEMANAGER_USER=root
export YARN_NODEMANAGER_USER=root 

shift+zz快速保存

  • 第二类4个:

    xxxx-site.xml ,site表示的是用户定义的配置,会覆盖default中的默认配置。

    • core-site.xml 核心模块配置

    • hdfs-site.xml hdfs 文件系统模块配置

    • mapred-site.xml MapReduce模块配置

    • yarn-site.xml yarn模块配置

配置文件很有多种类,在配置Hadoop时需要更改三类配置文件:

# vim打开core-site.xml,在两个configuration之间,o,将配置文件粘贴过来,shift+zz快速保存即可
​
<!-- 设置默认使用的文件系统 Hadoop支持file、HDFS、GFS、ali|Amazon云等文件系统 -->
<property>
    <name>fs.defaultFS</name>
    <value>hdfs://node1:8020</value>
</property>
​
<!-- 设置Hadoop本地保存数据路径 -->
<property>
    <name>hadoop.tmp.dir</name>
    <value>/export/data/hadoop-3.3.0</value>
</property>
​
<!-- 设置HDFS web UI用户身份 -->
<property>
    <name>hadoop.http.staticuser.user</name>
    <value>root</value>
</property>
​
<!-- 整合hive 用户代理设置 -->
<property>
    <name>hadoop.proxyuser.root.hosts</name>
    <value>*</value>
</property>
​
<property>
    <name>hadoop.proxyuser.root.groups</name>
    <value>*</value>
</property>
​
<!-- 文件系统垃圾桶保存时间 -->
<property>
    <name>fs.trash.interval</name>
    <value>1440</value>
</property> 
# vim打开hdfs-site.xml,在两个configuration之间,i,将配置文件粘贴过来,shift+zz快速保存即可
<!-- 设置SNN进程运行机器位置信息 -->
<property>
    <name>dfs.namenode.secondary.http-address</name>
    <value>node2:9868</value>
</property>
# vim打开mapred-site.xml,在两个configuration之间,o,将配置文件粘贴过来,shift+zz快速保存即可
<!-- 设置MR程序默认运行模式: yarn集群模式 local本地模式 -->
<property>
  <name>mapreduce.framework.name</name>
  <value>yarn</value>
</property>
​
<!-- MR程序历史服务地址 -->
<property>
  <name>mapreduce.jobhistory.address</name>
  <value>node1:10020</value>
</property>
 
<!-- MR程序历史服务器web端地址 -->
<property>
  <name>mapreduce.jobhistory.webapp.address</name>
  <value>node1:19888</value>
</property>
​
<property>
  <name>yarn.app.mapreduce.am.env</name>
  <value>HADOOP_MAPRED_HOME=${HADOOP_HOME}</value>
</property>
​
<property>
  <name>mapreduce.map.env</name>
  <value>HADOOP_MAPRED_HOME=${HADOOP_HOME}</value>
</property>
​
<property>
  <name>mapreduce.reduce.env</name>
  <value>HADOOP_MAPRED_HOME=${HADOOP_HOME}</value>
</property>
# vim打开yarn-site.xml,在两个configuration之间,o,将配置文件粘贴过来,shift+zz快速保存即可
<!-- 设置YARN集群主角色运行机器位置 -->
<property>
    <name>yarn.resourcemanager.hostname</name>
    <value>node1</value>
</property>
​
<property>
    <name>yarn.nodemanager.aux-services</name>
    <value>mapreduce_shuffle</value>
</property>
​
<!-- 在实际企业中,以下两个参数可以删除 -->
<!-- 是否将对容器实施物理内存限制 -->
<property>
    <name>yarn.nodemanager.pmem-check-enabled</name>
    <value>false</value>
</property>
​
<!-- 是否将对容器实施虚拟内存限制。 -->
<property>
    <name>yarn.nodemanager.vmem-check-enabled</name>
    <value>false</value>
</property>
​
<!-- 开启日志聚集 -->
<property>
  <name>yarn.log-aggregation-enable</name>
  <value>true</value>
</property>
​
<!-- 设置yarn历史服务器地址 -->
<property>
    <name>yarn.log.server.url</name>
    <value>http://node1:19888/jobhistory/logs</value>
</property>
​
<!-- 历史日志保存的时间 7天 -->
<property>
  <name>yarn.log-aggregation.retain-seconds</name>
  <value>604800</value>
</property>

第三类1个:

  • workers 对应的是datanode的机器

# vim打开workers,默认只有在localhost有,dd将local host删除,将以下三个机器添加进去,shift+zz快速保存即可
​
node1.itcast.cn
node2.itcast.cn
node3.itcast.cn

2.6分发同步安装包

# 在node1中
cd /export/server
​
scp -r hadoop-3.3.0 root@node2:$PWD
scp -r hadoop-3.3.0 root@node3:$PWD

2.7配置Hadoop环境变量

vim /etc/profile
​
# G,o,复制粘贴以下代码
​
export HADOOP_HOME=/export/server/hadoop-3.3.0
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
​
#别忘了scp给其他两台机器哦
scp -r /etc/profile root@node2:/etc/
​
# 重新加载环境变量文件
source /etc/profile

2.8NameNode format(格式化(初始化)操作)

首次启动hdfs时,需要对其进行初始化操作,format本质上时初始化操作,进行hdfs的清理和准备工作

# 注意,初始化只能操作一次,否则需要重新进行初始化操作
hdfs namenode -format

3、直接使用虚拟机快照切换至搭建好的环境

注:在切换快照时需要多台机器同时切换

四、Hadoop集群启停命令、Web UI

  • 脚本一键启动(前提是配置好ssh免密登录和workers)

# HDFS集群:
start-dfs.sh 
stop-dfs.sh
​
# Yarn集群
start-yarn.sh
stop-yarn.sh
​
# Hadoop集群
start-all.sh
stop-all.sh
  • 手动启停

优点:可以精确实现一台机器的开启和停止,避免群起群停的情况

缺点:当机器的数量很大时,手动实现启停较为麻烦

# HDFS
#hadoop2.x版本命令
hadoop-daemon.sh start|stop namenode|datanode|secondarynamenode
#hadoop3.x版本命令
hdfs --daemon start|stop namenode|datanode|secondarynamenode
# Yarn
#hadoop2.x版本命令
yarn-daemon.sh start|stop resourcemanager|nodemanager
#hadoop3.x版本命令
yarn --daemon start|stop resourcemanager|nodemanager
# 启动完成后使用jps来查看是否启动成功
# Hadoop启动日志路径:/export/server/hadoop-3.3.0/logs/
  • Web UI页面

    • HDFS集群:http://node1:9870/

      其中node1是namenode运行所在机器的主机名或者ip(如果使用主机名的话需要在host配置)

    • YARN集群:http://node1:8088/

      其中node1是namenode运行所在机器的主机名或者ip(如果使用主机名的话需要在host配置)

五、Hadoop

# HDFS本质就是一个文件系统--分布式文件系统
# 有目录树结构,和Linux类似,分文件、文件夹
# hdfs适合大文件存储,小文件存储会很慢
# 执行MapReduce的时候,首先请求YARN调动资源
# MapReduce是两个阶段,先Map,再Reduce
# 处理小数据的时候,MapReduce速度很慢

六、HDFS分布式文件系统

1、文件系统

  • 文件系统是一种存储和组织数据的方法,是为了方便用户访问和查找文件数据的

  • 文件系统使用树形目录使得抽象的实际存储容易理解(使用数据块存储在磁盘等物理设备上),用户不需要关心数据存储的底层,只需要记得文件的名字和存储目录即可

  • 传统的文件系统指的是单机文件系统,也就是不是跨多台机器实现的,有以下特征:

    • 带有抽象的目录树结构,树都是从/根目录开始往下蔓延;

    • 树中节点分为两类:目录和文件;

    • 从根目录开始,节点路径具有唯一性。

2、数据与元数据

  • 数据值得是存储内容本身,他包含了很多中类型,用户在使用时只要根据目录树进行增删改查即可

  • 元数据简单来说就是记录数据

3、分布式文件存储

# 分布式存储     --横向存储,理论上可以无限扩展
# 元数据记录     --记录文件的信息和存储信息,方便快速查找
# 分块存储       --可以实现并行操作,提高效率
# 副本机制       --不同的设备存储,冗余存储,保障数据安全

4、HDFS应用场景

# 适合:大文件、数据流式访问、一次写入多次读取、低成本部署、高容错
# 不适合:小文件、数据交互式访问、频繁任意修改、低延迟

5、整体概述

  1. 主从架构

    """
    1. HDFS集群是标准的master/slave主从架构集群。 
    2. 一般一个HDFS集群是有一个Namenode和一定数目的Datanode组成。 
    3. Namenode是HDFS主节点,Datanode是HDFS从节点,两种角色各司其职,共同协调完成分布式的文件存储服务。 
    4. 官方架构图中是一主五从模式,其中五个从角色位于两个机架(Rack)的不同服务器上。(实际在虚拟环境的应用中,不存在机架的概念,所以从角色的存储一般是随机分配的,但是遵守一个基本规则:同样的数据块分布在不同的机器(node)上)
    """
  2. 分块存储

    """
    1. HDFS中的文件在物理上是分块存储(block)的,默认大小是128M(134217728),不足128M则本身就是一块。
    2.  块的大小可以通过配置参数来规定,参数位于hdfs-default.xml中:dfs.blocksize。
    """
  3. 副本机制

    """
    1. 文件的所有block都会有副本。副本系数可以在文件创建的时候指定,也可以在之后通过命令改变。
    2. 副本数由参数dfs.replication控制,默认值是3,也就是会额外再复制2份,连同本身总共3份副本。
    """
  4. 元数据记录(NameNode)

    """
    在HDFS中,Namenode管理的元数据具有两种类型:
    一、文件自身属性信息
    文件名称、权限,修改时间,文件大小,复制因子,数据块大小。
    二、文件块位置映射信息
    记录文件块和DataNode之间的映射信息,即哪个块位于哪个节点上。
    """
  5. 抽象统一的目录树结构(NameSpace)

    """
    1.HDFS支持传统的层次型文件组织结构。用户可以创建目录,然后将文件保存在这些目录里。文件系统名字空间的层次结构和大多数现有的文件系统类似:用户可以创建、删除、移动或重命名文件。
    2.Namenode负责维护文件系统的namespace名称空间,任何对文件系统名称空间或属性的修改都将被Namenode记录下来。
    3.HDFS会给客户端提供一个统一的抽象目录树,客户端通过路径来访问文件,形如:hdfs://namenode:port/dir-a/dir-b/dir-c/file.data。
    """
  6. 数据块存储

    """
    1.文件的各个block的具体存储管理由DataNode节点承担。
    2.每一个block都可以在多个DataNode上存储。
    """

6、HDFS的shell命令操作

# 基本命令:hadoop fs [generic options]

1、文件系统协议

"""
1.HDFS Shell CIL支持多种文件操作系统:
    本地:file:///
    分布式文件系统:hdfs://node1:8020
2.具体操作的是什么文件操作系统取决于命令的前缀(URL协议)
3.如果没有指定前缀,默认的前缀在环境变量中的 fs.defaultFS来定义,默认是当前用户登录机器的根目录
"""
# 由于HDFS版本更新换代,操作命令有三种,都可以访问hdfs分布式文件系统:
    hadoop dfs  # 只能操作hafs文件操作系统,但是目前已经deprecated;
    hdfs dfs    # 只能操作hafs文件操作系统,常用
    hadoop fs   # 可以操作任意的文件系统,使用范围更广

2、常用的操作命令

"""
1.创建文件夹
    hadoop fs [-p] -mkdir /teddy
        -p确保文件目录存在,如果不存在就创建一个
        查看指定文件夹下的内容
    hadoop fs -ls [-h] [-R] [path]
        -h  人性化显示
        -R  递归显示指定目录及其子目录
2.上传,下载文件
    hadoop fs -put [-f] [-p] 本地文件操作系统及地址 目标文件操作系统及地址
        -f  如果目标文件已经存在,覆盖目标文件,可以在后面增加文件的名字,用以重命名
        -p  保留访问和修改时间,所有权和权限
        本地文件操作系统默认为当前客户端所在机器的文件操作系统(file:///)   目标文件操作系统默认为hdfs分布式文件操作系统            (hdfs://node1:8020)
        本地地址不填默认为当前目录,目标地址必须要填
    hadoop fs -get [-f] [-p] 本地文件操作系统及地址 目标文件操作系统及地址
        与上传类似
3.查看文件详细内容
    hadoop fs -cat 文件目录
        文件目录可以是相对路径也可以是绝对路径
        使用cat命令时要慎重,如果是大文件建议还是用tail,默认显示最后10行的内容
            hadoop fs - tail 文件目录
4.拷贝文件
    hadoop fs -cp [-f] 文件目录 目标文件目录
        hadoop fs -cp /small/1.txt /2222
        hadoop fs -cp /small/1.txt /2222/666.txt #重命名
5.追加数据到hdfs文件内容中
    hadoop fs -appendToFile file1 file2/target_file
        example:
        echo 1 > 1.txt
        echo 2 > 2.txt
        echo 3 > 3.txt
        hadoop fs -put 1.txt /
        hadoop fs -cat /1.txt
        hadoop fs -appendToFile 2.txt 3.txt/1.txt
        hadoop fs -cat /1.txt
6.hadoop数据移动
    hadoop fs mv 文件目录 目标目录
"""

七、HDFS工作流程与机制

1.HDFS集群角色与职责

  • 官方架构图

主角色:NameNode

 

"""
1.是Hadoop的核心
2.主要维护和管理文件系统的元数据,通过内存和磁盘进行管理,其中,磁盘上的数据包括Fsimage内存元数据镜像文件和edits log(Journal)编辑日志
3.是访问HDFS的唯一入口
4.namenode所在的机器通常都配置有大内存
5.namenode不持久化每个文件的各个块所在datanode的位置信息,在系统启动时会动datanode中重建
"""

从角色:DataNode

"""
1.负责具体的数据块的存储
2.其数量决定了hdfs集群的数据存储能力,和namenode一起维护数据块
3.datanode启动时,会将自己注册到namenode并汇报自己负责持有的块列表
4.实际数据的存储是在datanode中的,所以datanode所在的机器一般要有大量的磁盘空间
"""

从角色辅助角色:SecondaryNameNode

"""
1.充当的是namenode的辅助节点,但在namenode挂掉的时候不能代替namenode
2.主要作用是帮助namenode进行元数据的合并动作
"""

2.HDFS读写数据流程

  • 写数据的流程图:

 

  • 三个核心概念:

  1. Pipeline--线性传输

    HDFS采用管道传输数据,客户端将数据写入第一个节点,第一个节点再复制到第二个节点,再复制到第三个节点,这样能最大化利用每个节点的带宽,避免网络分配带宽和减少网络延迟

  2. ACK--应答相应(acknowledge character)

    在数据通信中,接收方发送给发送方的确认字符,表示数据确认接受无误,确保了数据的安全

  3. 默认3副本存储策略

    默认副本存储策略是由BlockPlacementPolicyDefault指定

    1. 第一块副本:优先客户端本地,否则随机

    2. 第二块副本:不同于第一块副本的不同机架。

    3. 第三块副本:第二块副本相同机架不同机器。

     

"""
1.HDFS客户端创建对象实例DistributedFileSystem, 该对象中封装了与HDFS文件系统操作的相关方法。
2、调用DistributedFileSystem对象的create()方法,通过RPC请求NameNode创建文件。
NameNode执行各种检查判断:目标文件是否存在、父目录是否存在、客户端是否具有创建该文件的权限。检查通过,NameNode就会为本次请求记下一条记录,返回FSDataOutputStream输出流对象给客户端用于写数据。
3、客户端通过FSDataOutputStream输出流开始写入数据。 
4、客户端写入数据时,将数据分成一个个数据包(packet 默认64k), 内部组件DataStreamer请求NameNode挑选出适合存储数据副本的一组DataNode地址,默认是3副本存储。DataStreamer将数据包流式传输到pipeline的第一个DataNode,该DataNode存储数据包并将它发送到pipeline的第二个DataNode。同样,第二个DataNode存储数据包并且发送给第三个(也是最后一个)DataNode。
5、传输的反方向上,会通过ACK机制校验数据包传输是否成功;
6、客户端完成数据写入后,在FSDataOutputStream输出流上调用close()方法关闭。
7、DistributedFileSystem联系NameNode告知其文件写入完成,等待NameNode确认。
因为namenode已经知道文件由哪些块组成(DataStream请求分配数据块),因此仅需等待最小复制块即可成功返回。最小复制是由参数dfs.namenode.replication.min指定,默认是1
"""
  • 读取数据的流程图:

 

"""
1、HDFS客户端创建对象实例DistributedFileSystem, 调用该对象的open()方法来打开希望读取的文件。
2、DistributedFileSystem使用RPC调用namenode来确定文件中前几个块的块位置(分批次读取)信息。
对于每个块,namenode返回具有该块所有副本的datanode位置地址列表,并且该地址列表是排序好的,与客户端的网络拓扑距离近的排序靠前。
3、DistributedFileSystem将FSDataInputStream输入流返回到客户端以供其读取数据。 
4、客户端在FSDataInputStream输入流上调用read()方法。然后,已存储DataNode地址的InputStream连接到文件中第一个块的最近的DataNode。数据从DataNode流回客户端,结果客户端可以在流上重复调用read()。
5、当该块结束时,FSDataInputStream将关闭与DataNode的连接,然后寻找下一个block块的最佳datanode位置。这些操作对用户来说是透明的。所以用户感觉起来它一直在读取一个连续的流。客户端从流中读取数据时,也会根据需要询问NameNode来检索下一批数据块的DataNode位置信息。 
6、一旦客户端完成读取,就对FSDataInputStream调用close()方法。
"""

部分内容类源于网络,如有侵权请联系删除,谢谢!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值