1,为什么要用mq?
2,引入mq会多哪些问题?
3,如何解决这些问题?
---
一:传统模式有哪些痛点
(1)有些复杂的业务系统,一次用户请求可能会同步调用N个系统的接口,需要等待所有的接口都返回了,才能真正的获取执行结果。这种同步接口调用的方式总耗时比较长,非常影响用户的体验,特别是在网络不稳定的情况下,极容易出现接口超时问题。
(2)系统之间耦合性太高,如果调用的任何一个子系统出现异常,整个请求都会异常,对系统的稳定性非常不利。
(3)对于类似于秒杀场景的峰值爆炸的场景,系统的稳定性堪忧;
二:为什么要用mq?
(1)异步:同步接口调用导致响应时间长的问题,使用mq之后,将同步调用改成异步,能够显著减少系统响应时间。避免耗时时间长,影响用户体验的事情发生
(2)解耦:子系统间耦合性太大的问题,使用mq之后,我们只需要依赖于mq,避免了各个子系统间的强依赖问题。这样就把之前复杂的业务子系统的依赖关系,转换为只依赖于mq的简单依赖,从而显著的降低了系统间的耦合度。
(3)削峰:由于突然出现的请求峰值,导致系统不稳定的问题。使用mq后,能够起到消峰的作用。
订单系统接收到用户请求之后,将请求直接发送到mq,然后订单消费者从mq中消费消息,做写库操作。如果出现请求峰值的情况,由于消费者的消费能力有限,会按照自己的节奏来消费消息,多的请求不处理,保留在mq的队列中,不会对系统的稳定性造成影响。
三:引入mq会出现哪些问题:
1,重复消息问题
重复消费问题可以说是mq中普遍存在的问题,不管你用哪种mq都无法避免。
有哪些场景会出现重复的消息呢?
消息生产者产生了重复的消息
kafka和rocketmq的offset被回调了
消息消费者确认失败
消息消费者确认时超时了
业务系统主动发起重试
如果重复消息不做正确的处理,会对业务造成很大的影响,产生重复的数据,或者导致数据异常;
(2)数据一致性问题
如果mq的消费者业务处理异常的话,就会出现数据一致性问题。
比如:一个完整的业务流程是,下单成功之后,送100个积分。下单写库了,但是消息消费者在送积分的时候失败了,就会造成数据不一致的情况,即该业务流程的部分数据写库了,另外一部分没有写库。
如果下单和送积分在同一个事务中,要么同时成功,要么同时失败,是不会出现数据一致性问题的。
但由于跨系统调用,为了性能考虑,一般不会使用强一致性的方案,而改成达成最终一致性即可。
(3)消息丢失问题:
哪些场景会出现消息丢失问题呢?
消息生产者发生消息时,由于网络原因,发生到mq失败了。
mq服务器持久化时,磁盘出现异常
kafka和rocketmq的offset被回调时,略过了很多消息。
消息消费者刚读取消息,已经ack确认了,但业务还没处理完,服务就被重启了。
导致消息丢失问题的原因挺多的,生产者、mq服务器、消费者 都有可能产生问题,我在这里就不一一列举了。最终的结果会导致消费者无法正确的处理消息,而导致数据不一致的情况。
(4)消息顺序问题
有些业务数据是有状态的,比如订单有:下单、支付、完成、退货等状态,如果订单数据作为消息体,就会涉及顺序问题了。如果消费者收到同一个订单的两条消息,第一条消息的状态是下单,第二条消息的状态是支付,这是没问题的。但如果第一条消息的状态是支付,第二条消息的状态是下单就会有问题了,没有下单就先支付了?
消息顺序问题是一个非常棘手的问题,比如:
kafka同一个partition中能保证顺序,但是不同的partition无法保证顺序。
rabbitmq的同一个queue能够保证顺序,但是如果多个消费者同一个queue也会有顺序问题。
如果消费者使用多线程消费消息,也无法保证顺序。
如果消费消息时同一个订单的多条消息中,中间的一条消息出现异常情况,顺序将会被打乱。
还有如果生产者发送到mq中的路由规则,跟消费者不一样,也无法保证顺序。
(5)消息堆积问题
如果消息消费者读取消息的速度,能够跟上消息生产者的节奏,那么整套mq机制就能发挥最大作用。但是很多时候,由于某些批处理,或者其他原因,导致消息消费的速度小于生产的速度。这样会直接导致消息堆积问题,从而影响业务功能。
这里以下单开通会员为例,如果消息出现堆积,会导致用户下单之后,很久之后才能变成会员,这种情况肯定会引起大量用户投诉。
(6)系统复杂度提升
这里说的系统复杂度和系统耦合性是不一样的,比如以前只有:系统A、系统B和系统C 这三个系统,现在引入mq之后,你除了需要关注前面三个系统之外,还需要关注mq服务,需要关注的点越多,系统的复杂度越高。
mq的机制需要:生产者、mq服务器、消费者。
有一定的学习成本,需要额外部署mq服务器,而且有些mq比如:rocketmq,功能非常强大,用法有点复杂,如果使用不好,会出现很多问题。有些问题,不像接口调用那么容易排查,从而导致系统的复杂度提升了。
如何解决?
1,重复消费问题的解决:
不管是由于生产者产生的重复消息,还是由于消费者导致的重复消息,我们都可以在消费者中这个问题。
这就要求消费者在做业务处理时,要做幂等设计;
在这里我推荐增加一张消费消息表,来解决mq的这类问题。消费消息表中,使用messageId做唯一索引,在处理业务逻辑之前,先根据messageId查询一下该消息有没有处理过,如果已经处理过了则直接返回成功,如果没有处理过,则继续做业务处理。
2,数据一致性的解决:
数据一致性分为:
强一致性
弱一致性
最终一致性
而mq为了性能考虑使用的是最终一致性,那么必定会出现数据不一致的问题。这类问题大概率是因为消费者读取消息后,业务逻辑处理失败导致的,这时候可以增加重试机制。
重试分为:同步重试 和 异步重试。
有些消息量比较小的业务场景,可以采用同步重试,在消费消息时如果处理失败,立刻重试3-5次,如何还是失败,则写入到记录表中。但如果消息量比较大,则不建议使用这种方式,因为如果出现网络异常,可能会导致大量的消息不断重试,影响消息读取速度,造成消息堆积。
而消息量比较大的业务场景,建议采用异步重试,在消费者处理失败之后,立刻写入重试表,有个job专门定时重试。
还有一种做法是,如果消费失败,自己给同一个topic发一条消息,在后面的某个时间点,自己又会消费到那条消息,起到了重试的效果。如果对消息顺序要求不高的场景,可以使用这种方式。
3,消息丢失问题
不管你是否承认有时候消息真的会丢,即使这种概率非常小,也会对业务有影响。生产者、mq服务器、消费者都有可能会导致消息丢失的问题。
为了解决这个问题,我们可以增加一张消息发送表,当生产者发完消息之后,会往该表中写入一条数据,状态status标记为待确认。如果消费者读取消息之后,调用生产者的api更新该消息的status为已确认。有个job,每隔一段时间检查一次消息发送表,如果5分钟(这个时间可以根据实际情况来定)后还有状态是待确认的消息,则认为该消息已经丢失了,重新发条消息。
4,消息顺序问题
消息顺序问题是我们非常常见的问题,我们以kafka消费订单消息为例。订单有:下单、支付、完成、退货等状态,这些状态是有先后顺序的,如果顺序错了会导致业务异常。
订单号路由到不同的partition,同一个订单号的消息,每次到发到同一个partition。
5,消息堆积
如果消费者消费消息的速度小于生产者生产消息的速度,将会出现消息堆积问题。其实这类问题产生的原因很多,如果你想进一步了解,可以看看我的另一篇文章《 我用kafka两年踩过的一些非比寻常的坑 》。
那么消息堆积问题该如何解决呢?
这个要看消息是否需要保证顺序。
如果不需要保证顺序,可以读取消息之后用多线程处理业务逻辑。
这样就能增加业务逻辑处理速度,解决消息堆积问题。但是线程池的核心线程数和最大线程数需要合理配置,不然可能会浪费系统资源。
如果需要保证顺序,可以读取消息之后,将消息按照一定的规则分发到多个队列中,然后在队列中用单线程处理。
当单台RabbitMQ的性能不能满足我们的要求时,我们需要对其进行分布式部署,官方提供了三种方式来实现RabbitMQ的分布式部署,他们之间可以多种组合共同部署,我们分别介绍下
这种方式属于原生的“集群”,该种方式在逻辑上将多台机器联合到一起,对外只相当于一个broker,集群中的所有机器必须运行RabbitMQ和Erlang的相同版本。除了queue以外所有的数据将在集群中复制,queue默认只驻留在一个节点上,但是连接到集群中任何节点的client都可以看到集群中的所有队列,即使它们不在该节点上。要使队列实现高可用,可以使用镜像队列。使用镜像集群的缺点就是节点之间要传输大量的数据。
不像其他软件的集群方案,RabbitMQ集群中节点之间没有主从节点之分。
这种方式利用插件的机制实现集群,它的目标是不通过集群在broker之间传递消息,俩个broker可以有不同的 virtual hosts和用户,也可以运行在不同版本的RabbitMQ和Erlang上,broker之间通过AMQP协议进行通讯。Federation使exchange和queue联合起来,Federation exchange和 Federation queue可以从其他broker上的exchanges和queues接收消息。Federation exchange可以将消息路由到本地的queue,Federation queue允许本地消费者从其他broker获取消息。
它也是利用插件的机制实现集群,它将消息可靠的从broker中的queue移动到broker中的exchange,源和目的的broker可以相同或者不同,与Federation类似,使用WAN连接,broker可以运行在不同的Erlang与RabbitMQ版本上,连功能都大致相同,只不过Shovel相比于Federation粒度更细。
由于Federation和Shovel类似,这里把他俩归为一类。
Federation/Shovel :每个broker在逻辑上是独立的;每个节点可以运行在不同的Erlang和RabbitMQ版本;broker之间通过广域网进行连接,消息传递使用AMQP协议;broker之间可以以各种拓扑形式连接,连接可以是双向或者单向;在CAP理论中更侧重于A(可用性)P(分区容错性);连接到集群中的一个节点只能看见该节点上的queue。
Clustering: 逻辑上是相当于一个broker;每个节点Erlang和RabbitMQ版本必须相同;broker之间通过LAN连接,broker之间的消息传递使用Erlang;在CAP理论中侧重于C(一致性)P(分区容错性);连接到其中一个节点即可看到所有节点上的queue。
最后关于在选择Federation和Shovel之间做出选择,推荐大家看一下stackoverflow上的这篇回答。 https://stackoverflowcom/questions/19357272/when-to-use-rabbitmq-shovels-and-when-federation-plugin
有些应用程序需要非常高的吞吐量,而其他一些应用程序却正在发布批处理作业,这些作业可能会延迟一段时间。在设计系统时,目标应该是最大限度地将性能和可用性结合起来,这对您的特定应用程序是有意义的。错误的体系结构设计决策或客户端错误,可能会损坏中间件或影响吞吐量。
您的发布服务器可能会停止运行,或者由于内存使用过多而导致服务器崩溃。本系列文章重点关注rabbitmq的最佳实践。应做和不应做两种不同使用类别的最佳实践相混合;高可用性和高性能(高吞吐量)。我们将讨论队列大小、常见错误、延迟队列、预取值、连接和通道、HIPE和集群中的节点数。这些通常都是最佳实践规则,基于我们在使用rabbitmq时获得的经验。
队列中的许多消息会对RAM的使用造成很大的负担。为了释放RAM,rabbitmq将(页面输出)消息刷新到磁盘。此过程会降低排队速度。当有许多消息需要分页取出时,分页过程通常会花费时间并阻止队列处理消息。许多消息可能会对中间件的性能产生负面影响。
当有许多消息重启集群时,也是费时的,因为必须重建索引。重新启动后,在群集中的节点之间同步消息也需要时间。
在rabbitmq 36中添加了一个名为lazy queues的功能。懒惰队列是消息自动存储到磁盘上的队列。只有在需要时才将消息加载到内存中。对于懒惰的队列,消息直接进入磁盘,因此RAM的使用被最小化,但是吞吐时间将花费更长的时间。
我们已经看到,懒惰的队列以更好的可预测性的方式,创建了一个更稳定的集群。要让您的消息不出现警告,请刷新到磁盘。你不会突然被一个性能冲击问题所困扰。如果您一次发送大量消息(例如处理批处理作业),或者如果您认为您的消费者一直无法跟上发布者的速度,我们建议您启用延迟队列。
对于经常受到消息峰值冲击的应用程序,以及要求吞吐量比其他任何东西都重要的应用程序,可以推荐的另一做法是设置队列的最大长度。这样可以通过丢弃来自队列头部的消息来保持队列的简短性,从而使队列永远不会超过max-length设置。
队列在rabbitmq中是单线程的,一个队列可以处理大约50k条消息/秒。如果您有多个队列和消费者,您可以在多核系统上获得更好的吞吐量。如果在底层节点上拥有与核心一样多的队列,那么您将获得最佳吞吐量。
rabbitmq管理接口为集群中的每个队列收集和计算度量。如果您有数千个活动队列和使用者,这可能会减慢服务器的运行速度。如果队列太多,CPU和RAM的使用也可能受到负面影响。
队列性能受限于一个CPU核心。因此,如果将队列拆分到不同的核心,您将获得更好的性能;如果您拥有rabbitmq集群,您也可以将他们拆分到不同的节点。
rabbitmq队列绑定到最初声明它们的节点。即使您创建了一个rabbitmq中间件集群,所有路由到特定队列的消息都将转到该队列所在的节点。您可以在节点之间平均地手动拆分队列,但缺点是您需要记住队列的位置。
如果您有多个节点或具有多个核心的单节点集群,我们建议使用两个插件来帮助您:
当您想要在生产者和消费者之间共享队列时,为队列命名是很重要的,但是如果您使用临时队列,则不重要。相反,您应该让服务器使用一个随机的队列名称,而不是你自己命名一个——或者修改rabbitmq策略。
客户机连接可能会失败,并可能留下未使用的资源(队列),留下许多队列可能会影响性能。自动删除队列有三种方法:
在 Erlang VM 的内部队列每个队列均使用用了一个优先级别,他们耗费了一些资源。在大多数情况下,不超过5个优先级就足够了。
一个常见的问题是如何处理发送到rabbitmq的消息的palyload(消息大小)。当然,您不应该在消息中发送非常大的文件信息,但是每秒的消息数是一个比它本身的消息大小更大的瓶颈。发送多个小消息可能是一个坏的选择。一个更好的办法是将它们捆绑成一个更大的消息,让消费者将其拆分。但是,如果捆绑多条消息,则需要记住这可能会影响处理时间。如果其中一条捆绑消息失败,是否需要重新处理所有这些消息?如何设置这个取决于带宽和体系结构。
每个连接使用大约100kb的RAM(如果使用TLS,甚至更多)。数千个连接可能是rabbitmq服务器的沉重负担。在最坏的情况下,服务器可能由于内存不足而崩溃。AMQP协议有一种称为“多路复用”的机制,它“复用”单个TCP连接。它建议每个进程只创建一个TCP连接,并在这个唯一一个连接的基础上为不同的线程使用多个通道。连接也应该是长连接的。AMQP连接的握手过程非常复杂,至少需要7个TCP数据包(如果使用了TLS,则需要更多)。
相反,如果需要,可以更频繁地打开和关闭通道。如果可能的话,甚至通道也应该是长寿命的,例如,在每个发布信息线程中复用相同的通道。每次发布信息时不用打开频道。最佳实践是复用连接,使用各通道在一个连接的基础上实现多路复用。理想情况下,每个进程只能有一个连接,然后在应用程序中为每个线程使用一个通道,而每个channel 复用同一个连接即可。
您还应该确保不在线程之间共享通道,因为大多数客户机不保证通道是线程安全的(因为这样会对性能产生严重的负面影响)。
确保不要在线程之间共享通道,因为大多数客户机不会使通道线程安全(因为这样会对性能产生严重的负面影响)。
为发布者和消费者区分连接以获得高吞吐量。当发布服务器向服务器发送太多要处理的消息时,rabbitmq可以对TCP连接施加反向压力。如果消费者使用相同的TCP连接,服务器可能不会从客户机接收消息确认。因此,消费性能也会受到影响。而随着消费速度的降低,服务器将不堪重负。
具有大量连接和通道的另一个影响为rabbitmq管理接口的性能。对于每个连接和通道性能,指标必须收集、分析和显示度量。
在连接失败的情况下,传输中的消息可能会丢失,并且可能需要重新传输此类消息。Acknowledgements 让服务器和客户机知道何时重新传输消息。客户机可以在收到消息时对其进行确认,也可以在客户机完全处理完消息后对其进行确认。Acknowledgement 具有性能影响,因此为了实现最快的吞吐量,应该禁用手动确认。
接收重要消息的消费应用程序在完成需要对其进行的任何操作之前不应确认消息,这样未处理的消息(工作进程崩溃、异常等)就不会丢失。
发布确认,是相同的事情,但用于发布。服务器收到来自发布服务器的消息时会进行确认。发布确认也会影响性能。但是,应该记住,如果发布者至少需要处理一次消息,就需要这样做。
所有未确认的消息必须驻留在服务器上的RAM中。如果您有太多未确认的消息,您将耗尽内存。限制未确认消息的一个有效方法是客户端预取的消息数做出相关设置。在预取部分了解有关预取的更多信息。
如果您不能承受丢失任何消息的代价,请确保您的队列声明为“持久”,并且您的消息以传递模式“持久”发送。
为了避免在中间件中丢失消息,需要为中间件重新启动、中间件硬件故障或中间件崩溃时做好准备。为了确保消息和中间件定义在重新启动后仍然存在,我们需要确保它们在磁盘上。在中间件重新启动期间,不持久的消息、交换和队列将会被丢失。
持久性消息更重,因为它们必须写入磁盘。请记住,即使您发送的是临时消息,懒惰的队列也会对性能产生相同的影响。对于高性能-请使用瞬态消息。
您可以通过amqps连接到rabbitmq,这是用tls包装的amqp协议。由于所有流量都必须加密和解密,因此TLS会影响性能。为了获得最大的性能,我们建议使用vpc对等,那么流量是私有的,并且是独立的,不涉及AMQP客户机/服务器。
在cloudamqp中,我们将rabbitmq服务器配置为只接受快速但安全的加密密码并确定其优先级。
预取值用于指定多少条消息将同时被发送给消费者。它被用来从你的消费者那里得到尽可能多的东西(饱和工作)。
From RabbitMQcom: “The goal is to keep the consumers saturated with work, but to minimise the client's buffer size so that more messages stay in Rabbit's queue and are thus available for new consumers or to just be sent out to consumers as they become free”
来自rabbitmqcom:“我们的目标是让消费者饱和工作,但要最大限度地减小客户机的缓冲区大小,因此更多的消息被留在Rabbit的队列中,从而对新的消费者可用,或者发送给那些变得空闲的消者。”
rabbitmq的默认预取设置为客户端提供了一个不受限制的缓冲区,这意味着rabbitmq在默认情况下会将尽可能多的消息发送给任何看起来准备接受它们的客户机。发送的消息由rabbitmq客户端库(在使用者中)缓存,直到对其进行处理。预取限制了在确认消息之前客户端可以接收的消息数。所有预取的消息都将从队列中删除,并且对其他使用者不可见。
A too small prefetch count may hurt performance since RabbitMQ is most of the time waiting to get permission to send more messages The image below is illustrating long idling time In the example, we have a QoS prefetch setting of 1 This means that RabbitMQ won't send out the next message until after the round trip completes (deliver, process, acknowledge) Round time in this picture is in total 125ms with a processing time of only 5ms
预取数太小可能会影响性能,因为rabbitmq大多数时间都在等待获得发送更多消息的许可。下图显示的是长时间的空转时间。在本例中,QoS预取设置为1。这意味着rabbitmq在往返完成(传递、处理、确认)之前不会发送下一条消息。中的整个周期时间总共为125ms,处理时间仅为5ms。
另一方面,大量的预取数可以接收队列中的大量消息并将其传递给同一个消费者,但是其他使用者却处于空闲状态。
如果您有一个或几个消费者快速处理消息,我们建议您一次预取多个消息。尽量让你的客户端繁忙。如果您一直有大约相同的处理时间,并且网络行为保持不变-您只需在客户机上为每个消息计算总的往返时间/处理时间,即可获得估计的预取值。
如果您有许多消费者,并且处理时间很短,我们建议预取值设置的应该比单个或少数使用者要低一些。太低的值会让消费者空转很多,因为他们需要等待消息到达。过高的值可能会使一个消费者忙碌,而其他消费者则处于空闲状态。
如果您有许多使用者和/或处理时间较长,我们建议您将预取计数设置为1,以便消息在所有消费者中均匀分布。
请注意,如果客户端自动确认消息,则预取值将不起作用。
一个典型的错误是有一个无限的预取,其中一个客户机接收所有的消息,耗尽内存并崩溃,然后所有的消息都被重新传递。
有关rabbitmq预取的信息,请参阅推荐的rabbitmq文档。
HIPE将以增加启动时间为代价增加服务器吞吐量。启用HIPE时,将在启动时编译rabbitmq。根据我们的基准测试,吞吐量增加了20-80%。HIPE的缺点是启动时间也增加了很多,大约1-3分钟。在rabbitmq的文档中,hipe仍然被标记为实验性的。
如果您需要高可用性,请不要启用HIPE。
当您用一个节点创建一个cloudamqp实例时,您将得到一个具有高性能的单个节点。一个节点将为您提供 最高的性能 ,因为消息不需要在多个节点之间进行镜像。
当您使用两个节点创建一个CloudAMQP实例时,与单个节点的相比,您将获得一半的性能。节点位于不同的可用性区域,队列在可用性区域之间自动镜像。两个节点将为您提供 高可用性 ,因为一个节点可能崩溃或被标记为受损,但另一个节点仍将启动并运行,准备接收消息。
当您使用三个节点创建一个CloudAMQP实例时,与单个节点的相同计划大小相比,您将获得1/4的性能。节点位于不同的可用性区域,队列在可用性区域之间自动镜像。您也可以暂停少数组件-与允许每个节点响应相比,通过关闭少数组件,您减少了重复传递。暂停少数组件是三节点集群中的一种分区处理策略,它可以防止由于网络拆分而导致数据不一致。
我们在cloudamqp集群中注意到的一个常见错误是,用户创建了一个新的vhost,但忘记为新的vhost启用一个ha策略。如果没有HA策略,则不会在节点之间同步消息。
直接交换是最快速。如果有许多bindings ,rabbitmq必须计算将消息发送到何处。
有些插件可能非常好用,但它们可能会消耗大量的CPU或RAM。因此,不建议将它们用于生产服务器。确保禁用不使用的插件。您可以通过CloudAmqp中的控制面板启用许多不同的插件。
将rabbitmq管理统计速率模式设置为detailed会严重影响性能,不应在生产中使用。
确保您使用的是最新推荐的客户端库版本
保持最新稳定版本的rabbitmq和erlang。在为客户发布新的主要版本之前,我们通常会在很大程度上对其进行测试。请注意,在为新集群选择版本的下拉列表中,我们始终使用最推荐的版本作为所选选项(默认)。
Dead lettering和TTL是rabbitmq中的两个流行功能,应该谨慎使用。TTL和Dead lettering可以产生您没有预料到的性能影响。
使用x-dead-letter-exchange属性声明的队列将向指定的dead-letter-exchange 发送被拒绝、非确认或过期(带有ttl)的消息。如果您指定了x-dead-letter-routing-key,则消息的路由键将在dead lettered时更改。
通过使用x-message-ttl属性声明队列,如果消息在指定的时间内未被使用,则将从队列中丢弃消息。
女生护肤的正确步骤全套应该包括清洁、爽肤水、精华素、眼霜、面霜和防晒。下面就让我们来一起了解一下吧!
清洁是皮肤护理的第一步,也是最重要的一步。选择适合自己肤质的洁面产品,用温水打湿脸部,取适量洁面产品在掌心搓揉起泡后,以打圈的方式轻轻按摩脸部,重点清洁T区和容易产生油脂的部位,最后用清水彻底冲洗干净。
接下来是爽肤水的使用。爽肤水可以帮助调节皮肤的PH值,并为后续的保养品做好吸收准备。将适量爽肤水倒在化妆棉上,然后轻轻拍打在脸部和颈部,注意避开眼部。
然后是精华素的涂抹。精华素是护肤中的重要环节,可以提供更多的营养和修复功效。将适量精华素倒在手心,均匀涂抹在脸部和颈部,并轻轻按摩促进吸收。
接着是眼霜的使用。眼部是肌肤最脆弱的地方,所以要特别重视眼部护理。取适量眼霜在无名指上,然后用无名指轻轻按压在眼周的骨架上,并进行按摩或拍打,促进吸收。
紧接着是面霜的涂抹。面霜可以为皮肤提供充足的水分和营养,形成保护膜防止水分流失。将适量面霜倒在手心,然后均匀涂抹在脸部和颈部,用指腹轻轻按摩至完全吸收。
最后是防晒的步骤。防晒是女生护肤中必不可少的一步,可以有效预防紫外线对皮肤的伤害。选择适合自己肤质的防晒产品,将适量防晒乳液均匀涂抹在脸部、颈部和身体暴露部位即可。
除了以上步骤外,还要注意饮食健康、保持良好的作息习惯、多喝水等,这些都是美丽肌肤的基础。
女生护肤的正确步骤全套包括清洁、爽肤水、精华素、眼霜、面霜和防晒。每个步骤都有其独特的作用,只有按照正确的方法和顺序进行护理,才能使肌肤保持健康光滑的状态。记住,坚持就是胜利,只要你坚持下去,你一定会拥有自己理想中的美丽肌肤!
欢迎分享,转载请注明来源:品搜搜测评网