自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+

loser与你

一个小菜鸡

  • 博客(74)
  • 收藏
  • 关注

原创 YARN工作流程

YARN工作流程1.基本架构 YARN总体上仍然是Master/Slave结构,ResourceManager为Master,NodeManager为Slave。ResourceManager负责对每个NodeManager上的资源进行统一管理和调度。当用户提交一个应用程序时,需要提供一个用以跟踪和管理这个程序的ApplicationMaster,它负责向ResourceManager申请资源,并要求NodeManager启动可以用一定资源的任务。 YARN主要由ResourceManager、Nod

2021-10-09 21:21:49 5723

原创 YARN与MapReduce1的区别

YARN与MapReduce1的区别1.组成对比 MapReduce1中,两类守护进程控制着作业执行过程:一个JobTracker和一个或多个TaskTracker。JobTracker通过调度TaskTracker上运行的任务来协调所有运行在系统上的作业。TaskTracker在运行任务的同时将运行进度报告发送给JobTracker,JobTracker由此记录每项作业任务的整体进度情况。JobTracker同时负责作业调度和任务进度监控。 Hadoop对JobTracker赋予的功能过多造成负载过

2021-10-03 23:19:48 1681

原创 HDFS客户端写流程

HDFS客户端写流程1.创建文件 首先调用DistributedFileSystem.creat()创建一个空的HDFS文件。 然后这个方法在底层会通过调用ClientProtocol.create()方法通知Namenode执行对应的操作,Namenode会首先在文件系统目录树中的指定路径下添加一个新的文件,然后将创建新文件的操作记录到editlog中。 调用的DistributedFileSystem.creat()方法会返回一个HdfsDataOutputStream对象,这个对象在底层包装了

2021-10-03 18:35:42 273

原创 HDFS客户端读流程

HDFS客户端读流程1.打开HDFS文件 用户读取一个HDFS文件时,首先会调用open()方法打开这个文件,并获取文件对应的FSDataInputStream输入流,然后在这个FSDataInputStream对象上调用read()方法读取数据。2.从Namenode获取Datanode地址 在DFSInputStream构造方法中,首先会初始化DFSInputStream的属性,然后会调用openInfo()方法从Namenode获取文件对应的数据块位置信息,并将信息保存至其相应字段中。 其首

2021-10-03 13:22:56 974

原创 单调栈

单调栈 就是利用一个栈来维持数据的单调性,任意时刻,栈中存放的元素都会是单调的。 单调栈适用于去解决在一串数字中,要获得某个数字其左右两边大小信息的问题,通常是可以将O(n^2)的问题去化为O(n)来解决。 比如,要获得所有数字左边离它最近的小于它的数,和右边离它最近的小于它的数这种问题,就可以使用单调栈。 让栈内保持单调递增,如果遇到小于栈顶元素的数(也就是它加入后会破坏单调性的情况),就出栈,然后记录栈顶元素的信息。对于栈顶元素来说,在栈中它下面压着的数是左边离它最近的小于它的数,而当前让它出栈

2021-09-27 17:24:59 253

原创 计算机网络分层

计算机网络分层五层

2021-09-27 14:58:11 113

原创 类加载器

类加载器 类加载器用于实现“通过一个类的全限定名来获取描述该类的二进制字节流”这个动作,这个动作放置在虚拟机外部,以便应用程序可以自己决定如何获取所需的类。 每一个类加载器,都拥有一个独立的类名称空间。对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性。 显然,要比较两个类是否相等,不仅要看其是否源于同一个Class文件,还要看它们是否是同一个类加载器加载的。而比较两个类相等,通常是由Class对象的equals()方法、isAssignableFrom()方

2021-09-27 10:57:20 115

原创 JVM中的类加载机制

JVM中的类加载机制 JVM的类加载机制就是Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型这一过程。 在Java语言里面,类型的加载、连接和初始化过程都是在程序运行期间完成的。1.类加载的顺序 一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载、验证、准备、解析、初始化、使用和卸载七个阶段,其中验证、准备、解析三个部分统称为连接。类的加载过程则按这个顺序进行。2.类加载的时机

2021-09-27 10:16:03 93

原创 KMP算法

KMP算法 KMP算法就是利用一个next[]数组让子串str2没必要每次匹配失败之后都要去从头开始又和str1的下一个位置匹配,这样一种加速策略能够加速字符串的匹配。而next[]数组中,next[i]表示的是该位置之前的字符串中,前缀子串与后缀子串相同的最大长度。就比如下图,i位置的前缀子串与后缀子串相同的最大长度为2。也就是对于i来说,前缀从0开始往后走,后缀从i-1开始往前走,直到不相遇或者前缀后缀重合为止。 而既然i位置前面这段后缀与从头开始的前缀相等,只是到了i位置之后才匹配不到,而i位置

