自定义博客皮肤VIP专享

*博客头图:

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

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

博客底图:

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

栏目图:

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

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(88)
  • 资源 (2)
  • 收藏
  • 关注

原创 Go 服务自动收集线上问题现场

对于 pprof,相信熟悉 Go 语言的程序员基本都不陌生,一般线上的问题都是靠它可以快速定位。但是实际项目中,很多时候我们为了性能都不会开启它,但是出了问题又要靠它来分析。好在 go-zero 已经帮我们很好的集成进来了,我们只需要像开关一样去开启、关闭它即可,这样我们就可以配合运维监控,当出现 cpu、内存等异常情况时候,自动开始开启收集(比如大半夜你睡的正香的时候),那么第二天可以通过分析当时的采样还原现场,那我们看看 go-zero 是如何做的。

2022-12-07 09:53:43 260 1

原创 熔断原理分析与源码解读

熔断原理分析与源码解读熔断机制(Circuit Breaker)指的是在股票市场的交易时间中,当价格的波动幅度达到某一个限定的目标(熔断点)时,对其暂停交易一段时间的机制。此机制如同保险丝在电流过大时候熔断,故而得名。熔断机制推出的目的是为了防范系统性风险,给市场更多的冷静时间,避免恐慌情绪蔓延导致整个市场波动,从而防止大规模股价下跌现象的发生。同样的,在高并发的分布式系统设计中,也应该有熔断的机制。...

2022-08-31 08:50:03 731

原创 负载均衡原理分析与源码解读

负载均衡原理分析与源码解读上一篇文章一起学习了Resolver的原理和源码分析,本篇继续和大家一起学习下和Resolver关系密切的Balancer的相关内容。这里说的负载均衡主要指数据中心内的负载均衡,即RPC间的负载均衡。...

2022-08-10 09:51:17 360

原创 服务发现原理分析与源码解读

彻底搞懂服务发现在微服务架构中,有许多绕不开的技术话题。比如服务发现、负载均衡、指标监控、链路追踪,以及服务治理相关的超时控制、熔断、降级、限流等,还有RPC框架。这些都是微服务架构的基础,只有打牢这些基础,才敢说对微服务是有了一点理解,出门也好意思和别人打招呼了,被人提问的时候也能侃侃而谈了,线上出了问题往往也能寻根溯源内心不慌了,旁边的女同事小芳看着你的时候也是满眼的小可爱了。和和大家一起学习微服务架构的方方面面,主要形式是理论+源码+案例,如果时间允许也可能会加上配套视频。...

2022-07-29 09:12:10 320

翻译 简单易懂的 Go 泛型使用和实现原理介绍

本文是对泛型的基本思想及其在 Go 中的实现的一个比较容易理解的介绍,同时也是对围绕泛型的各种性能讨论的简单总结。

2022-06-08 09:55:43 522

原创 Go单体服务开发最佳实践

对于很多初创公司来说,业务的早期我们更应该关注于业务价值的交付,并且此时用户体量也很小,`QPS` 也非常低,我们应该使用更简单的技术架构来加速业务价值的交付,此时单体的优势就体现出来了。

2022-04-26 10:07:31 476 1

原创 通过 SingleFlight 模式学习 Go 并发编程

在go-zero中SingleFlight的作用是:将并发请求合并成一个请求,以减少对下层服务的压力。

2022-04-25 09:33:12 399

原创 微服务从代码到k8s部署应有尽有系列(三、鉴权)

我们用一个系列来讲解从需求到上线、从代码到k8s部署、从日志到监控等各个方面的微服务完整实践。整个项目使用了go-zero开发的微服务,基本包含了go-zero以及相关go-zero作者开发的一些中间件,所用到的技术栈基本是go-zero项目组的自研组件,基本是go-zero全家桶了。实战项目地址:https://github.com/Mikaelemmmm/go-zero-looklook1、鉴权服务1.1 identity-apiidentity主要是用来做鉴权服务的,前面我们n

2022-04-19 09:10:57 239

原创 带你十天轻松入门 Go 微服务系列(二)

