中间件技术 实验三 消息中间件应用开发:ActiveMQ实现单线程多队列

中间件技术 实验三 消息中间件应用开发:ActiveMQ实现单线程多队列

工程及实验报告书下载

页面底部为实习指导书

1.环境配置

①运行消息服务器ActiveMQ:下载apache-activemq-5.4.3后随意解压并运行bin目录下的activemq.bat(若闪退则运行\bin\win32下的activemq.bat)
这里写图片描述
②cmd输入netstat -an|find “61616”,显示如图则成功

netstat -an|find “61616”

这里写图片描述

③登录管理界面
浏览器中输入:http://localhost:8161/admin/
这里写图片描述
在此处点击Queues可以查看消息队列的情况

2.测试Demo

以下为源码或直接工程下载

首先往Java工程内导入Jar包:activemq-all-5.4.3.jar,该文件在apache-activemq-5.4.3\lib中可找到

接收端

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

public class Receiver {
    public static void main(String[] args) {
        // ConnectionFactory :连接工厂,JMS 用它创建连接
        ConnectionFactory connectionFactory;
        // Connection :JMS 客户端到JMS Provider 的连接
        Connection connection = null;
        // Session: 一个发送或接收消息的线程
        Session session;
        // Destination :消息的目的地;消息发送给谁.
        Destination destination;
        // 消费者,消息接收者
        MessageConsumer consumer;
        connectionFactory = new ActiveMQConnectionFactory(
                ActiveMQConnection.DEFAULT_USER,
                ActiveMQConnection.DEFAULT_PASSWORD,
                "tcp://localhost:61616");
        try {
            // 构造从工厂得到连接对象
            connection = connectionFactory.createConnection();
            // 启动
            connection.start();
            // 获取操作连接
            session = connection.createSession(Boolean.FALSE,
                    Session.AUTO_ACKNOWLEDGE);
            // 获取session注意参数值xingbo.xu-queue是一个服务器的queue,须在在ActiveMq的console配置
            destination = session.createQueue("FirstQueue");
            consumer = session.createConsumer(destination);
            while (true) {
                //设置接收者接收消息的时间,为了便于测试,这里设定为100s
                TextMessage message = (TextMessage) consumer.receive(100000);
                if (null != message) {
                    System.out.println("收到消息" + message.getText());
                } else {
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != connection)
                    connection.close();
            } catch (Throwable ignore) {
            }
        }
    }
}

发送端

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

public class Sender {
    private static final int SEND_NUMBER = 5;

    public static void main(String[] args) {
        // ConnectionFactory :连接工厂,JMS 用它创建连接
        ConnectionFactory connectionFactory;
        // Connection :JMS 客户端到JMS Provider 的连接
        Connection connection = null;
        // Session: 一个发送或接收消息的线程
        Session session;
        // Destination :消息的目的地;消息发送给谁.
        Destination destination;
        // MessageProducer:消息发送者
        MessageProducer producer;
        // TextMessage message;
        // 构造ConnectionFactory实例对象,此处采用ActiveMq的实现jar
        connectionFactory = new ActiveMQConnectionFactory(
                ActiveMQConnection.DEFAULT_USER,
                ActiveMQConnection.DEFAULT_PASSWORD,
                "tcp://localhost:61616");
        try {
            // 构造从工厂得到连接对象
            connection = connectionFactory.createConnection();
            // 启动
            connection.start();
            // 获取操作连接
            session = connection.createSession(Boolean.TRUE,
                    Session.AUTO_ACKNOWLEDGE);
            // 获取session注意参数值xingbo.xu-queue是一个服务器的queue,须在在ActiveMq的console配置
            destination = session.createQueue("FirstQueue");
            // 得到消息生成者【发送者】
            producer = session.createProducer(destination);
            // 设置不持久化,此处学习,实际根据项目决定
            producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
            // 构造消息,此处写死,项目就是参数,或者方法获取
            sendMessage(session, producer);
            session.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != connection)
                    connection.close();
            } catch (Throwable ignore) {
            }
        }
    }

    public static void sendMessage(Session session, MessageProducer producer)
            throws Exception {
        for (int i = 1; i <= SEND_NUMBER; i++) {
            TextMessage message = session
                    .createTextMessage("ActiveMq 发送的消息" + i);
            // 发送消息到目的地方
            System.out.println("发送消息:" + "ActiveMq 发送的消息" + i);
            producer.send(message);
        }
    }
}