2021-09-23 16:43:06 91

原创 并查集

并查集 并查集要解决的问题就是并和查,并就是将两个集合合并成一个新的集合,查就是查两个个元素是否在同一个集合中。 因而其提供给用户的接口也应该为两个,一个isSameSet()方法用于查,一个union()方法用于并。 显然,如果是用链表来实现的话,并是一个O(1)的操作,但是查将会是O(n)的操作;而如果用哈希表来实现的话,查是一个O(1)的操作,但是并将会是O(n)的操作。因而,通常情况下使用的是类似链表+哈希表的形式。 原理就是设置头元素来表示一个集合的起始位置,初始情况下,每个元素就是自己集

2021-09-22 16:29:28 106

原创 前缀树

前缀树 前缀树,也就是字典树,用来搞单词统计或者匹配前缀时候很有帮助。树的节点可以用来保存一些辅助信息用来做题,而树枝则用来表示字符。 一般在刷题的时候,会遇到单词匹配或者单词统计之类的问题,此时的字符串会被限制为只是字母。这样的话树就更好来表示,就比如对于一个单词表[“a”, “aa”, “ab”, “ab” “b”, “ed”]可以用前缀树表示成下面这样 可以给节点附加pass和end分别表示有多少个字符串经过了该节点,以该节点上方的字符为结尾的字符串的个数。 代码如下://前缀树publi

2021-09-17 17:06:48 201

原创 Datanode中读取数据块的两种方式

Datanode中读取数据块的两种方式1.普通方式 Datanode读取数据块的普通方式在操作系统层面有四步:(1)Datanode首先将数据块从磁盘存储读入操作系统的内核缓冲区(2)在将数据跨内核推到Datanode进程(3)然后Datanode会再次跨内核将数据推回内核中的套接字缓冲区(4)最后将数据写入网卡缓冲区 显然,上面的步骤(2)和步骤(3)进行了两次多余的数据拷贝操作,此时Datanode只是起到缓存数据并将其传回套接字的作用。2.零拷贝方式 Java NIO提供了零拷贝模式

2021-09-13 21:38:10 473

原创 JVM垃圾收集算法

JVM垃圾收集算法1.标记-清除算法 最基础的垃圾收集算法。 算法分为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象。 缺点: (1)执行效率不稳定,当Java堆中包含大量对象且大部分是需要回收的时候,这时必须进行大量标记和清除工作,导致效率很低。 (2)内存空间的碎片化问题,会产生大量不连续的内存碎片。2.标记-复制算法 目前的商用Java虚拟机多采用此算法。 解决了标记-清除算法的两个缺点。 将可用的内存按容量划分为大小相等的两块,每次只使

2021-09-13 18:08:10 88

原创 Datanode升级机制

Datanode升级机制 Datanode升级机制是通过磁盘文件目录的配合来进行的,其升级可以分为三个步骤。1.升级操作 升级操作就是将Datanode从低版本升级到高版本,主要涉及这三个目录: (1)current目录:保存当前版本数据。 (2)previous.tmp目录:在升级过程中,保存当前版本数据的目录。 (3)previous目录:升级后,保存上一版本数据的目录。 Datanode升级时,会将当前版本数据所在的current目录改名为previous.tmp,然后为新版本数据重建c

2021-09-09 20:42:51 201

原创 HDFS的联邦机制

HDFS的联邦机制1.HDFS1.X架构 HDFS1.X架构从逻辑上可以分为两层:命名空间管理层和数据块存储管理层。其中数据块存储管理层又分为数据块管理和存储管理两部分。 Namenode实现了命名空间管理层以及数据块存储管理层中的数据块管理功能,而Datanode则实现了数据块存储管理层中的存储管理部分。 HDFS1.X架构使用一个Namenode来管理文件系统的命名空间以及数据块信息,使得HDFS的实现非常简单,但是单一的Namenode会导致以下缺点: (1)由于Namenode在内存中保存

2021-09-09 20:18:23 1890

原创 Namenode的启动与停止

Namenode的启动与停止 Namenode实体涉及到三个类:NameNode类、NameNodeRpcServer类、FSNamesystem类。NameNode类负责管理Namenode配置、RPC接口以及HTTP接等;NameNodeRpcServer类用于接收和处理所有的RPC请求;FSNamesystem类负责实现Namenode的所有逻辑。1.启动 Namenode的启动操作是在Namenode类中执行的,具体由其main()方法负责。 启动流程如下: (1)NameNode.mai

