当前位置: 首页 > news >正文

wordpress博客站搭建app开发公司启动资金有哪些

wordpress博客站搭建,app开发公司启动资金有哪些,数据库和网站建设的论文,wordpress网站数据迁移强烈建议在看本篇博客之前快速浏览文章#xff1a;RabbitMQ基础有这一篇就够了 RabbitMQ高级篇 0. 前言1. 发送者的可靠性1.1 生产者重试机制1.2 生产者确认机制1.3 实现生产者确认 2. MQ的可靠性2.1 MQ持久化2.2 LazyQueue 3. 消费者的可靠性3.1 消费者确认机制3.2 失败重试策… 强烈建议在看本篇博客之前快速浏览文章RabbitMQ基础有这一篇就够了 RabbitMQ高级篇 0. 前言1. 发送者的可靠性1.1 生产者重试机制1.2 生产者确认机制1.3 实现生产者确认 2. MQ的可靠性2.1 MQ持久化2.2 LazyQueue 3. 消费者的可靠性3.1 消费者确认机制3.2 失败重试策略3.3 业务幂等性3.3.1 唯一消息ID3.3.2 业务判断 4. 延迟消息4.1 死信交换机4.2 取消超时订单案例 常见面试题 0. 前言 消息从生产者到消费者的每一步都可能导致消息丢失 发送消息时丢失 生产者发送消息时连接MQ失败生产者发送消息到达MQ后未找到Exchange生产者发送消息到达MQ的Exchange后未找到合适的Queue消息到达MQ后处理消息的进程发生异常 MQ导致消息丢失 消息到达MQ保存到队列后尚未消费就突然宕机 消费者处理消息时 消息接收后尚未处理突然宕机消息接收后处理过程中抛出异常 解决消息丢失问题保证MQ的可靠性就必须从3个方面入手 确保生产者一定把消息发送到MQ确保MQ不会将消息弄丢确保消费者一定要处理消息 1. 发送者的可靠性 1.1 生产者重试机制 生产者发送消息时出现了网络故障导致与MQ的连接中断。SpringAMQP提供的消息发送时的重试机制。即当RabbitTemplate与MQ连接超时后多次重试。 修改publisher模块的application.yaml文件添加下面的内容 spring:rabbitmq:connection-timeout: 1s # 设置MQ的连接超时时间template:retry:enabled: true # 开启超时重试机制initial-interval: 1000ms # 失败后的初始等待时间multiplier: 1 # 失败后下次的等待时长倍数下次等待时长 initial-interval * multipliermax-attempts: 3 # 最大重试次数这段配置代码是针对 Spring 框架中 RabbitMQ 的设置主要用于配置 RabbitMQ 的连接和消息发送的重试机制。下面是对每个配置项的详细解释1. spring.rabbitmq.connection-timeout - RabbitMQ 连接的超时时间。 - 表示连接超时时间为 1 秒。如果在 1 秒内无法建立连接程序将抛出异常。2. spring.rabbitmq.template.retry 这个部分配置了消息发送时的重试策略。a. enabled - 开启或关闭重试机制。 - 表示开启超时重试机制。如果消息发送失败Spring 会自动尝试重新发送消息。b. initial-interval - 设置首次重试的等待时间。 - 表示首次重试的等待时间为 1 秒。如果第一次发送失败程序将在 1 秒后再次尝试发送。c. multiplier - 重试时等待时间的倍数。 - 表示每次重试的等待时间不增加。计算公式为 下次等待时长 initial-interval × multiplier。 在此配置中所有重试的等待时间都将保持在 1 秒因为乘以 1 不会改变初始间隔。d. max-attempts - 设置最大重试次数。 - 表示最多会尝试 3 次发送消息。如果消息在 3 次尝试后仍然无法发送成功则会抛出异常并终止重试。特别注意当网络不稳定的时候利用重试机制可以有效提高消息发送的成功率。不过SpringAMQP提供的重试机制是阻塞式的重试也就是说多次重试等待的过程中当前线程是被阻塞的。 如果对于业务性能有要求建议禁用重试机制。如果一定要使用请合理配置等待时长和重试次数当然也可以考虑使用异步线程来执行发送消息的代码。 1.2 生产者确认机制 只要生产者与MQ之间的网路连接顺畅基本不会出现发送消息丢失的情况因此大多数情况下我们无需考虑这种问题。不过在少数情况下也会出现消息发送到MQ之后丢失的现象比如 MQ内部处理消息的进程发生了异常生产者发送消息到达MQ后未找到Exchange程序员书写问题生产者发送消息到达MQ的Exchange后未找到合适的Queue因此无法路由程序员书写问题 针对上述情况RabbitMQ提供了生产者消息确认机制包括Publisher Confirm和Publisher Return两种。在开启确认机制的情况下当生产者发送消息给MQ后MQ会根据消息处理的情况返回不同的回执。返回的结果有以下几种情况 消息投递到了MQ但是路由失败。此时会通过Publisher Return返回路由异常原因然后返回ACK告知投递成功**例如成功送到交换机但是没有绑定队列一般是程序员忘记添加** 路由失败情况1发送者成功发送到MQ但是exchange没有绑定一个queue一般是程序员书写问题路由失败情况2发送者成功发送到MQ但是RountingKey 和 BindingKey都不一致一般是程序员书写问题 临时消息投递到了MQ并且入队成功返回ACK告知投递成功持久消息投递到了MQ并且入队完成持久化返回ACK 告知投递成功其它情况都会返回NACK告知投递失败(比如持久化消息没有写到磁盘等情况) 其中ack和nack属于Publisher Confirm机制ack是投递成功nack是投递失败。而return则属于Publisher Return机制。默认两种机制都是关闭状态需要通过配置文件来开启。 1.3 实现生产者确认 在publisher模块的application.yaml中添加配置 spring:rabbitmq:publisher-confirm-type: correlated # 开启publisher confirm机制并设置confirm类型publisher-returns: true # 开启publisher return机制这里publisher-confirm-type有三种模式可选 none关闭confirm机制simple同步阻塞等待MQ的回执correlatedMQ异步回调返回回执 测试案例 每个RabbitTemplate只能配置一个ReturnCallback因此我们可以在配置类中统一设置。在publisher模块定义一个配置类 package com.itheima.publisher.config;import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.core.ReturnedMessage; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.context.annotation.Configuration;import javax.annotation.PostConstruct;Slf4j AllArgsConstructor Configuration public class MqConfig {private final RabbitTemplate rabbitTemplate;PostConstructpublic void init(){rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {Overridepublic void returnedMessage(ReturnedMessage returned) {log.error(触发return callback,);log.debug(exchange: {}, returned.getExchange());log.debug(routingKey: {}, returned.getRoutingKey());log.debug(message: {}, returned.getMessage());log.debug(replyCode: {}, returned.getReplyCode());log.debug(replyText: {}, returned.getReplyText());}});} }定义ConfirmCallback: 由于每个消息发送时的处理逻辑不一定相同因此ConfirmCallback需要在每次发消息时定义。具体来说是在调用RabbitTemplate中的convertAndSend方法时多传递一个参数 这里的CorrelationData中包含两个核心的东西 id消息的唯一标示MQ对不同的消息的回执以此做判断避免混淆SettableListenableFuture回执结果的Future对象 将来MQ的回执就会通过这个Future来返回我们可以提前给CorrelationData中的Future添加回调函数来处理消息回执 新建一个测试向系统自带的交换机发送消息并且添加ConfirmCallback Test void testPublisherConfirm() {// 1.创建CorrelationDataCorrelationData cd new CorrelationData();// 2.给Future添加ConfirmCallbackcd.getFuture().addCallback(new ListenableFutureCallbackCorrelationData.Confirm() {Overridepublic void onFailure(Throwable ex) {// 2.1.Future发生异常时的处理逻辑基本不会触发log.error(send message fail, ex);}Overridepublic void onSuccess(CorrelationData.Confirm result) {// 2.2.Future接收到回执的处理逻辑参数中的result就是回执内容if(result.isAck()){ // result.isAck()boolean类型true代表ack回执false 代表 nack回执log.debug(发送消息成功收到 ack!);}else{ // result.getReason()String类型返回nack时的异常描述log.error(发送消息失败收到 nack, reason : {}, result.getReason());}}});// 3.发送消息,参数依次为交换机名称、RountingKey、需要发送的消息rabbitTemplate.convertAndSend(hmall.direct, q, hello, cd); }执行结果如下 由于传递的RoutingKey是错误的路由失败后触发了return callback同时也收到了ack。当我们修改为正确的RoutingKey以后就不会触发return callback了只收到ack。而如果连交换机都是错误的则只会收到nack。 2. MQ的可靠性 消息到达MQ以后如果MQ不能及时保存也会导致消息丢失所以MQ的可靠性也非常重要。 2.1 MQ持久化 为了提升性能默认情况下MQ的数据都是在内存存储的临时数据重启后就会消失。为了保证数据的可靠性必须配置数据持久化包括 交换机持久化队列持久化消息持久化 交换机持久化在控制台的Exchanges页面添加交换机时可以配置交换机的Durability参数设置为Durable就是持久化模式Transient就是临时模式。 队列持久化在控制台的Queues页面添加队列时同样可以配置队列的Durability参数。 消息持久化在控制台发送消息的时候可以添加很多参数而消息的持久化是要配置一个properties。 注意在开启持久化机制以后如果同时还开启了生产者确认那么MQ会在消息持久化以后才发送ACK回执进一步确保消息的可靠性。不过出于性能考虑为了减少IO次数发送到MQ的消息并不是逐条持久化到数据库的而是每隔一段时间批量持久化。一般间隔在100毫秒左右这就会导致ACK有一定的延迟因此建议生产者确认全部采用异步方式。 2.2 LazyQueue 在默认情况下RabbitMQ会将接收到的信息保存在内存中以降低消息收发的延迟。但在某些特殊情况下这会导致消息积压比如 消费者宕机或出现网络故障消息发送量激增超过了消费者处理速度消费者处理业务发生阻塞 为什么要提出LazyQueue的原因PageOut阻塞 一旦出现消息堆积问题RabbitMQ的内存占用就会越来越高直到触发内存预警上限。此时RabbitMQ会将内存消息刷到磁盘上这个行为成为PageOut. PageOut会耗费一段时间并且会阻塞队列进程。因此在这个过程中RabbitMQ不会再处理新的消息生产者的所有请求都会被阻塞。 为了解决这个问题从RabbitMQ的3.6.0版本开始就增加了Lazy Queues的模式也就是惰性队列。惰性队列的特征如下 接收到消息后直接存入磁盘而非内存消费者要消费消息时才会从磁盘中读取并加载到内存也就是懒加载支持数百万条的消息存储 而在3.12版本之后LazyQueue已经成为所有队列的默认格式。因此官方推荐升级MQ为3.12版本或者所有队列都设置为LazyQueue模式。 配置Lazy模式1控制台配置Lazy模式 在添加队列的时候添加x-queue-modlazy参数即可设置队列为Lazy模式 配置Lazy模式2代码配置Lazy模式 在利用SpringAMQP声明队列的时候添加x-queue-modlazy参数也可设置队列为Lazy模式 Bean public Queue lazyQueue(){return QueueBuilder.durable(lazy.queue).lazy() // 开启Lazy模式.build(); }也可以基于注解来声明队列并设置为Lazy模式 RabbitListener(queuesToDeclare Queue(name lazy.queue,durable true,arguments Argument(name x-queue-mode, value lazy) )) public void listenLazyQueue(String msg){log.info(接收到 lazy.queue的消息{}, msg); }3. 消费者的可靠性 3.1 消费者确认机制 问题提出当RabbitMQ向消费者投递消息以后需要知道消费者的处理状态如何。因为消息投递给消费者并不代表就一定被正确消费了可能出现的故障有很多比如 消息投递的过程中出现了网络故障消费者接收到消息后突然宕机消费者接收到消息后因处理不当导致异常 为了确认消费者是否成功处理消息RabbitMQ提供了消费者确认机制Consumer Acknowledgement。即当消费者处理消息结束后应该向RabbitMQ发送一个回执告知RabbitMQ自己消息处理状态。回执有三种可选值 ack成功处理消息RabbitMQ从队列中删除该消息nack消息处理失败RabbitMQ需要再次投递消息 网络延迟或者自身问题处理失败 reject消息处理失败并拒绝该消息RabbitMQ从队列中删除该消息 例如发送过来的json格式存在问题再次重试还是存在问题的所以直接决绝 由于消息回执的处理代码比较统一因此SpringAMQP帮我们实现了消息确认。并允许我们通过配置文件设置ACK处理方式有三种模式 none不处理。即消息投递给消费者后立刻ack消息会立刻从MQ删除。非常不安全不建议使用manual手动模式。需要自己在业务代码中调用api发送ack或reject存在业务入侵但更灵活auto自动模式。SpringAMQP利用AOP对我们的消息处理逻辑做了环绕增强当业务正常执行时则自动返回ack. 当业务出现异常时根据异常判断返回不同结果如果是业务异常会自动返回nack如果是消息处理或校验异常自动返回reject; 通过下面的配置可以修改SpringAMQP的ACK处理方式消费者端 spring:rabbitmq:listener:simple:acknowledge-mode: none # 不做处理acknowledge-mode: auto # 自动ack3.2 失败重试策略 当消费者出现异常后消息会不断requeue重入队到队列再重新发送给消费者。如果消费者再次执行依然出错消息会再次requeue到队列再次投递直到消息处理成功为止。 极端情况就是消费者一直无法执行成功那么消息requeue就会无限循环导致mq的消息处理飙升带来不必要的压力 为了应对上述情况Spring又提供了消费者失败重试机制在消费者出现异常时利用本地重试而不是无限制的requeue到mq队列。 spring:rabbitmq:listener:simple:retry:enabled: true # 开启消费者失败重试initial-interval: 1000ms # 初识的失败等待时长为1秒multiplier: 1 # 失败的等待时长倍数下次等待时长 multiplier * last-intervalmax-attempts: 3 # 最大重试次数stateless: true # true无状态false有状态。如果业务中包含事务这里改为false重启consumer服务重复之前的测试。可以发现 消费者在失败后消息没有重新回到MQ无限重新投递而是在本地重试了3次本地重试3次以后抛出了AmqpRejectAndDontRequeueException异常。查看RabbitMQ控制台发现消息被删除了说明最后SpringAMQP返回的是reject; 失败处理策略 本地测试达到最大重试次数后消息会被丢弃。这在某些对于消息可靠性要求较高的业务场景下显然不太合适了。因此Spring允许**自定义重试次数耗尽后的消息处理策略**这个策略是由MessageRecovery接口来定义的它有3个不同实现 RejectAndDontRequeueRecoverer重试耗尽后直接reject丢弃消息。默认就是这种方式ImmediateRequeueMessageRecoverer重试耗尽后返回nack消息重新入队RepublishMessageRecoverer重试耗尽后将失败消息投递到指定的交换机 失败后将消息投递到一个指定的专门存放异常消息的队列后续由人工集中处理。完整代码如下 import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.retry.MessageRecoverer; import org.springframework.amqp.rabbit.retry.RepublishMessageRecoverer; import org.springframework.context.annotation.Bean;Configuration // 根据配置文件中的属性决定是否加载这个配置类。 ConditionalOnProperty(name spring.rabbitmq.listener.simple.retry.enabled, havingValue true) public class ErrorMessageConfig {Beanpublic DirectExchange errorMessageExchange(){return new DirectExchange(error.direct);}Beanpublic Queue errorQueue(){return new Queue(error.queue, true);}// 关联交换机和队列Beanpublic Binding errorBinding(Queue errorQueue, DirectExchange errorMessageExchange){return BindingBuilder.bind(errorQueue).to(errorMessageExchange).with(error);}Beanpublic MessageRecoverer republishMessageRecoverer(RabbitTemplate rabbitTemplate){return new RepublishMessageRecoverer(rabbitTemplate, error.direct, error);} }3.3 业务幂等性 幂等是一个数学概念用函数表达式来描述是这样的f(x) f(f(x)) 。在程序开发中则是指同一个业务执行一次或多次对业务状态的影响是一致的。 常见的幂等 根据id删除数据查询数据新增数据 常见的非幂等 取消订单恢复库存的业务。如果多次恢复就会出现库存重复增加的情况退款业务。重复退款对商家而言会有经济损失。 非幂等性实际案例 情况1消息被重复消费如果消费者和mq之间的网络连接断开消费者的ack未能成功发送到mq,那么等到连接好了之后mq又会重新发送消息此时消息重复被消费。如果这个消息是用于扣减库存的那么就会出现问题。情况2用户在支付服务完成支付后MQ通知交易业务标记订单为已支付交易服务标记成功后返回ack给mq但是此时交易服务和MQ之间网络故障ack未被mq收到mq可能认为交易服务宕机消息重新入队此时用户又进行了退款交易服务立马将订单状态修改为退款中而mq和交易服务之间的网络又恢复了mq又将消息发送给交易服务其又将订单修改为已支付此时出现问题。 必须想办法保证消息处理的幂等性。这里给出两种方案 唯一消息ID业务状态判断 3.3.1 唯一消息ID 给每个消息都设置一个唯一id利用id区分是否是重复消息 每一条消息都生成一个唯一的id与消息一起投递给消费者。消费者接收到消息后处理自己的业务业务处理成功后将消息ID保存到数据库如果下次又收到相同消息去数据库查询判断是否存在存在则为重复消息放弃处理。 SpringAMQP的MessageConverter自带了MessageID的功能我们只要开启这个功能即可。以Jackson的消息转换器为例 Bean public MessageConverter messageConverter(){// 1.定义消息转换器Jackson2JsonMessageConverter jjmc new Jackson2JsonMessageConverter();// 2.配置自动创建消息id用于识别不同消息也可以在业务中基于ID判断是否是重复消息jjmc.setCreateMessageIds(true);return jjmc; }缺点业务侵入、且有数据库的操作影响业务性能 3.3.2 业务判断 业务判断就是基于业务本身的逻辑或状态来判断是否是重复的请求或消息不同的业务场景判断的思路也不一样。例如当前案例中处理消息的业务逻辑是把订单状态从未支付修改为已支付。因此我们就可以在执行业务时判断订单状态是否是未支付如果不是未支付状态则证明订单已经被处理过已支付申请退款等状态无需重复处理。相比较而言消息ID的方案需要改造原有的数据库所以我更推荐使用业务判断的方案。 以支付修改订单的业务为例修改OrderServiceImpl中的markOrderPaySuccess方法 Override public void markOrderPaySuccess(Long orderId) {// 1.查询订单Order old getById(orderId);// 2.判断订单状态// 订单的状态1、未付款 2、已付款,未发货 3、已发货,未确认 4、确认收货交易成功5、交易取消订单关闭6、交易结束已评价if (old null || old.getStatus() ! 1) {// 订单不存在或者订单状态不是1放弃处理return;}// 3.尝试更新订单Order order new Order();order.setId(orderId);order.setStatus(2);order.setPayTime(LocalDateTime.now());updateById(order); }// 考虑到线程安全问题可以进行合并 Override public void markOrderPaySuccess(Long orderId) {// UPDATE order SET status ? , pay_time ? WHERE id ? AND status 1lambdaUpdate().set(Order::getStatus, 2).set(Order::getPayTime, LocalDateTime.now()).eq(Order::getId, orderId).eq(Order::getStatus, 1).update(); }4. 延迟消息 在电商的支付业务中对于一些库存有限的商品为了更好的用户体验通常都会在用户下单时立刻扣减商品库存。例如电影院购票、高铁购票下单后就会锁定座位资源其他人无法重复购买。 但是这样就存在一个问题假如用户下单后一直不付款就会一直占有库存资源导致其他客户无法正常交易最终导致商户利益受损 因此电商中通常的做法就是对于超过一定时间未支付的订单应该立刻取消订单并释放占用的库存。例如订单支付超时时间为30分钟则我们应该在用户下单后的第30分钟检查订单支付状态如果发现未支付应该立刻取消订单释放库存。 如何才能准确的实现在下单后第30分钟去检查支付状态呢 像这种在一段时间以后才执行的任务我们称之为延迟任务而要实现延迟任务最简单的方案就是利用MQ的延迟消息了。 在RabbitMQ中实现延迟消息也有两种方案 死信交换机TTL延迟消息插件 4.1 死信交换机 注意要确保normal.direct 和 dlx.direct的RountingKey一致 4.2 取消超时订单案例 用户下单完成后发送15分钟延迟消息在15分钟后接收消息检查支付状态 已支付更新订单状态为已支付未支付更新订单状态为关闭订单恢复商品库存 查询支付状态有两次 查询本地订单状态如果已经正常通知了支付和交易服务的通知正常着订单状态已经修改未为支付了此时直接结束即可。如果本地查询到的订单状态不是已支付那么有可能是没能通知到此时需要去向支付服务查询支付流水状态如果是已支付则修改如果不是那么就说明超时了则取消订单。 定义常量无论是消息发送还是接收都是在交易服务完成因此我们在trade-service中定义一个常量类用于记录交换机、队列、RoutingKey等常量 package com.hmall.trade.constants;public interface MQConstants {String DELAY_EXCHANGE_NAME trade.delay.direct;String DELAY_ORDER_QUEUE_NAME trade.delay.order.queue;String DELAY_ORDER_KEY delay.order.query; }配置MQ: 在trade-service模块的pom.xml中引入amqp的依赖 !--amqp--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-amqp/artifactId/dependency在trade-service的application.yaml中添加MQ的配置 spring:rabbitmq:host: 192.168.150.101port: 5672virtual-host: /hmallusername: hmallpassword: 123改造下单业务发送延迟消息: 在下单完成后发送延迟消息查询支付状态。修改trade-service模块的com.hmall.trade.service.impl.OrderServiceImpl类的createOrder方法添加消息发送的代码 编写查询支付状态接口由于MQ消息处理时需要查询支付状态因此要在pay-service模块定义一个这样的接口并提供对应的FeignClient。 首先在hm-api模块定义三个类 PayOrderDTO支付单的数据传输实体PayClient支付系统的Feign客户端PayClientFallback支付系统的fallback逻辑 PayOrderDTO代码如下 package com.hmall.api.dto;import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data;import java.time.LocalDateTime;/*** p* 支付订单* /p*/ Data ApiModel(description 支付单数据传输实体) public class PayOrderDTO {ApiModelProperty(id)private Long id;ApiModelProperty(业务订单号)private Long bizOrderNo;ApiModelProperty(支付单号)private Long payOrderNo;ApiModelProperty(支付用户id)private Long bizUserId;ApiModelProperty(支付渠道编码)private String payChannelCode;ApiModelProperty(支付金额单位分)private Integer amount;ApiModelProperty(付类型1h5,2:小程序3公众号4扫码5余额支付)private Integer payType;ApiModelProperty(付状态0待提交1:待支付2支付超时或取消3支付成功)private Integer status;ApiModelProperty(拓展字段用于传递不同渠道单独处理的字段)private String expandJson;ApiModelProperty(第三方返回业务码)private String resultCode;ApiModelProperty(第三方返回提示信息)private String resultMsg;ApiModelProperty(支付成功时间)private LocalDateTime paySuccessTime;ApiModelProperty(支付超时时间)private LocalDateTime payOverTime;ApiModelProperty(支付二维码链接)private String qrCodeUrl;ApiModelProperty(创建时间)private LocalDateTime createTime;ApiModelProperty(更新时间)private LocalDateTime updateTime; }PayClient代码如下 package com.hmall.api.client;import com.hmall.api.client.fallback.PayClientFallback; import com.hmall.api.dto.PayOrderDTO; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable;FeignClient(value pay-service, fallbackFactory PayClientFallback.class) public interface PayClient {/*** 根据交易订单id查询支付单* param id 业务订单id* return 支付单信息*/GetMapping(/pay-orders/biz/{id})PayOrderDTO queryPayOrderByBizOrderNo(PathVariable(id) Long id); }PayClientFallback代码如下 package com.hmall.api.client.fallback;import com.hmall.api.client.PayClient; import com.hmall.api.dto.PayOrderDTO; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.openfeign.FallbackFactory;Slf4j public class PayClientFallback implements FallbackFactoryPayClient {Overridepublic PayClient create(Throwable cause) {return new PayClient() {Overridepublic PayOrderDTO queryPayOrderByBizOrderNo(Long id) {return null;}};} }最后在pay-service模块的PayController中实现该接口 ApiOperation(根据id查询支付单) GetMapping(/biz/{id}) public PayOrderDTO queryPayOrderByBizOrderNo(PathVariable(id) Long id){PayOrder payOrder payOrderService.lambdaQuery().eq(PayOrder::getBizOrderNo, id).one();return BeanUtils.copyBean(payOrder, PayOrderDTO.class); }监听消息查询支付状态在trader-service编写一个监听器监听延迟消息查询订单支付状态 package com.hmall.trade.listener;import com.hmall.api.client.PayClient; import com.hmall.api.dto.PayOrderDTO; import com.hmall.trade.constants.MQConstants; import com.hmall.trade.domain.po.Order; import com.hmall.trade.service.IOrderService; import lombok.RequiredArgsConstructor; import org.springframework.amqp.rabbit.annotation.Exchange; import org.springframework.amqp.rabbit.annotation.Queue; import org.springframework.amqp.rabbit.annotation.QueueBinding; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component;Component RequiredArgsConstructor public class OrderDelayMessageListener {private final IOrderService orderService;private final PayClient payClient;RabbitListener(bindings QueueBinding(value Queue(name MQConstants.DELAY_ORDER_QUEUE_NAME),exchange Exchange(name MQConstants.DELAY_EXCHANGE_NAME, delayed true),key MQConstants.DELAY_ORDER_KEY))public void listenOrderDelayMessage(Long orderId){// 1.查询订单Order order orderService.getById(orderId);// 2.检测订单状态判断是否已支付if(order null || order.getStatus() ! 1){// 订单不存在或者已经支付return;}// 3.未支付需要查询支付流水状态PayOrderDTO payOrder payClient.queryPayOrderByBizOrderNo(orderId);// 4.判断是否支付if(payOrder ! null payOrder.getStatus() 3){// 4.1.已支付标记订单状态为已支付orderService.markOrderPaySuccess(orderId);}else{// TODO 4.2.未支付取消订单回复库存orderService.cancelOrder(orderId);}} }常见面试题 问题1如何保证支付服务与交易服务之间的订单状态一致性 首先支付服务会在用户支付成功以后利用MQ消息通知交易服务完成订单状态同步。其次为了保证MQ消息的可靠性我们采用了生产者确认机制、消费者确认、消费者失败重试等策略确保消息投递和处理的可靠性。同时也开启了MQ的持久化避免因服务宕机导致消息丢失。最后我们还在交易服务更新订单状态时做了业务幂等判断避免因消息重复消费导致订单状态异常。 问题2如果交易服务消息处理失败有没有什么兜底方案 我们可以在交易服务设置定时任务定期查询订单支付状态。这样即便MQ通知失败还可以利用定时任务作为兜底方案确保订单支付状态的最终一致性。
http://www.dnsts.com.cn/news/179988.html

