记一次RabbitMQ解决分布式事务问题

通过RabbitMQ实现分布式事务 通过RabitMQ实现分布式事务题前言业务需求核心原理核心难点解决问题思路代码实现其他总结 前言 这篇文章是通过学习哔哩哔哩中的视频“阿里架构师如何30分钟基于MQ解决分布式事务问题”,原视频连接https://www.bilibili.com/video/BV15p4y1D7d7?p=2 业务需求 这是一次简单的模拟分布式事务的:假设我们现在需要支付宝向余额宝转账100元。支付宝(系统A)向余额宝(系统B)转账100元,支付宝余额-100,余额宝余额加100。这涉及到两个系统,需要用到分布式事务。 核心原 阅读详情

案例说明:

经典案例,以目前流行点外卖的案例,用户下单后,调用订单服务,让后订单服务调用派单系统通知送外卖人员送单,这时候订单系统与派单系统采用MQ异步通讯,保证订单表和派单表必须一致!

用传统的HTTP协议不能解决高并发:

RabbitMQ解决分布式事务原理方案

  1. 确保生产者一定要将数据投递到MQ服务器中
    • 生产者采用confirm,确认应答机制
    • 如果失败,生产者进行重试。
  2. MQ消费者消息能够正常消费消息。
    • 采用手动ACK模式,使用补偿机制,注意幂等性问题。
  3. 采用补单机制。
    • 在创建一个补单消费者进行监听,如果订单创建后,又回滚了(数据不一致),此时需要将订单进行补偿。
    • 交换机采用路由键模式,补单队列和派但队列都绑定同一个路由键。

代码

派件表:

create TABLE platoon(
  id INT PRIMARY KEY AUTO_INCREMENT,
  orderId VARCHAR(255),
  takeout_userId int
 
)

订单表:

create TABLE order_info(
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(30),  
  order_money INT,
  orderId VARCHAR(255)
  
);

生产者   

 1.实现接口 implements RabbitTemplate.ConfirmCallback

 2. 重写回调方法 ,成功、失败的调用  

