IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> HDFS(学习笔记) -> 正文阅读

[大数据]HDFS(学习笔记)

HDFS

Client:就是客户端

  • 文件切分。文件上传HDFS的时候,Client将文件切分成一个一个的Block,然后进行存储
  • 与NameNode交互,获取文件的位置信息
  • 与DataNode交互,读取或者写入数据
  • Client提供一些命令来管理和访问HDFS,比如启动或者关闭HDFS

NameNode:就是master,它是一个主管,管理者

  • 管理HDFS的名称空间
  • 管理数据库(Block)映射信息
  • 配置副本策略
  • 处理客户端读写请求

DataNode:就是Slave。NameNode下达命令,DataNode执行实际的操作

  • 存储的实际的数据快
  • 执行数据块的读/写操作

Secondary NameNode:并非NameNode的热备。当NameNode挂掉的时候,它并不能马上替换NameNode并提供服务

  • 辅助NameNode,分担其工作量
  • 定期合并fsimage和fsedits,并推送给NameNode。

NameNode作用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c3ChKmHU-1629014721314)(C:\Users\WANG\AppData\Roaming\Typora\typora-user-images\image-20210814111533003.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-48H0ctxf-1629014721316)(C:\Users\WANG\AppData\Roaming\Typora\typora-user-images\image-20210814111540214.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vkoicyJf-1629014721318)(C:\Users\WANG\AppData\Roaming\Typora\typora-user-images\image-20210814111547118.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gPDqjhHn-1629014721319)(C:\Users\WANG\AppData\Roaming\Typora\typora-user-images\image-20210814111559701.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Occb5SyE-1629014721320)(C:\Users\WANG\AppData\Roaming\Typora\typora-user-images\image-20210814111607742.png)]

DataNode的作用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JonqLVW9-1629014721321)(C:\Users\WANG\AppData\Roaming\Typora\typora-user-images\image-20210814112355801.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-knRrX8X8-1629014721321)(C:\Users\WANG\AppData\Roaming\Typora\typora-user-images\image-20210814112404159.png)]

HDFS的副本机制和机架感知

HDFS文件副本机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M2e7N8Qz-1629014721322)(C:\Users\WANG\AppData\Roaming\Typora\typora-user-images\image-20210814112607972.png)]

hdfs的命令行使用

ls

格式: hdfs dfs -ls URI
作用:类似于Linux的ls命令,显示文件列表
hdfs dfs -ls /

lsr

格式:hdfs dfs -lsr URI
作用:在整个目录下递归执行ls,与UNIX中的ls-R类似
hdfs dfs -lsr /

mkdir

格式:hdfs dfs [-p] -mkdir <paths>
作用:以<paths>中的URI作为参数,创建目录。使用-p参数可以队规创建目录

put

格式:hdfs dfs -put <localsrc > ... <dst>
作用:将单个源文件src或者多个源文件srcs从本地文件系统拷贝到目标文件系统中(<dst>对应的路径)。也可以从标准输入中读取输入,写入目标文件系统中
hdfs dfs -put /root/a.txt /dir1

moveFormLocal

格式:hdfs dfs -moveFromLocal <localsrc> <dst>
作用:和put命令类似,但是源文件localsrc拷贝之后自身被删除
hdfs dfs -moveFromLocal /root/install.log /

moveToLocal

未实现

get

格式 hdfs dfs -get [-ignorecrc ] [-crc] <src> <localdst>
作用:将文件拷贝到本地文件系统。CRC校验失败的文件通过-ignoecrc选项拷贝。文件和CRC校验和可以通过-CRC选项拷贝
hdfs dfs -get /install.log /export/servers

mv

格式:hdfs dfs -mv URI <dest>
作用:将hdfs上的文件从原路径移动到目标路径(移动之后文件删除),该命令不能跨文件系统
hdfs -dfs -mv /dir1/a.txt /dir2

rm

格式:hdfs dfs -rm[-r] [-skipTrash] URI [URI...]
作用:删除参数指定的文件,参数可以有多个。此命令值删除文件和非空目录
如果指定-skipTrash选项,那么在回收站可用的情况下,该选项将跳过回收站儿直接删除文件;
否则,在回收站可用时,在HDFS Shell中执行此命令,会将文件暂时放到回收站中
hdfs dfs -rm -r /dir1