上篇文章开始,我们通过一个系列文章跟大家详细展示一个 go-zero 微服务示例,整个系列分十篇文章,目录结构如下: 环境搭建 服务拆分(本文) 用户服务 产品服务 订单服务 支付服务 RPC 服务 Auth 验证 服务监控 链路追踪 分布式事务 期望通过本系列带你在本机利用 Docker 环境利用 go-zero 快速开发一个商城系统,让你快速上手微服务。完整示例代码:GitHub - nivin-st

2022-02-16 16:32:11 694 1

原创 带你十天入门 Go 微服务系列(一)

本文开始,我们会出一个系列文章跟大家详细展示一个 go-zero 微服务示例,整个系列分十篇文章,目录结构如下: 环境搭建(本文) 服务拆分 用户服务 产品服务 订单服务 支付服务 RPC 服务 Auth 验证 服务监控 链路追踪 分布式事务 期望通过本系列带你在本机利用 Docker 环境利用 go-zero 快速开发一个商城系统,让你快速上手微服务。完整示例代码:GitHub - nivin-stud

2022-02-16 16:21:04 1448

原创 Go 分布式令牌桶限流 + 兜底策略

令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一种算法。典型情况下,令牌桶算法用来控制发送到网络上的数据的数目,并允许突发数据的发送。

2022-01-13 10:26:09 313

原创 Go 中实现用户的每日限额(比如一天只能领三次福利)

如果你写一个 bug 管理系统,用了这个 PeriodLimit 你就可以限制每个测试人员每天只能给你提一个 bug。工作是不是就轻松很多了?:P如今微服务架构大行其道本质原因是因为要降低系统的整体复杂度,将系统风险均摊到子系统从而最大化保证系统的稳定性,通过领域划分拆成不同的子系统后各个子系统能独立的开发、测试、发布,研发节奏和效率能明显提高。但同时也带来了问题,比如:调用链路过长,部署架构复杂度提升,各种中间件需要支持分布式场景。为了确保微服务的正常运行,服务治理就不可或缺了,通常包括:...

2022-01-11 12:59:51 679

原创 Go 通过 Map/Filter/ForEach 等流式 API 高效处理数据

什么是流处理如果有 java 使用经验的同学一定会对 java8 的 Stream 赞不绝口,极大的提高了们对于集合类型数据的处理能力。intsum=widgets.stream().filter(w->w.getColor()==RED).mapToInt(w->w.getWeight()).sum();Stream 能让我们支持链式调用和函数编程的风格来实现数据的处...

2022-01-04 08:16:04 615

原创 梦想总是要有的 - 工作20年程序员的2021年度总结

我为啥给项目取 go-zero 这个名字,我希望解决问题的时候是我们回到原点去思考,而不是遇到钉子就找锤子。正所谓:做正确的事,正确的做事!

2021-12-27 13:28:46 4233

原创 用 Go + Redis 实现分布式锁

分布式锁,是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。

2021-12-14 16:13:25 1714

原创 详解布隆过滤器的原理和实现

为什么需要布隆过滤器想象一下遇到下面的场景你会如何处理: 手机号是否重复注册 用户是否参与过某秒杀活动 伪造请求大量 id 查询不存在的记录,此时缓存未命中,如何避免缓存穿透 针对以上问题常规做法是:查询数据库,数据库硬扛,如果压力并不大可以使用此方法,保持简单即可。改进做法:用 list/set/tree 维护一个元素集合,判断元素是否在集合内,时间复杂度或空间复杂度会比较高。如果是微服务的话可以用 redis 中的 list/set 数据结构, 数据规模非常大此方案

2021-12-09 11:34:32 8947

原创 一文讲透一致性哈希的原理和实现

为什么需要一致性哈希首先介绍一下什么是哈希Hash,一般翻译做散列,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。在分布式缓存服务中,经常需要对服务进行节点添加和删除操作,我们希望的是节点添加和删除操作尽量减少数据-节点之

2021-11-30 09:24:15 503

原创 微服务治理之如何优雅应对突发流量洪峰

