rabbitmq的基本设置

进入sbin目录

cd /usr/local/rabbitmq_server-3.8.3/sbin

新增用户

./rabbitmqctl add_user cf 123456

设置用户权限

./rabbitmqctl set_user_tags cf administrator

创建虚拟主句host

创建springBoot项目

导入相关的依赖

略过

创建数据库,创建表

CREATE TABLE `c_order` (
  `id` varchar(64) NOT NULL,
  `status` int(15) DEFAULT NULL,
  `name` varchar(15) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

application.properties

# 配置数据库连接池及数据库驱动
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# 配置mysql的链接信息
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC

#rabbitmq相关的信息
spring.rabbitmq.host=47.97.206.12
spring.rabbitmq.virtual-host=host2
spring.rabbitmq.username=cf
spring.rabbitmq.password=123456
spring.rabbitmq.port=5672

logging.level.com.itheima.rabbitmq_demo.dao=debug

rabbitMq的相关配置
首先要有个host2,然后创建两个交换机和队列,对于普通的队列,设置消息的存活时间,超过超过该时间则成为死信,通过路由规则进入死信队列

@Configuration
public class RabbitmqConfig {
    /**
     * 死信队列 和死信交换机
     */

    @Bean("dlx_queue")
    public Queue dlxQueue() {
        return QueueBuilder.durable("dlx_queue").build();
    }

    @Bean("dlx_exchange")
    public Exchange dlxExchange() {
        return ExchangeBuilder.directExchange("dlx_exchange").durable(true).build();
    }

    @Bean
    public Binding dlxBinding(@Qualifier("dlx_queue") Queue queue,
                              @Qualifier("dlx_exchange") Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("dlx").noargs();
    }

    /**
     * 普通的队列 设置消息的超时时间为30s
     */
    @Bean("order_queue")
    public Queue OrderQueue() {
        HashMap<String, Object>mp=new HashMap<>();
        mp.put("x-message-ttl", 120 * 1000);//消息的超时时间为60s
        mp.put("x-dead-letter-exchange", "dlx_exchange"); //消息过期后进入的交换机为dlx_exchange
        mp.put("x-dead-letter-routing-key", "dlx");//信息成为死信后,会以该路由重新发送到死信队列中
        return QueueBuilder.durable("order_queue").withArguments(mp).build();
    }

    @Bean("order_exchange")
    public Exchange OrderExchange() {
        return ExchangeBuilder.directExchange("order_exchange").durable(true).build();
    }

    @Bean
    public Binding OrderBinding(@Qualifier("order_queue") Queue queue,
                              @Qualifier("order_exchange") Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("order.biz").noargs();
    }

}

Order 实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {

    private String id;
    private Integer status;
    private String name;
}

OrderDao

@Repository
public interface OrderDao {

    @Select("select * from c_order")
    List<Order> findAll();

    @Select("select * from c_order where id= #{id}")
    Order getOrderById(String id);

    @Select("select * from c_order where status =#{status}")
    List<Order>getOrderByStatus(Integer status);

    @Insert("insert into c_order (id,status)values(#{id},#{status})")
    void insertOneOrder(Order order);

    @Update("update c_order set status= #{status} where id =#{id}")
    void updateOrderStatusById(@Param("id") String id, @Param("status") Integer status);
}

OrdererviceImpl

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public String placeOrder() {
        String id= UUID.randomUUID().toString();
        Order order=new Order();
        order.setId(id);
        order.setStatus(OrderStatus.TO_BE_PAID);
        log.info("用户下单成功,订单号为:{},订单状态为:{}",id,order.getStatus());
        orderDao.insertOneOrder(order);
        rabbitTemplate.convertAndSend("order_exchange","order.biz",id);
        log.info("用户生成了一个待付款的订单,订单号为:{}",id);
        return "等待付款";
    }

    @Override
    public String paidOrder(String orderId) {
        Order order = orderDao.getOrderById(orderId);
        Integer status=order.getStatus();
        if(status==OrderStatus.TO_BE_PAID){
            //如果此时该订单还未付款,则消费此订单
            String receivedOrderId = (String)rabbitTemplate.receiveAndConvert("order_queue");
            System.out.println(orderId);
            //这里的receivedOrderId 是队首元素,也就是第一个出队列的元素
            System.out.println(receivedOrderId);
            log.info("用户付款了一个订单,订单编号为:{}",receivedOrderId);
            orderDao.updateOrderStatusById(receivedOrderId, OrderStatus.HAS_PAID);
            log.info("订单号为:{}的订单状态修改为已支付", receivedOrderId);
            return "跳转到付款成功页面";
        }
        return "付款失败";
    }
}

OrderConsumer

@Component
@Slf4j
public class OrderConsumer {

    @Autowired
    private OrderDao orderDao;

    @RabbitListener(queues="dlx_queue")
    public void timeoutOrderMessage(@Payload String message){
        log.info("接收到用户还未支付的订单,订单编号为:{}",message);
        //将订单的状态修改为超时未支付
        orderDao.updateOrderStatusById(message, OrderStatus.CANCELLED_OVER_TIME);
        log.info("订单号为:{}的订单状态修改为超时未支付", message);

        //查询用户所有付款超时而取消的订单
        List<Order> overTimeOrder = orderDao.getOrderByStatus(OrderStatus.CANCELLED_OVER_TIME);
        log.info("用户所有付款超时而取消的订单为:{}", overTimeOrder);
    }
}

OrderController

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private OrderService orderService;
    /**
     * 用户下单接口(==>发送一条等待付款的消息)
     */
    @RequestMapping("/placeOrder")
    public String placeOrder(){
        return orderService.placeOrder();
    }
    /**
     * 用户付款
     */
    @GetMapping("/paidOrder/{orderId}")
    public String paidOrder(@PathVariable("orderId") String orderId){
        return orderService.paidOrder(orderId);
    }
    
}

项目存在的问题

设置了消息的过期时间,但是队列是先进先出的,如果在service层给某一条订单付款了,但是这个订单却不是队首元素,则此订单最终没有显示付款

项目地址

https://gitee.com/swifties270/rabbitmq-order-cancel