自定义博客皮肤VIP专享

*博客头图:

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

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

博客底图:

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

栏目图:

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

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(141)
  • 问答 (2)
  • 收藏
  • 关注

原创 《深入理解JVM》11-锁

根据java的内存模型引发的问题,数据竞争(data race),就是我们平常说的线程并发问题。线程并发的三大特性:可见行, 原子性, 有序性。遵循并发的这三大特性,我们要求线程在操作共享变量的时候要保证自己写入的内容要被其他线程读取到,并且保证写操作的操作一次执行完成,不可分割。并且执行过程的操作保证有序不被重排序。那JVM怎么保证并发的三大特性呢?锁JVM中使用锁保证并发的三大特性,编程时通常使用Synchronized 同步原语Synchronized同步实例..

2020-10-16 12:17:05 244

原创 《深入理解JVM》10-垃圾回收

本系列的第七篇写过一次垃圾回收,今天看起来优点脏乱差,贻笑大方。这节课是对郑雨迪老师的课程总结,希望对大家有点帮助。在第五节的时候我们已经总结过JVM的内存设计了就是上面这张图,相比大家对这个图已经熟记于心了,我们知道新生成的对象都会被分配到堆这块区域,但是随着系统的不断运行,堆内的对象越来越多,这个时候就要清理一下,回收无用的对象的内存空间,分配给新创建的对象。这就是垃圾回收机制,Java语言能够如此的风靡有很大一部分原因是JVM自带了优秀的垃圾回收器,让开发人员不用关注内存空间分配的问题。

2020-10-15 20:25:44 282

原创 《深入理解JVM》9-如何实现反射

在本系列的第8节,我们总结了一下jvm怎么执行方法调用,是在类中维护一个方法表,指向的是方法的实际内存地址。查找内存地址的执行时间不通,可以划分为静态绑定和动态绑定。那么反射是怎么执行的呢 public void invokeTest() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Class deClazs = Demo.class; Me

2020-10-15 16:57:35 232

原创 《深入理解JVM》9-jvm是如何处理异常的

上一节总结了方法调用,在类信息中的方法表中会标记目标方法的地址信息,这个地址信息一开始是符号引用,比如(类名+方法名+参数+返回参数)符号。在运行时必须要解析为实际的内存地址,那么这个过程是分为动态绑定和静态绑定,动态绑定也可以根据单态缓存来提高性能。这一节我们来了解一下异常处理的流程。jvm的异常体系Error:特别严重且无法修复的异常Exception:可以挽救的异常try-catch:在方法执行到RuntimeException时,如果有异常处理代码(try-catch)时就会查看

2020-10-15 16:25:34 237

原创 《深入理解JVM》8-jvm怎么执行方法调用

