转载请注明出处

0.目录

RabbitMQ-从基础到实战(2)— 防止消息丢失

RabbitMQ-从基础到实战(3)— 消息的交换(上)

RabbitMQ-从基础到实战(4)— 消息的交换(中)

RabbitMQ-从基础到实战(5)— 消息的交换(下)

RabbitMQ-从基础到实战(6)— 与Spring集成

1.简介

本篇博文介绍了在windows平台下安装RabbitMQ Server端,并用JAVA代码实现收发消息

2.安装RabbitMQ

  1. RabbitMQ是用Erlang开发的,所以需要先安装Erlang环境,在这里下载对应系统的Erlang安装包进行安装
  2. 点击这里下载对应平台的RabbitMQ安装包进行安装

Windows平台安装完成后如图

3.启用RabbitMQ Web控制台

RabbitMQ提供一个控制台,用于管理和监控RabbitMQ,默认是不启动的,需要运行以下命令进行启动

  1. 点击上图的Rabbit Command Prompt,打开rabbitMQ控制台
  2. 官方介绍管理控制台的页面,可以看到,输入以下命令启动后台控制插件

    rabbitmq-plugins enable rabbitmq_management

  3. 登录后台页面:http://localhost:15672/   密码和用户名都是 guest ,界面如下

目前可以先不用理会此界面,后面使用到时会详细介绍,也可以到这里查看官方文档。

4.编写MessageSender

Spring对RabbitMQ已经进行了封装,正常使用中,会使用Spring集成,第一个项目中,我们先不考虑那么多

在IDE中新建一个Maven项目,并在pom.xml中贴入如下依赖,RabbitMQ的最新版本依赖可以在这里找到

<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>4.1.0</version>
</dependency>

等待Maven下载完成后,就可以在Maven Dependencies中看到RabbitMQ的JAR

在这里,我们发现,RabbitMQ的日志依赖了slf4j-api这个包,slf4j-api并不是一个日志实现,这样子是打不出日志的,所以,我们给pom加上一个日志实现,这里用了logback

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.1</version>
</dependency>

之后maven依赖如下,可以放心写代码了

新建一个MessageSender类,代码如下

 import java.io.IOException;
import java.util.concurrent.TimeoutException; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory; public class MessageSender { private Logger logger = LoggerFactory.getLogger(MessageSender.class); //声明一个队列名字
private final static String QUEUE_NAME = "hello"; public boolean sendMessage(String message){
//new一个RabbitMQ的连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置需要连接的RabbitMQ地址,这里指向本机
factory.setHost("127.0.0.1");
Connection connection = null;
Channel channel = null;
try {
//尝试获取一个连接
connection = factory.newConnection();
//尝试创建一个channel
channel = connection.createChannel();
//这里的参数在后面详解
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//注意这里调用了getBytes(),发送的其实是byte数组,接收方收到消息后,需要重新组装成String
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
logger.info("Sent '" + message + "'");
//关闭channel和连接
channel.close();
connection.close();
} catch (IOException | TimeoutException e) {
//失败后记录日志,返回false,代表发送失败
logger.error("send message faild!",e);
return false;
}
return true;
}
}

然后在App类的main方法中调用sendMessage

 public class App {
public static void main( String[] args ){
MessageSender sender = new MessageSender();
sender.sendMessage("hello RabbitMQ!");
}
}

打印日志如下

打开RabbitMQ的控制台,可以看到消息已经进到了RabbitMQ中

点进去,用控制台自带的getMessage功能,可以看到消息已经成功由RabbitMQ管理了

至此,MessageSender已经写好了,在该类的31和33行,我们分别调用了队列声明和消息发送

channel.queueDeclare(QUEUE_NAME, false, false, false, null);
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());

queueDeclare,有很多参数,我们可以看一下他的源码,注释上有详细的解释,我简单翻译了一下

 /**
* Declare a queue 声明一个队列
* @see com.rabbitmq.client.AMQP.Queue.Declare
* @see com.rabbitmq.client.AMQP.Queue.DeclareOk
* @param queue the name of the queue队列的名字
* @param durable true if we are declaring a durable queue (the queue will survive a server restart)是否持久化,为true则在rabbitMQ重启后生存
* @param exclusive true if we are declaring an exclusive queue (restricted to this connection)是否是排他性队列(别人看不到),只对当前连接有效,当前连接断开后,队列删除(设置了持久化也删除)
* @param autoDelete true if we are declaring an autodelete queue (server will delete it when no longer in use)自动删除,在最后一个连接断开后删除队列
* @param arguments other properties (construction arguments) for the queue 其他参数
* @return a declaration-confirm method to indicate the queue was successfully declared
* @throws java.io.IOException if an error is encountered
*/
Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
Map<String, Object> arguments) throws IOException;

