最近项目开发中遇到两个问题,第一个是:订单创建后,5分钟没有付款,则取消订单。第二个是:每天晚上跑spring的定时任务,更新用户的访问次数。由于用户量多,用户访问记录表庞大且是分表,所以更新起来非常耗时,于是决定使用队列解决这两个问题。
使用rabbitmq,设置延时(需额外的插件),可以实现第一个问题。代码中我只需将用户信息取出,然后塞到队列中,由消费者慢慢消化队列,可以缓解第二个问题,下面,我们步入正题。
首先,我们创建一个消息生产者。消息生产者,顾名思义,就是产生消息的bean。我们需要在代码中生成自己需要的信息,可能是多个,以参数的形式,一个一个传递给消息生产者,然后消息生产者将消息一个一个放入队(quque),通过路由器(exchange)再转发给消费者。队列中的信息会一个一个传给消费者,只有消费者处理完这个信息,队列中的下一个信息才会继续传入。
生产者:
import java.io.IOException;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Service;
@Service
public class MessageProducer {
private Logger logger = LoggerFactory.getLogger(MessageProducer.class);
@Resource(name="amqpTemplate")
private AmqpTemplate amqpTemplate;
public void sendMessage(Object message) throws IOException {
logger.info("to send message:{}", message);
//message是需要传递的信息, 指定特定的queue:queueTestChris
amqpTemplate.convertAndSend("queueTestChris", message);
}
}
接着,创建消费者。作者这里的例子是简单的rabbitmq实现,只创建了一个消费者。实际中,生产者可以对应多个消费者,反之亦然。
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
public class MessageConsumer implements MessageListener {
@Override
public void onMessage(Message message) {
//这里是对消息的处理,new String(message.getBody())
System.out.print(new String(message.getBody()));
}
}
生产者和消费者创建完了,下面是最最关键的rabbitmq的配置了
根目录下创建rabbitmq.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.6.xsd">
<!--配置connection-factory,指定连接rabbit server参数 -->
<rabbit:connection-factory id="connectionFactory"
username="guest" password="guest" host="localhost" port="5672" />
<!--定义rabbit template用于数据的接收和发送 -->
<rabbit:template id="amqpTemplate" connection-factory="connectionFactory"
exchange="exchangeTest"/>
<!--通过指定下面的admin信息,当前producer中的exchange和queue会在rabbitmq服务器上自动生成 -->
<rabbit:admin id="connectAdmin" connection-factory="connectionFactory"/>
<!--定义queue -->
<rabbit:queue name="queueChris" durable="true" auto-delete="false" exclusive="false" declared-by="connectAdmin" />
<!-- 定义direct exchange,绑定queueTest -->
<rabbit:direct-exchange name="exchangeTest" durable="true" auto-delete="false" declared-by="connectAdmin" delayed="true">
<rabbit:bindings>
<rabbit:binding queue="queueChris" key="queueTestChris"></rabbit:binding>
</rabbit:bindings>
</rabbit:direct-exchange>
<!-- 消息接收者 -->
<bean id="receiverChris" class="com.hz.sunday.xlcl.service.mysql.consumer.MessageConsumer"></bean>
<!-- queue litener 观察 监听模式 当有消息到达时会通知监听在对应的队列上的监听对象-->
<rabbit:listener-container connection-factory="connectionFactory">
<rabbit:listener queues="queueChris" ref="receiverChris"/>
</rabbit:listener-container>
</beans>
然后applicationContext.xml引入:
<import resource="classpath*:rabbitmq.xml" ></import>
<!-- 激活annotation功能 -->
<context:annotation-config />
<!-- 激活annotation功能 -->
<context:spring-configured />
然后是代码中调用(这里读者在项目中随便放入哪里触发一下就好了,楼主没写过单元测试…):
for (int i = 0; i < 10; i++)
{
// 将消息放入队列,并发送
messageProducer.sendMessage(i);
}
可以看到控制台输出0到9.
一个简单的rabbitmq消息队列就完成啦。然后是延时队列的实现,这个比较简单http://www.rabbitmq.com/community-plugins.html网页下载适合的“rabbitmq_delayed_message_exchange插件”,下载完成后将它放到RabbitMQ插件安装目录({rabbitmq-server}/plugins/),然后执行命令rabbitmq-plugins enable rabbitmq_delayed_message_exchange启用插件,执行命令rabbitmq-plugins disable rabbitmq_delayed_message_exchange也可以关闭插件。插件也可以在最后的百度云盘下载。插件安装完成后,修改rabbitmq的配置文件:
<rabbit:direct-exchange name="exchangeTest" durable="true" auto-delete="false" declared-by="connectAdmin">
<rabbit:bindings>
<rabbit:binding queue="queueChris" key="queueTestChris"></rabbit:binding>
</rabbit:bindings>
</rabbit:direct-exchange>
新加delayed属性:
<!-- 定义direct exchange,绑定queueTest -->
<rabbit:direct-exchange name="exchangeTest" durable="true" auto-delete="false" declared-by="connectAdmin" delayed="true">
<rabbit:bindings>
<rabbit:binding queue="queueChris" key="queueTestChris"></rabbit:binding>
</rabbit:bindings>
</rabbit:direct-exchange>
然后生产者添加延时参数:
import java.io.IOException;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.stereotype.Service;
@Service
public class MessageProducer {
private Logger logger = LoggerFactory.getLogger(MessageProducer.class);
@Resource(name="amqpTemplate")
private AmqpTemplate amqpTemplate;
public void sendMessage(Object message) throws IOException {
logger.info("to send message:{}", message);
amqpTemplate.convertAndSend("queueTestChris", message,
new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message)
throws AmqpException {
//这个直接设置了30秒的延时,读者可以根据需求,将延时以参数形式传递过来
message.getMessageProperties().setDelay(30000);
return message;
}
});
}
}
最后,不要忘了删除原先的exchange和quequ,否则会报错
最后重启服务,一切都Ok啦。
最后是rabbitmq、rabbitmq延时插件、opt、所需jar的下载:
链接: https://pan.baidu.com/s/1c26NjhA
密码: kxdx