• 工程项目失败案例_失败工程

    千次阅读 2020-09-04 02:35:02
    工程项目失败案例Not so long ago, our systems were simple: we had one machine, with one process, probably no more than one external datastore, and the entire request lifecycle was processed and handled...


    Not so long ago, our systems were simple: we had one machine, with one process, probably no more than one external datastore, and the entire request lifecycle was processed and handled within this simple world.


    Our users were also accustomed to a certain SLA standard — a 2-second page load time could have been acceptable a few years ago, but waiting more than a second for an Instagram post is unthinkable nowadays.


    (Warning: buzzwords ahead)


    When systems get more complex, with strict latency requirements and a distributed infrastructure, an uninvited guest crawls up our systems — request failure.

    当系统变得更加复杂,具有严格的延迟要求和分布式基础架构时,不请自来的来宾会爬上我们的系统- 请求失败。

    With each additional request to an external service within the lifecycle of a user request, we’re adding another chance for failure. With every additional datastore, we’re open to an increased risk of failure. With every feature we add, we risk increasing our latency long-tail, resulting in a degraded user experience in some portion of the requests.

    在用户请求的生命周期内,对外部服务的每个其他请求都会给我们增加另一个失败的机会。 随着每个其他数据存储的增加,我们面临更大的失败风险。 使用我们添加的每个功能,都可能会增加长尾等待时间,从而导致某些部分请求的用户体验下降。

    In this article, I’ll cover some of the basic ways we at Riskified handle failures in order to provide maximal uptime and optimal service to our customers.

    在本文中,我将介绍“ 风险承担”中处理故障的一些基本方法,以便为我们的客户提供最大的正常运行时间和最佳服务。

    失败的例子 (Failure by example)

    Every external service, no matter how good and reliable, will fail at some point. We at Riskified learned this the hard way when we experienced short failures with a managed, highly available service that almost resulted in data loss. That incident taught us the hard lesson that request failures should be handled gracefully.

    任何外部服务,无论多么出色和可靠,都会在某个时候失败。 当我们在受管的,高可用性的服务中经历了短暂的故障,几乎会导致数据丢失时,我们就冒了险。 那次事件教会了我们一个艰苦的教训,即应该优雅地处理请求失败。

    In Google’s superbly written Site Reliability Engineering Book, they describe The Global Chubby Planned Outage, in which a service was so reliable, that its customers were using it without taking into account the possibility of failure, and even using it without a real essential need, just because it was so reliable.

    在Google出色撰写的《网站可靠性工程手册》中,他们描述了“全球胖乎乎的计划内停机” ,其中一项服务是如此可靠,以至于其客户在使用该服务时并未考虑失败的可能性,甚至在没有真正必要的情况下使用该服务,只是因为它是如此可靠。

    As a result, Chubby, Google’s distributed locking system, was set a Service Level Objective (SLO) for service uptime, and for each quarter this SLO is met, the team responsible for the service intentionally takes it down. Their goal is to educate users that the service is not fail-safe and that they need to account for external service failures in their products.

    结果,为 Google的分布式锁定系统Chubby设定了服务正常运行时间的服务水平目标(SLO),并且在达到该SLO的每个季度,负责该服务的团队都会有意将其关闭 。 他们的目标是教育用户该服务不是故障安全的,他们需要考虑产品中的外部服务故障。

    So how should engineers handle request failures? Let’s cover some comment patterns:

    那么工程师应该如何处理请求失败? 让我们涵盖一些评论模式:

    重试中 (Retrying)

    Image for post

    Retrying a failed request can, in many cases, solve the problem. This is the obvious solution, assuming network failures are sporadic and unpredictable. Just set a reasonable timeout for each request you send out to an external resource, and the number of retries you want, and you’re done! Your system is now more reliable.

    在许多情况下,重试失败的请求可以解决问题。 假设网络故障是偶发性的且不可预测的,这是显而易见的解决方案。 只需为发送到外部资源的每个请求设置合理的超时时间,然后设置所需的重试次数即可! 您的系统现在更加可靠。

    Something to consider, however, is that additional retries can cause additional load on the system you’re calling, and make an already failing system fail harder.


    Implementing and configuring short-circuiting mechanisms might be a thing to consider. You can read more about it in this interesting Shopify engineering blog post.

    实施和配置短路机制可能是要考虑的事情。 您可以在这个有趣的Shopify工程博客文章中了解有关它的更多信息

    预取-在主流之外失败 (Prefetching — Fail outside of the main flow)

    Image for post

    One of the best ways to avoid failure while calling an external service is to avoid calling this service at all.


    Let’s say we’re implementing an online store — we have a user service and an order service, and the order service needs the current user’s email address in order to send them an invoice for their last purchase.


    The fact that we need the email address, doesn’t mean we have to query the user service while the user is logged in and waiting for order confirmation. It just means that an email address should be available.

    我们需要电子邮件地址这一事实,并不意味着我们必须在用户登录并等待订单确认时查询用户服务。 这仅意味着电子邮件地址应该可用。

    In cases of fairly static data, we can easily pre-fetch all (or some) user details from the user service in a background process. This way, the email is already available during order processing, and we don’t need to call the external service. In the event the service fails to fetch user details, that failure remains outside of the main processing flow and is “hidden” from the user.

    在数据相当静态的情况下,我们可以在后台进程中轻松地从用户服务中预取所有(或某些)用户详细信息。 这样,在订单处理过程中就已经可以使用该电子邮件了,我们不需要致电外部服务。 如果服务无法获取用户详细信息,则该故障将保留在主要处理流程之外,并且对用户“隐藏”。

    In his talk, Jimmy Bogard explains it better than I do (the link starts from his explanation about prefetching, although the whole talk is great!)

    Jimmy Bogard演讲中比我解释得更好(链接从他对预取的解释开始,尽管整个演讲很棒!)

    尽力而为 (Best efforting)

    Image for post

    In some cases, we should just embrace failure, and continue processing without the data we were trying to get. You’re probably wondering — if we don’t need the data, why are we querying it at all?

    在某些情况下,我们应该只接受失败,然后继续处理而无需尝试获取的数据。 您可能想知道-如果我们不需要数据,为什么还要查询呢?

    The best example we have for this in Riskified is a Redis-based distributed locking mechanism that we use to block concurrent transactions in some cases. Since we’re a low-latency oriented service, we didn’t want a latency surge in lock acquiring to cause us to exceed the SLA requirements of our customers. We set a very strict timeout on lock acquiring so that when the timeout is reached, we continue unlocked — i.e we prefer race conditions over the increase in latency for our customers. In other words, the locking feature is a “nice to have” feature in our process.

    在Riskified中为此提供的最好的示例是基于Redis的分布式锁定机制,在某些情况下,该机制用于阻止并发事务。 由于我们是面向低延迟的服务,因此我们不希望锁获取中的延迟激增导致我们超过客户的SLA要求。 我们对锁定获取设置了非常严格的超时,以便在达到超时后,我们将继续解锁-即,相对于为客户增加延迟,我们更喜欢竞争条件。 换句话说,锁定功能是我们过程中的“必备”功能。

    退回到先前或估计的结果 (Falling back to previous or estimated results)

    Image for post

    In some cases, you may be able to use previous results or sub-optimal estimations to handle a request while other services are unavailable.


    Let’s say we’re implementing a navigation system, and one of the features we want is traffic jam predictions.


    We’d probably have a JammingService (not to be confused with the Bob Marley song), that we’d call with our route to estimate the probability of traffic jams. When this service is failing, we might choose a sub-optimal course of action, while still serving the request:

    我们可能会有一个JammingService(不要与Bob Marley的歌曲混淆),我们将使用该路线进行调用以估计交通拥堵的可能性。 当此服务失败时,我们可能会选择次优的操作方式,同时仍然处理请求:

    1. Using previous results: we might cache some “common” jam predictions and serve them, we might even pre-fetch the jam estimation for the most commonly used routes of some of our users.

    2. Estimate a result: Our service can hold a mapping of mean jam estimation per region and serve that estimation for all requests for routes in the region.


    In both examples, the solution is obviously not optimal, but probably be better than failing a request. The general idea here is to make a simple estimation of the result we’re trying to get from the external resource.

    在两个示例中,解决方案显然都不是最优的,但可能比失败的请求更好。 这里的总体思路是对我们试图从外部资源获得的结果进行简单的估计

    延迟回应 (Delaying a response)

    Image for post

    If the business of the product allows it, it’s possible to delay the processing of the request until the problem with the external resource is solved.


    As an example, let’s take the JammingService from the previous solution — when it fails we can decide to queue all requests in some internal queue, return a response to the user that the request cannot be processed at the moment, but a response will be available as soon as possible via push notification to the user’s phone, or via webhook for example.


    This is possible mostly in asynchronous services, where we can separate between the request and the response. (If you can design the service to be asynchronous to begin with, that’s even better!)

    这在异步服务中很可能实现,在异步服务中,我们可以在请求和响应之间进行分隔。 (如果您可以从一开始就将服务设计为异步的,那就更好了!)

    实现简化的后备逻辑 (Implement simplified fallback logic)

    Image for post

    On some mission-critical features, a more complex solution is needed. In some cases, the external service is so critical to our services, that we’d have to fail a request if the external service fails.

    在某些关键任务功能上,需要更复杂的解决方案。 在某些情况下,外部服务对于我们的服务至关重要,因此如果外部服务失败,我们将不得不使请求失败。

    One of the solutions we devised for such critical external resources, is to use “simplified” in-process versions of them. In other words, we’re re-implementing a simplified version of the external service as a fallback within our service, so that in the event the external service fails, we still have some data to work with, and can successfully process the request.

    我们为此类关键外部资源设计的解决方案之一是使用它们的“简化”进程内版本。 换句话说,我们将重新实现外部服务的简化版本,作为我们服务中的后备,以便在外部服务失败的情况下,我们仍然可以使用一些数据,并且可以成功处理请求。

    As an example, let’s go back to our navigation system. It might be such an important feature of our system, that we want each request to have a fairly good traffic jam estimation, even if our JammingService is down.

    作为示例,让我们回到导航系统。 这可能是我们系统的重要功能,即使我们的JammingService出现故障,我们也希望每个请求都具有相当好的交通拥堵估计。

    Our JammingService probably uses various complex machine learning algorithms and external data sources. In our simplified fallback version of it, we might choose, for example, to implement it using a simple greedy best-first algorithm, with simple optimizations.

    我们的JammingService可能使用各种复杂的机器学习算法和外部数据源。 在我们的简化后备版本中,例如,我们可能选择使用简单贪婪的“最佳优先”算法以及简单的优化来实现它。

    In this case, even if there’s a failure of the JammingService, some fairly good traffic jam estimation is available within our navigation system.


    This isn’t optimal since now we need to maintain two versions of the same feature, but when the feature is critical enough, and may be unstable enough — it could be worth it.


    结束思想-作为一种生活方式失败 (Closing thoughts — Failing as a way of life)

    At school, I was quite a bad student, so failing is not new to me. This taught me that as an engineer, anything I lay my hands on might fail, and simply catching the exception is not enough — we need to do something when we catch it, we still need to provide some level of service.

    在学校里,我是一个很糟糕的学生,所以失败对我来说并不新鲜。 这告诉我,作为一名工程师,我动手做的任何事情都可能失败,仅捕获异常是不够的-在捕获异常时我们需要做一些事情,我们仍然需要提供一定水平的服务。

    I encourage you to dedicate a big part of your time to failure handling, and to make it a habit to announce your systems are production-ready only when you handle your failures in a safe and business-oriented way.


    As always, you’re welcome to find me at my Twitter handle: @BorisCherkasky

    与往常一样,欢迎您在我的Twitter句柄中找到我: @BorisCherkasky

    翻译自: https://medium.com/riskified-technology/engineering-for-failure-f73bc8bc2e87


  • 剖析一个成功的失败案例项目
    • 为什么会有这个题目,来自电影《寒战2》,警方对寒战行动的总结是“最成功的失败案例”,怎么理解呢,即负正得负,虽说行动最终逮捕了头目李家俊,案件看似结束,但始终未找到失踪的冲锋车,背后谜题始终未知。
    • 联想到工作中,视乎也有类似的场景,所以就想整理一篇文章记录下。



    • 一直在思考一个产品的价值,体现在哪里?
    • 记得在一开始就咨询过leader,如何才能让大家拧成一股绳,撸起袖子加油干?当时给出过一个观点-挖掘产品价值,充分让团队认识产品的价值,认可他,才会有可能一直往前冲。
    • leader木有直面回答该问题,得到答复即从管理流程上来推动团队成长。
    • 观点无所谓对错,从不同角度看待问题,都是没有错的。




    • 指挥多
    • 做事少
    • 想破脑
    • 价值小
    • 组织臃




    • 服务简
    • 功能小
    • 拆分多
    • 活很少
    • 人很多



    • 微信群是吃瓜群众的据点,每次看到这么一番景象:某某在群里分享了头条文章《美团技术实战》、《阿里技术实战》、《百度实战》咱们可以借鉴,底下里面会有人跟进,收到,回去研究研究。
    • 企业最新的技术,一般都不会公开在网络上,这么跟法,可跟不上潮流哟。
    • 企业不差钱,请阿里专家过来分享,毫无下文。
    • 新年要铺设上万家店铺,过了半年似乎不了了之。
    • 蜘蛛人是理所当然的存在,一帮人哼哧哼哧的开发新功能,一帮人跟在后面哼哧哼哧的堵住漏洞。似乎木有意识到蜘蛛人就是管理流程最大的漏洞,不干掉蜘蛛人,产品是很难提升的,形成一个反馈电路。




    阿里的领导花名 @toy 说过一句话,还是挺受用的,切记不要成为你需要企业,要让企业需要你。







  • 一个失败项目管理案例

    千次阅读 2020-07-05 21:56:33
    从 2020 年三月底开始做一个设计管理平台项目,我被指派为这个项目项目经理,项目成员包括产品经理(1)、后端(4)、前端(2)、UED(1)、UI(1),共 10 位成员。从项目正式启动,到七月初,第一个被需求方、...


    从 2020 年三月底开始做一个设计管理平台的项目,我被指派为这个项目的项目经理,项目成员包括产品经理(1)、后端(4)、前端(2)、UED(1)、UI(1),共 10 位成员。从项目正式启动,到七月初,第一个被需求方、发起人都认可的 V1.0 版本原型才确定。在这接近四个月的过程中,几乎项目管理的全部坑都踩了个遍,特别是干系人管理、冲突管理以及变更管理与项目管理的基本要求几乎完成全部背离。这个项目为何会走到这个几乎失控的地步呢?


    1、 项目定位不清晰(干系人管理)
    此处项目经理有较大责任,需求的确认完全交由产品经理处理,但是在项目的进行过程中,明确发现产品经理确认的需求、设计的原型得不到技术领导与业务方的双方的认可,此时应该组织需求讨论会议,让相关的干系人(技术领导、业务领导)需同时参加,明确 alpha 版本的需求。而不是只埋头在进度控制中,需求没有得到核心干系人一致认可的时候,做的越多,偏离越远。
    2、 责任权限不清晰
    项目中有产品经理、项目经理,在项目启动阶段并没有明确说明产品经理与项目经理的职责与权限。产品经理管理需求,但是未对干系人进行有效管理,项目经理此时要不要去强力干预对干系人进行管理 ?
    项目节点是在 6.30 号发布第一版,实际开发时间有两个月,产品一边出原型,开发同时同步进行开发。按照约定的第一个原型版本,是有足够的时间进行开发的。这时对接的业务方要求增加四个审批流程,并且这四个流程可以循环往复交替进行。当时接到产品经理的这个需求时,我第一反应是拒绝的。这个地方描述增加一下产品经理背景描述,产品经理有深厚的行业背景,产品开发经验稍有不足。 我跟产品进行沟通,询问是否可以放到下个迭代,得到的回复是可以,但是没有这个流程,用户是无法使用这个产品的。我考虑到有当时还有较为充沛的时间,同时这个需求设计到核心的功能,决定在这个开发周期内将这个新增的流程功能开发完成。最终完成了任务流程功能的开发,但是由于业务逻辑较为复杂,一方面产品并未完全梳理清晰全部业务逻辑,另一方面,此功能为经过充分测试,在一次产品演示的时候,技术领导表示该流程交互太复杂,业务领导表示这这个任务流程完全不是他想要的。
    这里的问题是在需求原型确定的情况下,项目经理应该缩短每个迭代的任务周期,最多不超过三周。一个迭代版本的时间近两个月的时间,最后核心干系人告诉你这不是他想要的。应该在每完成一次迭代的时候,举行迭代回顾会议,演示产品,让核心干系人对阶段成果进行演示。必须完成第一个迭代的验收,才能进行下一个阶段的开发工作。 如若不然,还不学学新技术实在。


    这种情况在 PMO 的强力介入后,得到了极大的改善,推了一个核心干系人都满意的 v1.0 版本原型,那么是怎么做到的呢 ?
    1、强力的干系人管理。 每次进行需求确认时,确保全部干系人认可。具体流程是先与业务对接人进行需求讨论,然后与需求领导进行需求原型评审确认(需求确认时最好技术领导在场),最后与技术领导进行原型评审确认。若有异议,组织与业务领导的讨论,直到双方都同意该原型为止,最终确定原型为 1.0 版本。不管领导多么强势,一定要坚持自己的立场,守住自己的原则,不跟着领导天马行空,只做跟需求方确认过的需求,其他一切需求都要经过讨论后再进行设计开发。
    2、严格的范围控制。确定一个最小 mvp 进行迭代,迭代周期改为 2~3 周,这两到三周内任何变更都不做,只记录变更点,在下一个迭代启动时,再进行变更的讨论。


    这个项目遇到的问题都是项目管理中较为常见的问题。 PMO 介入后,用这一套组合拳,虽然非常简单,甚至是许多方法有点老生常谈,但是很有效。项目的新原型,业务方与技术方领导都较为满意。其实许多项目的点,我们开始也有做,但是没有强有力的去执行,遇到强势的领导以及需求方就妥协了。这种妥协的结果只能是一个妥协的产品,在可用性、稳健性上都无法得到保证。

  • 失败项目案例分析

    千次阅读 2015-01-06 22:43:22






  • Scrum 敏捷开发 基础及失败成功案例分析 什么是敏捷开发方法?何谓Scrum? 看到敏捷,想到的是什么? 有人说是动作敏捷、反映灵敏,速度快; 有人说是多快好省; 有人说是没有制度,松散的工作方式;...
  • 大数据,时下最火热的词之一,覆盖了经济、互联网、物流等各行各业,无论是大公司知名公司还是中小... 大数据项目失败案例  对数据过于相信:2008年,Google第一次开始预测流感就取得了很好的效果,比美国疾病预
  • 项目案例分享三:DHCP授权失败

    千次阅读 2017-09-01 13:47:14
    最近在做AD、DHCP和WSUS的升级项目,在两百多个Active Directory 站点之间的VPN链路带宽也比较小,所以升级后会偶尔出现各种各样的问题,但大部分还是由于同步的问题,今天一起来看看AD升级后DHCP无法授权的问题吧。...
  • 失败成就伟大:谷歌的23个失败案例

    千次阅读 2017-04-21 23:54:38
    失败成就伟大:谷歌的23个失败案例 大数据文摘作品,转载具体要求见文末 选文 | Aileen 翻译 | 蒋晔 校对 | 范玥灿 一路上不押注于几次失败,你是不可能成为一个像谷歌这样的互联网巨头。 企业创新是艰难的。即使是...
  • 华为失败案例摘录

    千次阅读 2019-07-12 10:34:55
  • 17个失败案例

    千次阅读 2012-11-19 11:17:52
  • IT风投失败案例和教训

    千次阅读 2017-12-19 09:36:25
  • system分区挂载失败案例分析

    万次阅读 2018-06-28 16:51:08
    最近项目需要升级高通base,具体从LA3.0.1升级到LA3.1.1,发现一个问题:Android go版本可以正常开机,但是Android O版本无法正常开机,UART log显示system分区挂载失败,会自动进入fastboot。[ 6.943432] EXT4-fs ...
  • Nginx - 记一次Nginx端口转发失败案例

    千次阅读 2020-09-06 21:31:26
    文章目录项目场景:问题描述:原因分析:解决方案: 项目场景: 生产环境F5 后面配置了4个Nginx , F5端口开放了 8585端口 ,用于将请求 问题描述: 原因分析: 提示:这里填写问题的分析: 例如:Handler ...
  • Redis项目案例

    千次阅读 2019-07-26 17:17:06
    实战案例 基于redis缓存实现用户增删改查的项目分析需求项目运行环境项目搭建1、父工程2、子工程运行效果 基于redis缓存实现用户增删改查的项目分析 需求 redis的作用是实现数据的缓存,以此来提升系统性能。 在本...
  • 本篇博文记录错误的需求分析对项目的重大影响: 需求:生成年度报表,每个月对报表数据更新,要求历史数据不能丢失 错误的需求分析:按月生成报表,不能在一张报表中看到每个月的数据,而是切割成了12张报表 转载...
  • 无人驾驶失败案例汇总

    千次阅读 2019-11-27 19:47:08
    受到该影响,Uber在坦佩、旧金山、匹兹堡以及多伦多都停止了他们的无人驾驶项目。   这起事故,被认为是世界上第一起自动驾驶汽车撞人死亡案件。     事故一出,对于无人驾驶可靠性的争论又一...
  • 软件项目管理案例教程 第4版 课后习题答案

    万次阅读 多人点赞 2019-11-30 01:18:05
    软件项目管理案例教程 第4版 课后习题答案 第一章 一、填空题 1.敏捷模型包括(4)个核心价值,对应(12)个敏捷原则。 2.项目管理包括(启动过程组)、(计划过程组)、(执行过程组)、(控制过程组)、(收尾...
  • 2017 十大失败 AI 案例

    千次阅读 2017-12-27 00:00:00
    价值 | 思考 | 共鸣简评:整体来说,2017 年的 AI 进步还是不...但即使 AI 有这么多里程碑式的事件,仍有不少人对 AI 持怀疑态度,特别是见过了 2017 年不少 AI 的失败案例之后。一些新技术,新想法在努力的执行它们的
  • 从缺乏产品与市场的相配到团队成员的不和,通过分析101个创业失败案例,我们总结了创业失败的前20大原因。
  • 项目管理案例剖析

    千次阅读 2007-08-02 10:40:00
    摘要: 成功项目失败项目的最大不同在于项目管理。曾经有这样一个项目,对于客户,是新开展的业务;对于集成商,大部分技术是未曾使用过的。通常说来,这样的项目存在极大的风险,那么,请看看其中的项目管理……1...
  • C++项目案例

    万次阅读 多人点赞 2018-05-12 08:54:23
    #include <iostream> #include <string> #include "windows.h" #include <iostream> #include <fstream> #include <vector> #include <conio.h> #include <...#def...
  • 中国知名企业ERP失败案例分析

    千次阅读 2009-07-25 09:35:00
  • 软件项目管理案例复习题

    万次阅读 2020-03-27 09:36:51
    项目管理包括(启动过程组)、(计划过程组)、(执行过程组)、(控制过程组)、(收尾过程 5个过程组。 、搬家属于项目。(√) 、项目是为了创造一个唯一的产品或提供一个唯一的服务而进行的永久性的努力。(×...
  • 信息系统项目管理案例分析

    千次阅读 2015-07-08 22:21:35
    M是负责某行业的一个大型信息系统集成项目...1.请用150字以内的文字,描述项目失败的可能原因 2.请用200字以内的文字,说明你认为M应该怎样做才能让小张作为子项目的项目经理,并避免项目失控? 3.请用400字以内
  • 联想ERP项目实施案例分析(3):业务蓝图定义与团队建设一、召开项目誓师暨启动大会,并进行全集团范围ERP基础知识培训项目启动及誓师大会召开十分必要,将“一把手”实施项目决心很直接传递给了各方面人员,将项目...
  • 项目终于熬上线,项目上线前组长请假了,回来时快上线了我才晓得有老项目需要对接,部分业务调用老项目,RPC框架用的是dubbo,通过zookeeper注册服务,新系统调用老系统。于是赶紧改接口 ┭┮﹏┭┮。项目采用maven...
  • Springboot项目简单案例-登陆

    千次阅读 2019-06-11 10:04:55
    项目代码已上传至github,下载地址:...1.通过springboot官网新建项目springbootLogin 2.点击generate the project 3.将压缩包解压并导入到eclipse中,Eclipse->file->import->ma...
  • 电商项目PC端网页支付,采用支付宝支付,用户登录成功,购买商品支付宝支付付款成功跳转到网站首页。
  • 公司需求控制失败案例

    千次阅读 2009-02-10 15:17:00
  • 项目管理协会(PMI)认为成功的项目必须满足六个条件: 按时交付。 成本在预算范围内。 能按照当初的设计正常运行。 有人使用。 满足项目最初的目标。 项目出资方对项目满意。 WikiPedia 上也有一个网页,列出来...



1 2 3 4 5 ... 20
收藏数 77,720
精华内容 31,088