cp

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fWvVz3Zq-1629014721323)(C:\Users\WANG\AppData\Roaming\Typora\typora-user-images\image-20210814122518856.png)]

cat

hdfs dfs -cat URI [uri ...]
作用:将参数所指示的文件内容输出到stdout
hdfs dfs -cat /install.log

chmod

格式:hdfs dfs -chmod [-R] URI[URI ...]
作用:改变文件权限,如果使用-R选项,则对整个目录有效递归执行,使用这一命令的用户必须是文件的所属用户,或者超级用户
hdfs dfs -chmod -R 777 /install.log

chown

格式:hdfs dfs -chmod [-R] URI[URI ..]
作用:改变文件的所属用户和用户组,如果使用-R选项,则对整个目录有效递归执行,使用这一命令的用户必须是文件的所属用户,或者超级用户
hdfs dfs -chown -R hadoop:hadoop /install.log

appendToFile

格式:hdfs dfs -appendToFile <localsrc> ... <dst>
作用:追加一个或者多个文件到hdfs指定文件中,也可以从命令行读取输入
hdfs dfs -appendToFile a.xml b.xml /big.xml

hdfs的高级使用命令

HDFS文件 限额配置

hdfs dfs -count -q -h /user/root/dir1	#查看配额信息

数量限额

hdfs dfs -mkdir -p user/root/dir	#创建hdfs文件夹
hdfs dfsadmin -setQuota 2 dir	#给该文件夹下面设置最多上传两个文件,发现只能上传一个文件
hdfs dfsadmin -clrQuota /user/root/dir	#清楚文件数量限制

空间大小限额

在设置空间配额时,设置的空间至少是block_size*3大小

hdfs dfsadmin -setSpaceQuota 4k /user/root/dir	#限制空间大小4kb
hdfs dfs -put /root/a.txt /user/root/dir

生成任意大小文件的命令

dd if=/dev/zero of=1.txt bs=1M count=2	#生成2M的文件

清楚空间配额限制

hdfs dfsadmin -setSpaceQuota dirq

hdfs的安全模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NbN1YHpQ-1629014721324)(C:\Users\WANG\AppData\Roaming\Typora\typora-user-images\image-20210814174354575.png)]

安全模式操作命令

hdfs dfsadmin -safemode get #查看安全模式状态
hdfs dfsadmin -safemode enter #进入安全模式
hdfs dfsadmin -safemode leave	#离开安全模式

HDFS基准测试

测试写入速度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x9T2oTvp-1629014721325)(C:\Users\WANG\AppData\Roaming\Typora\typora-user-images\image-20210814174905487.png)]

完成之后查看写入速度结果

hdfs dfs -text /benchmarks/TestDFSIO/io_write/part-0000

测试读取速度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IE8bPbFT-1629014721325)(C:\Users\WANG\AppData\Roaming\Typora\typora-user-images\image-20210814175737924.png)]

清除测试数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ugWw4fV-1629014721326)(C:\Users\WANG\AppData\Roaming\Typora\typora-user-images\image-20210814204225595.png)]

HDFS文件写入过程

  1. Client发起文件上传请求,通过RPC与NameNode建立通讯,NameNode检查目标文件是否存在,父目录是否存在,返回是否可以上传
  2. Client请求第一个block该传输到哪些DataNode服务器上
  3. NameNode根据配置文件中指定的被封数量及机架感知原理进行文件分配,返回可用的DataNode地址如:A,B,C
    Hadoop在设计时3考虑到数据的安全与高效,数据文件默认在HDFS上存放三份,存储测量为本地一份,同机机架内其他某一节点上一份,不同机架的某一节点上一份。
  4. Client请求3台DataNode中的一台A上传数据(本质上是一个RPC调用,建立pipeline),A收到请求会继续调用B,然后B调用C,将整个pipeline建立完成,后逐级返回client
  5. Client开始往A上传第一个block(先从磁盘读取数据放到一个本地内存缓存),以packet为单位(默认64K),A收到一个packet就会传给B,B传给C,A每传爱一个packet会放入一个应答队列等待应答
  6. 数据被分割成一个个packet数据包在pipeline上依次传输,在pipeline反方向上,逐个发送ack(命令正确应答),最终由pipeline中第一个DataNode节点A将pipelineack发送给Client
  7. 当一个block传输完成之后,Client再吃请求NameNode上传第二个block到服务1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pMBOpW5h-1629014721326)(C:\Users\WANG\AppData\Roaming\Typora\typora-user-images\image-20210814205646759.png)]