启动ActiveMQ服务器后,运行Receiver后登录http://localhost:8161/admin/可以发现Queue已经新建了一个队列
这里写图片描述

接下来运行Sender类会呈现5条输出,实际上是向ActiveMQ服务器中的“FirstQueue”队列中发送了5条消息,并消费了。
这里写图片描述

3.问题需求(错误示范)

本实验要求使用:
①接收类必须实现为一个单独的线程
②至少使用2个以上的队列
即:
ActiveMQ实现单线程多队列

发送端1

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

public class Sender {
    private static final int SEND_NUMBER = 5;

    ConnectionFactory connectionFactory;//连接工厂,JMS 用它创建连接
    Connection connection = null;//JMS 客户端到JMS Provider 的连接
    Session session;//一个发送或接收消息的线程
    Destination destination;//消息的目的地
    MessageProducer producer;//生产者,消息发送者

    Sender() {
        // TextMessage message;
        // 构造ConnectionFactory实例对象,此处采用ActiveMq的实现jar
        connectionFactory = new ActiveMQConnectionFactory(
                ActiveMQConnection.DEFAULT_USER,
                ActiveMQConnection.DEFAULT_PASSWORD,
                "tcp://localhost:61616");
        try {
            connection = connectionFactory.createConnection();//构造从工厂得到连接对象
            connection.start();//启动
            session = connection.createSession(Boolean.TRUE,Session.AUTO_ACKNOWLEDGE);//获取操作连接,此处是Boolean.TRUE,是否支持事务,如果为true,则会忽略第二个参数
            destination = session.createQueue("AQueue");//获取session注意参数值xingbo.xu-queue是一个服务器的queue,须在在ActiveMq的console配置
            producer = session.createProducer(destination);// 得到消息生成者【发送者】
            producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);// 设置不持久化,此处学习,实际根据项目决定
            sendMessage(session, producer);// 构造消息,此处写死,项目就是参数,或者方法获取
            session.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    public void sendMessage(Session session, MessageProducer producer) throws Exception {
        for (int i = 1; i <= SEND_NUMBER; i++) {
            TextMessage message = session.createTextMessage(this.getClass().getName()+" 发送的消息" + i);
            System.out.println(this.getClass().getName()+" 发送的消息" + i);// 发送消息到目的地方
            producer.send(message);
        }
    }

    public static void main(String[] args) {
        new Sender();
    }
}

发送端2,直接将以上发送端Copy一份,并把队列改成“BQueue”即可

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

public class Sender2 {
    private static final int SEND_NUMBER = 5;

    ConnectionFactory connectionFactory;//连接工厂,JMS 用它创建连接
    Connection connection = null;//JMS 客户端到JMS Provider 的连接
    Session session;//一个发送或接收消息的线程
    Destination destination;//消息的目的地
    MessageProducer producer;//生产者,消息发送者

    Sender2() {
        // TextMessage message;
        // 构造ConnectionFactory实例对象,此处采用ActiveMq的实现jar
        connectionFactory = new ActiveMQConnectionFactory(
                ActiveMQConnection.DEFAULT_USER,
                ActiveMQConnection.DEFAULT_PASSWORD,
                "tcp://localhost:61616");
        try {
            connection = connectionFactory.createConnection();//构造从工厂得到连接对象
            connection.start();//启动
            session = connection.createSession(Boolean.TRUE,Session.AUTO_ACKNOWLEDGE);//获取操作连接,此处是Boolean.TRUE,是否支持事务,如果为true,则会忽略第二个参数
            destination = session.createQueue("BQueue");//获取session注意参数值xingbo.xu-queue是一个服务器的queue,须在在ActiveMq的console配置
            producer = session.createProducer(destination);// 得到消息生成者【发送者】
            producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);// 设置不持久化,此处学习,实际根据项目决定
            sendMessage(session, producer);// 构造消息,此处写死,项目就是参数,或者方法获取
            session.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != connection)
                    connection.close();
            } catch (Throwable ignore) {
            }
        }
    }



    public void sendMessage(Session session, MessageProducer producer) throws Exception {
        for (int i = 1; i <= SEND_NUMBER; i++) {
            TextMessage message = session.createTextMessage(this.getClass().getName()+" 发送的消息" + i);
            System.out.println(this.getClass().getName()+" 发送的消息" + i);// 发送消息到目的地方
            producer.send(message);
        }
    }

    public static void main(String[] args) {
        new Sender2();
    }
}

