配置POM
配置application.properties
发送RabbitMQ消息分为两种,第一种是同步调用(RPC),也就是发送完消息后需要等待消息处理结果,第二种是异步消息,发送者发消息后可以处理其他业务,不等待消息处理结果。
同步消息
消息发送类
package com.test;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
@Component
public class SendMsg {
@Value("${spring.rabbitmq.host}")
private String host = "localhost";
@Value("${spring.rabbitmq.port}")
private String port = "5672";
@Value("${spring.rabbitmq.username}")
private String userName = "admin2";
@Value("${spring.rabbitmq.password}")
private String pwd = "admin2";
public byte[] toBytes(Object obj)
{
try
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
byte[] data = baos.toByteArray();
baos.close();
return data;
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
public boolean sendSyncObject(Object obj) {
try
{
System.out.println("host="+host+",port="+port);
//1.creeate ConnectionFactory
ConnectionFactory cf = new ConnectionFactory();
cf.setHost(host);
cf.setPort(Integer.parseInt(port));
cf.setUsername(userName);
cf.setPassword(pwd);
//2.create Conection
Connection con = cf.newConnection();
//3.create Channel
Channel channel = con.createChannel();
//4.create exchage
String exgName = "test_rpc_exg";
channel.exchangeDeclare(exgName, "direct");
String queueName = "test_rpc";
String routeKey = "java.io.File";
boolean durable = true;
boolean exclusive = false;
boolean autoDelete = false;
channel.queueDeclare(queueName, durable, exclusive, autoDelete, null);
String replyTo = channel.queueDeclare().getQueue();
final String corrId = java.util.UUID.randomUUID().toString();
//5.bind exchange and queue
channel.queueBind(queueName,exgName,routeKey);
//6.send msg
AMQP.BasicProperties prop = new AMQP.BasicProperties.Builder()
.correlationId(corrId).replyTo(replyTo).build();
channel.basicPublish(exgName, routeKey, prop,toBytes(obj));
final BlockingQueue<byte[]> response = new ArrayBlockingQueue<byte[]>(1);
Consumer csum = new DefaultConsumer(channel){
@Override
public void handleDelivery(java.lang.String consumerTag,
Envelope envelope, AMQP.BasicProperties properties,
byte[] body)
{
String corrId2 = properties.getCorrelationId();
if(corrId.equals(corrId2))
{
response.offer(body);
}
}
};
channel.basicConsume(replyTo,true,csum);
//从JVM阻塞队列中获取回复消息,如果没收到当前线程阻塞
byte[] result = response.take();
String str = new String(result);
//7.close Connection
channel.close();
con.close();
if("true".equals(str))
return true;
else
return false;
}
catch(Exception e)
{
e.printStackTrace();
}
return false;
}
}
消息接收者
消息接收者使用定时器实现,消息接收后需要解析消息并调用保存消息的方法,需要注入用户的Service对象
package com.test;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.test.bean.TvInfo;
import com.test.service.ITvService;
@Component
public class ReceiveMsg {
@Autowired
private ITvService serv;
@Value("${spring.rabbitmq.host}")
private String host = "localhost";
@Value("${spring.rabbitmq.port}")
private String port = "5672";
@Value("${spring.rabbitmq.username}")
private String userName = "admin2";
@Value("${spring.rabbitmq.password}")
private String pwd = "admin2";
@Scheduled(cron="0/2 * * ? * *")
public void receive() {
try
{
//System.out.println("receive = "+new java.sql.Timestamp(System.currentTimeMillis()));
//1.creeate ConnectionFactory
ConnectionFactory cf = new ConnectionFactory();
cf.setHost(host);
cf.setPort(Integer.parseInt(port));
cf.setUsername(userName);
cf.setPassword(pwd);
//2.create Conection
Connection con = cf.newConnection();
//3.create Channel
final Channel channel = con.createChannel();
String queueName = "test_rpc";
Consumer csum = new DefaultConsumer(channel){
@Override
public void handleDelivery(java.lang.String consumerTag,
Envelope envelope, AMQP.BasicProperties properties,
byte[] body)
{
try
{
String replyTo = properties.getReplyTo();
String corrId = properties.getCorrelationId();
AMQP.BasicProperties replyProp = new AMQP.BasicProperties().builder()
.correlationId(corrId).build();
Object obj = toObject(body);
System.out.println("obj==="+obj);
if(obj instanceof TvInfo)
{
TvInfo tv = (TvInfo)obj;
boolean rtn = serv.saveTv(tv);
String result = rtn+"";
channel.basicPublish("", replyTo, replyProp, result.getBytes());
}
channel.basicAck(envelope.getDeliveryTag(), false);
}
catch(Exception e)
{
e.printStackTrace();
}
}
};
boolean isAck = false;
channel.basicConsume(queueName,isAck,csum);
//7.close Connection
//channel.close();
//con.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
public Object toObject(byte[] data)
{
try
{
ByteArrayInputStream bais = new ByteArrayInputStream(data);
ObjectInputStream ois = new ObjectInputStream(bais);
Object obj = ois.readObject();
bais.close();
return obj;
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
}
Controller层调用发送同步消息
启动类,使能定时器
异步消息
同步消息是工具类自动定义的交换机和消息队列,异步消息需要人为地定义的交换机和消息队列,有两种方式,一是使用RabbitMQ的控制台,二是使用代码,本例中使用代码配置交换机和消息队列
配置交换机与队列
package com.test.util;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 配置Rabbitmq交换机与队列
* 如果通过Rabbitmq控制台创建好交换机与队列,此类不再需要
* @author java
*
*/
@Configuration
public class RabbitmqConf {
public static String QUEUE1 = "test_queue1";
public static String EXCHANGE1 = "test_exg1";
@Bean
public Queue queue1()
{
return new Queue(QUEUE1);
}
@Bean
public DirectExchange exg1()
{
return new DirectExchange(EXCHANGE1);
}
@Bean
public Binding directBinding1() {
return BindingBuilder.bind(queue1()).to(exg1()).with("java");
}
}
注入消息发送模板对象
消息发送
需要指明交换机,路由关键字。
在发送异步消息时,发送方立刻执行下面操作,不等待消息是否接收到数据库,所以有可能在页面上立时不能显示,通常情况下我们让发送线程等待一段时间,以保证消息被接收到数据库,前台页面能够查询显示出来。
消息接收处理
使用消息监听器,需要注入消息处理对象,将消息解析到数据库中。
package com.test.util;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.test.bean.TvInfo;
import com.test.service.ITvService;
/**
* 接收RabbitMQ异步消息的监听器
* 当监听的队列上有消息,获取消息并保存到数据库,需要注意Service接口
* @author java
*
*/
@Component
public class RabbitmqListener {
@Autowired
private ITvService serv;
//@RabbitListener(queues="test_queue1")
public void receive(Message msg)
{
try
{
byte[] data = msg.getBody();
Object obj = toObject(data);
TvInfo tv = (TvInfo)obj;
boolean rtn = serv.saveTv(tv);
System.out.println("Receive Msg="+new String(data));
}
catch(Exception e)
{
e.printStackTrace();
}
}
public Object toObject(byte[] data)
{
try
{
ByteArrayInputStream bais = new ByteArrayInputStream(data);
ObjectInputStream ois = new ObjectInputStream(bais);
Object obj = ois.readObject();
bais.close();
return obj;
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
}