@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {

send方法里面调用回调函数:

this.rabbitTemplate.setMandatory(true);
this.rabbitTemplate.setConfirmCallback(this);

yml需要配置回调机制:

###开启消息确认机制 confirms
publisher-confirms: true
publisher-returns: true

 注意:重试也是有一定次数限制的 如果超过一定次数 就需要进行人工补偿了

 下单:

pom:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mayikt</groupId>
    <artifactId>rabbitmq_order</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.1.RELEASE</version>
</parent>
    <dependencies>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!-- mysql 依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- 阿里巴巴数据源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.14</version>
        </dependency>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- springboot- 整个 lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- 添加springboot对amqp的支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <!--fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.49</version>
        </dependency>
    </dependencies>
</project>

基础包:

public interface ApiConstants {
    // 响应请求成功
    String HTTP_RES_CODE_200_VALUE = "success";
    // 系统错误
    String HTTP_RES_CODE_500_VALUE = "fial";
    // 响应请求成功code
    Integer HTTP_RES_CODE_200 = 200;
    // 系统错误
    Integer HTTP_RES_CODE_500 = 500;
    // 未关联QQ账号
    Integer HTTP_RES_CODE_201 = 201;

}
import org.springframework.stereotype.Component;

@Component
public class BaseApiService {

    public ResponseBase setResultError(Integer code, String msg) {
        return setResult(code, msg, null);
    }

    // 返回错误,可以传msg
    public ResponseBase setResultError(String msg) {
        return setResult(ApiConstants.HTTP_RES_CODE_500, msg, null);
    }

    // 返回成功,可以传data值
    public ResponseBase setResultSuccess(Object data) {
        return setResult(ApiConstants.HTTP_RES_CODE_200, ApiConstants.HTTP_RES_CODE_200_VALUE, data);
    }

    // 返回成功,沒有data值
    public ResponseBase setResultSuccess() {
        return setResult(ApiConstants.HTTP_RES_CODE_200, ApiConstants.HTTP_RES_CODE_200_VALUE, null);
    }

    // 返回成功,沒有data值
    public ResponseBase setResultSuccess(String msg) {
        return setResult(ApiConstants.HTTP_RES_CODE_200, msg, null);
    }

    // 通用封装
    public ResponseBase setResult(Integer code, String msg, Object data) {
        return new ResponseBase(code, msg, data);
    }

}
import lombok.Data;

@Data
public class ResponseBase {

    private Integer rtnCode;
    private String msg;
    private Object data;

    public ResponseBase() {

    }

    public ResponseBase(Integer rtnCode, String msg, Object data) {
        super();
        this.rtnCode = rtnCode;
        this.msg = msg;
        this.data = data;
    }

    @Override
    public String toString() {
        return "ResponseBase [rtnCode=" + rtnCode + ", msg=" + msg + ", data=" + data + "]";
    }

}

补偿队列:

import lombok.Data;

@Data
public class ResponseBase {

    private Integer rtnCode;
    private String msg;
    private Object data;

    public ResponseBase() {

    }

    public ResponseBase(Integer rtnCode, String msg, Object data) {
        super();
        this.rtnCode = rtnCode;
        this.msg = msg;
        this.data = data;
    }

    @Override
    public String toString() {
        return "ResponseBase [rtnCode=" + rtnCode + ", msg=" + msg + ", data=" + data + "]";
    }

}

entity:

import lombok.Data;

@Data
public class OrderEntity {

    private Long id;
    // 订单名称
    private String name;
    // 下单金额
    private Double orderMoney;
    // 订单id
    private String orderId;
}

mapper:

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import com.mayikt.entity.OrderEntity;

public interface OrderMapper {

    @Insert(value = "INSERT INTO `order_info` VALUES (#{id}, #{name}, #{orderMoney},#{orderId})")
    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
    public int addOrder(OrderEntity orderEntity);

    @Select("SELECT id as id ,name as name , order_money as orderMoney,orderId as orderId from order_info where orderId=#{orderId};")
    public OrderEntity findOrderId(@Param("orderId") String orderId);

}

MQ的配置:

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class RabbitmqConfig {

    // 下单并且派单存队列
    public static final String ORDER_DIC_QUEUE = "order_dic_queue";
    // 补单队列,判断订单是否已经被创建
    public static final String ORDER_CREATE_QUEUE = "order_create_queue";
    // 下单并且派单交换机
    private static final String ORDER_EXCHANGE_NAME = "order_exchange_name";

    // 1.定义订单队列
    @Bean
    public Queue directOrderDicQueue() {
        return new Queue(ORDER_DIC_QUEUE);
    }

    // 2.定义补订单队列
    @Bean
    public Queue directCreateOrderQueue() {
        return new Queue(ORDER_CREATE_QUEUE);
    }

    // 2.定义交换机
    @Bean
    DirectExchange directOrderExchange() {
        return new DirectExchange(ORDER_EXCHANGE_NAME);
    }

    // 3.订单队列与交换机绑定
    @Bean
    Binding bindingExchangeOrderDicQueue() {
        return BindingBuilder.bind(directOrderDicQueue()).to(directOrderExchange()).with("orderRoutingKey");
    }

    // 3.补单队列与交换机绑定
    @Bean
    Binding bindingExchangeCreateOrder() {
        return BindingBuilder.bind(directCreateOrderQueue()).to(directOrderExchange()).with("orderRoutingKey");
    }

}

service:

import java.util.UUID;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.mayikt.base.BaseApiService;
import com.mayikt.base.ResponseBase;
import com.mayikt.entity.OrderEntity;
import com.mayikt.mapper.OrderMapper;

@Service
public class OrderService extends BaseApiService implements RabbitTemplate.ConfirmCallback {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public ResponseBase addOrderAndDispatch() {
        //先下单 订单表插入数据
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setName("黄焖鸡米饭");
        // 价格是300元
        orderEntity.setOrderMoney(300d);
        // 商品id
        String orderId = UUID.randomUUID().toString();
        orderEntity.setOrderId(orderId);
        // 1.先下单,创建订单 (往订单数据库中插入一条数据)
        int orderResult = orderMapper.addOrder(orderEntity);
        System.out.println("orderResult:" + orderResult);
        if (orderResult <= 0) {
            return setResultError("下单失败!");
        }
        // 2.订单表插插入完数据后 订单表发送 外卖小哥
        send(orderId);
    //    int i = 1/0;   //发生异常
        return setResultSuccess();
    }

    private void send(String orderId) {
        JSONObject jsonObect = new JSONObject();
        jsonObect.put("orderId", orderId);
        String msg = jsonObect.toJSONString();
        System.out.println("msg:" + msg);
        // 封装消息
        Message message = MessageBuilder.withBody(msg.getBytes()).setContentType(MessageProperties.CONTENT_TYPE_JSON)
                .setContentEncoding("utf-8").setMessageId(orderId).build();
        // 构建回调返回的数据
        CorrelationData correlationData = new CorrelationData(orderId);
        // 发送消息
        this.rabbitTemplate.setMandatory(true);
        this.rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.convertAndSend("order_exchange_name", "orderRoutingKey", message, correlationData);

    }

    // 生产消息确认机制 生产者往服务器端发送消息的时候 采用应答机制
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        String orderId = correlationData.getId(); //id 都是相同的哦  全局ID
        System.out.println("消息id:" + correlationData.getId()); 
        if (ack) { //消息发送成功
            System.out.println("消息发送确认成功");
        } else {
            //重试机制
            send(orderId); 
            System.out.println("消息发送确认失败:" + cause);
        }

    }

}

