上一篇记录了rabbitmq的安装,这一篇记录一下rabbitmq的java客户端的简单使用,当然在项目中我们有更为复杂的应用场景,这里只有最简单的点对点生产者与消费者模式。

1、建立工程

首先建立一个简单的maven工程,我这边使用了平时使用的demo工程

pom.xml配置,本次案例中只需要两个包即可,是用commons包的序列化,amqp则是rabbitmq的java包。


2、新建点对点抽象类

因为这个例子只讲述非常简单的点对点生产者与消费者关系,在某种程度上两者有很多共性,所以这里干脆抽象成一个类了。具体代码如下:

package ucs_test.rabbitmq;

import java.io.IOException;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory; /**
* @author 作者 ucs_fuqing
* @date 创建时间:2017年8月11日 下午2:21:27
* @version 1.0
* @parameter
* @since
* @return
*/
public abstract class PointToPoint {
protected Channel channel;
protected Connection connection;
protected String pointName; /**
* 获取一个队列的连接
* @param pointName
* @throws IOException
*/
public PointToPoint(String pointName) throws IOException{
this.pointName = pointName; //创建连接工厂
ConnectionFactory cf = new ConnectionFactory(); //设置rabbitmq服务器地址
cf.setHost("192.168.149.133"); //设置rabbitmq服务器用户名
cf.setUsername("hxb"); //设置rabbitmq服务器密码
cf.setPassword("hxb"); //获取一个新的连接
connection = cf.newConnection(); //创建一个通道
channel = connection.createChannel(); //申明一个队列,如果这个队列不存在,将会被创建
channel.queueDeclare(pointName, false, false, false, null);
} /**
*
* @Title: close
* @Description: 其实在程序完成时一般会自动关闭连接,但是这里提供手动操作的入口,
* @param @throws IOException 设定文件
* @return void 返回类型
* @throws
*/
public void close() throws IOException{
this.channel.close();
this.connection.close();
}
}

在上面代码中,实现的是创建一个队列或者关闭它,在默认的情况下channel和connection会自动关闭,但是我觉得还是提供手动关闭的入口更好一些。


3、生产者

这个例子中的生产者其实非常简单,我们创建了一个连接,并且获取了通道,接下来就可以直接往我们指定的队列(queue)中发送消息了,如果这个队列不存在,则会被程序自动创建。

package ucs_test.rabbitmq;

import java.io.IOException;
import java.io.Serializable; import org.apache.commons.lang.SerializationUtils; import com.mchange.io.SerializableUtils; /**
* @author 作者 ucs_fuqing
* @date 创建时间:2017年8月11日 下午2:33:13
* @version 1.0
* @parameter
* @since
* @return
*/
public class Producer extends PointToPoint{ public Producer(String pointName) throws IOException {
super(pointName);
// TODO Auto-generated constructor stub
} /**
*
* @Title: sendMessage
* @Description: 生产消息
* @param @param Object
* @param @throws IOException 设定文件
* @return void 返回类型
* @throws
*/
public void sendMessage(Serializable Object) throws IOException{
channel.basicPublish("", pointName, null, SerializationUtils.serialize(Object));
}
}

上面代码看到,我们只是简单的向pointName的队列发送了一个对象。


4、消费者

我们这里的消费者也非常简单,仅仅只是拿到并打印出消息即可