上面我们讲到类加载的过程分为三步:加载:查找读取class文件 链接:验证,准备,解析三个阶段,在准备阶段会给静态属性分配空间,并且生成方法表 初始化:执行<clinit>方法,执行静态语句块,并且给静态属性赋值那么我们把类加载到JVM的方法区后,怎么执行的方法呢,之前我们说到,调用方法的时候,我们会把目标方法生成java方法栈帧,这个栈帧包含可执行代码,参数数组等信息。那么就会产生一个问题:class Father{ abstract void sayHi();

2020-10-14 21:50:55 141

原创 《深入理解JVM》7-类加载

从Class文件到类的过程要经过加载- 链接- 初始化三个过程。根据上一节讲到的内容,java语言中分为基本类型和引用类型基本类型是虚拟机预先定义好的,引用类型分为数组-类-接口。数组也是虚拟机直接生成的,只有类和接口对应的是字节流(操作码流)操作码可以是class文件形式,也可以是网络上读取,也可以是机器内部生成。我们以常见的class文件为例。加载查找类文件,并读取字节码到内存双亲委派机制jvm提供了默认的三个类加载器启动类加载器(boot strap classL

2020-10-14 16:30:24 167

原创 《深入理解jvm》6-java的基本类型

上一节讲到了jvm中代码执行的过程,class文件被加载到方法区,需要执行的时候,会生成java方法栈的栈帧。然后提供给解释执行器进行命令翻译。java语言中有几个基本类型,boole/byte/short/char/int/long/float/double。jvm提供基本类型的初衷是为了提高计算速度和节省内存空间,java语言中有基本类型和引用类型两大类基础类型在堆中存储的长度分别为:boolean 1字节 byte 1字节 short 2字节 char 2字节 int 4字..

2020-10-14 15:33:51 109

原创 《深入理解jvm》5-java代码是怎么运行的

深入理解jvm系列是对郑雨迪老师在极客时间的课程深入拆解java虚拟机的笔记,想要查看完整课程的可以点击前面的链接,需要购买的同学可以扫面下方的二维码大家都知道java代码都是运行在java虚拟机上的,那么到底代码怎么样执行的呢?下面两个图帮大家有个总体的认识java的代码需要编译成class文件,class文件保存的是java的操作码(opcode),操作码又被称为java字节码,因为每条java的操作码都是一个字节。java的操作码可以被反汇编成人类能读懂的指令:# 最左列是偏移;中间

2020-10-14 11:39:33 158

原创 《Kafka》3-Kafka的分区机制

有讲到分区机制了,我发现只要是数据存储系统,都会涉及到分区的概念。例如mysql的分表/redis的分区/kafka的分区。在概览这一节我们看到了Kafka的基本组件,在Broker里面可以有多个主题的消息存储单元。在存储单元内可以有多个分区。生产者生成消息之后发送给Broker,但是怎么进行分区呢。分区又会带来什么优点呢?负载均衡,分区主要的目标是可扩展性,不同的分区可以部署在不同的机器上,我们可以扩展机器,增加吞吐量 保证消息的有序性。在同一个分区的消息,能保证消息的消费顺序。负载均衡策略.

2020-10-13 17:36:39 222

原创 《Kafka》2-Kafka集群方案

Kafka一般都是集群使用,那么在搭建集群的时候,需要选择什么样的服务器部署kafka,需要部署多少台Kafka机器,需要多大磁盘空间呢。其实都是有迹可循的操作系统现在服务器一般有三种操作系统可以选,linux/windows/macos。对于Kafaka而言,Linux是最合适的操作系统。主要有以下三个优点。i/o模型较优。 外部设备读取速度较快 社区支持良好在redis章节我们就比较过各种I/O模型的概念。有5中,同步阻塞IO/同步非阻塞IO/多路复用IO/信号驱动IO/异步IO。这几

2020-10-13 16:12:34 275

原创 《Kafka》1-概述

在平常的开发工作中,大家或多或少的会用到消息中间件。可能是为了解除循环依赖,可能是为了削峰填谷。不管怎么样,消息中间件已经成为开发架构中不可缺少的重要组成部分。消息中间件有很多选型,包括RabbitMq/RocketMq/ActiveMq/Kafka/。这个系列是主要总结一下胡希老师在极客时间的Kafka核心技术与实践课程。大家像购买的话可以扫描最下方的二维码。Kafka是什么?Kafka是一个消息引擎,提供消息传递功能。 Kafka是一个实时流处理引擎,提供实时的流处理能力。消息引擎大

2020-10-13 12:40:52 179 1

原创 《redis》10-redis切片集群

上面的章节我们讲完了redis主从模式,但是主库和从库的数据基本上保持一致,那么就会遇到一个问题,在主库不断写入的情况下,数据越来越多,redis作为内存存储系统,数据存储在内存中,内存不能无限扩大,而且随着存储数据的不断增加,fork的子线程在做RDB文件备份的时候,速度越来越慢。mysql也会遇到这样的问题,之前Facebook出现的问题,用户信息表随着注册的用不端增加,导致主键字段长度不够,全球10亿用户的信息难道都存储在一个数据库里?redis提出了解决方案,切片集群。纵向扩展,随着数据的不

2020-10-11 17:58:58 400

原创 《redis》9-哨兵集群谁做主

上一节我们讲到哨兵集群做的三件事情协商主库是否客观下线 选出新的主库 通知从库和客户端1客观下线是哨兵集群中协商处理的,比如A哨兵通过ping 主库的ip_port发现主库已经主观下线了,然后发起哨兵集群间通信,获取其他哨兵对于主库的判断,如果超过了阈值(可设置),就可以标记主库为客观下线但是第2/3步只需要一个哨兵单独执行即可,那到底谁来执行第2/3步呢。这个时候我们引入哨兵集群另外一个特别重要的概念,哨兵选举制度哨兵leader选举在哨兵集群当中,需要选出一个主席,主持选择新主库和

2020-10-11 17:06:59 140

原创 《redis》8-主库宕机怎么办

上面一节我们讲了redis通过repliceof 组成的主从模式,主库负责执行读写操作,从库负责执行读操作。写的操作通过长链接同步给从库。这个地方有个很大的风险,主库宕机了怎么办,主库宕机之后,客户端的写操作就没有服务器可以执行了为了解决这个隐患,redis推出了哨兵机制。哨兵是一个特殊的redis线程,通过定时ping主库和从库的ip-port来检测redis服务的状态。哨兵负责的三个主要工作:哨兵机制判断主库是否下线 选出新的主库,切换主库 把新的主库通知给其他从库和客户端判断主库下线

2020-10-11 16:11:26 212

原创 《redis》7-redis的主从一致性

redis为了保证高可用性,除了使用AOF保证启动之后能恢复数据,还要保证不中断服务,这样的才能实现高可用,最简单的例子就是增加服务器的数量,却别于一般的web服务的增加,redis是数据存储,需要保证数据的正确性和一致性。所以redis设计了多服务的主从模式:redis主从模式:主从模式,一个master,多个follower从master服务器同步数据主从从模式,一个master,A,B,C三个follower从master同步数据,E,F,G从A同步数据,即redis服务A既是maste

2020-10-11 13:45:37 588

原创 《redis》6-redis宕机之后快速恢复数据

上一节讲到redis为了保证高可用,使用了AOF机制来确保数据不丢失,宕机之后,如果想要恢复数据,只能顺序执行aof的日志。但是对于4G满的redis数据,逐条执行aof日志时间太长,有没有其他方式呢?redis又引入了另外一个方式:RDBRDB:redis databaseRDB其实就是redis内存数据的快照-snapshot。这个快照保存的是某一个时间节点,redis内容中所有的数据,生成RDB文件,我们把它保存在文件系统,然后在宕机之后可以读取到内存中,完成数据的快速恢复。如何生成快照:

2020-10-11 13:11:45 1512

原创 《redis》5-redis的写后日志AOF

redis作为内存存储系统,数据都保存在内存中,当服务器断电之后数据都会丢失,为了能恢复数据,redis准备了AOF(Append only file).AOF是写后日志,在执行完redis的操作命令之后,会把操作记录追加到日志文件中。redis写命令:put aaa:123432在执行完当前命令之后,会把put aaa:123432这条命令追加到日志文件中,这个就是AOF机制。Mysql:WALmysql 又一个WAL机制非常类似,Write ahead logging,在引擎中会追加

2020-10-11 12:05:03 387

原创 《redis》4-redis是单线程?

江湖传言,redis是单线程的,习惯了多线程高并发的高大上基数架构之后,猛然回头,发现支持三高的redis是单线程的,这个你能信?“单线程”描述redis固然不准确,我们只能说,redis在访问存储部分的时候是单线程的。学习过tomcat的应该清楚,tomcat跟redis的架构有些类似。tomcat有个专门处理connector的连接器,可以同时处理N个请求,但是connector在提交请求到容器部分的时候,提交的其实是runner,tomcat的自己的线程池会持续处理提交的runner,

2020-10-10 19:16:00 1999

原创 《redis》3-redis的数据结构

redis的访问速度为什么那么快,主要是redis使用了hash表来存储索引,索引的key是存储key-value的key,value是指针,指向了真正的key-value实体。hash表的主要特性就是根据key的hash值找到hash表的下标,然后访问下标指向的hash桶,而桶一般是个链表,保存的是hash之后相同下标的数据。hash表有个缺点是,随着数据的增大或者是因为数据特性,造成hash冲突,在相同的hash桶后面又一个长长的数据链表。保存的是hash冲突的key-value数据的实体。我们

2020-10-10 17:47:21 167

原创 《redis》2-redis的基础组件

上一节讲到redis为了实现三高做的设计,这一节讲一下redis的基础组件,就像spring学习的时候,我们要分析spring的基础功能和基础组件,从总体上了解一个优秀的框架的基本情况。redis也是一个优秀的架构,在学习redis的最前面,我们也要线懂得redis的基础架构,redis是内存存储系统。存储的数据都是key-value的键值对。key都是String类型,value的数据结构多一些,比如常用string,列表,集合,hash表。redis支持的基本操作,put/get/delete/

2020-10-10 16:27:40 447 1

原创 《redis》1-概述

在基本的开发环境中,总会用到缓存中间件,redis使用的频率比其他的要高很多,所以在使用的同时,理解redis的特性,能够在以后的特定场景中,充分的利用到redis的性能优势。其实在很多的面试中,也会问到redis的特性,比如redis的线程模型,比如redis的集群模式,比如redis的数据结构。而且redis在设计上有很多优秀的特点,在高性能,高可用,高扩展上都能给我们一些启发。所以我在geektime上订阅了蒋德钧老师的redis课程,希望在学习的过程中不断总结,加深理解。大家都知道redis

2020-10-10 14:25:04 606

原创 《mysql》10-为什么mysql会“抖”一下?

之前的章节,我们总结过mysql执行语句的一个机制,WAL(write-ahead-logging)。即,执行引擎为了能保证写入的速度,会在内存中存有一个小黑板-redolog,把执行的结果集写入redolog,当前结果集是prepared状态,然后执行器会把逻辑操作语句写到执行器的日志-binlog。然后调用执行器的commit语句,修改redolog的状态为commit。这个过程就是我们的两阶段提交过程。那么在写完redolog之后,其实磁盘上的数据就不太准确了,这个时候,磁盘上的记录就可以被当作“

2020-10-10 09:42:47 93

原创 《mysql》9-mysql为什么会“选错”索引

mysql执行查询语句的时候,按照我们之前梳理的执行顺序,首先是连接器, 查询缓存,命中之后返回 分析器,分析语法是否正确 优化器,优化执行语句,选择索引 执行器,执行器校验权限等。 执行引擎,具体的引擎只有到了5这一步,才会真正的执行查询语句,这个过程非常的有意思,我们在第四步优化器的时候会选择当前查询语句依赖的索引。比如sql语句:select a.name,a.age,a.address from table_a a where a.name like 'zhang%' and

2020-10-09 16:10:57 150

原创 《mysql》8-索引

索引是个好东西,从诞生就闪耀着智慧的光芒。索引的数据结构可以是hash表,有序数组,B树,B+数。mysql中如果数据库引擎是innodbhash表hash表其实跟HashMap是一样的数据结构,我们可以根据索引值的hash找到桶的位置,然后添加到桶对应的链表中。缺点是,hash表对区间查询不兼容,因为桶的列表是无序的,对应的链表是无序的,那么查找【k,k+1】的数据就需要遍历整个数据结构。所以hash表对等值查询的场景,比如Memcached或其他nosql引擎有序数组在《数据结构和算法》章节

2020-09-30 09:58:34 111

原创 《Springcloud设计思想》4-springcloud怎么扩展的Springboot

上面一节我们简单的了解spring-cloud-commons做了云服务的一些标准抽象,各大厂商在此标准上做了很多不同的云服务实现方案,比如netflix-eureka,cloud-zookeeper,cloud-alibaba等,这些实现方案是对sping-cloud-commons的具体实现。能够提供完整的Spring-cloud的功能。但是这些实现都是基于Spring-cloud体系实现的,而Spring-boot可以基于Spring-boot进行快速搭建和开发。那我们就简单的看下Spring-clo

2020-09-27 17:05:27 194

原创 《springCloud设计思想》3-springcloud的标准抽象

上一节我们尝试着搭建了一个使用netflix-eureka框架的springcloud可用服务,client1/client2注册到server上,实现client1和client2的相互调用。但是我们还可以使用spring-cloud-alibaba或者其他的springCloud的框架,Springcloud是怎么对接的这么多不同的实现呢。原因就在于Springcloud的标准抽象-Spring-cloud-commonsspring-cloud-commons:common abstraction

2020-09-27 14:45:06 205

原创 《SpringCloud设计思想》2-实现相互调用

上一节简单的能启动Eureka的sever和client,但是离互相调用服务还有一段距离。这一节我们通过调试,使Client之间能够相互调用。有个特别的地方,eureka-server在启动的时候,会默认注册到服务器。但是我们单机的Server启动时根本没有eureka-server可用,为了避免启动失败我们需要添加配置,一般Spring-cloud的配置文件放在~/resources/application.yaml文件中;eureka: client: register-with-

2020-09-24 09:22:48 129

原创 《SpringCloud设计思想》1-云服务的基础组件

学习spring的最终是为了快速搭建服务,在服务框架不断发展的终点,是分布式服务。分布式服务的核心功能分布式/版本配置 服务注册与发现 路由: 服务端调用 负载均衡 Circuit Breakers:断路器 全局锁 选举和集群管理 分布式消息云服务的几大基础组件:服务注册与发现 路由 负载均衡 断路器上面的4个组件基本上就能实现了云服务的基础功能。我们以Spring-cloud-netflix-eureka为例一起学习一下Spring-cloud的功能和使用。首先我们尝

2020-09-23 15:30:34 554

原创 《数据结构与算法》7-快排-归并排序

上一节分析了一下冒泡排序,插入排序,选择排序。时间纷杂度都是O(n^2),这一节分析一下速度较快的排序算法,归并排序和快速排序 归并排序 归并排序的思想比较简单,分而治之;把排序的数组平均分为左右两部分,递归此操作 然后对左右两部分进行排序。4,6,3,7,2,8,1,9分为两组{4,6,3,7}-{2,8,1,9}再分{4,6}-{3,7}-{2,8}-{1,9}再分4-6-3-7-2-8-1-9分解为单独的数据之后使用合并时排序4-6合并不用交换位置3-7合并

2020-09-21 21:04:37 135

原创 《数据结构与算法》6-排序

排序是一种对线性数据结构做的数据顺序调整,获得从大到小或者从小到大的有序的结果。比如 1 6 4 3 9 0 从小到大排序之后是 0 1 3 4 6 9 。从大到小排序的顺序是 9 6 4 3 1 0排序有很多方法,一般的排序算法的两个基本操作是比较和位移比较比较是排序的基础,如果线性表里面的数据无法相互比较大小,就没有排序的概念。上面的例子是比较的数字,开发环境中有很多场景,列表里面的数据可能是对象,有很多属性,这样的话就要开发自己指定排序的方法,或者扩展数据类型继承Comparable&l

2020-09-18 17:09:33 107

原创 《数据结构与算法》5-递归

递归是一种算法,算法是对计算规律对总结,递归适用于什么样的计算呢。可分解的计算,即总的计算公式可以分解为几个小的计算公式 重复性的计算,没有重复性就不能归纳出基本计算单元 有退出计算的条件,就是说到某个条件满足的时候会退出计算...

2020-09-17 15:56:43 122

原创 《数据结构与算法》4-队列

队列(queue)也是一种操作受限的线性表。他的操作特点是先进先出队列有头节点 队列有尾节点 入队 enQueue(T t)只能增加到尾节点 出队 T dequeue() 只能从头节点出队上面的四个特性决定了队列的先进先出性。在需要公平排队的情境下适合使用队列队列也可以根据底层实现分为两种底层是数组的顺序队列 底层是链表的链式队列顺序队列的主要构造T[] data 存储数据的数组 int size 队列的大小 int head 队列的头下标 int tail 队列...

2020-09-15 20:49:44 105

原创 《数据结构与算法》3-栈

栈跟数组和链表一样也是一种线性表,只有前后节点,并且一一对应。区别就是栈的操作受限,只能从一头插入元素或者删除元素。不像数组和链表可以任意读取和删除。读取的操作叫T pop插入的操作叫 push(T t)栈的底层实现可以是数组或者链表。底层是数组的栈叫顺序栈,底层是链表的栈叫链式栈顺序栈顺序栈的最大的特点是,底层使用数组存储数据。只能使用固定大小的数组,当数组容量满时,需要申请一个更大的数组(一般是1.5倍),然后原始数组的数据copy到新的数组,然后在新的数组上插入新的数据链式栈使

2020-09-15 13:50:02 178

原创 《数据结构与算法》2-链表

上一节我们总结了一下数组,数组的特性就是要吧数据存储在一块连续的存储块内。所以当我们知道了第一个数据的内存地址,就可以随机访问其他的数据。访问的事件复杂度是O(1)。但是数组的缺陷也比较明显:首先物理内存必须是连续的,数据量大的时候难以保证存储够用 插入删除操作相对复杂,需要做数据的复制移位。链表的出现就解决了上述问题。首先链表的基本元素是节点:Node。Node数据结构分两部分:1-数据域,用来保存数据 2-指向下一个数据的指针。上面的指针给了链表的极大扩展性:链表不需要物理存储的连续

2020-09-14 20:27:43 183

原创 《数据结构与算法》1-数组

大学的时候学习Java的数据结构特别的难过,各种刁钻的demo示例展示数据结构的恶心,那时候死记硬背的结果就是快速的遗忘。在工作之后,写出能运行的代码的时候就算是交工了。很少会认真思考数据结构对代码带来的影像。直到后来为了面试,经常被问到数据结构的问题才开始后悔平时没有多思考总结。知识也不会因为你的临阵磨枪给你任何奖赏。只有回国头来仔细的思考其中的原理,才可能融汇贯通,牢记心间。数据结构这门课是在Springboot 设计思想学习过程中穿插学习的内容。因为对于数据结构抱着深深的怨念,所以私认为此役一定要

2020-09-13 22:41:37 88

原创 《springboot设计思想》5-AutoConfiguration点石成金

上面我们讲Springboot自动装配,大大的提高了springboot的搭建效率,这一节我们就整体的梳理一下SpringBoot的自动装装配原理。上面一节我们讲了SpringApplication的构造方法,在构造方法中,我们提到了扫描jar包中的spring.factories文件中的ApplicationContextInitializer和ApplicationListener的对象。作用是在应用启动和监听声明周期事件时的回调,这一部分的内容帮助我们集成外部化配置,修改应用行为有比较好的效果

2020-09-11 10:00:36 116

原创 《Springboot设计思想》4-SpringApplication的构造方法的奥秘

SpringBoot的一大核心功能就是自动装配,在第三节的SpringApplication#run方法中,SpringBoot可以自己决定创建适用于当前应用的Environment实现,和ApplicationContext实现,这也是SpringBoot能够实现自动装配的一小部分。这一节我们看下SpringBoot是如何做到自适应的。SpringApplication创建自适应的Environment创建自适应的Environment方法:org.springframework.boot

2020-09-06 11:48:58 145

原创 《Springboot设计思想》3-SpringApplication浅析

作为Springboot最重要的组件,SpringApplication扮演着创建并启动ApplicationContext及其他组件的总指挥角色。SpringApplication是打开SpringBoot应用的钥匙,这一节我们简单的梳理以下SpringApplicationSpringApplication类解析SpringApplication的属性列表,上面有很多重要的属性,比如DEFAULT_***_CONTEXT_CLASS;primarySources;sources;mainA

2020-09-05 22:20:19 137

原创 《Springboot设计思想》2-Springboot青出于蓝

相信使用过Springboot的各位应该深有体会,Springboot的快速搭建能力比传统的Spring framework高出不少。我个人认为归功于一下几点:版本依赖管理基于Springboot initializer创建的Springboot工程,继承了Spring-boot-stater的父pom <parent> <groupId>org.springframework.boot</groupId> <artifactId>spr

2020-09-05 18:03:46 275

原创 《springboot设计思想》1-Springboot纵览

在梳理完Spring的设计思想之后,趁热打铁又学习了一下sprigboot的基本使用,因为现在大部分项目开发中都应用到Springboot的自动装配功能,实现了精简maven依赖,快速开发,单独部署的功能。Spring-boot基于Spring实现了自动化装配,外部化配置,嵌入式容器,生产监控四个大的部分自动化装配:自动化装配是基于规约大于配置的准则,简化了架构人员的系统搭建主要依赖:@EnableAutoConfiguration注解和各个派生@***AutoConfigurati

2020-09-05 16:16:03 284 1

空空如也

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

TA关注的人

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