错误的接收端

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

public class WrongReceiver{
    ConnectionFactory connectionFactory;//连接工厂,JMS 用它创建连接
    Connection connection = null;//JMS 客户端到JMS Provider 的连接
    Session session;//一个发送或接收消息的线程
    Destination destination;//A消息的目的地
    MessageConsumer consumer;//A消费者,消息接收者
    Destination destination2;//B消息的目的地
    MessageConsumer consumer2;//B消费者,消息接收者

    WrongReceiver() {
        try {
            System.out.println("ActiveMQ 服务器正在接收信息");
            connectionFactory = new ActiveMQConnectionFactory(
                    ActiveMQConnection.DEFAULT_USER,
                    ActiveMQConnection.DEFAULT_PASSWORD,
                    "tcp://localhost:61616");
            connection = connectionFactory.createConnection();//构造从工厂得到连接对象
            connection.start();//启动
            session = connection.createSession(Boolean.FALSE,Session.AUTO_ACKNOWLEDGE);//获取操作连接,此处是Boolean.FALSE,第二个参数为false时,paramB的值可为Session.AUTO_ACKNOWLEDGE,Session.CLIENT_ACKNOWLEDGE,DUPS_OK_ACKNOWLEDGE其中一个,此处为自动确认,客户端发送和接收消息不需要做额外的工作。哪怕是接收端发生异常,也会被当作正常发送成功。
            destination = session.createQueue("AQueue");
            consumer = session.createConsumer(destination);
            destination2 = session.createQueue("BQueue");
            consumer2 = session.createConsumer(destination2);
            while (true) {
                TextMessage message = (TextMessage) consumer.receive(100000);//设置接收者接收消息的时间,为了便于测试,这里设定为100s
                TextMessage message2 = (TextMessage) consumer2.receive(100000);
                if (null != message) {
                    System.out.println("收到A消息" + message.getText());
                }
                else if (null != message2) {
                    System.out.println("收到B消息" + message2.getText());
                } else {
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != connection)
                    connection.close();
            } catch (Throwable ignore) {
            }
        }
    }


    public static void main(String[] args) {
        new WrongReceiver();
    }
}

为了实现两个队列,这里直接粗暴地将上面的Demo里的Queue改成了两个,并且直接接收信息。
①运行后WrongReceiver类
②运行Sender类
③运行Sender2类
此时会发现ActiveMQ服务器只会接收到Sender发出的A队列内的信息
这里写图片描述

4.问题解决(分析API)

直接观察,运行接收端后再运行Sender类发现此时Sender类已阻塞了
经过分析,接收端的while循环接收两队列信息阻塞了
解决方案
第一个是直接new run一个Thread后接收不同队列即可
第二个是让接收不阻塞

查看ActiveMQ API后发现:
这里写图片描述

发现原来是接收端的receive(100000)这个方法互相阻碍了,于是将receive(100000)改成receiveNoWait()
直接运行发现只能接收一次信息接收端就关闭了,原来是else { break; }直接跳出循环了,将这句删掉即可。

以下给出正确的接收端

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

public class Receiver{
    ConnectionFactory connectionFactory;//连接工厂,JMS 用它创建连接
    Connection connection = null;//JMS 客户端到JMS Provider 的连接
    Session session;//一个发送或接收消息的线程
    Destination destination;//A消息的目的地
    MessageConsumer consumer;//A消费者,消息接收者
    Destination destination2;//B消息的目的地
    MessageConsumer consumer2;//B消费者,消息接收者

