当前位置: 首页 > 网络应用技术

RabbitMQ模型的详细说明和示例

时间:2023-03-09 00:07:17 网络应用技术

  首先做,然后考虑其他。

  MQ(消息队列)消息队列,FIFO首先发布。服务器的请求首先添加到消息队列中,然后分发消息队列。

  它也是上游和下游传输消息的跨过程通信机制。在Internet体系结构中,MQ是一种非常常见的新闻通信服务,用于上游和下游“逻辑解耦 +物理解决方案”。

  使用MQ之后,上游只需要将消息发送到MQ。无需注意下游是否接受。MQ将监督消息接收,而不再需要依靠其他服务。

  1.1.1流量流了一个示例。如果订单系统最多可以处理10,000个订单,则该处理能力应足以在正常时间内下订单。我们可以在正常时间内一秒钟后返回结果。在高峰期,如果有20,000个订单操作系统无法处理,则只能限制在订单超过10,000之后的订单。

  使用消息队列作为缓冲区,我们可以取消此限制,并在一秒钟内将订单分散到一段时间内处理它。目前,在下订单超过十秒钟后,有些用户可能能够获得成功的操作,但是它比无法放置它要好。单曲体验更好。

  将消息添加到MQ,然后通过MQ分配峰值处理。处理时间将增加,但是由于消息过多而导致的服务器风险降低。

  1.1.2应用程序解耦以E -Commerce应用程序为例。在应用程序中,应用程序中有订单系统,库存系统,物流系统和支付系统。

  用户创建订单后,如果耦合调用库存系统,物流系统和支付系统,则任何子系统都会失败,这将导致异常订单操作。当转换变成消息队列时,系统之间的问题是系统之间的问题。将减少很多。例如,由于故障,需要修复物流系统几分钟。

  在这几分钟内,可以在消息队列中完成由物流系统处理的内存系统,并且可以正常完成用户的订单操作。恢复物流系统后,可以通过订单信息进行处理。中间用户无法感受到物流系统的故障并提高系统的可用性。

  用户下订单后,其余操作(例如:检索付款系统,库存系统等)将不再直接将消息发送给所有系统每个系统分别与每个系统中的每个系统一样,当系统处理系统中的每个系统时,都会处理返回结果和MQ将监督消息的执行。

  1.1.3某些服务之间的异步处理是异步的。例如,A呼叫B,B需要长时间执行,但是A需要知道何时可以执行B。过去,通常有两种方法。B的查询API QUERY.或A提供回调API。执行B后,将API通知致电服务。

  这两种方法不是很优雅。使用消息总线可以轻松解决此问题。呼叫B服务后,您只需要监视B处理的消息即可。当B处理完成后,将发送一条消息到MQ。MQ将是MQ. Repost此消息到Service。在这样,A服务不需要回忆B的查询API,也不需要提供回调API。相同的B服务不需要执行这些操作。服务也可以及时处理。

  A呼叫B和执行B方法,但是A不知道执行B多长时间,然后您需要继续查询B是否完成。

  可以通过MQ解决此问题。A将需要执行B的消息发送到MQ,MQ将由MQ告知。当执行B时,将通知MQ,最后将通知MQ。

  A不需要继续询问B的执行进度来减少消耗。

  1.2.1单节吞吐量,及时性MS级别,高可用性,基于主要和从属体系结构的高可用性的优势

  现在,官方社区的缺点保持越来越少,而ActiveMQ 5.X越来越少,并且较高的吞吐量情景较少。

  1.2.2卡夫卡的杀手,在大数据领域谈论消息传递时,它不能围绕kafka。被LinkedIn,Uber,Twitter,Netflix等大型公司采用。

  优势非常好,单基机器人写作TPS每秒约为100万件。最大的优点是吞吐量很高。可用的及时性MS级别非常高。Kafka分发了。一个数据多个副本。一些机器是停机时间。他们不会丢失数据,也不会导致无法使用。消费者使用拉动方法获取消息。该消息是有序的。它可以确保所有消息都被消耗掉,并且仅消耗一次;有出色的第三方Kafkaweb管理接口Kafka-Manager;它们在日志领域相对成熟,并由多个公司和多个开源项目使用;它们被使用;

  功能支持功能相对简单,主要支持简单的MQ函数,大数据和日志集合领域中的真实时间计算是大规模使用

  劣势KAFKA具有64多个队列/分区,负载将具有显着的高度现象。队列越多,负载越高,发送消息响应的时间更长。使用短轮查询的使用取决于询问,消费,消费,消费,消费,消费,消费的时间间隔时间不支持重试;支持消息的顺序,但是在代理下降后,将生成消息;社区更新很慢;

  1.2.3.RocketMQRocketMQ来自阿里巴巴的开源产品。它以Java语言实现。设计时,我指的是Kafka并进行一些改进。阿里巴巴广泛使用了秩序,交易,充值,流量计算,消息推动,日志流处理,binglog分布和其他场景。

  单个机器吞吐量为100,000的优点,可用性很高,分布式体系结构,消息可以丢失0,MQ函数相对完整,仍然分配,可伸缩性很好,支持了积累10亿级,不会由积累引起。性能降低。源代码是Java。我们可以自己阅读源代码并自定义公司的MQ。

  不利的客户并不多。目前,Java和C ++,其中C ++不成熟;社区活动是平均水平的,并且在MQ核心中没有JMS等接口。一些系统需要修改大量代码以迁移。

  1.2.4 RabbitMQ2007已发布,该兔子是根据AMQP(高级消息队列协议)完成的。可重复使用的公司消息系统是最主流消息中间件之一。

  由于Erlang语言的高平行特征,优势是良好的。吞吐量为10,000级,MQ功能相对完整,健壮,稳定,易于使用,跨平台,支持多种语言,例如:Python,Ruby,.Net,.net,Java,JMS,JMS,C,C,PHP,PHP,actionScript,XMPP,XMPP,Stomp,Stomp,stomp,stomp,stomp,stomp,stomp,stomp,stomp,stomp,stomp,stomp,stomp,stomp,stomp,stomp,stomp,stomp,c.等,支持Ajax文档完整;

  开源提供的管理接口非常好,使用非常有用,社区活动很高;更新频率很高。

  您需要建立一个Erlang环境。

  商业版的缺点需要收取费用,学习成本很高

  1.3.1 Kafkkakafka的主要功能是基于拉的模型,用于处理消息消耗并追求高吞吐量。第一次的目的是收集和传输数据收集和传输。large公司建议您可以选择。如果有日志收集功能,它绝对是Kafka的首选。

  1.3.2 RocketMQ天生是为金融互联网领域而诞生的。对可靠性的要求很高的场景,尤其是电子商务和业务峰的订单扣除额。这些业务方案在Ali Double 11中经历了许多测试。如果您的业务具有上述并发场景,建议选择RocketMQ。

  1.3.3 RabbitMQ结合了Erlang自己的良好性能微秒级的同时优势,社区活动相对较高,并且管理接口非常方便。complete.completeRabbitmq。

  RabbitMQ是消息中间件:它接受并转发消息。

  您可以将其视为快递站。当您想发送包裹时,您将包裹放在快递站。快递员最终将使您的快递员送往收件人。立式,快递员将帮助您通过Express。

  RabbitMQ和Express交付站之间的主要区别在于,它不处理Express邮件,而是接收,存储和重新发布消息数据。

  RabbitMQ不会处理消息,并且仅接收,存储并转发消息。

  2.2.1生产者生成数据发送消息的过程是生产者。

  2.2.2开关是RabbitMQ的非常重要的部分。一方面,它收到了生产者的消息,另一方面,它将消息推向队列。开关必须知道如何处理收到的消息。是将消息推入特定队列还是将其推入多个队列,还是丢弃消息,必须由开关类型确定。

  开关必须绑定到队列。迈克消息分布。

  2.2.3队列队列是兔子中使用的数据结构。尽管新闻流过兔子和应用程序,但它们只能存储在队列中。队列仅受主机的内存和磁盘限制的限制,这实际上是一个大的消息缓冲区。消费者可以尝试从队列接收数据。这就是我们使用队列的方式。

  2.2.4消费者的消费与接收的含义相似。消费者主要是一个等待收据的程序。请注意,生产者,消费者和消息通常不在同一台计算机上。同一应用程序可以是生产者和消费者。

  队列中的消息只能被一个消费者接受;相同的消息通过交换机发送到多个队列,因此多个消费者可以接收此消息。

  消费者只能接收消息并将其分发给特定处理器,而消费者本身也不处理消息。

  2.3.1核心模式

  2.3.2工作原理2.3.2.1工作原理概述

  2.3.2.2名词说明2.3.2.3总结了消费者在一段时间内完成任务。如果其中一位消费者处理一项漫长的任务,并且只完成突然悬挂的任务,那将会发生什么?

  一旦RabbitMQ传递了一条消息,就会立即将消息标记为已删除。在这种情况下,消费者突然挂断了电话,我们将失去正在处理的新闻。以及发送给消费的消息,因为它无法接收它。

  为了确保在发送过程中不会丢失消息,RabbitMQ引入了消息响应机制。

  答:收到消息并处理了消息后,消费者告诉RabbitMQ,它已经进行了处理,并告诉RabbitMQ删除消息。这可以避免在转发过程中丢失消息的丢失。

  为了避免工作线程在收到消息后未完成消息,删除了队列中的消息,并添加了消息响应机制。如果工作线程未完成消息,则无法通知队列。处理任务后,可以通知队列以避免新闻的丢失。

  发送消息后,它被认为是成功的。该模型需要在高通量和数据传输安全性方面进行权衡,因为如果在接收到消息之前连接该消息,则将连接消费者或关闭通道(频道)。然后丢失了新闻。

  当然,另一方面,此模型可以传递可以传递过载且不会限制传递消息数的信息。这可能会使消费者的信息为时已晚。最后,记忆已经用尽,这些消费者的线程被操作系统杀死,因此该模型仅适用于消费者可以有效和有效的情况可以以一定的速度处理这些消息。

  尽管自动响应看起来很漂亮,但是当消费者收到消息时,响应队列 - 已处理消息,这将在收到消息后导致处理过程。处理和执行。

  消费者在收到消息时对队列做出了回应,并且不知道随后的处理。随后的处理情况可能会与消息紧密相关。消费者治疗效率的要求太高,通常不适合实际开发方案。

  通常,手动答案用于关闭答案(设置相应的参数)。开发人员对响应的开发。

  3.3.1相关方法(Java)3.3.2确认方法的相关参数,您可以设置一个参数:要设置链接中的消息是否应一次回答;

  如果设置为true,则由于此设置而在链接中收到的所有频道都将完成。(一批消息的到达将由于处理之一的处理而整体处理到MQ,并且不会注意其他消息的处理)

  通常设置为false,以及对队列的答案。

  设置为true,由于对队列的响应之一,队列将被队列视为同一组。

  如果由于某些原因而失去了连接(频道已经关闭,连接已关闭或丢失了TCP连接等),则该消息将无法发送ACK确认。RabbitMQ将了解该消息没有完全处理并将重新调整。如果其他消费者目前可以处理它,它将迅速将其分发给另一个消费者。这样,即使消费者偶尔死亡,也可以确保确保不会丢失任何消息。

  如果没有处理,但是由于某种原因而关闭处理工作线程,因为新闻没有对队列的响应,则队列将在及时分发给其他消费者。

  :如果设置了新的队列名称,则必须首先启动生产者,否则如果首先是消费者,它将报告错误。

  1.创建消费者

  2.创建生产者

  3.操作生产者和消费者阻止消费者缓慢

  您会看到快速消费者已经处理了3。其他DD最初是由另一个消费者处理的,但设置了手动答案并停止了缓慢的消费者。

  刚才我们已经看到了如何处理任务不要丢失,但是如何确保当兔子服务停止时,消息生产者产生的消息不会丢失。除非被告知不应该这样做,否则它会忽略队列和消息。

  确保消息不会丢失,需要两件事:我们需要将队列和消息标记为持久。

  队列的持久性和新闻的耐用性被分开,为了确保消息的持久性,队列还需要耐用。

  消息的发件人需要设置队列的耐用性。当发件人声明消息队列时,将参数设置队列设置为最后一个。设置队列持久后,也将相应地识别RabbitMQ的背景管理接口。

  队列持续持续后,兔子的重新启动仍在那里。如果队列不持续,那么权力就会损失。

  如果您需要修改队列的持久状态和已声明要修改的队列,则需要首先删除当前队列,然后创建一个新的队列。如果您直接在长时间的队列上进行修改,将报告错误。

  只要修改了队列的参数,就需要删除原始队列并重新创建新队列。

  该消息是耐用的,并且需要在消息推送过程中执行参数设置,并且该参数设置为:支持消息持久性。

  将消息标记为持久性并不能完全保证不会丢失该消息。尽管它会告诉RabbitMQ将消息保存到磁盘上,但是当消息准备存储在磁盘上时,该消息仍然存在剪接,但是它尚未存储。该消息仍然是缓存的。这次,磁盘不是真正写的。耐用的保证不是强大的,但是对于我们简单的任务队列,这已经足够了。

  消息的持久性不能完全保证。如果在持久过程中发生事故,仍然可能会失去新闻。

  在最简单的RabbitMQ分发消息中,它是用于旋转训练的分布式(每个队列都按顺序分配),但是在某种情况下,此策略不是很好。例如,有两个消费者处理任务。1处理速度非常快,而另一个消费者2处理速度非常慢。目前,我们仍然使用旋转训练的营业额来快速处理。消费者一直在工作。在这种情况下,这种分发方法实际上不好,但是RabbitMQ并不知道这种情况仍然公平分布。

  为了避免这种情况,我们可以设置参数通道。Basicqos(1);变成不公平的分裂。

  兔子默认值用于使用旋转分布。当工作线程空闲时,任务将不会发送到空闲工作线程,但是将等待下一个旋转询问和分发工作线程。这将允许大量空闲时间来处理快速处理线程。通过在消费者接收消息之前设置参数,打开不公平的分配分配,那些可以工作的人。

  只有不公平划分的消费者才能具有这种特征。

  新闻本身的发送是异步发送的,因此,随时必须有更多的新闻,并且消费者的手动确认也异步。开发人员可以限制此缓冲区的大小,以避免在缓冲区中无限无限的无弹性消息。这次,可以通过使用Basic.QOS方法设置“ pre -counting”值来完成。

  该值定义了频道上允许的最大未解决消息的数量。一旦数量达到配置的数量,RabbitMQ将停止在频道上传递更多消息,除非确认至少有一条未改变的消息,例如频道5、6、7、8和频道和频道的未解决消息和频道的预付费计数设置为4。此时,RabbitMQ不会在频道上传递任何消息,除非至少有一条未知的消息是例如,例如,TAG = 6的新闻刚刚被证实,Ack,RabbitMQ将感知到这种情况并发送另一个消息。

  消息响应和QoS Pre -pre -value对用户吞吐量有重大影响。从基因上讲,预付以提高向消费者传达消息的速度。尽管自动回答传输消息速率是最好的,但已传输但未处理的消息数量在这种情况下,将增加消费者RAM的消耗量(随机访问记忆),应无限制地使用无限的USETHE前处理自动确认模式或手动确认模式。

  消费者消耗了很多新闻。如果未确认,它将导致消费者连接到节点的内存消耗。因此,找到合适的预价值是重复试验的过程。不同的负载不同于100到300。该范围内的值通常提供最佳的吞吐量,并且不会给消费者带来太大风险。预价值是最保守的。当然,这将使吞吐量非常低,尤其是当消费者连接延迟非常严重时,尤其是在等待消费者连接的环境中。对于大多数应用程序,将是最好的价值。

  在上一个不公平的分发设置中设置的参数的名称是预值值,除1之外,还可以将其设置为其他数字。

  设置数字的大小,本消费者中的消息数可以累积。例如:如果该消费者的预先设定值设置为:3,当消费者频道接收消息时,您可以继续接收消息处理时,可以将最大积累堆积起来。

  让我们从概念解释中找出这个定义。顾名思义,死亡信是无法消耗的信息。可以通过这种方式理解字面意义。一般而言,该产品是将产品传递给经纪人或直接发送给队列的。消费,但是在某些时候,由于特定原因,无法消费一些队列中的某些消息。如果没有随后的处理,那么此消息将成为一封死信,并且自然会有一封死信。

  应用程序方案:为了确保订单业务的信息不会丢失,有必要使用RabbitMQ的致命线机制。当消息消耗异常时,将消息放入死信队列中。也有:当用户成功地放入购物中心并单击付款时,用户将自动失败。

  由于消费者收到了消息但没有处理消息,因此消费者丢失了。该消息没有处理,但是不需要该消息来重新输入团队,而是要直接将此无关的消息发送到“死信”队列以进行处理。

  5.3.1概述

  5.3.2 TTL到期1.创建消息的生产者

  2.创建消息的消费者

  结果:当普通消费者无法接收消息后,在超过消息的TTL之后,消息是由Dead Letter队列接收的。

  5.3.3队列作为上一个演示的最大长度达到最大长度,并且删除了生产者中的TTL参数;向消费者添加参数:

  另一个结果是最终结果,制片人发送了10条消息,最后收到了6条,其余4个是由死信队列收到的。

  5.3.4新闻拒绝的结果:被拒绝的消息是由死书队列收到的。

  延迟队列在队列内有序,最重要的功能反映在其延迟属性中。延迟队列中的元素希望在指定的时间后删除并处理。简而言之,延迟队列是需要在指定时间处理的队列。

  延迟队列是《死书新闻》中新闻过期的结果。

  延迟队列是将消息放在延迟队列中并设置倒计时。到达时间时,该消息将在稍后执行,处理和根据处理结果进行更新。

  什么是TTL?

  TTL是RabbitMQ中的消息或队列属性,指示队列中所有消息的消息或最大生存时间。该单元是毫秒。

  换句话说,如果消息设置了ttl属性或输入设置TTL属性的队列,则如果在TTL设置期间未消耗此消息,它将成为“死信”。如果您配置了队列的TTL,则并同时使用消息的ttl,将使用较小的值。设置TTL的方法有两种。

  如果设置了队列的TTL属性,一旦消息到期,将被队列丢弃(如果将死信队列扔进死字母队列中),第二种方式,该消息不一定立即立即立即立即立即立即受到到期的影响。Discard,因为该消息在即将传递消费者之前确定之前已确定。如果当前队列有严重的信息积压,那么过期的新闻可能会长期生存;

  此外,有必要注意,如果未设置TTL,则该消息将永远不会到期。如果将TTL设置为0,则意味着该消息将被丢弃,除非您目前可以直接将消息提交给消费者。

  在上一节中,我们介绍了死信队列,我刚刚介绍了TTL。到目前为止,延迟队列的两个主要要素已在兔子中使用。接下来,您只需要融合它们,然后添加一些调味料。它可以新鲜发布。

  考虑一下,不是延迟队列,不是消息延迟多长时间吗?TTL可以使消息在延迟后成为一封死信。这样,消费者中的消息将始终消耗死亡信的消息,因为内部的消息是我想立即处理的消息。

  通过设置普通队列的死信队列和消息TTL,可以实现延迟队列的效果。

  在正常情况下,该消息由交换机发出,然后发送到指定的队列,但是在消息的消息中未处理队列,然后将消息移交给死信队列,然后将其移交给死信。队列将被处理以达到死亡信件,并将其到达并达到它。延迟了队列的效果。

  将延迟的TTL延迟到发件人,而不是由消费者定义。

  6.6.1问题发生了变化后,发现该新闻是一一发送的。当先前的新闻未达到其TTL时,后来的新闻将不会继续发送。

  通过添加延迟的队列插头来优化。

  6.6.2安装延迟插件下载https://www.rabbitmq.com/community-plugins.html下载,在/usr/lib/rabbitmq/libbitmq/libbitmq_server-3.8/plugins中安装。

  添加此插头 - 除了将来可以创建的队列类型外,还添加了延迟的消息开关,以便可以将消息的延迟移交给开关,而不是延迟像以前一样向队列发出消息。无法发送TTL背后的消息。

  6.6.3示例1.创建延迟开关

  结果:先前的问题已解决。

  6.6.4总结延迟队列在需要延迟的场景中非常有用。使用RabbitMQ实现延迟队列可以很好地使用RabbitMQ的特征,例如:==消息可靠发送,消息传递,死信队列Queue Compersales Protection of Protectry of Protection不会至少一旦消费就会丢弃,并且没有处理过的新闻正确。==

  另外,通过兔子集群的特性,可以很好地解决单点故障的问题,并且由于悬挂单个节点而导致的延迟队列将无法获得或丢失延迟队列。

  当然,还有许多其他选择延迟队列的选项,例如使用redis的zset,使用Quartz或使用Kafka的时间轮。这些方法具有自己的特征,可以看到适用的场景。

  7.1.1概念用户一个请求的结果或同一操作发起的多个请求的结果是一致的,并且由于多次点击,它们将不会产生副作用。

  以最简单的例子,即付款,用户在购买商品后付款以及付款扣除成功,但是在返回结果时,网络是异常的。目前,这笔钱已被扣除,用户再次单击按钮。在此Timethe模型成功的情况下,用户查询余额发现货币已被扣除,流记录已成为两个。立即,但是在响应客户时,网络中断或异常可能会发生。

  由于某些原因,消费者已经消耗了这一消息,但无法回复MQ,然后MQ再次分发了这一消息,从而导致多重消费。

  7.1.2当消息重复消费者MQ中的消费者的消息时,MQ已将消息发送给消费者。消费者在将ACK返回MQ时中断网络,因此MQ不会收到确认信息,并且该消息将再次重新发布。在其他消费者或Internet重新连接之后,它将再次发送给消费者,但实际上消费者成功地消耗了新闻,导致消费者消耗了重复的新闻。

  7.1.3解决方案MQ消费者的权力和其他性解决方案通常使用全球ID或编写独特的徽标,例如时间戳或UUID或消费者消费者消费者消费者MQ.ITS规则生成全球唯一ID,并使用此ID来确定是否确定每次消费消息都被消费时,该消息就会使用此ID消耗。

  7.1.4消费者端消费者端的功率保证是在大规模订单生成的高峰期间,生产方可能会重复新闻。目前,消费者方必须意识到,即使我们收到了同样的新闻,Powerit已被消耗多次。

  7.1.5唯一的ID +指纹代码机制指纹代码:我们的某些规则或时间戳以及给定唯一信息代码的其他服务,它不一定是我们的系统生成的,基本上是由我们的业务规则和缝制的缝制,但是您必须确保确保唯一性,然后使用查询语句确定ID是否具有数据库中的ID。优势是实现简单的缝制,然后检查是否重复判断;

  缺点是,当头发高,如果是单个数据库,就会有一些写作表现的瓶颈。当然,您也可以使用拆分表来提高性能,但这不是我们最建议的方式。

  7.1.6 Redis Atomic使用REDIS执行SETNX命令,该命令自然具有力量和其他性别。结果,请勿重复消耗。

  通常以这种方式。

  7.2.1使用方案设置为敦促订单的命令。我们的客户在TMALL上的订单会及时向我们推动订单。SMS提醒,一个非常简单的功能,对吗?但是,对我们而言,Tmall商人必须分为大客户和小客户,例如Apple,例如小米等大商人,至少可以为我们创造一年的时间来创造我们非常多的利润。必须先处理他们的订单,我们的后端系统用于使用REDIS来存储时间调查。每个人都知道Redis只能使用列表来制作简单的消息队列。无法获得优先级场景。

  通过将优先范围设置为队列,然后发送消息,请设置消息的优先级。

  队列接收消息必须批处理。如果是接收和发送一条消息,则没有优先问题。它必须同时发送给发件人队列,然后将批处理消息优先考虑。

  7.2.2从Spring引导中的队列中逐步出现以声明参数,在发送消息时设置消息优先级。

  7.3.1使用方案RabbitMQ已从版本3.6.0引入了惰性队列的概念。惰性队列将尽可能将消息保存到磁盘中,当消费者消费到相应的消息时,它将加载到内存中。当消费者引起各种原因时(例如消费者离线,停机时间或由于维护等导致的关闭等),当无法消耗消息长时间累积时,惰性队列是必要的。

  默认情况下,当生产者将消息发送到RabbitMQ时,队列中的消息将尽可能地存储在内存中,以便可以更快地将消息发送给消费者。即使持续的新闻将在内存中留下备份,被写入磁盘。当RabbitMQ需要发布内存时,内存中的消息将更改为磁盘。此操作将花费更长的时间,并且还将阻止队列的操作,然后无法接收新消息。尽管RabbitMQ的开发人员一直在升级相关算法,但效果并不理想,尤其是当消息的数量时特别大。

  在正常情况下,队列中的消息存储在内存中,惰性队列是将消息保存在磁盘中。

  内存中的消息很快,并且磁盘中的消息较少。对于消费者停机时间,惰性队列可用于将消息保存到磁盘中。

  13.3.2步骤将索赔的参数设置为惰性队列。

  原始:|https://juejin.cn/post/7095687502073167909