2021-09-09 14:23:25 2675

原创 HDFS的高可用HA

HDFS的高可用HA 在Hadoop2.X之前,Namenode是HDFS集群中可能发生单点故障的节点,每个HDFS集群中只有一个Namenode,一旦这个节点不可用,则整个HDFS集群将处于不可用状态。 HDFS的高可用HA方案就是为了解决上述问题而产生的,在HA HDFS集群中会同时运行两个Namenode...

2021-09-09 13:16:26 446

原创 Namenode中的安全模式

Namenode中的安全模式 安全模式是Namenode的一种状态,处于安全模式中的Namenode不接受任何对于命名空间的修改操作,同时也不触发任何复制和删除数据块的操作。 Namenode启动时会自动进入安全模式状态,用户可以通过“dfsadmin -safemode value”命令来操作安全模式,这个命令在底层是由DFSClient调用setSafeMode方法实现的。 Namenode启动时会首先加载fsimage文件并且合并编辑editlog文件,完成这些操作之后Namenode的第一关系

2021-09-03 15:36:14 2243

原创 ConcurrentHashMap实现原理

ConcurrentHashMap实现原理1.HashMap实现原理 HashMap是线程不安全的。HashMap是由数组+链表实现的,当无冲突发生时是在数组中存放,当发生hash冲突时在该位置下面挂链表,链表越长代表冲突越多。在多线程情况下,在put的时候,插入的元素超过了容量就会触发扩容操作,即rehash,此时将该hash表中的元素重新hash到另一个更大容量的hash表,在多线程环境下,存在同时其他元素也在进行put操作,如果hash值相同,可能在同一链表下出现闭环,从而导致出现死循环。2.H

2021-09-03 10:50:46 2134

原创 CAS机制

CAS机制 CAS机制是一种无锁策略,使用比较交换的技术来鉴别线程冲突,一旦检测到冲突产生,就重试当前操作直到没有冲突为止。1.CAS算法过程: CAS(V, E, N)包含三个参数,V表示要更新的变量,E表示预期值,N表示新值。仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,说明已经有其他线程做了更新,当前线程什么也不用做。最后,CAS返回当前V的真实值。 也就是,当前线程要去修改V的时候它看见的V值为E,若在进行修改的时候它还是E说明没有线程修改V,则它可以放心地将V修改为N。若是等

2021-09-03 10:39:19 761

原创 volatile和synchronized有什么区别

volatile和synchronized有什么区别1.工作原理 · volatile本质上是在告诉JVM当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取。 · synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住2.使用级别 · volatile仅能使用在变量级别 · synchronized可以使用在变量、方法和类级别3.可见性和原子性 · volatile仅能实现变量的可见性,不能保证原子性 · synchronized可以保证变量的可

2021-09-03 10:26:11 126

原创 ReenTrantLock和synchronized有什么区别

ReenTrantLock和synchronized有什么区别1.使用区别 Lock是一个接口,而synchronized是关键字,是内置的语言实现。 Synchronized发生异常时,会自动释放线程占用的锁,故不会发生死锁现象。Lock发生异常时,若没有主动释放,极有可能造成死锁,故需要在finally中调用unlock()方法释放锁。 通过Lock可以知道有没有成功获得锁,而synchronized就不可以了. Lock有着显式的操作过程,开发人员必须手动指定何时加锁,何时释放锁,因而重入锁

2021-09-03 10:18:42 1504

原创 Java中的锁

Java中的锁1.悲观锁 总是假设最坏的情况,每次取数据都认为其他线程会修改,所以都会加锁,当其他线程想要访问数据时,都需要阻塞挂起。 使用synchronized来实现。2.乐观锁 总是认为不会产生并发问题,每次取数据时都认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改. 使用CAS机制。3.偏向锁(偏向锁->轻量级锁->重量级锁->自旋锁) 偏向于第一个访问锁的线程,如果在线程运行过程中,同步锁只有一个线程访问,不存

2021-09-03 10:14:35 58

原创 Java线程同步的几种方式

Java线程同步的几种方式1.使用synchronized关键字 它的工作是对同步的代码加锁,使得每一次只能有一个线程进入同步块,从而保证线程间的安全性。 synchronized关键字的用法:(1)同步方法 即有synchronized修饰的方法,由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。代码如:public synchronized void save(){}。 synchronized也可以修饰静

2021-09-02 16:25:13 2106

原创 线程基本操作

