项目需要将用户的消费记录以异步方式存入到数据库表中,故引入了MQ,用户消费时发送MQ消息,文件处理服务监听到MQ消息后进行入库操作,以下是MQ的具体实现方案
1、消息生产者端实现
1.1、在pom.xml中加入MQ的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
1.2、在SpringBoot核心配置文件中加好RabbitMQ的配置
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.publisher-confirms=true
spring.rabbitmq.publisher-returns=true
1.3、新建AmqpConfig.java(自定义RabbitTemplate )
package com.xrq.demo.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.xrq.demo.common.CommonConst;
@Configuration
public class AmqpConfig {
private static final Logger logger = LoggerFactory.getLogger(AmqpConfig.class);
//queue初始化
@Bean
public Queue userAccountChangeQueue() {
return new Queue(CommonConst.USERACCOUNTCHANGEQUEUE);
}
@Bean(name = "myRabbitTemplete")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
RabbitTemplate myRabbitTemplete(ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplete = new RabbitTemplate(connectionFactory);
rabbitTemplete.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (!ack) {
logger.info("MQ消息发送失败,消息重发");
}
}
});
rabbitTemplete.setMessageConverter(new Jackson2JsonMessageConverter());
return rabbitTemplete;
}
}
1.4、新建接口IAmqpStoredObject.java
package com.xrq.demo.amqp.bo;
public interface IAmqpStoredObject {
/**
* Queue
*
* @return
*/
String getQueueName();
}
1.5、新建MQSender.java
package com.xrq.demo.amqp;
import java.io.IOException;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.xrq.demo.amqp.bo.IAmqpStoredObject;
/**
* MQ生产者类
*
* @author XRQ
*
*/
@Component
public class MQSender {
@Autowired
@Qualifier("myRabbitTemplete")
RabbitTemplate myRabbitTemplete;
/**
* 发送MQ
*
* @param t
* @throws IOException
*/
public <T extends IAmqpStoredObject> void send(T t) throws IOException {
// Connection connection = connectionFactory.createConnection();
// Channel channel = connection.createChannel(false);
// channel.queueDeclare(t.getQueueName(), true, false, false, null);
this.myRabbitTemplete.convertAndSend(t.getQueueName(), t);
}
}
1.6、新建UserAccountChange.java(MQ信息载体)
package com.xrq.demo.amqp.bo;
import java.io.Serializable;
import org.springframework.stereotype.Component;
/**
* 用户账户变更 MQ消息实体类
*
* @author XRQ
*
*/
@Component
public class UserAccountChange implements Serializable, IAmqpStoredObject {
private static final long serialVersionUID = 1L;
// 用户id,对应user表的主键
private long userID;
// 账号类型,默认为系统用户1
private int accountTypeID;
// 发生金额
private long price;
// 余额
private long amount;
// 操作类型,1、扣款,2退款
private int handelType;
private String changeReason;
public long getUserID() {
return userID;
}
public void setUserID(long userID) {
this.userID = userID;
}
public int getAccountTypeID() {
return accountTypeID;
}
public void setAccountTypeID(int accountTypeID) {
this.accountTypeID = accountTypeID;
}
public long getAmount() {
return amount;
}
public void setAmount(long amount) {
this.amount = amount;
}
public long getPrice() {
return price;
}
public void setPrice(long price) {
this.price = price;
}
public int getHandelType() {
return handelType;
}
public void setHandelType(int handelType) {
this.handelType = handelType;
}
public String getChangeReason() {
return changeReason;
}
public void setChangeReason(String changeReason) {
this.changeReason = changeReason;
}
//Queue
private String queueName = CommonConst.USERACCOUNTCHANGEQUEUE;
public void setQueueName(String queueName) {
this.queueName = queueName;
}
@Override
public String getQueueName() {
return this.queueName;
}
}
1.7、新建IUserAccountService.java
package com.xrq.demo.user.service;
/**
* 用户账户业务接口
*
* @author XRQ
*
*/
public interface IUserAccountService {
/**
* 账户金额操作
* @param userID
* @param price
* @param handelType
* @throws Exception
*/
public void sendMQForUserAccount(long userID, long price, int handelType) throws Exception;
}
1.8、新建UserAccountService.java
package com.xrq.demo.user.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.xrq.demo.MQSender;
import com.xrq.demo.amqp.bo.UserAccountChange;
@Service
public class UserAccountService implements IUserAccountService {
@Autowired
MQSender mqSender;
/**
* 发送MQ更新数据库
*
* @param totalPrice
* @throws Exception
*/
@Override
public void sendMQForUserAccount(long userID, long price, int handelType) throws Exception {
UserAccountChange userAccountChange = new UserAccountChange();
userAccountChange.setUserID(userID);
userAccountChange.setAccountTypeID(1);
userAccountChange.setHandelType(handelType);
userAccountChange.setPrice(price);
mqSender.send(userAccountChange);
}
}
2、消息消费者端实现(文件处理服务)
2.1、在pom.xml中加入MQ的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.2、在SpringBoot核心配置文件中加好RabbitMQ的配置
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.publisher-confirms=true
spring.rabbitmq.publisher-returns=true
spring.rabbitmq.listener.simple.acknowledge-mode=MANUAL
spring.rabbitmq.listener.simple.retry.enabled=true
2.3、新建消费者UserAccountChangeReceiver.java
package com.xrq.demo.mqreceiver;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.rabbitmq.client.Channel;
import com.xrq.demo.amqp.bo.UserAccountChange;
import com.xrq.demo.common.CommonConst;
import com.xrq.demo.user.service.IUserAccountService;
import com.xrq.demo.utils.JsonUtils;
/**
* MQ消费者类
*
* @author XRQ
*
*/
@Component
public class UserAccountChangeReceiver {
@Autowired
IUserAccountService userAccountService;
@Autowired
@Qualifier("myRabbitTemplete")
RabbitTemplate myRabbitTemplete;
@RabbitListener(queues = CommonConst.USERACCOUNTCHANGEQUEUE)
public void onMessage(Message message, Channel channel) throws Exception {
String str = null;
int result = 0;
try {
str = new String(message.getBody());
if (StringUtils.isNotBlank(str)) {
UserAccountChange data = JsonUtils.JsonToBean(str, UserAccountChange.class);
//写入txt的逻辑
result = userAccountService.changeAccount(data);
}
} catch (Exception e) {
result = 99999999;
//异常情况下,将信息转发到QueueName_error的队列里面去,方便排错或者手工处理
String errorQueue = message.getMessageProperties().getConsumerQueue() + "_error";
channel.queueDeclare(errorQueue, true, false, false, null);
myRabbitTemplete.convertAndSend(errorQueue, message);
} finally {
if (result <= 0) {
channel.basicQos(0, Integer.MAX_VALUE, false);
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
} else {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}
}
}
至此,MQ相关代码就完成了