HDFS基础使用

目录

1.HDFS前言

2.HDFS相关概念和特性

2.1.HDFS设计思路

2.2.HDFS架构

2.3.概念和特性

3.HDFS优缺点

3.1.HDFS优点

3.2.HDFS缺点

4.HDFS的shell(命令行客户端)操作

5.HDFS的Java API操作

5.1.利用eclipse查看hdfs集群的文件信息

5.2.搭建开发环境

5.3.FileSystem实例获取讲解(重点)

5.4.DistributedFileSystem实例所具备的方法介绍

5.5.HDFS常用Java API代码演示

5.6.HDFS流式数据访问

5.7.经典案例

6.HDFS核心设计

6.1.HDFS心跳机制(heartbeat)

6.2.HDFS安全模式

6.3.HDFS副本存放策略

6.4.负载均衡


1.HDFS前言

HDFS:Hadoop Distributed File System Hadoop 分布式文件系统,主要用来解决海量数据的存储问题

1. 设计思想:分而治之,即将大文件,大批量文件,分布式的存放于大量服务器上。以便于采取分而治之的方式对海量数据进行运算分析。

2.在大数据系统架构中的应用:为各类分布式运算框架(MapReduce,Spark,Tez,Flink,…)提供数据存储服务。

3. 重点概念:数据块/副本,负载均衡,心跳机制,副本存放策略,元数据/元数据管理,安全模式,机架感知…

2.HDFS相关概念和特性

2.1.HDFS设计思路

HDFS 被设计成用来使用低廉的服务器来进行海量数据的存储,那是怎么做到的呢?

1.大文件被切割成小文件,使用分而治之的思想让很多服务器对同一个文件进行联合管理

2. 每个小文件做冗余备份,并且分散存到不同的服务器,做到高可靠不丢失

2.2.HDFS架构

主节点 Namenode:集群老大,掌管文件系统目录树,处理客户端读且请求

SecondaryNamenode:严格说并不是 namenode 备份节点,主要给 namenode 分担压力之用

从节点 Datanode:存储整个集群所有数据块,处理真正数据读写

2.3.概念和特性

首先,它是一个文件系统,用于存储文件,通过统一的命名空间——目录树来定位文件。其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器都有各自清晰的角色定位。

重要特性如下:

1.HDFS 中的文件在物理上是分块存储(block),块的大小可以通过配置参数(dfs.blocksize)来规定,默认大小在 hadoop2.x 版本中是 128M,老版本中是 64M,hadoop3.x版本中是256M。

2.HDFS 文件系统会给客户端提供一个统一的抽象目录树,客户端通过路径来访问文件,形如:

hdfs://namenode:port/dir-a/dir-b/dir-c/file.data

hdfs://hadoop02:9000/soft/hadoop-2.6.5-centos-6.7.tar.gz

3.目录结构及文件分块位置信息(元数据)的管理由 namenode 节点承担,namenode 是 HDFS 集群主节点,负责维护整个 hdfs 文件系统的目录树,以及每一个路径(文件)所对应的 block 块信息(block 的 id,及所在的 datanode 服务器)。

4.文件的各个 block 的存储管理由 datanode 节点承担,datanode 是 HDFS 集群从节点,每一个 block 都可以在多个 datanode 上存储多个副本(副本数量也可以通过参数设置 dfs.replication,默认是 3)。

5.HDFS 是设计成适应一次写入,多次读出的场景,且不支持文件的修改

(PS:适合用来做数据分析,并不适合用来做网盘应用,因为,不便修改,延迟大,网络开销大,成本太高)

3.HDFS优缺点

3.1.HDFS优点

1.可构建在廉价机器上:通过多副本提高可靠性,提供了容错和恢复机制

2.高容错性:数据自动保存多个副本,副本丢失后,自动恢复

3.适合批处理:移动计算而非数据,数据位置暴露给计算框架

4.适合大数据处理:GB、TB、甚至 PB 级数据,百万规模以上的文件数量,10K+节点规模

5.流式文件访问:一次性写入,多次读取,保证数据一致性

3.2.HDFS缺点