HDFS文件读取过程

  1. Client向NameNode发起RPC请求,来确定请求文件block所在的位置
  2. NameNode会视情况返回文件的部分或者全部block列表,对于每个block,NameNode都会返回含有该block副本的DataNode地址;这些返回的DN地址,会按照集群拓扑结构得出DataNode与客户端的距离,然后进行排序,排序两个规则:网络拓扑结构中距离Client近的排靠前;心跳机制中超时汇报的DN状态为STALE,这样的排靠后
  3. Client选取排序靠前的DataNode来读取block,然后客户端本身就是DataNode,那么将从本地直接获取数据(短路读取特性)
  4. 底层上本质是建立Socket Stream(FSDataInputStream),重复的的调用父类DataInputStream的read方法,直到这个块上的数据读取完毕;
  5. 当读完列表的block后,若文件读取还没有结束,客户端会继续想NameNode获取下一批block列表;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IOiRiZ3H-1629014721327)(C:\Users\WANG\AppData\Roaming\Typora\typora-user-images\image-20210814212944070.png)]

HDFS的元数据辅助管理

当Hadoop的集群当中,NameNode的所有元数据信息都保存到FsImage与Eidts文件当中,这两个文件啊就记录了所有的是数据的元数据信息,元数据信息的保存目录配置在了hdfs-site.xml当中

<property>
	<name>dfs.namenode.name.dir</name>
	<value>
		file:///export/servers/hadoop2.7.5/hadoopDatas/namenodeDatas,file:///export/servers/hadoop-2.7.5/hadoopDatas/namenodeDatas2
	</value>
</property>
<property>
	<name>dfs.namenode.edits.dir</name>
	<value>file:///export/servers/hadoop-2.7.5/hadoopoDatas/nn/edits</value>
</property>

FsImage和Edits详解

edits

  • edits存放了客户端最近一段时间的操作日志
  • 客户端对HDFS进行写文件时会首先记录在edits文件中
  • edits修改时元数据也会更新

fsimage

  • NameNode中关于元数据的镜像,一般称为检查点,fsimage存放了一份比较完整的元数据信息
  • 因为fsimage是NameNode的完整的镜像,如果每次加载到内存生成树拓扑结构,这是非常耗内存和CPU,所以一般开始时对NameNode的操作都放在edits中
  • fsimage内容包含了NameNode管理下的所有DataNode文件及文件block及block所在的DataNode的元数据信息
  • 随着edits内容增大,就需要一定时间点和fsimage合并

fsimage中的文件信息查看

使用命令 hdfs oiv

cd /export/servers/hadoop2.7.5/hadoopDatas/namenodeDatas
hdfs oiv -i fsimage_0000000000000000864 -p XML -o hello.xml

edits中的文件信息查看

使用命令 hdfs oev

cd /export/servers/hadoop2.7.5/hadoopDatas/namenodeDatas
hdfs oev -i edits_0000000000000000865-0000000000000000866 -p XML -o myedit.xml

SecondaryNameNode如何辅助管理fsimage与edits文件?

  • SecondaryNameNode定期合并fsimage和edits,把edits控制在一个范围内

  • 配置SecondaryNameNode

    • SecondayNameNode在conf/masters中指定

    • 在masters指定的机器上,修改hdfs-site.xml

      <property>
      	<name>dfs.http.address</name>
      	<value>host:50070</value>
      </property>
      
    • 修改core-site.xml,这一步不做配置保持默认也可以

      //多久记录一次 HDFS镜像,默认1小时
      <property>
      	<name>fs.checkpoint.period</name>
      	<value>3600</value>
      </property>
      //一次记录多大,默认64M
      <property>
      	<name>fs.checkpoint</name>
      	<value>67108864</value>
      </property>
      
  1. SecondaryNameNode通知NameNode切换editlog
  2. SecondaryNameNode从NameNode中获得fsimage和editlog(通过http方式)
  3. SecondaryNameNode将fsimage载入内存,然后开始合并editlog,合并之后称为新的fsimage
  4. SecondaryNameNode将新的fsimage发回给NameNode
  5. NameNode用新的fsimage替换旧的fsimage

特点