controller:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.mayikt.base.BaseApiService;
import com.mayikt.base.ResponseBase;
import com.mayikt.service.OrderService;

@RestController
public class OrderController extends BaseApiService {
    @Autowired
    private OrderService orderService;

    @RequestMapping("/addOrder")
    public ResponseBase addOrder() {
        return orderService.addOrderAndDispatch();
    }

}

yml:

spring:
  rabbitmq:
  ####连接地址
    host: 192.168.91.6
   ####端口号   
    port: 5672
   ####账号 
    username: admin
   ####密码  
    password: admin
   ### 地址
    virtual-host: /admin_toov5
    ###开启消息确认机制 confirms
    publisher-confirms: true
    publisher-returns: true 
  #数据库连接信息
  datasource: 
        name: test
        url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE
        username: root
        password: root
        # 使用druid数据源
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver

启动类:

package com.mayikt;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("com.mayikt.mapper")
@SpringBootApplication
public class AppOrder {

    public static void main(String[] args) {
        SpringApplication.run(AppOrder.class, args);
    }

}

派单:

 

Entity:

package com.mayikt.entity;

import lombok.Data;

@Data
public class DispatchEntity {

    private Long id;
    // 订单号
    private String orderId;
    // 外卖员id
    private Long takeoutUserId;
    
}

Mapper:

package com.mayikt.mapper;

import org.apache.ibatis.annotations.Insert;

import com.mayikt.entity.DispatchEntity;

public interface DispatchMapper {

    /**
     * 新增派单任务
     */
    @Insert("INSERT into platoon values (null,#{orderId},#{takeoutUserId});")
    public int insertDistribute(DispatchEntity distributeEntity);

}

consumer:

 做一些路由器 队列 路由键的绑定 声明工作

package com.mayikt.rabbitmq.consumer;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class RabbitmqConfig {

    // 下单并且派单存队列
    public static final String ORDER_DIC_QUEUE = "order_dic_queue";
    // 补单队列,判断订单是否已经被创建
    public static final String ORDER_CREATE_QUEUE = "order_create_queue";
    // 下单并且派单交换机
    private static final String ORDER_EXCHANGE_NAME = "order_exchange_name";

    // 1.定义派单队列
    @Bean
    public Queue OrderDicQueue() {
        return new Queue(ORDER_DIC_QUEUE);
    }
/*
    // 2.定义补订单队列
    @Bean
    public Queue directCreateOrderQueue() {
        return new Queue(ORDER_CREATE_QUEUE);
    }*/

    // 2.定义交换机
    @Bean
    DirectExchange directOrderExchange() {
        return new DirectExchange(ORDER_EXCHANGE_NAME);
    }

    // 3.订单队列与交换机绑定
    @Bean
    Binding bindingExchangeOrderDicQueue() {
        return BindingBuilder.bind(OrderDicQueue()).to(directOrderExchange()).with("orderRoutingKey");
    }

    /*// 3.补单队列与交换机绑定
    @Bean
    Binding bindingExchangeCreateOrder() {
        return BindingBuilder.bind(OrderDicQueue()).to(directOrderExchange()).with("orderRoutingKey");
    }*/

}

派件消费:

package com.mayikt.rabbitmq.consumer;

import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONObject;
import com.mayikt.entity.DispatchEntity;
import com.mayikt.mapper.DispatchMapper;
import com.rabbitmq.client.Channel;