线程基本操作1.线程创建添加链接描述2.线程终止 Thread.stop()方法,是一个被标注为废弃的方法,原因是stop()方法过于暴力,强行把执行到一半的线程终止,可能会引起一些数据不一致的问题。stop()方法在结束线程时,会直接终止线程,并立即释放这个线程所持有的锁,而这些锁是用来维持对象一致性的。3.线程中断 线程中断并不会使线程立即退出,而是给线程发送一个通知,告诉目标线程,有人希望你退出了,至于目标线程接到通知后如何处理,则完全由目标线程自行决定。 有三个方法与线程中断有关: 

2021-09-02 16:15:27 175

原创 Java创建线程的几种方式

Java创建线程的几种方式 新建线程只需要使用new关键字创建一个线程对象,并且将它start()起来即可。1.继承Thread类 继承Thread类,重写run()方法即可2.实现Runnable接口(优先使用这种方法) 实现Runnable接口,重写run()方法,然后借助Thread的start()方法开启线程。要注意的是,没有start(),直接调用run()方法,只是作为一个普通的方法调用,并不能新建一个线程。 Runnable接口是一个只有run()方法的单方法接口,Thread类

2021-09-02 16:09:23 207

原创 并发级别有哪些

并发级别有哪些 1.阻塞:一个线程是阻塞的,那么在其他线程释放资源之前,当前线程无法继续执行。当使用synchronized关键字或者重入锁时,得到的就是阻塞的线程。阻塞的控制方式是悲观策略,系统认为两个线程之间很有可能发生不幸的冲突,以保护共享数据为第一优先级。 2.无饥饿:公平锁可以保证无饥饿 3.无障碍:最弱的非阻塞调度,两个线程如果无障碍地执行,那么不会因为临界区的问题导致一方被挂起。无障碍的调度是一种乐观的策略,系统认为多个线程之间很有可能不会发生冲突,大家都应该无障碍地执行,但是一旦检测到

2021-09-02 16:02:57 284

原创 Java线程的状态有哪些

Java线程的状态有哪些 1.NEW(新建状态):线程创建之后 2.RUNNABLE(可运行):可能正在运行,也可能在等待CPU时间片 3.BLOCKED(阻塞):暂停执行,直到获得请求的锁 4.WAITING(无期限等待):等待其他线程显式地唤醒,否则不会被分配CPU时间片 5.TIMED_WAITING(有时限等待):等待到其他线程唤醒,若时间到了还未被其他线程唤醒,则会被系统自动唤醒 6.TERMINATED(终止):可以是线程结束任务之后自己结束,也可以是产生了异常而结束...

2021-09-02 16:01:23 54

原创 happen-before原则是什么

happen-before原则是什么 1.程序顺序原则:一个线程内保证语义的串行性 2.volatile规则:volatile变量的写先于读发生 3.锁规则:解锁必然发生在随后的加锁前 4.传递性:A先于B,B先于C,那么A必然先于C 5.线程的start()方法先于它的每一个动作 6.线程的所有操作先于线程的终结(Thread.join()) 7.线程的中断(interrupt())先于被中断线程的代码 8.对象的构造函数的执行、结束先于finalize()方法...

2021-09-02 15:59:21 94

原创 Java内存模型中的可见性、原子性和有序性

Java内存模型中的可见性、原子性和有序性 1.原子性:指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。(用锁来保证原子性) 2.可见性:指当一个线程修改了某一个共享变量的值时,其他线程是否能够立即知道这个修改。Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值,这种依赖主内存作为传递媒介的方法来实现可见性的。(重点是volatile) 3.有序性:程序在执行时,可能会进行指令重排,重排后的指令与原指令的顺序未必一致。对

2021-09-02 15:57:36 138

原创 Java内存模型

Java内存模型 Java内存模型(Java Memory Model)JMM主要是为了规定线程和内存之间的一些关系。根据JMM的设计,系统存在一个主内存,Java中所有变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存,工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主内存完成。 关于主内存与工作内存之间的具体交互协议,即一个变量如何从主内存拷贝到工作内存,如何从工作内存同步回主内存子类的细节实现,JMM

2021-09-02 15:55:34 70

原创 HDFS集群的Namenode对缓存的管理

HDFS集群的Namenode对缓存的管理 HDFS的2.3.0版本新增了集中式缓存管理功能,允许用户将一些文件和目录保存到HDFS缓存中。HDFS集中式缓存是由分布在Datanode上的堆外内存组成的,并由Namenode统一管理。 添加了集中式缓存功能的HDFS集群的优势: 1.阻止了频繁使用的数据从内存中清除 2.HDFS客户端可以根据数据块的缓存情况调度任务,提高数据块的读性能 3.数据块被Datanode缓存后,客户端就可以使用一个新的更高效的零拷贝机制读取数据块,零拷贝读取数据块的客户