微服务集群中,调用链路错综复杂,作为服务提供者需要有一种保护自己的机制,防止调用方无脑调用压垮自己,保证自身服务的高可用。

2021-11-23 10:12:03 468

原创 一文讲透自适应熔断的原理和实现

为什么需要熔断微服务集群中,每个应用基本都会依赖一定数量的外部服务。有可能随时都会遇到网络连接缓慢,超时,依赖服务过载,服务不可用的情况,在高并发场景下如果此时调用方不做任何处理,继续持续请求故障服务的话很容易引起整个微服务集群雪崩。 比如高并发场景的用户订单服务,一般需要依赖一下服务: 商品服务 账户服务 库存服务 假如此时 账户服务 过载,订单服务持续请求账户服务只能被动的等待账户服务报错或者请求超时,进而导致订单请求被大量堆积,这些无效请求依然会占用系统资源:cp

2021-11-18 08:03:50 1528

原创 go-zero 实战之 blog 系统

go-zero 实战项目:blog本文以 blog 的网站后台为例,着重介绍一下如何使用 go-zero 开发 blog 的用户模块。本文涉及的所有资料都已上传 github 仓库 kougazhang/go-zero-demo,感兴趣的同学可以自行下载。用户模块是后台管理系统常见的模块,它的功能大家也非常熟悉。管理用户涉及到前端操作,用户信息持久化又离不开数据库。所以用户模块可谓是 "麻雀虽小五脏俱全"。本文将详细介绍一下如何使用 go-zero 完成用户模块功能,如:用户登录、添加用户、删除

2021-10-25 09:09:28 979 1

原创 你了解微服务的超时传递吗?

为什么需要超时控制?很多连锁故障的场景下的一个常见问题是服务器正在消耗大量资源处理那些早已经超过客户端截止时间的请求,这样的结果是,服务器消耗大量资源没有做任何有价值的工作,回复已经超时的请求是没有任何意义的。超时控制可以说是保证服务稳定性的一道重要的防线,它的本质是快速失败(fail fast),良好的超时控制策略可以尽快清空高延迟的请求,尽快释放资源避免请求的堆积。服务间超时传递如果一个请求有多个阶段,比如由一系列 RPC 调用组成,那么我们的服务应该在每个阶段开始前检查截止时间以避免做

2021-10-18 10:57:06 728

原创 多图详解万星 Restful 框架原理与实现