完成合并的SecondaryNameNode,会请求NameNode停止使用edits,暂时将新鞋操作放入一个新的文件夹中edits_new

Hadoop核心-HDFS

HDFS的API操作

配置Window下的Hadoop环境

在windows系统需要配置hadoop运行环境,否则直接运行代码会出现以下问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BXzJTdOh-1629014721328)(C:\Users\WANG\AppData\Roaming\Typora\typora-user-images\image-20210814233506617.png)]

步骤:

第一步:将hadoop2.7.5文件夹拷贝到一个没有中文没有空格的路径下面

第二步:在windows上面配置hadoop的环境变量:HADOOP_HOME,并将%HADOOP_HOME%\bin添加到path中

第三步:把hadoop2.7.5文件夹中bin目录下的hadoop.dll文件放到系统盘:C:\Window\System32目录

第四步:关闭windows重启

导入Maven依赖

<dependencies>
	<dependency>
		<groupId>org.apache.hadoop</groupId>
		<artifactId>hadoop-common</artifactId>
		<version>2.7.5</version>
	</dependency>
	<dependency>
		<groupId>org.apache.hadoop</groupId>
		<artifactId>hadoop-client</artifactId>
		<version>2.7.5</version>
	</dependency>
	<dependency>
		<groupId>org.apache.hadoop</groupId>
		<artifactId>hadoop-hdfs</artifactId>
		<version>2.7.5</version>
	</dependency>
	<dependency>
		<groupId>org.apache.hadoop</groupId>
		<artifactId>hadoop-mapreduce-client-core</artifactId>
		<version>2.7.5</version>
	</dependency>
</dependencies>

使用url方式访问数据(了解)

@Test
public void demo1() throws Exception{
	//第一步:注册hdfs的url
	URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
	//获取文件输入流
	InputStream inputStream = new URL("hdfs://node01:8020/a.txt").openStream();
	//获取文件输出流
	FileOutputStream outputStream = new FileOutputStream(new File("D:\\hello.txt"));
	//实现文件的拷贝
	IOUtils.copy(inputStream,outputStream);
	//关闭流
	IOUtils.closeQuietly(inputStream);
	IOUtils.closeQuietly(outputStream);
}

使用文件系统方式访问数据(掌握)

涉及的主要类

在Java中操作HDFS,主要涉及一下class:

Configuration

  • 该类的对象封装了客户端或者服务器的配置

FileSystem

该类的对象是一个文件系统对象,可以用该对象的一些方法来对文件进行操作,通过FileSystem的静态方法get获得该对象

FileSystem fs = FileSystem.get(conf)
get方法从conf中的一个参数fs.defaultFS的配置值判断具体是什么类
如果我们的代码中没有指定fs.defaultFS,并且工程ClassPath下也没有给定相应的配置,conf中的默认值就来自于Hadoop的Jar包中的core-default.xml
默认值为file:///,则获取的不是一个DistributedFileSystem的实例,而是一个本地文件系统的客户端对象

获取FileSystem的几种方式

第一种方式

@Test
public void getFileSystem1() throws IOException{
	//创建Configuration对象
	Configuration configuration = new Configuration();
	//指定我们使用的文件系统类型
	configuration.set("fs.defaultFS","hdfs://node01:8020/");
	//获取指定的文件系统
	FileSystem fileSystem = FileSystem.get(configuration);
	System.out.println(fileSystem.toString());
}

第二种方式

@Test
public void getFileSystem2() throws Exception{
	FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
	System.out.println("fileSystem:"+fileSystem);
}

第三种方式

@Test
public void getFileSystem3() throws Exception{
	Configuration configuration = new Configuration();
	configuratioin.set("fs.defaultFS","hdfs://node01:8020");
	FileSystem fileSystem = FileSystem.newInstance(configurartion);
	System.out.println(fileSystem.toString());
}

第四种方式

@Test
public void getFileSystem4() throws Exception{
	FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://node01:8020"),new Configuration());
	System.out.println(fileSystem.toString());
}

遍历HDFS中所有文件

使用API遍历

