RabbitMQ学习笔记

来源:互联网 发布:mac重装系统磁盘成灰色 编辑:程序博客网 时间:2024/06/06 00:49

RabbitMQ学习笔记

闲来无事,最近学习了RabbitMQ,写了简单的工程。

下面关于MQ的信息是来自于百度百科:

MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过 队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。其中较为成熟的MQ产品有IBM WEBSPHERE MQ等等。

MQ是消费-生产者模型的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取或者订阅队列中的消息。MQ和JMS类似,但不同的是JMS是SUN JAVA消息中间件服务的一个标准和API定义,而MQ则是遵循了AMQP协议的具体实现和产品。

RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统。他遵循Mozilla Public License开源协议。

关于RabbitMQ服务器的安装这里不再阐述,有情趣者可以自行尝试或百度搜索.
RabbitMQ 官方下载地址: http://www.rabbitmq.com/download.html

下面开始阐述:

1.工程结构:
这里写图片描述

2.添加jar包

spring
rabbit-client

spring jar包的添加不再列出,这里仅列出rabbit-client的依赖:

<dependency>            <groupId>rabbitmq-client</groupId>            <artifactId>rabbitmq-client</artifactId>            <version>3.5.6</version></dependency>

首先是ConnectionFactoryUtil.java类:

public class ConnectionFactoryUtil {    /**     *     *返回ConnectionFactory 连接工厂     */    public static ConnectionFactory get() {        ConnectionFactory cfconn = new ConnectionFactory();        cfconn.setHost("127.0.0.1");        cfconn.setPort(5672);        cfconn.setUsername("admin");        cfconn.setPassword("admin");        //设置虚拟主机        cfconn.setVirtualHost("vhost1");        //设置线程池,为了避免产生意想不到的异常,这里将线程池大小设为1.        //因为多个线程之间共享Channel时,有些操作会出现问题。如:可能会影响发布者对确认消息的接收        //这样设置也是和MqFactory.java类相关的,因为Channel在运行时是单例的.        cfconn.setSharedExecutor(Executors.newFixedThreadPool(1));        return cfconn;    }}

MqFactory.java 类:

@Component@Scope("singleton")public class MqFactory implements BeanDefinitionRegistryPostProcessor {    private static Logger log = LoggerFactory.getLogger(MqFactory.class);    private static Channel channel = null;    private static Map<String, Boolean> hasBind = new HashMap<String, Boolean>();    /**     * 获取通道     * @return     * @throws IOException     * @throws TimeoutException     */    private Channel getConnection() throws IOException, TimeoutException {        if (channel == null) {            ConnectionFactory factory = ConnectionFactoryUtil.get();            channel = factory.newConnection().createChannel();        }        return channel;    }    /**     * 发布消息     * @param msg  发布的对象(消息)     */    public void publishMessage(Object msg) {        try {            //队列名(自定义)            String queueName = "admin";            //交换机名(自定义)            String exchange = "optimus_exchange";            //路由key            String routingKey = queueName + "_routingKey";            Channel channel = getConnection();            //声明交换机            channel.exchangeDeclare(exchange, "direct", true);            //声明队列            channel.queueDeclare(queueName, true, false, false, null);            //将队列和交换机绑定            channel.queueBind(queueName, exchange, routingKey);            //序列化            byte[] buf = ObjectUtil.serializeJdk(msg);            //发布消息            //MessageProperties.PERSISTENT_TEXT_PLAIN 消息属性            channel.basicPublish(exchange, routingKey, MessageProperties.PERSISTENT_TEXT_PLAIN, buf);        } catch (Exception e) {            log.error(e.getMessage(), e);            try {                channel = null;                hasBind.clear();                Thread.sleep(2000);            } catch (Exception e2) {            }        }    }    //ioc 容器    private ConfigurableListableBeanFactory factory;    // MqConsumer 所有的实现类    private static List<MqConsumer> queueList = new ArrayList<>();    //线程池    private static ExecutorService workers = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());    //MqFactory初始化方法    //获取MqConsumer实现类,调用消费消息的方法.    public void init() {        queueList.clear();        Map<String, MqConsumer> map = factory.getBeansOfType(MqConsumer.class);        for (Map.Entry<String, MqConsumer> entry : map.entrySet()) {            MqConsumer consume = entry.getValue();            queueList.add(consume);        }        /**         * MqFactory仅是一个简单的实现,交换机和队列都只有一个,且是一对一绑定的         *          * 此方法是发布着发布消息成功后,消费者进行消费消息         */        doConsumerTask(queueList);    }    private void doConsumerTask(final List<MqConsumer> consumerList) {        //用了两个死循环        //第一个是为了做到获取连接失败或成功消费消息后,仍继续连接队列消费消息.        //第二个是为了将发布者发布的消息,发送给所有关注了对应队列的消费者(实现了MqConsumer实现类)        //这里只有一个队列,没有复杂的模块划分,所有这里的实现简单一些        workers.submit(new Runnable() {            @Override            public void run() {                while (true) {                    try {                        ConnectionFactory connFactory = ConnectionFactoryUtil.get();                        Connection conn = connFactory.newConnection();                        Channel channel = null;                        QueueingConsumer consumer = null;                        try {                            channel = conn.createChannel();                            channel.basicQos(1);                            consumer = new QueueingConsumer(channel);                            channel.basicConsume("admin", false, "", false, false, null, consumer);                        } catch (Exception e) {                            System.out.println("无法连接");                            try {                                // 暂停2秒后重连                                Thread.sleep(2000);                            } catch (Exception e2) {                            }                            continue;                        }                        // 连接成功后,开始消费消息                        while (true) {                            try {                                QueueingConsumer.Delivery delivery = consumer.nextDelivery();                                Object message = ObjectUtil.deserializeJdk(delivery.getBody());                                for (MqConsumer mc : consumerList) {                                    try {                                        mc.comsum(message);                                    } catch (Exception e) {                                        log.error(e.getMessage(), e);                                    }                                }                                // ack                                channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);                            } catch (Exception e) {                                log.error("rabbitmq error", e);                                try {                                    channel.abort();                                    conn.abort();                                } catch (Exception e2) {                                }                                break;                            }                        }                    } catch (Exception e) {                        continue;                    }                }            }        });    }    public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException {        this.factory = arg0;    }    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry arg0) throws BeansException  {    }}

MqFactory 实现了 BeanDefinitionRegistryPostProcessor这个接口,意在将ConfigurableListableBeanFactory 它的实例注入进来. 以此来获取所有实现MqConsumer接口的类.

MqInit.java

@Servicepublic class MqInit implements InitializingBean{    @Autowired    private MqFactory factory;    public MqInit(){    }    @Override    public void afterPropertiesSet() throws Exception {        this.factory.init();    }}

MqInit.java 主要是执行MqFactory实例的初始化方法的.

总结:

对于上面的代码而言:

针对特定的业务逻辑处理类,只需要将MqFactory注入进去,调用发布消息的方法,然后再实现MqConsumer接口(要交由spring来管理)。

这样在程序运行时,只要有发布者发布了消息,MqConsumer.java的实现类都能够收到消息并处理

MqConsumer.java接口是用来处理消息的.

下面是一个简单的处理,将消息直接打印出来:

@Servicepublic class ConsumerOne implements MqConsumer {    @Override    public void comsum(Object msg) {        System.out.println(msg);    }}

运行:

以Hello World为例:

public class App {    public static void main( String[] args ) throws IOException, TimeoutException {        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:config/spring-view.xml");        MqFactory factory = context.getBean(MqFactory.class);        factory.publishMessage("Hello World!!!!!!!!");    }}

输出:
这里写图片描述

大功告成!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

0 0