rest框架概览我们先通过 go-zero 自带的命令行工具 goctl 来生成一个 api service,其 main 函数如下:funcmain(){flag.Parse()varcconfig.Configconf.MustLoad(*configFile,&c)ctx:=svc.NewServiceContext(c)server:=rest.MustNewServer(c.RestConf)deferserver.Stop()...

2021-10-11 07:48:04 402

原创 定制个机器人帮你和Ta聊天

## 自动聊天示例这是基于200万聊天记录训练出来的,你可以用自己和女朋友的记录训练了试试效果 :P至于微信机器人怎么用,你可以 `GitHub` 搜搜看哈聊天1:```textuser: 在吗?bot: 在user: 在干嘛呢?bot: 看电视user: 看啥电视呀bot: 活色生香user: 很好看吗?bot: 特搞笑user: 你在哪里呀?bot: 家里user: 家里就你一个人嘛?bot: 我喜欢一个人玩user: 那我过来找你?bot: 不可以,乖乖上班

2021-10-08 08:41:10 159

原创 高可用延迟队列设计与实现

延迟队列:一种带有 延迟功能 的消息队列 延时 → 未来一个不确定的时间 mq → 消费行为具有顺序性 这样解释,整个设计就清楚了。你的目的是 延时,承载容器是 mq。背景列举一下我日常业务中可能存在的场景: 建立延时日程,需要提醒老师上课 延时推送 → 推送老师需要的公告以及作业 为了解决以上问题,最简单直接的办法就是定时去扫表:服务启动时,开启一个异步协程 → 定时扫描 msg table,到了事件触发事件,调用对应的 handler几个缺..

2021-09-22 07:57:43 212

原创 服务注册与发现的原理和实现

什么是服务注册发现?对于搞微服务的同学来说,服务注册、服务发现的概念应该不会太陌生。简单来说,当服务A需要依赖服务B时,我们就需要告诉服务A,哪里可以调用到服务B,这就是服务注册发现要解决的问题。 Service B 把自己注册到 Service Registry 叫做 服务注册 Service A 从 Service Registry 发现 Service B 的节点信息叫做 服务发现 服务注册服务注册是针对服务端的,服务启动后需要注册,分为几个部分: 启动注..

2021-09-12 23:15:04 253

原创 高性能 C++ HTTP 客户端原理与实现

一、什么是Http ClientHttp协议,是全互联网共同的语言,而Http Client,可以说是我们需要从互联网世界获取数据的最基本方法,它本质上是一个URL到一个网页的转换过程。而有了基本的Http客户端功能,再搭配上我们想要的规则和策略,上至内容检索下至数据分析都可以实现了。继上一次介绍用Workflow可以10行C++代码实现一个高性能Http服务器,今天继续给大家用C++实现一个高性能的Http客户端也同样很简单!//[http_client.cc]#include"std..

2021-08-25 10:07:35 1026

原创 自适应负载均衡算法原理与实现

## 背景在选择负载均衡算法时,我们希望满足以下要求:1. 具备分区和机房调度亲和性 - 每次选择的节点尽量是负载最低的 - 每次尽可能选择响应最快的节点2. 无需人工干预故障节点 - 当一个节点有故障时,负载均衡算法可以自动隔离该节点 - 当故障节点恢复时,能够自动恢复对该节点的流量分发基于这些考虑,`go-zero` 选择了 `p2c+EWMA` 算法来实现。## 算法的核心思想### p2c`p2c (Pick Of 2 Choices)` 二选一: ...

2021-08-18 11:37:00 976

原创 一文搞懂一致性hash的原理和实现

在 go-zero 的分布式缓存系统分享里,Kevin 重点讲到过一致性hash的原理和分布式缓存中的实践。本文来详细讲讲一致性hash的原理和在 go-zero 中的实现。以存储为例,在整个微服务系统中,我们的存储不可能说只是一个单节点。 一是为了提高稳定,单节点宕机情况下,整个存储就面临服务不可用; 二是数据容错,同样单节点数据物理损毁,而多节点情况下,节点有备份,除非互为备份的节点同时损毁。 那么问题来了,多节点情况下,数据应该写入哪个节点呢?hash所以本质来讲:

2021-07-20 11:12:41 263 1

原创 听说过对 Go map 做 GC 吗?

在 Golang 中的 map 结构,在删除键值对的时候,并不会真正的删除,而是标记。那么随着键值对越来越多,会不会造成大量内存浪费?首先答案是会的,很有可能导致 OOM,而且针对这个还有一个讨论:[https://github.com/golang/go/issues/20135](https://github.com/golang/go/issues/20135)。大致的意思就是在很大的 `map` 中,`delete` 操作没有真正释放内存而可能导致内存 OOM。所以一般的做法:就是 **重建

2021-07-14 13:50:15 164

原创 一文讲懂服务的优雅重启和更新

在服务端程序更新或重启时,如果我们直接 `kill -9` 杀掉旧进程并启动新进程,会有以下几个问题:1. 旧的请求未处理完,如果服务端进程直接退出,会造成客户端链接中断(收到 `RST`)2. 新请求打过来,服务还没重启完毕,造成 `connection refused`3. 即使是要退出程序,直接 `kill -9` 仍然会让正在处理的请求中断很直接的感受就是:在重启过程中,会有一段时间不能给用户提供正常服务;同时粗鲁关闭服务,也可能会对业务依赖的数据库等状态服务造成污染。所以我们服务重

2021-06-07 13:16:19 346 1

原创 自适应微服务治理背后的算法

前言go-zero 群里经常有同学问:服务监控是通过什么算法实现的?滑动窗口是怎么工作的?能否讲讲这块的原理?熔断算法是怎么设计的?为啥没有半开半闭状态呢?本篇文章,来分析一下 go-zero 中指标统计背后的实现算法和逻辑。指标怎么统计这个我们直接看 breaker :type googleBreaker struct { k float64 stat *collection.RollingWindow proba *mathx.Proba}go-zero

2021-06-01 07:57:14 133

原创 一文带你搞懂 RPC 到底是个啥

RPC(Remote Procedure Call),是一个大家既熟悉又陌生的词,只要涉及到通信,必然需要某种网络协议。我们很可能用过HTTP,那么RPC又和HTTP有什么区别呢?RPC还有什么特点,常见的选型有哪些?1. RPC是什么RPC可以分为两部分:用户调用接口 + 具体网络协议。前者为开发者需要关心的,后者由框架来实现。举个例子,我们定义一个函数,我们希望函数如果输入为“Hello World”的话,输出给一个“OK”,那么这个函数是个本地调用。如果一个远程服务收到“Hello Wor..

2021-05-31 09:35:03 1105

原创 通用连接池帮你解决资源管理难题

前言群里老有同学问,go-zero 的 数据库 和 redis 库是否有连接池支持。先说结论:有的,可以放心大胆用!从框架设计来说,对于数据库连接这种资源当然是尽可能减少频繁操作:为业务减负提升框架自身的性能池化技术是一个通用化技术,本身就应该作为一个通用库支撑框架的上层业务所以不管是 sqlx,redis,以及 mongo,等以后可能要支持的数据源类型,底层的池化处理都是通用的;所以当开发者需要一个池化处理组件时,go-zero 也是提供的。池化技术支持的库就位于:core/resour

2021-05-26 08:34:56 98

原创 懂得取舍才是缓存设计的真谛

Previously前两篇文章(缓存稳定性 和 缓存正确性)跟大家讨论了缓存的『稳定性』和『正确性』,缓存常见问题还剩下『可观测性』和『规范落地&工具建设』稳定性正确性可观测性规范落地和工具建设上周文章发完之后,很多同学对我留的问题进行了深入的讨论,我相信经过深度的思考,会让你对缓存一致性的理解更加深刻!首先,各个 Go 群和 go-zero 群里有很多的讨论,但是大家也都没有找到非常满意的答案。让我们来一起分析一下这个问题的几种可能解法:利用分布式锁让每次的更新变成一个原

2021-05-24 08:58:00 100 1

原创 进程内缓存助你提高并发能力!

前言缓存,设计的初衷是为了减少繁重的IO操作,增加系统并发能力。不管是 CPU多级缓存,page cache,还是我们业务中熟悉的 redis 缓存,本质都是将有限的热点数据存储在一个存取更快的存储介质中。计算机本身的缓存设计就是 CPU 采取多级缓存。那对我们服务来说,我们是不是也可以采用这种多级缓存的方式来组织我们的缓存数据。同时 redis 的存取都会经过网络IO,那我们能不能把热点数据直接存在本进程内,由进程自己缓存一份最近最热的这批数据呢?这就引出了我们今天探讨的:local cache,本

2021-05-19 08:33:25 190

原创 缓存数据一致性 - 架构师峰会演讲实录

Previously缓存系统涉及的问题和知识点是比较多的,我主要分为以下几个方面来跟大家探讨:稳定性正确性可观测性规范落地和工具建设上篇 我们分析了缓存系统的稳定性,介绍了 go-zero 是怎么解决缓存穿透、缓存击穿、缓存雪崩问题的。比较浅显易懂,且具有比较强的实战意义,推荐一读。本文作为系列文章第二篇,主要跟大家探讨『缓存数据一致性』缓存正确性上篇文章提到,我们引入缓存的初衷是为了减小DB压力,增加系统稳定性,所以我们一开始关注的是缓存系统的稳定性。当稳定性解决之后,一般我们就会

2021-05-17 09:03:23 69 1

原创 如何让消息队列达到最大吞吐量?

你在使用消息队列的时候关注过吞吐量吗?思考过吞吐量的影响因素吗?考虑过怎么提高吗?总结过最佳实践吗?本文带你一起探讨下消息队列消费端高吞吐的 Go 框架实现。Let’s go!关于吞吐量的一些思考写入消息队列吞吐量取决于以下两个方面网络带宽消息队列(比如Kafka)写入速度最佳吞吐量是让其中之一打满,而一般情况下内网带宽都会非常高,不太可能被打满,所以自然就是讲消息队列的写入速度打满,这就就有两个点需要平衡批量写入的消息量大小或者字节数多少延迟多久写入go-zero 的.

2021-05-13 08:05:00 707 4

原创 缓存系统稳定性 - 架构师峰会演讲实录

前言大家好!我是万俊峰,go-zero 作者。感谢 ArchSummit 提供这么好的机会来跟大家分享一下go-zero的缓存最佳实践。首先,大家可以想一想:我们在流量激增的情况下,服务端哪个部分最有可能会是第一个瓶颈?我相信大部分人遇到的都会是数据库首先扛不住,量一起来,数据库慢查询,甚至卡死。此时,上层服务有怎么强的治理能力都是无济于事的。所以我们常说看一个系统架构设计的好不好,很多时候看看缓存设计的如何就知道了。我们曾经遇到过这样的问题,在我加入之前,我们的服务是没有缓存的,虽然当时流量还不算高

2021-05-10 10:31:38 123

原创 一文带你理解最简消息队列实现

最近在看公司的 redis queue 时,发现底层使用的是 go-zero 的 queue 。本篇文章来看看 queue 的设计,也希望可以从里面了解到 mq 的最小型设计实践。使用结合其他 mq 的使用经历,基本的使用流程:创建 producer 或 consumer启动 mq 生产消息/消费消息对应到 queue 中,大致也是这个:创建 queue// 生产者创建工厂producer := newMockedProducer()// 消费者创建工厂consumer := n

2021-05-07 10:12:47 91 1

原创 10行C++代码实现高性能HTTP服务

前言是不是觉得C++写个服务太累,但又沉迷于C++的真香性能而无法自拔?作为一个老牌C++程序员(可以看我 github 上十几年前的C++项目:https://github.com/kevwan ),这几天听一个好友跟我聊起他写的C++框架,说极简代码即可完成各种C++服务的开发,不禁让我心生好奇!于是我去研究了一下,发现确实有点意思!实战(干货)话不多说,我们来一起看看,10行C++代码怎么实现一个高性能的Http服务,轻松QPS几十万。Linus说:talk is cheap,show me t

2021-04-28 11:44:59 292 1

测试驱动开发 test driven development by example chm

测试驱动开发是一种可以在开发过程中控制忧虑感的开发方法。我并非指那些毫无意义的没有必要的担忧──(pow widdle prwogwammew needs a pacifiew)──而是指合理的担忧,担忧是否合理是个很困难的问题,不能从一开始就看出来。如果说疼痛自然就会叫 “停!”,那么担忧自然就会说“细心!”。

2011-04-11

实现模式 Implementation Patterns pdf

在这本新书《实现模式》里面, Kent Beck将自己多年形成的编程习惯以及阅读既有代码的体验凝练成了编程中的价值观、原则和77种实现模式。   沟通、简单和灵活的价值观应当被所有开发人员所铭记,无时或忘。局部影响、最小化重复、将逻辑与数据捆绑等原则同样是通用性的指导思想,比价值观更贴近编程场景,在价值观和模式之间搭建了桥梁。在77个实现模式中,每一个模式都覆盖了编写简洁、清晰、易扩展、易维护的代码这一原则的某个方面。它们为日常的编程提供了丰富翔实的参考依据,并告诉大家这些代码如何为降低沟通成本和提高有效产出提供保障。   本书适用于各个阶段的开发者群体,刚刚涉足软件开发领域的新人能够透过大师的眼睛来看待编程,了解编程的价值观与原则;而具有丰富经验的资深工程师则可以通过这些模式进行反思,探究成功实践背后的意义。把价值观、原则和开发实践结合之后,日常开发工作便会以崭新迷人的形式呈现在我们面前。

2011-04-11

空空如也

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

TA关注的人

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