Use RabbitMQ implementation of distributed transactions

  Achieved points: 1, and the timing of the message table build a native task of ensuring reliable message transmission; 2, RabbitMQ reliable consumption; 3, redis ensure idempotent

  Two services: order service and messaging services

  Order service relevant code, the key code has notes

spring:
  datasource:
    druid:
      url: jdbc:postgresql://127.0.0.1:5432/test01?characterEncoding=utf-8
      username: admin
      password: 123456
      driver-class-name: org.postgresql.Driver
      initial-size: 1
      max-active: 20
      max-wait: 6000
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=2000
      min-idle: 1
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      validation-query: select 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
  rabbitmq:
    username: guest
    password: guest
    host: 127.0.0.1
    virtual-host: /
    port: 5672
    publisher-confirms: true
server:
  port: 8087
logging:
  level:
    org.springframework.jdbc.core.JdbcTemplate: DEBUG
     <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
package com.jlwj.mqtransaction.bean;

import lombok.Data;

import java.io.Serializable;

/**
 * @author hehang on 2019-06-27
 * @descriptionsdf
 */

@Data
public class OrderBean implements Serializable {

    private String orderNo;
    private String orderInfo;
}
package com.jlwj.mqtransaction.service;

import com.alibaba.fastjson.JSON;
import com.jlwj.mqtransaction.bean.OrderBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

/**
 * @author hehang ON 2019-07-01 
 * @description launch message to the MQ 
 * / 
@Service 
@ SLF4J 
public  class RabbitMQService { 

    @Autowired 
    Private RabbitTemplate rabbitTemplate; 

    @Autowired 
    Private the JdbcTemplate the jdbcTemplate; 

    @PostConstruct 
    Private  void initRabbitTemplate () {
         // setup message callback sends an acknowledgment, a state table update message has been sent successfully 
        rabbitTemplate.setConfirmCallback ((correlationData correlationData, Boolean ACK, the cause is String) -> { 
            log.info (String.valueOf (ACK)); 
            IF (ACK) { 
                String SQL= "update t_confirm set send_status = ? where id = ?";
                jdbcTemplate.update(sql,1,correlationData.getId());
            }
        });
    }
    public void sendMessage(OrderBean orderBean){
        rabbitTemplate.convertAndSend("orderExchange","orderRoutingKey", JSON.toJSONString(orderBean),
                message -> { message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);return message;},
                new CorrelationData(orderBean.getOrderNo()));
    }

}
package com.jlwj.mqtransaction.service;

import com.alibaba.fastjson.JSON;
import com.jlwj.mqtransaction.bean.OrderBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
importorg.springframework.stereotype.Service;
 Import org.springframework.transaction.annotation.Propagation;
 Import org.springframework.transaction.annotation.Transactional; 

Import javax.annotation.PostConstruct; 

/ ** 
 * @author hehang ON 2019-06-27 
 * @description order service, no longer write simple interfaces period 
 * / 

@Service 
@ SLF4J 
public  class OrderService { 

    @Autowired 
    Private the jdbcTemplate jdbcTemplate; 

    @Autowired 
    Private RabbitMQService rabbitMQService; 

    // note plus Affairs notes 
    the @Transactional (propagation = Propagation.REQUIRED)
    public void save(OrderBean orderBean){
        String sql1 = "insert into t_order(order_no,order_info) values (?,?)";
        String sql2 = "insert into t_confirm(id,message_info,send_status) values (?,?,?)";
        jdbcTemplate.update(sql1,orderBean.getOrderNo(),orderBean.getOrderInfo());
        jdbcTemplate.update(sql2,orderBean.getOrderNo(), JSON.toJSONString(orderBean),0);
        rabbitMQService.sendMessage(orderBean);
    }


}
package com.jlwj.mqtransaction.service;

import com.jlwj.mqtransaction.bean.OrderBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author hehang on 2019-07-01
 * @description 定时扫描confirm表,重复发送未发送的消息
 */
@Service
@Slf4j
public  class OrderScheduleService { 

    @Autowired 
    Private the JdbcTemplate the jdbcTemplate; 

    @Autowired 
    Private RabbitMQService rabbitMQService; 

    // timing of scanning the recording sheet, and sends the status message sent again 0, the number of retransmissions can be recorded even when the need for human intervention, the need for separate production environment Timing deployment task 
    @Scheduled (the cron = "* * * * 30/30?" )
     public  void scanOrder () { 
        log.info ( "scan plane confirm the timing table" ); 
        String SQL . = "SELECT * O O t_order from the Join ON = C o.order_no c.ID t_confirm WHERE c.send_status = 0 " ; 
        List <the OrderBean> orderBeanList jdbcTemplate.queryForList = (. SQL, the OrderBean class );
        for (OrderBean orderBean : orderBeanList) {
            rabbitMQService.sendMessage(orderBean);
        }
    }
}

  Messaging services relevant code