2021-09-02 15:51:24 389

原创 HDFS集群的Namenode对租约的管理

HDFS集群的Namenode对租约的管理 HDFS集群的Namenode对租约的管理租约管理器 HDFS文件是write-once-read-many,并且不支持客户端的并行写操作,因而需要一种机制保证对HDFS文件的互斥操作。HDFS提供了租约机制来实现这个功能,租约是Namenode给予租约持有者(通常是客户端)在规定时间内拥有文件权限(写文件)的合同。 在HDFS中,客户端写文件时需要先从租约管理器申请一个租约,成功申请租约之后客户端就成为了租约持有者,也就拥有了对该HDFS文件的独占权限,其

2021-09-02 11:43:25 475

原创 强、软、弱、虚引用

强、软、弱、虚引用1.强引用 最传统的“引用”的定义 指在程序代码中普遍存在的引用赋值,即类似Object obj = new Object()这种引用关系 无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象2.软引用 用来描述一些还有用,但非必须的对象 只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这些回收还没有足够的内存,才会抛出内存溢出异常3.弱引用 用来描述那些非必须对象 被弱引用关联的对象只能生存

2021-09-01 21:19:51 49

原创 如何判断对象已经死亡

如何判断对象已经死亡1.引用计数算法(主流Java虚拟机不使用) 在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1。任何时刻计数器为零的对象就是不可能再使用的。 原理简单,判定效率高 存在缺陷,比如对于对象相互循环引用问题难以解决2.可达性分析算法(多数使用这个) 通过一系列“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”,如果某个对象到GC Roots间没有任何引用链相连,即不可

2021-09-01 21:17:14 341

原创 JVM内存区域

JVM内存区域1.程序计数器 一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器 每个线程有一个独立的程序计数器,各线程的程序计数器互不影响,独立存储,即线程私有 若线程正在执行一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;若正在执行的是本地方法,这个计数器值应为空 此内存区域是唯一的在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域2.虚拟机栈 线程私有,生命周期与线程相同 描述的是Java方法执行的线程内存模型 每个

2021-09-01 21:11:12 64

原创 HDFS集群的Namenode对数据块和数据节点的管理

HDFS集群的Namenode对数据块和数据节点的管理 HDFS集群的Namenode对数据块和数据节点的管理数据块管理1.数据块2.数据块副本3.数据块的增、删、改、查 Namenode维护着HDFS两个最重要的关系,第一关系是文件系统目录树,第二关系便是数据块和数据节点的关系了。 数据块和数据节点的对应关系,就是指定数据块的副本保存在哪些数据节点上的信息。这个信息是在Datanode启动时,由Datanode上报给Namenode的。Namenode收到后再去更新内存中的数据,以维护数据块和数据节

2021-09-01 20:39:20 2384

原创 HDFS集群的Namenode对文件系统目录树的管理

HDFS集群的namenode文件系统目录树管理 HDFS集群的namenode文件系统目录树管理基本信息管理特性相关信息管理持久化 HDFS的目录和文件在内存中是以一颗树的形式存储的,目录树由namenode维护,namenode会修改这个树形结构以对外提供添加和删除文件等操作。文件系统目录树上的节点还保存了HDFS文件与数据块的对应关系,这一对应关系也是由namenode维护的。 namenode中最重要的两个功能之一就是维护文件系统的命名空间,文件系统的命名空间是以“/”为根的整个目录树,通过F

2021-08-31 12:21:02 1314

原创 Hadoop安全认证

Hadoop安全认证 Hadoop中采用了SASL(Simple Authentication and Security Layer,简单层和安全层)进行安全认证,具体方法涉及DIGEST-MD5和Kerberos两种。1.SASL SASL是一种用来扩充C/S模式验证能力的认证机制,核心思想是把用户认证和安全传输从应用程序中隔离出来。 SASL支持多种认证方法,主要包括以下几种: (1)ANONYMOUS:无须认证 (2)PLAIN:最简单但危险的机制,信息采用明文密码方式传输 (3)DIG

2021-08-26 17:27:48 1379

原创 Java组合和继承的区别

Java组合和继承的区别 组合与继承都是为了复用代码,利用现有类型生成新类型。 组合是在新的类中产生现有类的对象,由于新的类是由现有类的对象所组成的,因而叫作组合。类似于下面这种形式: 继承是按照现有类的类型来创建新类,无需改变现有类的形式,采用现有类的形式并在其中添加新代码。...

2021-08-19 21:05:19 390

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除