@Test
public void listMyFiles() throws Exception{
	//获取fileSystem类
	FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"),new Configuration());
	//获取RemoteIterator得到所有的文件或者文件夹,第一个参数指定遍历的路径,第二个参数表示是否要递归遍历
	RemoteIterator<LocatedFileStatus> locatedFileStatusRemoteIterator = fileSystem.listFiles(new Path("/"),true);
	while(locatedFileStatusRemoteIterator.hasNext()){
	LocatedFileStatus next = locatedFileStatusRemoteIterator.next();
	System.out.println(next.getPath().toString());
	}
	fileSystem.close();
}

HDFS上创建文件夹

@Test
public void mkdirs() throws Exception{
	FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
	boolean mkdirs = fileSystem.mkdirs(new Path("/hello/mydir/test"));
	fileSystem.close();
}

下载文件

@Test
public void getFileToLocal() throws Exception{
	FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"),new Configuration());
	FSDataInputStream inputStream = fileSystem.open(new Path("/timer.txt"));
	FileOutputStream outputStream = new FileOutStream(new File("e:\\timer.txt"));
	IOUtils.copy(inputStream,outputStream);
	IOUtils.closeQuietly(inputStream);
	IOUtils.closeQuietly(outputStream);
	fileSystem.close();
}

方式2:
@Test
public void downloadFile2() throws Exception{
	//1、获取FileSystem
	FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"),new Configuration());
	//2、调用方法,实现文件的下载
	fileSystem.copyToLocalFile(new Path("/a.txt"),new Path("D://a.txt"));
	//3、关闭FileSystem
	fileSystem.close();
}

文件上传

@Test
public void putData() throws Exception{
	FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"),new Configuration());
	fileSystem.copyFromLocalFile(new Path("file:///c:\\install.log"),new Path("/hello/mydir/test"));
	fileSystem.close();
}

hdfs访问权限控制

1、停止hdfs集群,在node01机器上执行以下命令

cd /export/servers/hadoop-2.7.5
sbin/stop-dfs.sh

2、修改node01机器上的hdfs-site.xml当中的配置文件

cd /export/servers/hadoop-2.7.5/etc/hadoop
vim hdfs-site.xml
<property>
	<name>dfs.permissions.enabled</name>
	<value>true</value>
</property>

小文件合并

由于Hadoop擅长存储大文件,因为大文件的元数据信息比较少,如果Hadoop集群当中有大量的小文件,那么每个小文件都需要维护一份元数据信息,会大大的增加集群管理元数据的内存压力,所以在实际工作当中,如果有必要一定要将小文件合并成大文件进行一起处理

在我们的HDFS的Shell命令模式下,可以通过命令行将很多hdfs文件合并成一个大文件下载到本地

cd /export/servers
hdfs dfs -getmerge /config/*.xm ./hello.xml

既然可以在下载的时候将这些小文件合并成一个大文件一起下载,那么肯定就可在上传的时候将小文件合并到一个大文件里面去

@Test
public void mergeFile() throws Exception{
	//获取分布式文件系统
	FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.52.250:8020").new Configuration(),"root");
	FSDataOutputStream outputStream = fileSystem.create(new Path("/bigfile.txt"));
	//获取本地文件系统
	LocalFileSystem local = FileSystem.getLocal(new Configuration());
	//通过本地文件系统获取文件列表,为一个集合
	FileStatus[] fileStatuses = local.listStatus(new Path("file:///E:\\input"));
	for(FileStatus fileStatus: fileStatuses){
		FSDataInputStream inputStream = local.open(fileStatus.getPath());
		IOUtils.copy(inputStream,outputStream);
		IOUtils.closeQuietly(inputStream);
	}
	IOUtils.closeQuietly(outputStream);
	local.close();
	fileSystem.close();
}

tem = FileSystem.get(new URI(“hdfs://192.168.52.250:8020”).new Configuration(),“root”);
FSDataOutputStream outputStream = fileSystem.create(new Path("/bigfile.txt"));
//获取本地文件系统
LocalFileSystem local = FileSystem.getLocal(new Configuration());
//通过本地文件系统获取文件列表,为一个集合
FileStatus[] fileStatuses = local.listStatus(new Path(“file:///E:\input”));
for(FileStatus fileStatus: fileStatuses){
FSDataInputStream inputStream = local.open(fileStatus.getPath());
IOUtils.copy(inputStream,outputStream);
IOUtils.closeQuietly(inputStream);
}
IOUtils.closeQuietly(outputStream);
local.close();
fileSystem.close();
}


  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2021-08-16 11:48:52  更:2021-08-16 11:51:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 12:49:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码