前面4个都非常好理解,最后一个“其他参数”,到底是什么其他参数,这个东西真的很难找,用到再解释吧,官方文档如下

  • TTL Time To Live  存活时间

basicPublish的翻译如下

  /**
* Publish a message.发送一条消息
*
* Publishing to a non-existent exchange will result in a channel-level
* protocol exception, which closes the channel.
*
* Invocations of <code>Channel#basicPublish</code> will eventually block if a
* <a href="http://www.rabbitmq.com/alarms.html">resource-driven alarm</a> is in effect.
*
* @see com.rabbitmq.client.AMQP.Basic.Publish
* @see <a href="http://www.rabbitmq.com/alarms.html">Resource-driven alarms</a>
* @param exchange the exchange to publish the message to 交换模式,会在后面讲,官方文档在这里 * @param routingKey the routing key 控制消息发送到哪个队列
* @param props other properties for the message - routing headers etc 其他参数
* @param body the message body 消息,byte数组
* @throws java.io.IOException if an error is encountered
*/
void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;

这里又有个其他参数,它的类型是这样的,设置消息的一些详细属性

5.编写MessageConsumer

为了和Sender区分开,新建一个Maven项目MessageConsumer

 package com.liyang.ticktock.rabbitmq;

 import java.io.IOException;
import java.util.concurrent.TimeoutException; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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; public class MessageConsumer { private Logger logger = LoggerFactory.getLogger(MessageConsumer.class); public boolean consume(String queueName){
//连接RabbitMQ
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
Connection connection = null;
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
//这里声明queue是为了取消息的时候,queue肯定会存在
//注意,queueDeclare是幂等的,也就是说,消费者和生产者,不论谁先声明,都只会有一个queue
channel.queueDeclare(queueName, false, false, false, null); //这里重写了DefaultConsumer的handleDelivery方法,因为发送的时候对消息进行了getByte(),在这里要重新组装成String
Consumer consumer = 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");
logger.info("Received '" + message + "'");
}
};
//上面是声明消费者,这里用声明的消费者消费掉队列中的消息
channel.basicConsume(queueName, true, consumer); //这里不能关闭连接,调用了消费方法后,消费者会一直连接着rabbitMQ等待消费 } catch (IOException | TimeoutException e) {
//失败后记录日志,返回false,代表消费失败
logger.error("send message faild!",e);
return false;
} return true;
}
}

然后在App的main方法中调用Cunsumer进行消费

 public class App
{
//这个队列名字要和生产者中的名字一样,否则找不到队列
private final static String QUEUE_NAME = "hello"; public static void main( String[] args )
{
MessageConsumer consumer = new MessageConsumer();
consumer.consume(QUEUE_NAME);
}
}

结果如下,消费者一直在等待消息,每次有消息进来,就会立刻消费掉

6.多个消费者同时消费一个队列

改造一下Consumer

在App中new多个消费者

改造Sender,使它不停的往RabbitMQ中发送消息

启动Sender

启动Consumer,发现消息很平均的发给四个客户端,一人一个,谁也不插队

如果我们把速度加快呢?把Sender的休息时间去掉,发现消费开始变得没有规律了,其实呢,它还是有规律的,这个是RabbitMQ的特性,称作“Round-robin dispatching”,消息会平均的发送给每一个消费者,可以看第一第二行,消息分别是56981和56985,相应的82、82、84都被分给了其他线程,只是在当前线程的时间片内,可以处理这么多任务,所以就一次打印出来了

7.结束语

这一章介绍了从安装到用JAVA语言编写生产者与消费者,在这里只是简单的消费消息并打印日志,如果一个消息需要处理的时间很长,而处理的过程中,这个消费者挂掉了,那消息会不会丢失呢?答案是肯定的,而且已经分配给这个消费者,但还没来得及处理的消息也会一并丢失掉,这个问题,RabbitMQ早就考虑到了,并且提供了解决方案,下一篇博文将进行详细介绍