spring:
  rabbitmq:
    username: guest
    password: guest
    host: 127.0.0.1
    virtual-host: /
    port: 5672
    listener:
      simple:
        acknowledge-mode: manual
  redis:
    host: 127.0.0.1
      port: 6379
      timeout: 5000
      jedis:
        pool:
          max-idle: 8
          min-idle: 0
          max-active: 8
          max-wait: 1
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
package com.jlwj.messageservice.config;

import org.springframework.amqp.core.*;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

/**
 * @author hehang on 2019-06-27
 * @descriptionmq配置类
 */
@Configuration
public class RabbitConfig {

    /**
     * 死信队列
     * @return
     */
    @Bean
    public Queue dlQueue(){
        return QueueBuilder.durable("dlQueue")
                .build();
    }

    @Bean
    public DirectExchange dlExchange(){
        return (DirectExchange) ExchangeBuilder.directExchange("dlExchange").build();
    }
    @Bean
    public Binding dlMessageBinding(){
        return BindingBuilder.bind(dlQueue()).to(dlExchange()).with("dlRoutingKey");
    }

    
    @Bean
    public DirectExchange messageDirectExchange() {
        return (DirectExchange) ExchangeBuilder.directExchange("orderExchange")
                .durable(true)
                .build();
    }

    @Bean
    public Queue messageQueue() {
        return QueueBuilder.durable("orderQueue")
                //配置死信
                .withArgument("x-dead-letter-exchange","dlExchange")
                .withArgument("x-dead-letter-routing-key","dlRoutingKey")
                .build();
    }

    @Bean
    public Binding messageBinding() {
        return BindingBuilder.bind(messageQueue())
                .to(messageDirectExchange())
                .with("orderRoutingKey");
    }

}
package com.jlwj.messageservice.listener;

import com.alibaba.fastjson.JSON;
import com.jlwj.messageservice.bean.OrderBean;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @author hehang on 2019-06-28
 * @description订单监听
 */
@Component
@Slf4j

public class OrderListener {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @RabbitListener(queues = "orderQueue")
    public void HandlerMessage(Channel channel, @Payload String message, @Header(AmqpHeaders.DELIVERY_TAG) long tag,
                                @Header(AmqpHeaders.REDELIVERED) boolean reDelivered ) throws IOException {
        log.info(message);
        OrderBean orderBean = JSON.parseObject(message,OrderBean.class);
        try {
            log.info("收到的消息为{}",JSON.toJSONString(orderBean));
            //保证幂等性
            if(stringRedisTemplate.opsForValue().get(orderBean.getOrderNo())==null){
                sendMessage(orderBean);
                stringRedisTemplate.opsForValue () SET (orderBean.getOrderNo (),. ". 1" ); 
            } 
            channel.basicAck (Tag, to false ); 
        } the catch (Exception E) {
             IF (redelivered) { 
                log.info ( "processed message has been repeated failure: {} " , message); 
                channel.basicReject (Tag, to false ); 
            } the else { 
                log.error ( " treatment failure message " , E);
                 // re-queued one 
                channel.basicNack (Tag, to false , to true ) ;
            } 

        } 
    } 


    Private  void the sendMessage (the OrderBean orderBean) throws Exception {
         IF (. OrderBean.getOrderNo () the equals ( "0007" )) {
             int A =. 3/0 ; 
        } 
        log.info ( "analog SMS" ); 
    } 
}
package com.jlwj.messageservice.listener;

import com.alibaba.fastjson.JSON;
import com.jlwj.messageservice.bean.OrderBean;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @author hehang on 2019-06-28
 * @description订单监听
 */
@Component
@Slf4j

public class DlListener {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @RabbitListener(queues = "dlQueue")
    public void HandlerMessage(Channel channel, Message message, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
        log.info ( new new String (message.getBody ()));
         // manual processing, dead letter queue message 
        handlerDl ( new new String (message.getBody ())); 
        channel.basicAck (Tag, to false ); 
    } 


    Private  void handlerDl (String message) { 
        log.info ( "E-mail, requesting manual intervention: {}" , message); 
    } 
}

 

Guess you like

Origin www.cnblogs.com/hhhshct/p/11496536.html