/**
 * 派单服务
 */
@Component
public class DispatchConsumer {
    @Autowired
    private DispatchMapper dispatchMapper;

    @RabbitListener(queues = "order_dic_queue")
    public void process(Message message, @Headers Map<String, Object> headers, Channel channel) throws Exception {
        String messageId = message.getMessageProperties().getMessageId();
        String msg = new String(message.getBody(), "UTF-8");
        System.out.println("派单服务平台" + msg + ",消息id:" + messageId);
        JSONObject jsonObject = JSONObject.parseObject(msg);
        String orderId = jsonObject.getString("orderId");
        if (StringUtils.isEmpty(orderId)) {
            // 日志记录
            return;
        }
        DispatchEntity dispatchEntity = new DispatchEntity();
        // 订单id
        dispatchEntity.setOrderId(orderId);
        // 外卖员id
        dispatchEntity.setTakeoutUserId(12l);
        
        try {
            int insertDistribute = dispatchMapper.insertDistribute(dispatchEntity);
            if (insertDistribute > 0) {
                // 手动签收消息,通知mq服务器端删除该消息
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            }
        } catch (Exception e) {
            e.printStackTrace();
            // // 丢弃该消息
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
        }
    }

}

启动类:

package com.mayikt;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("com.mayikt.mapper")
@SpringBootApplication
public class AppDispatch {

    public static void main(String[] args) {
        SpringApplication.run(AppDispatch.class, args);
    }

}

yml:

spring:
  rabbitmq:
  ####连接地址
    host: 192.168.91.6
   ####端口号   
    port: 5672
   ####账号 
    username: admin
   ####密码  
    password: admin
   ### 地址
    virtual-host: /admin_toov5
    listener:
      simple:
        retry:
        ####开启消费者(程序出现异常的情况下会)进行重试
          enabled: true
         ####最大重试次数
          max-attempts: 5
        ####重试间隔次数
          initial-interval: 3000
        ####开启手动ack  
        acknowledge-mode: manual
         
  #数据库连接信息
  datasource: 
        name: test
        url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE
        username: root
        password: root
        # 使用druid数据源
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
server:
  port: 8081

pom:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.itmayiedu</groupId>
    <artifactId>rabbitmq_stock</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
    </parent>
    <dependencies>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!-- mysql 依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- 阿里巴巴数据源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.14</version>
        </dependency>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- springboot- 整个 lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- 添加springboot对amqp的支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <!--fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.49</version>
        </dependency>
    </dependencies>
</project>
如何用 RabbitMQ 解决分布式事务? 要说哪种分布式事务处理方案效率高,必然绕不开消息中间件!基于消息中间件的两阶段提交方案,通常用在高并发场景下。这种方式通过牺牲数据的强一致性换取性能的大幅提升,不过实现这种方式的成本和复杂度是比较高的,使用时还要看实际业务情况。今天小编想通过一个简单的案例,来和大家聊一聊如何通过消息中间件来处理分布式事务。先来说说整体思路。有一个名词叫做消息驱动的微服务,相信很多小伙伴都听说过。怎么理解呢?在微服务系统中,服务之间的互相调用,我们可以使用 HTTP 的方式,例如 OpenFeign,也可以使用 RPC 的方 阅读详情

相关推荐

分布式事务解决方案之RabbitMQ

