原生实现rabbitMQ
我们模拟这样一个场景,假如你在网上购物商城买了东西,你支付过程5s、发送短信通知5s、邮件通知等时间就更长了,你整个支的过程我们模拟这样一个场景,假如你在网上购物商城买了东西,你支付过程5s、发送短信通知5s、邮件通知等时间就更长了,你整个支付的过程就和很长了,因为代码是耦合在一起,但是如果你使用消息中间系统的话,我们支付过程就很短了,因为消息通知都是异步的,由消息系实现。
创建连接工厂–>给工厂设置连接属性(地址,端口、账号密码等)–>由工厂得到连接
–>通过连接得到信道–>由信道设置交换器->信道发布消息
生产者 :
//创建连接工厂,设置工厂属性
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setUsername("guest");
factory.setPassword("guest");
//声明路由键和交换器
private final static String EXCHANGE_NAME = "direct_cc_confirm_1";
//路由键
private final static String ROUTE_KEY = "error";
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//通过信道声明一个交换器 第一个参数时交换器的名字 第二个参数时交换器的种类
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
//信道发布消息
channel.basicPublish(EXCHANGE_NAME,ROUTE_KEY,null,message.getBytes());
//关闭连接
channel.close();
connection.close();
消费者:
// 在信道声明交换器前步骤相同,在以后就是不同了
channel.queueBind(queueName,EXCHANGE_NAME,routingKey);
System.out.println("Waiting message.......");
// 创建队列消费者 设置一个监听器监听消费消息
final Consumer consumerB = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println( "Received ["+ envelope.getRoutingKey() + "] "+message);
}
};
//消费者自动确认:autoAck参数为true
channel.basicConsume(queueName, true, consumerB);
}
以上只有一个生产者和消费者只有一个路由键,但是如果生产者由很多,消费者只有一个那就会只接受路由键匹配的生产者发送的消息
生产者:
//定义一组路由键
String[]routingKeys = {"error","info","warning"};
for(int i=0;i<3;i++){
//路由键
String routingKey = routingKeys[i];
//要发送的消息
String message = "Hello world_"+(i+1);
/**
* 发送消息到交换器上
* 参数1:交换器的名字
* 参数2:路由键
* 参数3:BasicProperties
* 参数4:要发送的消息
*/
channel.basicPublish(EXCHANGE_NAME,routingKey,null,message.getBytes());
System.out.println("Sent "+routingKey+":"+message);
}
消费者:
//只匹配生产者由error路由键发送的消息
String routingKey = "error";
//7.队列通过路由键绑定到交换器上
channel.queueBind(queueName,EXCHANGE_NAME,routingKey);
System.out.println("Waiting message.......");
//8.设置一个监听器监听消费消息
Consumer consumerB = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body)
throws IOException {
String message = new String(body,"UTF-8");
System.out.println("Accept:"+envelope.getRoutingKey()+":"+message);
}
};
//9.自动确认:autoAck参数为flase
channel.basicConsume(queueName,flase,consumerB);
}
//消费者如果要接受所有信息的话
//5.声明随机队列
String queueName = channel.queueDeclare().getQueue();
//6.定义一组路由键消费所有日志
String[]routingKeys = {"error","info","warning"};
//7.队列通过路由键绑定到交换器上
for(String routingKey:routingKeys){
//队列和交换器的绑定
channel.queueBind(queueName,EXCHANGE_NAME,routingKey);
}
System.out.println("Waiting message.......");
//8.设置一个监听器监听消费消息
Consumer consumerA = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body)throws IOException {
String message = new String(body,"UTF-8");
System.out.println("Accept:"+envelope.getRoutingKey()+":"+message);
}
};
channel.basicConsume(queueName,flase,consumerA);
自动确认机制分为同步确认和异步确认,就是在发送方发了消息,消费者返回ack,才确认这个消息为发送成功,否则就是发送失败,unacked.
同步实现:
channel.confirmSelect();
//发布消息的交换器上
for(int i=0;i<2;i++){
String msg = "Hello "+(i+1);
channel.basicPublish(EXCHANGE_NAME,ROUTE_KEY,null,msg.getBytes());
//等待RabbitMQ返回消息确认消息已送达RabbitMQ服务器
if (channel.waitForConfirms()){
System.out.println("发送方同步确认: "+ROUTE_KEY+":"+msg);
}
}
异步实现
//将信道设置为发送方确认
channel.confirmSelect();
//信道被关闭监听器 用于RabbitMQ服务器断线重连
//channel.addShutdownListener();
/**
* 生产者异步确认监听
* 参数deliveryTag代表了当前channel唯一的投递
* 参数multiple:false
*
*/
channel.addConfirmListener(new ConfirmListener() {
//RabbitMQ服务器确认收到消息
public void handleAck(long deliveryTag, boolean multiple)
throws IOException {
System.out.println("RabbitMQ服务器确认收到消息Ack deliveryTag="+deliveryTag
+"multiple:"+multiple);
}
//RabbitMQ服务器由于自己内部出现故障没有收到消息
public void handleNack(long deliveryTag, boolean multiple)
throws IOException {
System.out.println("RabbitMQ服务没有收到消息Ack deliveryTag="+deliveryTag
+"multiple:"+multiple);
}
});
//生产者异步返回监听 这里和发布消息时的mandatory参数有关
//参数mandatory:mandatory=true,投递消息时无法找到一个合适的队列,把消息返回给生产者,mandatory=false 丢弃消息(缺省)
channel.addReturnListener(new ReturnListener() {
public void handleReturn(int replyCode, String replyText,
String exchange, String routingKey,
AMQP.BasicProperties properties, byte[] body)
throws IOException {
String msg = new String(body);
System.out.println("replyText:"+replyText);
System.out.println("exchange:"+exchange);
System.out.println("routingKey:"+routingKey);
System.out.println("msg:"+msg);
}
});
//后面是进行发布操作
//9.消费者自动确认:autoAck参数为true
channel.basicConsume(queueName, true, consumerB);