自定义博客皮肤VIP专享

*博客头图:

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

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

博客底图:

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

栏目图:

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

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+

qq:489366879

程序员

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

原创 JAVA GC - STW

我们先假设GC拥有独立的线程, 与用户的逻辑并行的运行, 看看会有哪些不符合预期的情况.情况一: 该回收的没回收在标记的过程中, 在对象被标记之后, 用户的线程做了一些操作, 所有对象都不再依赖这个对象了, 那么这个对象应该被回收, 但是在这次遍历中不会被回收了情况二: 不该回收的回收了在标记的过程中, 用户的线程又创建一些新对象, 并且与旧的对象存在一些依赖关系, 但是依赖这些对象的旧对象在这次标记中都已经被遍历过了, 也就是说这些新产生的对象在这次的遍历过程中不会被标记了, 那么它们应该

2022-04-25 18:45:32 117

原创 Redis和数据库的数据一致性问题

在数据读多写少的情况下作为缓存来使用,恐怕是Redis使用最普遍的场景了。当使用Redis作为缓存的时候,一般流程是这样的。如果缓存在Redis中存在,即缓存命中,则直接返回数据如果Redis中没有对应缓存,则需要直接查询数据库,然后存入Redis,最后把数据返回一致性问题但是,在Redis的key值未过期的情况下,用户修改了个人信息,我们此时既要操作数据库数据,也要操作Redis数据。现在我们面临了两种选择:先操作Redis的数据,再操作数据库的数据 先操作数据库的数据,..

2022-04-21 14:24:32 405

原创 敏捷式开发

1、敏捷开发的工具:Leangoo;Teambition2、Scrum敏捷开发流程主要包扩三个角色、四个会议和个三物件。(1) Scrum团队中包括三个角色,他们分别是产品负责人、开发团队和 项目的直接管理者(Scrum Master)。产品负责人是管理产品待办事项列表的唯一责任人。产品待办事项列表的管理包括:● 清晰地表达产品代办事项列表条目● 对产品代办事项列表中的条目进行排序,最好地实现目标和使命● 确保开发团队所执行工作的价值● 确保产品代办事项列表对所有人可见、透明、清

2022-04-21 13:54:34 113

原创 实现分布式锁的三种方式

目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。”所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。在很多

2022-04-21 12:01:59 39

原创 浅谈如何解决MQ消息堆积的问题

MQ消息堆积是指生产者发送的消息短时间内在Broker端大量堆积,无法被消费者及时消费,从而导致业务功能无法正常使用。 消息堆积常见于以下几种情况:(1)新上线的消费者功能有BUG,消息无法被消费。(2)消费者实例宕机或因网络问题暂时无法同Broker建立连接。(3)生产者短时间内推送大量消息至Broker,消费者消费能力不足。(4)生产者未感知Broker消费堆积持续向Broker推送消息。 解决上述问题就要做到:(1)解决问题一,要做好 ...

2022-04-21 11:06:46 4283

原创 Flink 事件时间与处理时间

Flink 在数据流中支持几种不同概念的时间。1. 处理时间Processing Time(处理时间)是指执行相应操作的机器系统时间,是操作算子在计算过程中获取到的所在主机的系统时间。当用户选择使用处理时间时,所有和时间相关的算子,例如 Windows 计算,在当前任务中所有的算子直接使用所在主机的系统时间。例如,一个基于处理时间按每小时进行处理的时间窗口将处理一个小时内(以系统时间为标准)到达指定算子的所有的记录。处理时间是最简单的一个时间概念,不需要在数据流和机器之间进行协调。具有...

2022-04-19 15:05:02 1399

原创 Skywalking — 微服务链路跟踪

微服务架构已经是一个很通用的系统架构,常见的技术栈如下图所示,这张架构图基本涵括了当前微服务体系下的各种技术栈,可能不同的技术栈有不同的开源实现。链路追踪介绍对于一个大型的几十个,几百个微服务构成的微服务架构系统,通常会遇到下面的一系列问题。如何串联整个调用链路,快速定位问题? 如何澄清各个微服务之间的依赖关系? 如何进行各个微服务接口的性能分析? 如何追踪各个业务流程的调用处理顺序?Skywalking介绍Skywalking是一个国产的开源框架,2015年有吴晟个人开源.

2022-04-19 13:40:42 2014

原创 微服务拆分原则