分布式事务问题 以电商业务为例,订单服务对应的订单数据库,库存服务对应的库存数据库,多个数据源之间存在了分布式事务问题。如何保证在订单生成后,正确的扣除库存,或者在订单生成失败时,还原扣除的库存,这就是分布式事务将要解决的问题。 解决分布式事务前的业务逻辑: @Transaction(rollbackFor=Exception.class) publicvoidcreateOrder(OrderInfoorderInfo)throwsException{ //订单...

m0_58683072的博客 2145

RabbitMQ——解决分布式事务问题,RabbitMQ的重要作用之一!!!通过可靠生产和可靠消费来完美解决!

分布式事务是指涉及多个独立的计算机系统(也称为节点或参与者)之间的事务处理。在分布式系统中,每个节点可能各自拥有自己的数据存储和事务管理机制。分布式事务的目标是保证在跨多个节点执行的一系列操作可以以一致和可靠的方式执行和提交,即使在面对故障或并发操作时也能保持数据的完整性和一致性。实现分布式事务可以采用两阶段提交(Two-Phase Commit)协议或三阶段提交(Three-Phase Commit)协议等机制。这些协议通过协调参与者之间的状态和决策来实现分布式事务的一致性。

我热爱学习,也乐于分享。无论是科技前沿的洞见,还是生活中的小技巧,我都会在这里与你分享。我相信,知识就是力量,而分享则能让这份力量得到更好的传递。 2004

Rabbitmq的分布式事务解决方案(十二)

一、前言 1.1 简述 分布式事务指事务的操作位于不同的节点上,需要保证事务的 AICD 特性。 例如在下单场景下,库存和订单如果不在同一个节点上,就涉及分布式事务。 1.2 分布式事务的几种解决方案 1.2.1 2PC(两阶段提交) 2PC即两阶段提交协议,是将整个事务流程分为两个阶段,准备阶段(Prepare phase)、提交阶段(commit phase),2是指两个阶段,P是指准备阶段,C是指提交阶段。 在计算机中部分关系数据库如Oracle、MySQL支持两阶段提交协议,如下图: 准备阶段(P

weixin_41979002的博客 2637

RabbitTemplate中convertSendAndReceive方法与convertAndSend方法的区别

场景 这里只展示核心的代码,完整的代码请参见 SpringBoot整合RabbitMQ(完整版) // 向发布订阅模式里面发送消息 public void sendPublish() { for (int i = 0; i < 5; i++) { // rabbitTemplate.convertSendAndReceive("exchange_fanout", ""...

孔明的博客 8万+

RabbitMq分布式事务解决方案第一篇

前言 微服务架构下,相比单机事务,一个比较复杂的地方在于,在分布式环境下,面对的是分布式事务,分布式事务整体来说无法严格遵循传统的ACID4个特性,而只能根据系统的业务指标,通常满足可用性,和最终一致性,这也是不少互联网产品的实践结果的选择 在分布式事务一章中,探讨了有关常用分布式事务的几种解决方案,可以依据自己所在项目的特点,有选择的使用,比如对数据的一致性要求严格而对并发数可容忍的,可以考虑使用seata解决,对并发要求高,同时对数据一致性的要求也比较高的,可以考虑使用rocketMq事务消息 下面要介

congge 5万+

GPU大规模集群运维

CUDA(Compute Unified Device Architecture),是显卡厂商NVIDIA推出的通用并行计算平台和程序模型,该架构使GPU能够解决复杂的计算问题。它包含了CUDA指令集架构(ISA)以及GPU内部的并行计算引擎。开发人员可以使用C语言来为CUDA架构编写程序,所编写出的程序可以在支持CUDA的处理器上以超高性能运行。CUDA官方文档CUDA的软件环境允许开发者使用C++等高级编程语言开发程序。

weixin_42795092的博客 1738

RabbitMQ

基本消息队列的消息发送流程:建立connection创建channel利用channel声明队列利用channel向队列发送消息基本消息队列的消息接收流程:建立connection创建channel利用channel声明队列定义consumer的消费行为handleDelivery()利用channel将消费者与队列绑定Work模型的使用:多个消费者绑定到一个队列,同一条消息只会被一个消费者处理通过设置prefetch来控制消费者预取的消息数量交换机的作用是什么?...

qq_57216731的博客 2711

告别CAPL编程!用Matlab/Simulink+CANoe实现复杂ECU仿真的5个技巧

本文介绍了如何利用Matlab/Simulink与CANoe进行联合仿真,以替代复杂的CAPL编程,高效实现复杂ECU仿真。文章分享了5个核心技巧,涵盖信号映射、模型编译集成、实时参数调整、性能优化及数据分析,帮助算法工程师构建稳定强大的仿真环境,显著提升开发与测试效率。

rgv234567的博客 296

《Spring Boot 实战派》--12.集成RabbitMQ,实现系统间的 数据交换

第12章集成RabbitMQ,实现系统间的数据交换 RabbitMQ是近年来使用非常广泛的消息中间件。 本章首先介绍它的原理、概念、6种工作模式、常用的注解; 然后用实例讲解在Spring Boot 中如何使用AmqpTemplate接口实现消息的发送和监听。 12.1认识RabbitMQ 12.1.1介绍RabbitMQ RabbitMQ 是开源的高级消息队列协议(Advanced Message Queueing Protocol, AMQP) 的实现,用Erlang语言编写,...

tumu6889的博客 3796

ctfshow 萌新22 (类似级客巅峰web4)

在之前ctfshow平台的萌新22,大家都认为是无解的,题目也介绍了"还能搞,阿呆表示将直播倒立放水"。但是在级客巅峰结束的那天晚上,群里的师傅介绍说,如果修改了php.ini里面的配置(register_argc_argv)可以做出来。最后是真的做出来了。 使用阿呆~~~? 下面是自己的思考,感谢Guoke师傅和ha1c9on师傅的帮助 一. 源代码 <?php if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_mat.

Firebasky的博客 4215

xctf攻防世界 MISC高手进阶区 low

1. 进入环境,下载附件 给出的一张bmp图片,没有其他信息 2. 问题分析 扔进binwalk中,没有发现有用信息 使用zsteg,没有有用信息 塞入StegSolve中,发现有点猫腻,如图: 有点二维码的感觉,emmm,需要像素处理,但不知道如何下手,参考网上的wp,发现是让使用lsb隐写,copy代码: from PIL import Image img = Image.open('./pic/low.bmp') img_tmp = img.copy() pix = img_tmp.load()

l8947943的博客 1万+

rabbitmq | rabbitTemplate的convertAndSend部分源码解析

在RabbitMQ中,事务是一种确保消息发送的可靠性的机制。Spring AMQP提供了对RabbitMQ事务的抽象,而作为Spring AMQP的核心组件,提供了许多简化消息发送的方法。在这篇博客中,我们将深入探讨RabbitMQ事务机制的源码实现,以及Spring封装的的使用。

weixin_43268715的博客 3613

【UE5】RTS游戏的框选功能+行军线效果实现

步骤: 一、项目准备; 二、框选NPC并移动到指定地点; 三、框选效果; 四、行军线效果

ChaoChao66666的博客 2378

rabbit和mysql事务_记一次RabbitMQ解决分布式事务问题

案例说明:经典案例,以目前流行点外卖的案例,用户下单后,调用订单服务,让后订单服务调用派单系统通知送外卖人员送单,这时候订单系统与派单系统采用MQ异步通讯,保证订单表和派单表必须一致!用传统的HTTP协议不能解决高并发:RabbitMQ解决分布式事务原理方案确保生产者一定要将数据投递到MQ服务器中生产者采用confirm,确认应答机制如果失败,生产者进行重试。MQ消费者消息能够正常消费消息。采用手...

weixin_33362555的博客 593

Android PMS——概述(一)

PMS(Package Manager Service)是 Android 操作系统的一个重要组件,它负责管理应用程序的包信息以及与应用程序的安装、卸载、权限控制等相关功能,确保应用程序的安全性和稳定性。 Android PMS 负责维护一个应用程序包的清单信息,该清单是一个 XML 文件,包含应用程序的各种元数据,例如应用程序的包名、版本号、权限要求、组件信息等。PMS通过访问该清单文件来获取应用程序的信息。 此外,Android PMS 还负责应用程序的安装和卸载。在安装应用

小旭的专栏 1667

ESP32设备驱动-TM1637-驱动4位7段数码管

TM1637是一款带键盘扫描接口的LED(发光二极管显示器)驱动控制专用电路,内部集成了MCU数字接口、数据锁存、LED高压驱动、键盘扫描等功能。 TM1637使用DIP20/SOP20封装,主要适用于电磁炉、微波炉、小家电的显示驱动。

视觉与物联智能 2118

rabbitmq-04 rabbitmq解决分布式事务

1.RabbitMQ消息重试机制 消费者在消费消息的时候,如果消费者业务逻辑出现程序异常,这时候应该如何处理? 答案:使用消息重试机制。(springboot默认有消息重试机制) 1.1 如何合适选择重试机制 消费者获取到消息后,调用第三方接口,但接口暂时无法访问,是否需要重试? (需要重试机制) 消费者获取到消息后,抛出数据转换异常,是否需要重试?(不需要重试机制)需要发布进行解决。 ...

沐宇熙IT成长之路 1万+

HBV模型

据说mHM模型也是基于HBV模型原理。 网上不同版本的HBV模型稍有差别 def hbv_main(n_days, params, air_temp, prec, dpem): #n_days 模拟天数 #air_temp 每月温度 #prec 降水 #dpem 每月蒸发 Tsnow_thresh = 0.0 ca = 410. # Initialize arrays for the simiulation snow = np.zeros(air_temp.size

m0_46268055的博客 2330

项目总结|我是如何用RabbitMQ解决分布式事务问题的

文章目录前言本地事务和分布式事务简单一说本地事务分布式事务项目解决方案RabbitMQ延时队列(实现定时任务)解决了最终一致性,怎么保证消息可靠呢?总结 前言 回顾之前做的b2c项目,在订单服务那块,涉及到分布式事务的问题,情景是这样的:提交订单的时候,需要创建一笔订单,并且还需要调用远程库存服务进行库存锁定,等到支付成功之后将这个锁定的库存减掉。这里就涉及到远程调用的时候就可能会出现两个事务状态不一致的情况 本地事务和分布式事务 简单一说 本地事务: 它是在一个服务内的事务 分布式事务: 在微服务下,

MarkusZhang的博客 3254

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

基于RabbitMQ消息队列的分布式事务解决方案

1、5分钟了解MQ 介绍Rabbitmg用于解决分布式事务必须掌握的5个核心概念 一款分布式消息中间件,基于erlang语言开发, 具备语言级别的高并发处理能力。和Spring框架是同一家公司。 支持持久化、高可用 核心5个概念: Queue: 真正存储数据的地方 Exchange: 接收请求,转存数据 Bind: 收到请求后存储到哪里 消息生产者:发送数据的应用 消息消费者: 取出数据处理...

JavaEdge全是干货的技术号 6261

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

基于RabbitMQ解决分布式事务

1、可查询操作:服务操作具有全局唯一的标识,操作唯一的确定的时间。 2、幂等操作:重复调用多次产生的业务结果与调用一次产生的结果相同。一是通过业务操作实现幂等性,二是系统缓存所有请求与处理的结果,最后是检测到重复请求之后,自动返回之前的处理结果。 3、TCC操作:Try阶段,尝试执行业务,完成所有业务的检查,实现一致性;预留必须的业务资源,实现准隔离性。Confirm阶段:真正的去执行业务,不做任何检查,仅适用Try阶段预留的业务资源,Confirm操作还要满足幂等性。Cancel阶段:取消执行业务,释放T

Tiger_Paul的博客 2687

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

RabbitMQ解决分布式事务

RabbitMQ解决分布式事务 案例: 经典案例,以目前流行点外卖的案例,用户下单后,调用订单服务,让后订单服务调用派单系统通知送外卖人员送单,这时候订单系统与派单系统采用MQ异步通讯。 RabbitMQ解决分布式事务原理: 采用最终一致性原理。 需要保证以下三要素1、确认生产者一定要将数据投递到MQ服务器中(采用MQ消息确认机制)2、MQ消费者消息能够正确消费消息,采用手动ACK模式,使用不...

art_code的博客 1565

分布式事务之rabbitMQ最终一致性

一般的大型电商网站都会面临的问题:分布式事务,在面临分布式微服务等项目使用传统的单一事务已经无法满足,解决分布式事务的方案也比较多,有TCC事务补偿(基于2PC的实现)、2PC(两阶段提交)、3PC(三阶段提交)等,框架有JTA atomiks等。很多公司也有自己的分布式事务解决方案,比如最开始支付宝的XTS等 像JTA atomiks等2PC的方案效率并不高,中间需要一个协调者,并且是同步的,...

程序员 1万+

SpringBoot整合消息中间件 RabbitMQ 第 10 篇 —— RabbitMQ 解决分布式事务(完结)

什么是分布式事务? 在微服务环境下,因为会根据不同的业务会拆分成不同的服务,比如会员服务、订单服务、商品服务等,每个服务都有自己独立的数据库,并且是独立运行,互不影响。在多数据源的情况下,两个服务相互通讯的时候,就会出现数据不一致的问题,从而出现分布式事务产生的原因。举例:某电商平台,用户先下单后,扣库存失败,那么将会导致超卖;如果下单不成功,扣库存成功,那么会导致少卖。 解决分布式事务基本思路 先了解一些概念:ACID原理、CAP、Base理论、柔性事务与刚性事务、理解最终一致性思想...

流放深圳 1305
上一篇: RabbitMQ解决分布式事务
下一篇: Elasticsearch6.0简介入门介绍
Art&Code
博客等级 码龄7年 77粉丝 554原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值