    Receiver() {
        try {
            System.out.println("ActiveMQ 服务器正在接收信息");
            connectionFactory = new ActiveMQConnectionFactory(
                    ActiveMQConnection.DEFAULT_USER,
                    ActiveMQConnection.DEFAULT_PASSWORD,
                    "tcp://localhost:61616");
            connection = connectionFactory.createConnection();//构造从工厂得到连接对象
            connection.start();//启动
            session = connection.createSession(Boolean.FALSE,Session.AUTO_ACKNOWLEDGE);//获取操作连接,此处是Boolean.FALSE,第二个参数为false时,paramB的值可为Session.AUTO_ACKNOWLEDGE,Session.CLIENT_ACKNOWLEDGE,DUPS_OK_ACKNOWLEDGE其中一个,此处为自动确认,客户端发送和接收消息不需要做额外的工作。哪怕是接收端发生异常,也会被当作正常发送成功。
            destination = session.createQueue("AQueue");
            consumer = session.createConsumer(destination);
            destination2 = session.createQueue("BQueue");
            consumer2 = session.createConsumer(destination2);
            while (true) {
                TextMessage message = (TextMessage) consumer.receiveNoWait();//设置接收者接收消息的时间,为了便于测试,这里设定为100s
                TextMessage message2 = (TextMessage) consumer2.receiveNoWait();
                if (null != message) {
                    System.out.println("收到A消息" + message.getText());
                }
                else if (null != message2) {
                    System.out.println("收到B消息" + message2.getText());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != connection)
                    connection.close();
            } catch (Throwable ignore) {
            }
        }
    }


    public static void main(String[] args) {
        new Receiver();
    }
}

5.实现效果

①运行Receiver类
②运行Sender类
③运行Sender2类
Console中观察到:
这里写图片描述

若加上图形界面:
这里写图片描述





























































































实习指导书:
实习三 消息中间件应用开发
一 实习目的
利用消息中间件接口JMS、消息服务器ActiveMQ 和Java Swing实现一个简单的聊天工具,掌握消息中间件的应用。
二 实习要求
1、开发环境
Windows 7/XP,Eclipse, JDK1.6, apache-activemq-5.4.3, Java Swing
2、功能要求
(1)实现界面化的聊天工具,支持二人之间互发消息。
(2)界面上必须标注软件版权信息,包括姓名、学号等个性化信息。
(3)发送消息过程中,发送这首先把消息发送到消息服务器ActiveMQ,接收者从消息服务器上取消息;消息发送者和接收者不需要同时运行(在线),实现即使对方处在离线状态下也可以进行消息的发送和接收。
三 实习过程
1、ActiveMQ 安装和配置
(1)解压缩到apache-activemq-5.4.3-bin.zip到一个目录,例如C:\ apache-activemq-5.4.3
(2)配置文件在C:\apache-activemq-5.4.3\conf目录中:activemq.xml、credentials.properties、log4j.properties(本次实习不需要配置)。
(3)启动ActiveMQ:运行C:\ apache-activemq-5.4.3\bin\activemq.bat
(4)测试
ActiveMQ默认使用的TCP连接端口是61616, 通过查看该端口的信息可以测试ActiveMQ是否成功启动 netstat -an|find “61616”
在CMD下输入:netstat -an|find “61616” 显示下列信息表示成功。
TCP 0.0.0.0:61616 0.0.0.0:0 LISTENING
(5)登陆管理界面(本次实习不需要进入管理界面)
浏览器中输入:http://localhost:8161/admin/,用户名密码:admin,admin

2、开发
(1)在eclipse中导入并运行MessageMiddleWare中的三个例子,了解消息发送者、接收者编程。
(2)在建立一个工程,导入库文件activemq-all-5.4.3.jar(在apache-activemq-5.4.3\lib可找到该文件);也可直接在MessageMiddleWare工程中开发。
(3)使用Java Swing开发界面,界面上包括消息发送窗口、接收窗口(或二者在同一个窗口)、发送按钮等,界面下方需显示个性化的版权信息。
(4)参考MessageMiddleWare中的例子,编写消息发送类和接收类。发送类中需要连接ActiveMQ 服务器,创建队列,发送消息;接收类中需要ActiveMQ 服务器,读取发送者发送消息所用的队列。接收类必须实现为一个单独的线程,时刻侦听是否有消息到来。
(5)至少使用2个以上的队列,A给B发送消息时使用一个队列,B给A发送时使用另一个队列(若使用同一个队列,可能导致消息混乱)。
(6)开发完毕,打包为可运行包(不要把MessageMiddleWare放进去),双击可以直接运行。

3、相关软件见附件

猜你喜欢

转载自blog.csdn.net/lly1122334/article/details/80139790