RabbitMQ学习笔记

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

@Service
public 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接口是用来处理消息的.

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

@Service
public 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值