微服务拆分原则拆分的大原则是当一块业务不依赖或极少依赖其它服务,有独立的业务语义,为超过 2 个的其他服务或客户端提供数据,那1 .单一职责、高内聚低耦合:简单来说一张表划分为一个服务2. 服务粒度适中:服务不要太细(有的团队甚至一个接口一个服务)3. 以业务模型切入:比如产品,用户,订单为一个模型来切入4., 演进式拆分:刚开始不要划分太细,可以随着迭代过程来逐步优化5. 避免环形依赖与双向依赖:尽量不要做服务之间的循环依赖...

2022-04-19 12:05:02 1403

原创 spring - 拦截器, 过滤器

一、Interceptor定义:拦截器是在面向切面编程中应用的,就是在你的service或者一个方法前调用一个方法,或者在方法后调用一个方法。是基于JAVA的反射机制1.拦截器(Interceptor)执行顺序:1. 请求到达 DispatcherServlet2. DispatcherServlet 发送至 Interceptor ,执行 preHandle3. 请求达到 Controller4. 请求结束后,postHandle 执行2.拦截器(Interceptor)和过滤器(Fi

2022-04-19 11:09:45 127

原创 AbstractRoutingDataSource 实现动态数据源切换 determineCurrentLookupKey方法

首先看下AbstractRoutingDataSource类结构,继承了AbstractDataSourcepublic abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean既然是AbstractDataSource,当然就是javax.sql.DataSource的子类,于是我们自然地回去看它的getConnection方法:public Connect

2022-04-19 10:53:49 303

原创 springboot -RestControllerAdvice

1.RestControllerAdvice来捕获全局异常@RestControllerAdvice都是对Controller进行增强的,可以全局捕获spring mvc抛的异常。@ExceptionHandler(value = Exception.class)ExceptionHandler的作用是用来捕获指定的异常。@RestControllerAdvice(annotations = RestController.class)public class UniformReponseHa

2022-04-19 09:55:41 423

原创 MySQL的四种事务隔离级别

一、事务的并发问题  1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据  2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。  3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。  小结:不可重复读的和

2022-04-18 10:55:18 25

原创 Nginx 负载均衡 - TCP

 关于Nginx负载均衡的简单配置,我以前博客配置过基于HTTP的负载均衡。这次的负载均衡有点不一样,就是基于TCP的负载均衡。基于HTTP负载均衡是默认的Nginx版本支持的,配置也很简单,但是基于TCP的负载均衡,配置起来就有一点点麻烦了。1. 下载安装由于我们要用到四层TCP层负载均衡,所以要自己编译Nginx,在这里下载最新版http://nginx.org/download/nginx-1.12.2.tar.gz中间我们可能要进行调试一些参数编译:1. ./con...

2022-02-03 23:55:47 80

原创 Rabbitmq 的正常安装流程

安装相关依赖yum updateyum install epel-releaseyum install gcc gcc-c++ glibc-devel make ncurses-devel openssl-devel autoconf java-1.8.0-openjdk-devel git wget wxBase.x86_64安装ErLangwget http://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.r

2021-11-22 17:34:53 1125

原创 Canal原理及在缓存上的使用

1 什么是canal canal是用java开发的基于数据库增量日志解析,提供增量数据订阅&消费的中间件。目前,canal主要支持了MySQL的binlog解析,解析完成后才利用canal client 用来处理获得的相关数据。(数据库同步需要阿里的otter中间件,基于canal)2 canal使用场景-更新缓存 如果有大量的请求发送到mysql的话,mysql查询速度慢,QPS上不去,光查mysql可能会瘫痪,那就可以在前面加个缓存,这个缓存有2个主要的问题...

2021-11-18 16:55:15 932

原创 Spring WebFlux 入门

1. WebFlux介绍Spring WebFlux 是 Spring Framework 5.0中引入的新的响应式web框架。与Spring MVC不同,它不需要Servlet API,是完全异步且非阻塞的,并且通过Reactor项目实现了Reactive Streams规范。Spring WebFlux 用于创建基于事件循环执行模型的完全异步且非阻塞的应用程序。(PS:所谓异步非阻塞是针对服务端而言的,是说服务端可以充分利用CPU资源去做更多事情,这与客户端无关,客户端该怎么请求还是怎么请求

2021-09-20 09:43:13 72

原创 RocketMQ定时(延迟)消息

ocketMQ 不支持任意时间自定义的延迟消息,仅支持内置预设值的延迟时间间隔的延迟消息。预设值的延迟时间间隔为:1s、 5s、 10s、 30s、 1m、 2m、 3m、 4m、 5m、 6m、 7m、 8m、 9m、 10m、 20m、 30m、 1h、 2h延时消息的使用场景比如电商里,提交了一个订单就可以发送一个延时消息,1h后去检查这个订单的状态,如果还是未付款就取消订单释放库存。生产package com.xin.rocketmq.demo.testrun;import

2021-08-31 15:22:44 117

原创 Netty-源码分析ByteBuf-readSlice和readRetainedSlice使用细节

readSlice返回从当前readerIndex开始的此缓冲区的子区域的新分片,并将readerIndex增加新分片的大小(=长度)。另请注意,此方法将不会调用retain(),因此不会增加引用计数。跟slice极为相似,只是把原始缓冲区的readerIndex进行了增加@Overridepublic ByteBuf readSlice(int length) { checkReadableBytes(length); ByteBuf slice = sl

2021-08-10 11:01:38 415

原创 Zipkin — 微服务链路跟踪

一、Zipkin 介绍Zipkin 是什么? Zipkin的官方介绍:https://zipkin.apache.org/ Zipkin是一款开源的分布式实时数据追踪系统(Distributed Tracking System),基于 Google Dapper的论文设计而来,由 Twitter 公司开发贡献。其主要功能是聚集来自各个异构系统的实时监控数据。分布式跟踪系统还有其他比较成熟的实现,例如:Naver的Pinpoint、Apache的HTrace、阿里的鹰眼Tracing、京东的Hyd

2021-08-05 17:20:45 95

原创 Java 性能诊断利器 -JProfiler

Java 性能诊断工具简介在 Java 的世界里,有许多诊断工具可供选择,既包括像 jmap、jstat 这样的简单命令行工具,又包括 JVisualvm、JProfiler 等图形化综合诊断工具,同时还有 SkyWalking、ARMS 这样的针对分布式应用的性能监控系统。下面分别对其进行介绍。简单命令行工具JDK 内置了许多命令行工具,它们可用来获取目标 JVM 不同方面、不同层次的信息。jinfo - 用于实时查看和调整目标 JVM 的各项参数。 jstack - 用于获取目标 Ja

2021-08-05 16:36:06 151

原创 LRU算法 原理及实现

LRU原理LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。最常见的实现是使用一个链表保存缓存数据,详细算法实现如下新数据插入到链表头部; 每当缓存命中(即缓存数据被访问),则将数据移到链表头部; 当链表满的时候,将链表尾部的数据丢弃。 【命中率】 当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。.

2021-07-15 15:10:57 224

原创 Java-阻塞队列(BlockingQueue)

1.阻塞队列(BlockingQueue)BlockingQueue 继承了 Queue 接口,是队列的一种。Queue 和 BlockingQueue 都是在 Java 5 中加入的。java.util.concurrent.BlockingQueuepublic interface BlockingQueue<E> extends Queue<E> {...}BlockingQueue 有 6 种最主要的实现如下ArrayBlockingQueueLink

2021-07-09 11:40:35 137 2

原创 Java-@interface 自定义注解

@interface使用1.@interface自定义注解 <1>@interface自定义注解自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。 <2>在定义注解时,不能继承其他的注解或接口。 <3>使用@interface来声明一个注解,1>.每一个方法实际上是声明了一个配置参数,2>.方法的名称就是参数的名称,3>.返回值类型就是参数的类型,(返回值类型只能是基本类型...

2021-07-08 17:43:51 211

原创 Java--ReentrantReadWriteLock

概述  ReentrantReadWriteLock是Lock的另一种实现方式,我们已经知道了ReentrantLock是一个排他锁,同一时间只允许一个线程访问,而ReentrantReadWriteLock允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。相对于排他锁,提高了并发性。在实际应用中,大部分情况下对共享数据(如缓存)的访问都是读操作远多于写操作,这时ReentrantReadWriteLock能够提供比排他锁更好的并发性和吞吐量。  读写锁内部维护了两个锁,一个用

2021-07-05 19:41:28 33

原创 Java-CyclicBarrier(循环栅栏)

1. CyclicBarrier 是什么?从字面上的意思可以知道,这个类的中文意思是“循环栅栏”。大概的意思就是一个可循环利用的屏障。它的作用就是会让所有线程都等待完成后才会继续下一步行动。举个例子,就像生活中我们会约朋友们到某个餐厅一起吃饭,有些朋友可能会早到,有些朋友可能会晚到,但是这个餐厅规定必须等到所有人到齐之后才会让我们进去。这里的朋友们就是各个线程,餐厅就是 CyclicBarrier。2. 怎么使用 CyclicBarrier2.1 构造方法public Cyclic

2021-07-05 18:21:08 52

原创 Java-countDownLatch

1.背景:countDownLatch是在java1.5被引入,跟它一起被引入的工具类还有CyclicBarrier、Semaphore、concurrentHashMap和BlockingQueue。 存在于java.util.cucurrent包下。2.概念countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。 是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待

2021-07-05 17:17:07 28

原创 Java-Semaphore(信号量)

Semaphore(信号量):是一种计数器,用来保护一个或者多个共享资源的访问。如果线程要访问一个资源就必须先获得信号量。如果信号量内部计数器大于0,信号量减1,然后允许共享这个资源;否则,如果信号量的计数器等于0,信号量将会把线程置入休眠直至计数器大于0.当信号量使用完时,必须释放。实例代码: final Semaphore semaphore = new Semaphore(2); ExecutorService executorService = Executors...

2021-07-05 16:48:50 29

原创 Java-CLH锁

锁值:我把自旋条件定义为锁值 locked。locked == true 表示节点的处于加锁状态或者等待加锁状态,locked == false 表示节点处于解锁状态。

2021-07-02 16:44:24 63

原创 Sentinel-分布式服务架构的高可用流量防护组件

Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。Sentinel 特性:1 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。2 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚

2021-06-24 17:56:43 91

原创 Java-消息中间件使用场景

系统解耦假设你有个系统A,这个系统A会产出一个核心数据,现在下游有系统B和系统C需要这个数据。那简单,系统A就是直接调用系统B和系统C的接口发送数据给他们就好了。整个过程,如下图所示:但是现在要是来了系统D、系统E、系统F、系统G,等等,十来个其他系统慢慢的都需要这份核心数据呢?如下图所示:大家可别以为这是开玩笑,一个大规模系统,往往会拆分为几十个甚至上百个子系统,每个子系统又对应N多个服务,这些系统与系统之间有着错综复杂的关系网络。如果某个系统产出一份核心数据,可能下游无数的其他系统都需要

2021-06-24 16:29:14 177 1

原创 消息中间件-如何保证消息仅仅被消费一次?

消息中间件使用广泛,常用来削峰填谷、系统解耦、异步处理。异步处理可能是使用的最多的场景了,比如现在的技术博客网站,都采用积分制,用户发表一篇文章后,可以获取想要的积分,为了提升系统的性能,给用户加积分的操作可以异步处理,并不需要放在同步流程中。我们可以把用户ID,需要增加的积分封装成一条消息投递到消息系统中,异步处理加积分操作,由于这是发生在不同服务器之间,消息有可能投递失败、处理失败等问题,从而导致用户加积分失败,还有一种可能是消息重复投递,那么用户就有可能重复加积分,不管出现那种情况,都是不正常的情况

2021-06-24 14:56:05 69

原创 Java-RocketMQ事务消息

前言得益于MQ削峰填谷,系统解耦,操作异步等功能特性,在互联网行业,可以说有分布式服务的地方,MQ都往往不会缺席。由阿里自研的RocketMQ更是经历了多年的双十一高并发挑战,其中4.3.0版本推出了事务消息的新特性解决什么问题假设我所在的系统现在有这样一个场景:本地开启数据库事务进行扣款操作,成功后发送MQ消息给库存中心进行发货。有人会想到开启mybatis事务实现,把本地事务和MQ消息放在一起不就行了吗?如果MQ发送成功,就提交事务,发送失败就回滚事务,整套操作一气呵成。transactio

2021-06-24 14:32:56 95

原创 Java-消息架构的设计难题以及应对之道

概述在微服务开发中我们经常会引入消息中间件实现业务解耦,执行异步操作, 现在让我们来看看使用消息中间件的好处和弊端。首先需要肯定是使用消息组件有很多好处,其中最核心的三个是:解耦、异步、削峰。解耦:客户端只要讲请求发送给特定的通道即可,不需要感知接收请求实例的情况。异步:将消息写入消息队列,非必要的业务逻辑以异步的方式运行,加快响应速度。削峰:消息中间件在消息被消费之前一直缓存消息,消息处理端可以按照自己处理的并发量从消息队列中慢慢处理消息,不会一瞬间压垮业务。当然消息中间件并不是银弹,引入消息

2021-06-24 14:21:22 102

原创 高并发高可用-Redis Cluster

redis最开始使用主从模式做集群,若master宕机需要手动配置slave转为master;后来为了高可用提出来哨兵模式,该模式下有一个哨兵监视master和slave,若master宕机可自动将slave转为master,但它也有一个问题,就是不能动态扩充;所以在3.x提出cluster集群模式。1.为什么要实现Redis Cluster1.主从复制不能实现高可用2.随着公司发展,用户数量增多,并发越来越多,业务需要更高的QPS,而主从复制中单机的QPS可能无法满足业务需求3.数据量.

2021-06-23 14:02:24 56

原创 Java - Hystrix 原理与实战

背景分布式系统环境下,服务间类似依赖非常常见,一个业务调用通常依赖多个基础服务。如下图,对于同步调用,当库存服务不可用时,商品服务请求线程被阻塞,当有大批量请求调用库存服务时,最终可能导致整个商品服务资源耗尽,无法继续对外提供服务。并且这种不可用可能沿请求调用链向上传递,这种现象被称为雪崩效应。雪崩效应常见场景硬件故障:如服务器宕机,机房断电,光纤被挖断等。 流量激增:如异常流量,重试加大流量等。 缓存穿透:一般发生在应用重启,所有缓存失效时,以及短时间内大量缓存失效时。大量的缓存不命中

2021-06-23 11:33:26 286

原创 Java - sharding-jdbc

核心概念一、 背景在互联网海量数据时代的今天,我们需要存储的数据也越来越多,在使用关系型数据库例如mysql等时,单表所需要存储的数据也越来越多,但是关系型数据库在单表数据库量较大的情况下,单表性能会急剧下降,面对这个问题,常见的做法就是进行分表,但是单纯的分表只能减少单表的压力,不能减轻数据库的压力,所以在分表的同时往往也会进行分库操作。二、分库分表分库分表主要是为了解决互联网应用的大数据量存储问题,分表通常分为:垂直划分、水平划分;垂直划分通常是根据业务场景将一个多字段大表拆分成多个多个少字

2021-06-22 21:20:35 308

原创 Java - 雪花算法的原理和实现

SnowFlake 算法,是 Twitter 开源的分布式 id 生成算法。其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 id。在分布式系统中的应用十分广泛,且ID 引入了时间戳,基本上保持自增的,后面的代码中有详细的注解。这 64 个 bit 中,其中 1 个 bit 是不用的,然后用其中的 41 bit 作为毫秒数,用 10 bit 作为工作机器 id,12 bit 作为序列号。给大家举个例子吧,比如下面那个 64 bit 的 long 型数字:第一个..

2021-06-22 19:45:57 32

原创 Java - Seata分布式事务XA与AT 对比

AT与XA的关系无论是AT还是XA,他们都是有利用到数据库自带的事务特性,来保证数据一致性和隔离性比如AT一阶段提交和二阶段回滚,都是执行了本地事务。比如XA的一阶段和二阶段,也都是利用了数据库本身的事务特性那么这样一样我们是否应该在数据库层面进行挖掘,AT与XA的关系呢?首先这个时候,我们肯定要从中找相同,与找不同。AT首当其冲,他有个必须品,也就是undolog表,undolog,相信了解数据库的同学肯定是知道。数据库有六种日志分别是:重做日志(redo log)、回滚日志(und

2021-06-22 18:38:23 444 2

原创 java-分布式事务

1、什么是分布式事务分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。以上是百度百科的解释,简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。2、分布式事务的产生的原因2.1、数据库分库分表当数据库单表一年产生的数据超过1000W,那么就要考虑分库分表,具体分库分表的原理

2021-06-22 14:55:51 1312

原创 java -池化技术

java性能优化,通常要考虑GC, 线程上下文切换,网络IO操作的影响;池化技术可在一定场景下很好的规避这些问题,如对象(内存)池,线程池,连接池等; 本文讲几个典型案例;一. 规避GC--对象池 apache common-pool对象池,对象复用,完整的状态管理;二. 规避线程上下文切换损失---线程池1 线程池主要类型:newCachedThreadPool如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程; newFixedThreadPo...

2021-06-22 10:42:39 276

空空如也

空空如也

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

TA关注的人

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