不适于以下操作

  • 低延迟数据访问:比如毫秒级,低延迟与高吞吐率
  • 小文件存取:占用 NameNode 大量内存 150b* 1000W = 15E,1.5G,寻道时间超过读取时间
  • 并发写入、文件随机修改:一个文件只能有一个写者,仅支持 append

HDFS 不适合存储小文件

  • 元信息存储在 NameNode 内存中:一个节点的内存是有限的
  • 存取大量小文件消耗大量的寻道时间:类比拷贝大量小文件与拷贝同等大小的一个大文件
  • NameNode 存储 block 数目是有限的:一个 block 元信息消耗大约 150 byte 内存,存储 1 亿个 block,大约需要 20GB 内存,如果一个文件大小为 10K,则 1 亿个文件大小仅为 1TB(但要消耗掉 NameNode 20GB内存)。

4.HDFS的shell(命令行客户端)操作

HDFS 提供 shell 命令行客户端,使用方法如下:

HDFS 支持的其他命令行参数如下:

  • [-appendToFile <localsrc> ... <dst>]
  • [-cat [-ignoreCrc] <src> ...]
  • [-checksum <src> ...]
  • [-chgrp [-R] GROUP PATH...]
  • [-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]
  • [-chown [-R] [OWNER][:[GROUP]] PATH...]
  • [-copyFromLocal [-f] [-p] <localsrc> ... <dst>]
  •  [-copyToLocal [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
  •  [-count [-q] <path> ...]
  •  [-cp [-f] [-p] <src> ... <dst>]
  •  [-createSnapshot <snapshotDir> [<snapshotName>]]
  •  [-deleteSnapshot <snapshotDir> <snapshotName>]
  •  [-df [-h] [<path> ...]]
  •  [-du [-s] [-h] <path> ...]
  •  [-expunge]
  •  [-get [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
  •  [-getfacl [-R] <path>]
  •  [-getmerge [-nl] <src> <localdst>]
  •  [-help [cmd ...]]
  •  [-ls [-d] [-h] [-R] [<path> ...]]
  •  [-mkdir [-p] <path> ...]
  •  [-moveFromLocal <localsrc> ... <dst>]
  •  [-moveToLocal <src> <localdst>]
  •  [-mv <src> ... <dst>]
  •  [-put [-f] [-p] <localsrc> ... <dst>]
  •  [-renameSnapshot <snapshotDir> <oldName> <newName>]
  •  [-rm [-f] [-r|-R] [-skipTrash] <src> ...]
  •  [-rmdir [--ignore-fail-on-non-empty] <dir> ...]
  •  [-setfacl [-R] [{-b|-k} {-m|-x <acl_spec>} <path>]|[--set <acl_spec> <path>]]
  •  [-setrep [-R] [-w] <rep> <path> ...]
  •  [-stat [format] <path> ...]
  •  [-tail [-f] <file>]
  •  [-test -[defsz] <path>]
  •  [-text [-ignoreCrc] <src> ...]
  •  [-touchz <path> ...]
  •  [-usage [cmd ...]]

常用命令参数介绍:

-help
功能:输出这个命令参数手册

[hadoop@hadoop02 ~]$ hadoop -help
[hadoop@hadoop02 ~]$ hadoop fs -help
[hadoop@hadoop02 ~]$ hadoop fs -help ls

-ls
功能:显示目录信息

示例: hadoop fs -ls hdfs://hadoop02:9000/
备注:这些参数中,所有的 hdfs 路径都可以简写成 hadoop fs -ls / 等同上条命令的效果

-mkdir
功能:在 hdfs 上创建目录

示例:hadoop fs -mkdir -p /aa/bb/cc/dd

-put
功能:等同于 copyFromLocal,进行文件上传

示例:hadoop fs -put /aaa/jdk.tar.gz /bbb/jdk.tar.gz.2

-get
功能:等同于 copyToLocal,就是从 hdfs 下载文件到本地

示例:hadoop fs -get /aaa/jdk.tar.gz
-getmerge
功能:合并下载多个文件

示例:比 getmerge 如 hdfs 的目录 /aaa/下有多个文件:log.1, log.2,log.3,...
hadoop fs -getmerge /aaa/log.* ./log.sum

-cp 
功能:从 hdfs 的一个路径拷贝 hdfs 的另一个路径

示例: hadoop fs -cp /aaa/jdk.tar.gz /bbb/jdk.tar.gz.2

-mv 
功能:在 hdfs 目录中移动文件

示例: hadoop fs -mv /aaa/jdk.tar.gz /

-rm 
功能:删除文件或文件夹

示例:hadoop fs -rm -r /aaa/bbb/
-rmdir 
功能:删除空目录

示例:hadoop fs -rmdir /aaa/bbb/ccc

-moveFromLocal 
功能:从本地剪切到 hdfs

示例:hadoop fs - moveFromLocal /home/hadoop/a.txt /aa/bb/cc/dd
-moveToLocal 
功能:从 hdfs 剪切到本地

示例:hadoop fs - moveToLocal /aa/bb/cc/dd /home/hadoop/a.txt

-copyFromLocal 
功能:从本地文件系统中拷贝文件到 hdfs 文件系统去

示例:hadoop fs -copyFromLocal ./jdk.tar.gz /aaa/
-copyToLocal 
功能:从 hdfs 拷贝到本地

示例:hadoop fs -copyToLocal /aaa/jdk.tar.gz

-appendToFile 
功能:追加一个文件到已经存在的文件末尾

示例:hadoop fs -appendToFile ./hello.txt hdfs://hadoop-server01:9000/hello.txt
可以简写为:
hadoop fs -appendToFile ./hello.txt /hello.txt

-cat 功能:显示文件内容
hadoop fs -cat /hello.txt

-tail 
功能:显示一个文件的末尾

示例:hadoop fs -tail /weblog/access_log.1

-text 
功能:以字符形式打印一个文件的内容

示例:hadoop fs -text /weblog/access_log.1

-chgrp
-chmod
-chown
功能:linux 文件系统中的用法一样,对文件所属权限

示例:
hadoop fs -chmod 666 /hello.txt
hadoop fs -chown someuser:somegrp /hello.txt

-df
功能:统计文件系统的可用空间信息

示例:hadoop fs -df -h /
-du 
功能:统计文件夹的大小信息

示例:hadoop fs -du -s -h /aaa/*

-count 
功能:统计一个指定目录下的文件节点数量

示例:hadoop fs -count /aaa/

-setrep
功能:设置 hdfs 中文件的副本数量

示例:hadoop fs -setrep 3 /aaa/jdk.tar.gz

补充:查看 dfs 集群工作状态的命令
hdfs dfsadmin -report

5.HDFS的Java API操作

hdfs 在生产应用中主要是客户端的开发,其核心步骤是从 hdfs 提供的 api 中构造一个 HDFS的访问客户端对象,然后通过该客户端对象操作(增删改查)HDFS 上的文件。

5.1.利用eclipse查看hdfs集群的文件信息

1.下载一个 eclipse 开发工具 eclipse-jee-luna-SR1-win32-x86_64.zip

2.解压到一个文件夹 C:\myProgram\eclipse

3.把 hadoop-eclipse-plugin-2.6.5.jar 放入到 eclipse/plugins 文件夹下

4.双击启动 eclipse

5.将” windows 平台编译 hadoop-2.6.1.zip”解压到 windows 系统下一个文件夹下,文件夹的路径最好不要带中文。我的目录是:C:\myProgram\hadoop-2.6.1

6.打开了 eclipse 之后,点击 windows –> prefrences -> 会出现一个对话框。找到如图所示Hadoop MapReduce 选项:然后把你安装的 hadoop 路径配置上,就是上一步你解压的那个文件夹:C:\myProgram\hadoop-2.6.1

然后保存

7. 然后点击 windows -> show view -> other 在列表中找到图中这个东西:

然后双击

8. 会出现这么一个显示框,如下图

9.咱们点击红框中这个东西,会出现相应的这么一个对话框,修改相应的信息

10.填完以上信息之后,点击 finish 会出现:

11.最重要的时候在左上角的这个地方会出现:

至此,我们便完成了,利用 hadoop 的 eclipse 插件链接 hdfs 集群实现查看 hdfs 集群文件的功能,大功告成。!!!!恭喜各位

5.2.搭建开发环境

可以有两种方式:

第一种:建立普通 java 工程,加入操作 hdfs 的 jar 包

1.按键 ctrl + N 搜索 java project 建立普通 java 工程,如图所示:

2.输入项目名字,点击确定,生成一个普通 java 工程

注意:建议大家使用jdk的版本和linux服务上的jdk版本一致。在这儿我选择的都是jdk1.7的大版本

3.加入依赖 jar,有两种方式

A.先在项目中建立一个文件夹 lib,然后添加 hadoop 安装包下 share/hadoop/common和 share/hadoop/hdfs 下的 jar 和它所依赖的 lib 目录下的 jar 包,然后把加入的所有的 jar 包都 add classpath

B.添加自己的依赖库,具体操作请按图所示操作进行:

第一步,在项目名字上鼠标右键选择 Build Path ,然后点击右边出现的 Configure Build Path,出现:

都添加好了,点击 OK ,再点击 Finish,再点击 OK,项目会变成这样:

到此表示,我们利用 HDFS 的 api 编写业务代码所依赖的 jar 包都添加完成,接下来便可以愉快的玩耍代码了。

5.3.FileSystem实例获取讲解(重点)

在 java 中操作 hdfs,首先要获得一个客户端实例:

  • Configuration conf = new Configuration()
  • FileSystem fs = FileSystem.get(conf)

而我们的操作目标是 HDFS,所以获取到的 fs 对象应该是 DistributedFileSystem 的实例;

get 方法是从何处判断具体实例化那种客户端类呢?从 conf 中的一个参数 fs.defaultFS 的配置值判断;

如果我们的代码中没有指定 fs.defaultFS,并且工程 classpath 下也没有给定相应的配置,conf中的默认值就来自于 hadoop 的 jar 包中的 core-default.xml,默认值为: file:///,则获取的将不是一个 DistributedFileSystem 的实例,而是一个本地文件系统的客户端对象。

5.4.DistributedFileSystem实例所具备的方法介绍

5.5.HDFS常用Java API代码演示

1.建立文件夹

2.上传文件

3.下载文件

4.删除文件或文件夹

5.重命名文件或者文件夹

6.查看目录信息,只显示该文件夹下的文件信息

7.查看文件及文件夹信息

5.6.HDFS流式数据访问

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.junit.Before;
import org.junit.Test;

/**
* 相对那些封装好的方法而言的更底层一些的操作方式 上层那些 mapreduce spark 等运算
框架,去 hdfs 中获取数据的时候,就是调的这种底层的 api
*/
public class StreamAccess {
	FileSystem fs = null;
	@Before
	public void init() throws Exception {
		Configuration conf = new Configuration();
		System.setProperty("HADOOP_USER_NAME", "root");
		conf.set("fs.defaultFS", "hdfs:// hadoop01:9000");
		fs = FileSystem.get(conf);
		// fs = FileSystem.get(new URI("hdfs://hadoop01:9000"), conf, "hadoop");
	}
	@Test
	public void testDownLoadFileToLocal() throws IllegalArgumentException,IOException {
		// 先获取一个文件的输入流----针对 hdfs 上的
		FSDataInputStream in = fs.open(new Path("/jdk-7u65-linux-i586.tar.gz"));
		// 再构造一个文件的输出流----针对本地的
		FileOutputStream out = new FileOutputStream(new File("c:/jdk.tar.gz"));
		// 再将输入流中数据传输到输出流
		IOUtils.copyBytes(in, out, 4096);
	}
	@Test
	public void testUploadByStream() throws Exception {
		// hdfs 文件的输出流
		FSDataOutputStream fsout = fs.create(new Path("/aaa.txt"));
		// 本地文件的输入流
		FileInputStream fsin = new FileInputStream("c:/111.txt");
		IOUtils.copyBytes(fsin, fsout, 4096);
	}
	/**
	* hdfs 支持随机定位进行文件读取,而且可以方便地读取指定长度 用于上层分布式运
	算框架并发处理数据
	*/
	@Test
	public void testRandomAccess() throws IllegalArgumentException, IOException {
		// 先获取一个文件的输入流----针对 hdfs 上的
		FSDataInputStream in = fs.open(new Path("/iloveyou.txt"));
		// 可以将流的起始偏移量进行自定义
		in.seek(22);
		// 再构造一个文件的输出流----针对本地的
		FileOutputStream out = new FileOutputStream(new File("d:/iloveyou.line.2.txt"));
		IOUtils.copyBytes(in, out, 19L, true);
	}
}

5.7.经典案例

在 mapreduce 、spark 等运算框架中,有一个核心思想就是将运算移往数据,或者说,就是要在并发计算中尽可能让运算本地化,这就需要获取数据所在位置的信息并进行相应范围读取。以下模拟实现:获取一个文件的所有 block 位置信息,然后读取指定 block 中的内容。

@Test
public void testCat() throws IllegalArgumentException, IOException {
	FSDataInputStream in = fs.open(new Path("/weblog/input/access.log.10"));
	// 拿到文件信息
	FileStatus[] listStatus = fs.listStatus(new Path("/weblog/input/access.log.10"));
	// 获取这个文件的所有 block 的信息
	BlockLocation[] fileBlockLocations = fs.getFileBlockLocations(
	listStatus[0], 0L, listStatus[0].getLen());
	// 第一个 block 的长度
	long length = fileBlockLocations[0].getLength();
	// 第一个 block 的起始偏移量
	long offset = fileBlockLocations[0].getOffset();
	System.out.println(length);
	System.out.println(offset);
	// 获取第一个 block 写入输出流
	// IOUtils.copyBytes(in, System.out, (int)length);
	byte[] b = new byte[4096];
	FileOutputStream os = new FileOutputStream(new File("d:/block0"));
	while (in.read(offset, b, 0, 4096) != -1) {
		os.write(b);
		offset += 4096;
		if (offset > length)
		return;
	}
	os.flush();
	os.close();
	in.close();
}

6.HDFS核心设计

6.1.HDFS心跳机制(heartbeat)

1.Hadoop 是 Master/Slave 结构,Master 中有 NameNode 和 ResourceManager,Slave 中有Datanode 和 NodeManager。

2. Master 启动的时候会启动一个 IPC(Inter-Process Comunication,进程间通信)server 服务,等待 slave 的链接。

3.Slave 启动时,会主动链接 master 的 ipc server 服务,并且每隔 3 秒链接一次 master,这个间隔时间是可以调整的,参数为 dfs.heartbeat.interval,这个每隔一段时间去连接一次的机制,我们形象的称为心跳。Slave 通过心跳汇报自己的信息给 master,master 也通过心跳给 slave 下达命令。

4. NameNode 通过心跳得知 Datanode 的状态,ResourceManager 通过心跳得知 NodeManager 的状态。

5. 如果 master 长时间都没有收到 slave 的心跳,就认为该 slave 挂掉了。

Namenode 感知到 Datanode 掉线死亡的时长计算:

HDFS 默认的超时时间为 10 分钟+30 秒。这里暂且定义超时时间为 timeout,计算公式为:
timeout = 2 * heartbeat.recheck.interval + 10 * dfs.heartbeat.interval

而默认的 heartbeat.recheck.interval 大小为 5 分钟,dfs.heartbeat.interval 默认的大小为 3 秒。需要注意的是 hdfs-site.xml 配置文件中的 heartbeat.recheck.interval 的单位为毫秒,dfs.heartbeat.interval 的单位为秒。所以,举个例子,如果heartbeat.recheck.interval 设置为 5000(毫秒),dfs.heartbeat.interval设置为 3(秒,默认),则总的超时时间为 40 秒。

<property>
 <name>heartbeat.recheck.interval</name>
 <value>5000</value>
</property>
<property>
 <name>dfs.heartbeat.interval</name>
 <value>3</value>
</property>

6.2.HDFS安全模式

问题引出:集群启动后,可以查看目录,但是上传文件时报错,打开web页面可看到namenode正处于 safemode 状态,怎么处理?

解释:safemode 是 namenode 的一种状态(active/standby/safemode 安全模式)

namenode 进入安全模式的原理

1.namenode 发现集群中的 block 丢失率达到一定比例时(0.1%),namenode 就会进入安全模式,在安全模式下,客户端不能对任何数据进行操作,只能查看元数据信息(比如 ls/mkdir),这个丢失率是可以手动配置的,默认是 dfs.safemode.threshold.pct=0.999f

2.如何退出安全模式?

  • 找到问题所在,进行修复(比如修复宕机的 datanode)
  • 或者可以手动强行退出安全模式(但是并没有真正解决问题)

在 hdfs 集群正常冷启动时,namenode 也会在 safemode 状态下维持相当长的一段时间,此时你不需要去理会,等待它自动退出安全模式即可。

正常启动的时候进入安全的原理:

namenode 的内存元数据中,包含文件路径、副本数、blockid,及每一个 block 所在datanode 的信息,而 fsimage 中,不包含 block 所在的 datanode 信息,那么,当 namenode冷启动时,此时内存中的元数据只能从 fsimage 中加载而来,从而就没有 block 所在的datanode 信息——>就会导致 namenode 认为所有的 block 都已经丢失——>进入安全模式——>datanode 启动后,会定期向 namenode 汇报自身所持有的 blockid 信息,——>随着datanode 陆续启动,从而陆续汇报 block 信息,namenode 就会将内存元数据中的 block 所在 datanode 信息补全更新——>找到了所有 block 的位置,从而自动退出安全模式。

安全模式常用操作命令

hdfs dfsadmin -safemode leave //强制 NameNode 退出安全模式

hdfs dfsadmin -safemode enter //进入安全模式

hdfs dfsadmin -safemode get //查看安全模式状态

hdfs dfsadmin -safemode wait //等待,一直到安全模式结束

如果你使用的版本是 2.X 之前的版本,那么这个 hdfs 命令可以替换成 hadoop,它们都在 bin目录下。

6.3.HDFS副本存放策略

1.作用

数据分块存储和副本的存放,是保证可靠性和高性能的关键。

2.方法

将每个文件的数据进行分块存储,每一个数据块又保存有多个副本,这些数据块副本分布在不同的机器节点上。

3.存放说明

在多数情况下,HDFS 默认的副本系数是 3。Hadoop 默认对 3 个副本的存放策略如下图,其中 Block1,Block2,Block3 分别表示 Block的三个副本:

第一个 block 副本放在和 client 所在的 node 里(如果 client 不在集群范围内,则这第一个 node是随机选取的,系统会尝试不选择哪些太满或者太忙的 node)。

第二个副本放置在与第一个节点不同的机架中的 node 中(近乎随机选择,系统会尝试不选择哪些太满或者太忙的 node)。

第三个副本和第二个在同一个机架,随机放在不同的 node 中。

4.修改副本数

第一种方式:修改集群文件 hdfs-site.xml

<property>
      <name>dfs.replication</name>
      <value>1</value>
</property>

第二种方式:命令设置 bin/hadoop fs -setrep -R 1 /

6.4.负载均衡

机器与机器之间磁盘利用率不平衡是 HDFS 集群非常容易出现的情况,尤其是在 DataNode 节点出现故障或在现有的集群上增添新的 DataNode 的时候。

分析数据块分布和重新均衡 DataNode 上的数据分布的工具:

命令:

sbin/start-balancer.sh

sbin/start-balancer.sh -threshold 5

自动进行均衡非常慢,一天能移动的数据量在 10G-10T 的级别,很难满足超大集群的需求。

原因:HDFS 集群默认不允许 balance 操作占用很大的网络带宽,这个带宽是可以调整的

hdfs dfsadmin -setBalanacerBandwidth newbandwidth

hdfs dfsadmin -setBalanacerBandwidth 10485760

该数值的单位是字节,上面的配置是 10M/s,默认是 1M/s

另外,也可以在 hdfs-site.xml 配置文件中进行设置:

<property>
<name>dfs.balance.bandwidthPerSec</name>
<value>10485760</value>
<description> Specifies the maximum bandwidth that each datanode can utilize for the
balancing purpose in term of the number of bytes per second. </description>
</property>

sbin/start-balancer.sh -t 10%     ##机器容量最高的那个值 和 最低的那个值得差距 不能超过 10%

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值