RabbitMQ-从基础到实战(1)— Hello RabbitMQ的更多相关文章

  1. RabbitMQ-从基础到实战(3)— 消息的交换

    1.简介 在前面的例子中,每个消息都只对应一个消费者,即使有多个消费者在线,也只会有一个消费者接收并处理一条消息,这是消息中间件的一种常用方式.还有另外一种方式,生产者生产一条消息,广播给所有的消费者 ...

  2. RabbitMQ-从基础到实战(2)— 防止消息丢失

    转载请注明出处 1.简介 RabbitMQ中,消息丢失可以简单的分为两种:客户端丢失和服务端丢失.针对这两种消息丢失,RabbitMQ都给出了相应的解决方案. 2.防止客户端丢失消息 如图,生产者P向 ...

  3. RabbitMQ-从基础到实战(4)— 消息的交换(下)

    0.目录 RabbitMQ-从基础到实战(1)- Hello RabbitMQ RabbitMQ-从基础到实战(2)- 防止消息丢失 RabbitMQ-从基础到实战(3)- 消息的交换(上) 1.简介 ...

  4. RabbitMQ-从基础到实战(5)— 消息的交换(下)

    转载请注明出处 0.目录 RabbitMQ-从基础到实战(1)- Hello RabbitMQ RabbitMQ-从基础到实战(2)- 防止消息丢失 RabbitMQ-从基础到实战(3)- 消息的交换 ...

  5. RabbitMQ-从基础到实战(6)— 与Spring集成

    0.目录 RabbitMQ-从基础到实战(1)- Hello RabbitMQ RabbitMQ-从基础到实战(2)- 防止消息丢失 RabbitMQ-从基础到实战(3)- 消息的交换(上) Rabb ...

  6. RabbitMQ-从基础到实战(3)— 消息的交换(上)

    转载请注明出处 0.目录 RabbitMQ-从基础到实战(1)— Hello RabbitMQ RabbitMQ-从基础到实战(2)— 防止消息丢失 RabbitMQ-从基础到实战(4)— 消息的交换 ...

  7. C# RabbitMQ延迟队列功能实战项目演练

    一.需求背景 当用户在商城上进行下单支付,我们假设如果8小时没有进行支付,那么就后台自动对该笔交易的状态修改为订单关闭取消,同时给用户发送一份邮件提醒.那么我们应用程序如何实现这样的需求场景呢?在之前 ...

  8. C#消息队列(RabbitMQ)零基础从入门到实战演练

    一.课程介绍 如果您从工作中之听过但未有接触过消息对队列(MQ),如果你接触过一点关于MQ的知识,如果没有这么的多如果的话......,那么阿笨将通过本次<C#消息队列零基础从入门到实战演练&g ...

  9. rabbitmq在ios中实战采坑

    1. rabbitmq在ios中实战采坑 1.1. 问题 ios使用rabbitmq连接,没过多久就断开,并报错.且用android做相同的步骤并不会报错,错误如下 Received connecti ...

随机推荐

  1. 大咖,我能转行做UX设计师吗?

    前几天,有个朋友找到我,叫我给分析下他适不适合转UX设计.他的专业是建筑设计,之所以要辞职,也就是公司破事多,老板又不看重他.看到UX设计这个行业的前景很不错,想要转行.他说的也没错, 现在的UX设计 ...

  2. HDU5882

    Balanced Game Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Tot ...

  3. CodeForces 512B(区间dp)

    D - Fox And Jumping Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64 ...

  4. UE4里的渲染线程

    记的上次看过UniRx里的源代码,说是参考微软的响应式编程框架,响应式编程里的一些理论不细说,只单说UniRx里的事件流里的事件压入与执行,与UE4的渲染线程设计有很多相同之处,如果有了解响应式编程相 ...

  5. C# 数组、ArrayList、List、Dictionary的用法与区别

    前言 在工作中经常遇到C#数组.ArrayList.List.Dictionary存取数据,但是该选择哪种类型进行存储数据,对于初学者的我一直不知道该怎么取舍.于是抽空好好看了下他们的用法和比较,在这 ...

  6. maven常用命令介绍(持续更新)

    一.Maven的基本概念 主要服务于基于Java平台的项目构建,依赖管理和项目信息管理. 1.1.项目构建 项目构建过程包括[清理项目]→[编译项目]→[测试项目]→[生成测试报告]→[打包项目]→[ ...

  7. (@WhiteTaken)设计模式学习——简单工厂

    最近工作比较忙,所以没有怎么写博客,这几天将集中学习一下(厉风行)讲解的设计模式的相关知识,并对主要的代码进行介绍. 言归正传,接下来介绍最简单也是最基础的简单工厂设计模式. 什么是简单工厂? 简单工 ...

  8. 计算机程序的思维逻辑 (64) - 常见文件类型处理: 属性文件/CSV/EXCEL/HTML/压缩文件

    对于处理文件,我们介绍了流的方式,57节介绍了字节流,58节介绍了字符流,同时,也介绍了比较底层的操作文件的方式,60节介绍了随机读写文件,61节介绍了内存映射文件,我们也介绍了对象的序列化/反序列化 ...

  9. ArcGIS制图表达Representation-制图表达使用须知

    ArcGIS制图表达Representation-制图表达使用须知 by 李远祥 前面章节也介绍了一些制图表达的适用范围和场景,如果有觉得需要使用制图表达去完成其工作的话,还需要注意制图表达的一些技术 ...

  10. std::list 源代码解析

    首先声明,下面的讲解都是针对GCC2.9,std::alloc 通过下面的源代码大家可以看到list类的内部成员是一个node,而他的类型是linktype,前面的typedef里面有介绍是一个指针, ...