package ucs_test.rabbitmq;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map; import org.apache.commons.lang.SerializationUtils; import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.ShutdownSignalException; /**
* @author 作者 ucs_fuqing
* @date 创建时间:2017年8月11日 下午2:39:51
* @version 1.0
* @parameter
* @since
* @return
*/
public class QueueConsumer extends PointToPoint implements Runnable,Consumer{ public QueueConsumer(String pointName) throws IOException {
super(pointName);
// TODO Auto-generated constructor stub
} public void run(){
try {
channel.basicConsume(pointName,true,this);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} @Override
public void handleConsumeOk(String consumerTag) {
// TODO Auto-generated method stub
System.out.println("Consumer "+consumerTag +" registered"); } @Override
public void handleCancelOk(String consumerTag) {
// TODO Auto-generated method stub } @Override
public void handleCancel(String consumerTag) throws IOException {
// TODO Auto-generated method stub } @Override
public void handleDelivery(String consumerTag, Envelope env, BasicProperties props, byte[] body) throws IOException {
// TODO Auto-generated method stub
Map map = (HashMap)SerializationUtils.deserialize(body);
System.out.println("Message Number "+ map.get("tagId") + " received.");
//channel.basicAck(env.getDeliveryTag(), false);
} @Override
public void handleShutdownSignal(String consumerTag, ShutdownSignalException sig) {
// TODO Auto-generated method stub } @Override
public void handleRecoverOk(String consumerTag) {
// TODO Auto-generated method stub } }

以上代码中,我们指定了消费的队列,并从中拿到消息,打印出来。


5、测试类

至此我们的生产者与消费者都写完了,接着写个测试类来验证一下

package ucs_test.rabbitmq;

import java.io.IOException;
import java.util.HashMap; /**
* @author 作者 ucs_fuqing
* @date 创建时间:2017年8月11日 下午2:44:59
* @version 1.0
* @parameter
* @since
* @return
*/
public class MainTest {
public MainTest() throws IOException{
QueueConsumer consumer = new QueueConsumer("testqueue");
Thread cuThread = new Thread(consumer); cuThread.start(); Producer producer = new Producer("testqueue");
int i = 0;
while (i<10000) {
HashMap<String, Object> hm = new HashMap<>();
hm.put("tagId", i);
producer.sendMessage(hm);
System.out.println("发送第"+i+"消息");
i++;
}
} public static void main(String[] args) throws IOException {
new MainTest();
}
}

在这里我们的生产者生产10000条消息,消费者拿到并打印出来。看看运行结果:

可以看到虽然有点乱序,但是10000条消息全部被消费完毕。


6、消息应答

在上面的例子中,我们的生产者只管发送消息,消费者只管消费消息,而RabbitMQ在上面的例子中,将消息交付给消费者之后,会从内存中移除掉这个消息。在正式的项目中,消费消息可能需要那么几秒钟,那么问题来了:如果我们拿到消息后需要进行更为复杂的业务处理,而这个业务处理失败或者中断了,那么意味着这条消息代表的工作并未完成,但是消息已经不存在了,我们会丢失掉正在处理的信息,也会丢失掉发给消费者但是并未被消费的消息。
现在我们使用两个消费者来接受同一个队列的消息,测试类如下:

package ucs_test.rabbitmq;

import java.io.IOException;
import java.util.HashMap; /**
* @author 作者 ucs_fuqing
* @date 创建时间:2017年8月11日 下午2:44:59
* @version 1.0
* @parameter
* @since
* @return
*/
public class MainTest {
public MainTest() throws IOException{
QueueConsumer consumer = new QueueConsumer("testqueue");
Thread cuThread = new Thread(consumer);
QueueConsumer consumer2 = new QueueConsumer("testqueue");
Thread cuThread2 = new Thread(consumer2);
cuThread.start();
cuThread2.start();
Producer producer = new Producer("testqueue");
int i = 0;
while (i<10000) {
HashMap<String, Object> hm = new HashMap<>();
hm.put("tagId", i);
producer.sendMessage(hm);
//System.out.println("发送第"+i+"消息");
i++;
}
} public static void main(String[] args) throws IOException {
new MainTest();
}
}

在这种情况下,MQ将会均匀的将消息发送给两个消费者消费,但是如果consumer2半路终止或者异常,那么将会导致我们的测试结果显示接受到的消息少于10000条,消失的消息被异常的消费者吃掉了,而我们没有任何办法。。。

为了保证消息不会丢失,或者说肯定被消费,RabbitMQ支持消息应答模式,简单的只需要修改两个位置:

消费者类QueueConsumer中

设置basicConsume方法参数为false,打开消息应答

消费完成之后,向mq返回应答消息。

这样,当消费者异常时,MQ没有收到消费者消息应答,将会把消息发送给其他消费者,保证这条消息被消费掉。

OK,简单的RabbitMQ服务器Java端例子就这样了。下一篇会在此基础上增加一些高级的应用。

RabbitMQ安装以及java使用(二)的更多相关文章

  1. RabbitMQ安装以及java使用(一)

    最近闲来无事,整理下基础知识,本次安装 1.RabbitMQ版本是3.6.10 2.操作系统是centOS 7 64位  虚拟机IP:192.168.149.133 1.安装更新系统环境依赖 yum ...

  2. 4.0.3的mongodb 安装和java使用

    一 整合 由于本人的码云太多太乱了,于是决定一个一个的整合到一个springboot项目里面. 附上自己的github项目地址 https://github.com/247292980/spring- ...

  3. java架构之路-(MQ专题)RabbitMQ安装和基本使用

    RabbitMQ安装 我这里安装是使用阿里云的CentOS7.5来安装的,使用CentOS版本低于7的可能会报错. 1.安装rabbitmq所需要的依赖包 输入$ yum install build- ...

  4. RabbitMQ学习系列(二): RabbitMQ安装与配置

    上一篇,简单介绍了RabbitMQ的情况还有一些相关的概念,这一篇,会讲讲 RabbitMQ安装与配置. 1.安装 Rabbit MQ 是建立在强大的Erlang OTP平台上,因此安装RabbitM ...

  5. RabbitMQ安装使用详解

    1.下载相应的版本安装:http://www.rabbitmq.com/download.htmleg:http://www.rabbitmq.com/releases/rabbitmq-server ...

  6. [Java面试二]Java基础知识精华部分.

    一:java概述(快速浏览): 1991 年Sun公司的James Gosling等人开始开发名称为 Oak 的语言,希望用于控制嵌入在有线电视交换盒.PDA等的微处理器: 1994年将Oak语言更名 ...

  7. Ubuntu下安装了java但启动eclipse报错说没装java

    参考资料:http://blog.csdn.net/happyteafriends/article/details/8290950 一.问题 在Ubuntu下安装了java并在~/.bashrc配置了 ...

  8. Java 学习第一步-JDK安装和Java环境变量配置

    Java学习第一步——JDK安装及Java环境变量配置 [原文]  2014-05-30 9:09  Java SE  阿超  9046 views Java作为当下很主流的编程语言,学习Java的朋 ...

  9. P6 EPPM R16.1安装与配置指南(二)

    P6 EPPM R16.1安装与配置指南(一) http://www.cnblogs.com/endv/p/5634620.html P6 EPPM R16.1安装与配置指南(二) 环境变量配置 新建 ...

随机推荐

  1. JAVA程序员成长历程(一)

    程序员的20个常见瓶颈 在扩展性的艺术一书中,Russell给出了20个有意思的估计:大约有20个经典瓶颈. Russell说,如果在他年轻时他就知道这些瓶颈该有多好!这些论断包括: * Databa ...

  2. 第14章 Linux开机详细流程

    本文目录: 14.1 按下电源和bios阶段 14.2 MBR和各种bootloader阶段 14.2.1 boot loader 14.2.2 分区表 14.2.3 采用VBR/EBR方式引导操作系 ...

  3. 2.vue 安装教程

    安装node.js 从node.js官网下载并安装node,安装过程很简单,一路"下一步"就可以了(傻瓜式安装). 安装完成之后,打开命令行工具,输入 node -v,如下图,如果 ...

  4. HTML5 进阶系列:文件上传下载

    前言 HTML5 中提供的文件API在前端中有着丰富的应用,上传.下载.读取内容等在日常的交互中很常见.而且在各个浏览器的兼容也比较好,包括移动端,除了 IE 只支持 IE10 以上的版本.想要更好地 ...

  5. 通用JSONHelp 的通用的封装

    1. 最近项目已经上线了 ,闲暇了几天 想将JSON  的序列化 以及反序列化进行重新的封装一下本人定义为JSONHelp,虽然Microsoft 已经做的很好了.但是我想封装一套为自己开发的项目使用 ...

  6. python编程快速上手之第4章实践项目参考答案

    #!/usr/bin/env python3.5 # coding:utf-8 # 假定有一个列表,编写函数以一个列表值作为参数,返回一个字条串 # 该字符串包含所有表项,之间以逗号和空格分隔,并在最 ...

  7. 初步研究一下sourceTree

    今天研究sourceTree,在此小结一下 1.下载链接:https://www.atlassian.com/software/sourcetree 2.安装,注册账户登录,连接到GitHub账号上, ...

  8. JavaSE中Collection集合框架学习笔记(3)——遍历对象的Iterator和收集对象后的排序

    前言:暑期应该开始了,因为小区对面的小学这两天早上都没有像以往那样一到七八点钟就人声喧闹.车水马龙. 前两篇文章介绍了Collection框架的主要接口和常用类,例如List.Set.Queue,和A ...

  9. CentOS7.2设置静态IP

    首先将源文件进行备份: [zgw@localhost 桌面]$ sudo cp /etc/sysconfig/network-scripts/ifcfg-eno16777736 /etc/syscon ...

  10. Linux系统——运行级别

    学习之前先了解下Linux系统的运行级别和其原理,博主使用的是Linux系统中的Redhat9.0版本,之后的学习也是基于这个系统版本. Linux系统的7个运行级别(runlevel) 运行级别0: ...