相关文章:

  • 有了网站源代码山东网站建设服务商
  • 社交网站模板下载写网站软件
  • 有什么做海报网站网页模板免费下载源代码
  • 网站建设属于哪种公司看优秀摄影做品的网站
  • 网站模板的功能佛山网站建设改版
  • 外网图片素材网站dw做网站
  • 查看公司信息的网站莱芜职业技术学院暗号
  • ajax 网站模板辽宁天一建设有限责任公司网站
  • 中国铁道工程建设协会查证网站上海人才网站首页
  • 安徽公司网站建设建站软件怎么免费升级
  • 东莞网站制作方案定制青岛开发区网站建设哪家好
  • 百度认证官方网站西安seo外包机构
  • 哪些软件属于网页制作工具企业网站改造优化
  • 正在建设的网站可以随时进入吗米拓建站下载
  • 企业管理定制软件莱阳seo排名
  • 用腾讯云怎么建设网站wordpress 批量删除
  • 不用服务器怎么做网站免费广告发布平台app
  • 什么是规划网站久久建筑网解析
  • 上海网站建设框架图免费的会计做账系统
  • 沧州市网站建设价格上海传媒公司总裁是谁
  • 网站开发有名的公司ps在线网站
  • 网站商城建设需求表织梦免费购物网站
  • 互联网网站模块wordpress需要多少内存
  • 塘厦仿做网站泗县住房和城乡建设局网站
  • 手机端便民服务平台网站建设在线种子资源网
  • 鄂州网站设计制作公司山东网站建设方案制作
  • 网站建设实习招聘建设银行征信中心网站
  • 永久免费网站建立手机运用网站
  • 湖北联诺建设网站中国建设网官方网站硅灰
  • 陕西省建设监理协会网站主页.案例 商务网站的推广策略