RabbitMQ的工作模式介绍

MQ 基本概念

MQ 概述

MQ全称 Message Queue(消息队列),是在消息的传输过程中保存消息的容器,多用于分布式系统之间进行通信

MQ 的优势

  • 应用解耦:提高系统容错性和可维护性
  • 异步提速:提升用户体验和系统吞吐量
  • 削峰填谷:提高系统稳定性

MQ 的劣势

  • 系统可用性降低:系统引入的外部依赖越多,系统稳定性越差。一旦MQ宕机,就会对业务造成影响。
  • 系统复杂度提高:MQ的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过MQ进行异步调用。

AMQP

AMQP,即Advanced Message Queuing Protocol(高级消息队列协议),是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。2006年,AMQP规范发布

RabbitMQ

RabbitMQ 简介

2007年,Rabbit 技术公司基于 AMQP 标准开发的 RabbitMQ 1.0 发布。RabbitMQ 采用 Erlang 语言开发。Erlang 语言由 Ericson 设计,专门为开发高并发和分布式系统的一种语言,在电信领域使用广泛。

RabbitMQ 中的相关概念

  • Broker :接受和分发消息的应用,RabbitMQ Server就是Message Broker
  • Virtual host :处于多租户和安全因素设计的,把AMQP的基本组件划分到一个虚拟的分组中,类似于网络中的 namespace 概念。当多个不同的用户使用同一个RabbitMQ Server提供的服务时,可以划分出多个vhost,每个用户在自己的vhost创建 exchagne/queue等。
  • Connectionpublisher / consumerbroker 之间的TCP连接
  • Channel :如果每一次访问RabbitMQ都建立一个Connection,在消息量大的时候建立TCP Connection的开销将是巨大的,效率也较低。Channel是在connection内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的channel进行通讯,AMQP method包含了 channel id 帮助 客户端message broker 识别channel,所以channel之间是完全隔离的。Channel作为轻量级的Connection极大减少了操作系统建立TCP connection的开销。
  • Exchange :message到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发消息到queue中去。常用的类型有:direct(point-to-point),topic(publish-subscribe)和fanout(multicast)。
  • Queue :消息最终被送到这里等待 consumer 取走。
  • Bindingexchangequeue 之间的虚拟连接,binding中可以包含routing key。Binding信息被保存到exchange中的查询表中,用于message的分发依据。

RabbitMQ 的工作模式

RabbitMQ提供了6种工作模式,这里介绍其中的5种(代码通过Java展示)

1. 简单模式

简单模式
Producer:

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.study.rabbitmq.utils.RabbitConstant;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Producer {

    public static void main(String[] args) throws IOException, TimeoutException {

        ConnectionFactory connectionFactory = new ConnectionFactory();

        // 设置主机信息
        connectionFactory.setHost("192.168.3.14");
        // 端口号
        connectionFactory.setPort(5672);
        // 用户名
        connectionFactory.setUsername("admin");
        // 密码
        connectionFactory.setPassword("admin");
        // 虚拟机位置
        connectionFactory.setVirtualHost("/leng");
        // 获取TCP长连接
        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();
        // 队列名称ID
        // 是否持久化 false不持久化 MQ停掉之后数据丢失
        // 是否队列私有化 false 代表所有消费者都可以访问 true代表只有第一次拥有它的人才可以使用它
        // 是否自动删除 false 代表停掉后不自动删除这个队列
        // 其他额外参数 null
        channel.queueDeclare(RabbitConstant.QUEUE_HELLOWORLD, false, false, false, null);
        String message = "晶晶小宝贝流批";
        // exchange 交换机
        // 队列名称ID
        // 额外属性
        // 消息字节数组
        channel.basicPublish("", RabbitConstant.QUEUE_HELLOWORLD, null, message.getBytes());
        channel.close();
        connection.close();
        System.out.println("===数据发送成功===");
    }
}

Consumer:

public class Consumer {

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory connectionFactory = new ConnectionFactory();

        // 设置主机信息
        connectionFactory.setHost("192.168.3.14");
        // 端口号
        connectionFactory.setPort(5672);
        // 用户名
        connectionFactory.setUsername("admin");
        // 密码
        connectionFactory.setPassword("admin");
        // 虚拟机位置
        connectionFactory.setVirtualHost("/leng");
        // 获取TCP长连接
        Connection connection = RabbitUtils.getConnection();
        Channel channel = connection.createChannel();
        // 队列名称ID
        // 是否持久化 false不持久化 MQ停掉之后数据丢失
        // 是否队列私有化 false 代表所有消费者都可以访问 true代表只有第一次拥有它的人才可以使用它
        // 是否自动删除 false 代表停掉后不自动删除这个队列
        // 其他额外参数 null
        channel.queueDeclare(RabbitConstant.QUEUE_HELLOWORLD, false, false, false, null);

        // 从MQ服务器中拉取数据
        // 队列名称
        // 是否自动确认收到消息, false代表手动变成来确认消息,这是MQ推荐的做法
        // 传入DefaultConsumer的实现类

        channel.basicConsume(RabbitConstant.QUEUE_HELLOWORLD, false, new Reciver(channel));
    }
}

其中 Reciver 需要继承 DefaultConsumer

class Reciver extends DefaultConsumer {

    private Channel channel;

    public Reciver(Channel channel) {
        super(channel);
        this.channel = channel;
    }

    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
        String message = new String(body);
        System.out.println("===接收到的消息:" + message + "===");
        System.out.println("===消息的TagId:" + envelope.getDeliveryTag() + "===");
        // 签收消息
        // false只确认签收当前的消息 设置为true的时候则代表签收所有消息
        channel.basicAck(envelope.getDeliveryTag(), false);
    }
}

2. Work queues模式

Work queues模式
这种模式会将消息轮询发送给相应的消费者
接下来所有的RabbitMQ的连接都将通过以下工具类获取

import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class RabbitUtils {

    public static Connection getConnection() throws IOException, TimeoutException {
        ConnectionFactory connectionFactory = new ConnectionFactory();

        // 设置主机信息
        connectionFactory.setHost(RabbitConstant.HOST);
        // 端口号
        connectionFactory.setPort(RabbitConstant.PORT);
        // 用户名
        connectionFactory.setUsername("admin");
        // 密码
        connectionFactory.setPassword("admin");
        // 虚拟机位置
        connectionFactory.setVirtualHost("/leng");
        // 获取TCP长连接
        Connection connection = connectionFactory.newConnection();
        return connection;
    }
}

OrderSystem

import com.google.gson.Gson;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.study.rabbitmq.utils.RabbitConstant;
import com.study.rabbitmq.utils.RabbitUtils;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 发送者
 */
public class OrderSystem {
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = RabbitUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_SMS, false, false, false, null);
        for (int i = 1; i <= 100; i++) {
            SMS sms = new SMS("乘客" + i, "手机号" + i, "您的订单已出票成功");
            String json = new Gson().toJson(sms);
            channel.basicPublish("", RabbitConstant.QUEUE_SMS, null, json.getBytes());
        }
    }
}

SMSSender

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.study.rabbitmq.utils.RabbitConstant;
import com.study.rabbitmq.utils.RabbitUtils;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * SmsSender1
 */
public class SMSSender1 {

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = RabbitUtils.getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_SMS, false, false, false, null);
        channel.basicQos(1); // 处理完一个取一个
        channel.basicConsume(RabbitConstant.QUEUE_SMS, false, new SMSSenderConsumer(channel, "SMSSender1"));
    }

}

这里可以建立多个SMSSender,消息分发的时候会轮询SMSSender进行发送

3. Publish/Subscribe发布与订阅模式

Publish/Subscribe模式
WeatherBureau:

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.study.rabbitmq.utils.RabbitConstant;
import com.study.rabbitmq.utils.RabbitUtils;

import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;

public class WeatherBureau {

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = RabbitUtils.getConnection();
        String input = new Scanner(System.in).next();
        Channel channel = connection.createChannel();
        channel.basicPublish(RabbitConstant.EXCHANGE_WEATHER, "", null, input.getBytes());
        channel.close();
        connection.close();
    }
}

Baidu:

import com.rabbitmq.client.*;
import com.study.rabbitmq.utils.RabbitConstant;
import com.study.rabbitmq.utils.RabbitUtils;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Baidu {
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = RabbitUtils.getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_BAIDU, false, false, false, null);
        channel.queueBind(RabbitConstant.QUEUE_BAIDU, RabbitConstant.EXCHANGE_WEATHER, "");
        channel.basicQos(1); // 处理完一个取一个
        channel.basicConsume(RabbitConstant.QUEUE_BAIDU, false, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(new String(body));
                channel.basicAck(envelope.getDeliveryTag(), true);
            }
        });
    }

}

Sina:

import com.rabbitmq.client.*;
import com.study.rabbitmq.utils.RabbitConstant;
import com.study.rabbitmq.utils.RabbitUtils;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Sina {
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = RabbitUtils.getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_SINA, false, false, false, null);
        channel.queueBind(RabbitConstant.QUEUE_SINA, RabbitConstant.EXCHANGE_WEATHER, "");
        channel.basicQos(1); // 处理完一个取一个
        channel.basicConsume(RabbitConstant.QUEUE_SINA, false, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(new String(body));
                channel.basicAck(envelope.getDeliveryTag(), true);
            }
        });
    }

}

在这种模式下, channel.queueBindroutingKey 可以设置为一个空字符串

4.Routing路由模式

WeatherBureau:

import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;

public class WeatherBureau {

    public static void main(String[] args) throws IOException, TimeoutException {
        Map area = new LinkedHashMap<String, String>();
        area.put("china.jiangsu.yancheng.20201127", "中国江苏盐城20201127天气数据");
        area.put("china.shanghai.qingpu.20201127", "中国上海青浦20201127天气数据");
        area.put("china.jiangsu.wuxi.20201127", "中国江苏无锡20201127天气数据");
        area.put("us.cal.lsj.20201127", "美国加州洛杉矶20201127天气数据");

        area.put("china.jiangsu.yancheng.20201128", "中国江苏盐城20201128天气数据");
        area.put("china.shanghai.qingpu.20201128", "中国上海青浦20201128天气数据");
        area.put("china.jiangsu.wuxi.20201128", "中国江苏无锡20201128天气数据");
        area.put("us.cal.lsj.20201128", "美国加州洛杉矶20201128天气数据");
        Connection connection = RabbitUtils.getConnection();
        Channel channel = connection.createChannel();
        Iterator<Map.Entry<String, String>> itr = area.entrySet().iterator();
        while (itr.hasNext()) {
            Map.Entry<String, String> me = itr.next();
            //第一个参数交换机名字   第二个参数作为 消息的routing key
            channel.basicPublish(RabbitConstant.EXCHANGE_WEATHER_TOPIC,me.getKey() , null , me.getValue().getBytes());

        }
        channel.close();
        connection.close();
    }
}

Baidu

import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * 消费者
 */
public class Biadu {

    public static void main(String[] args) throws IOException {
        Connection connection = RabbitUtils.getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_BAIDU, false, false, false, null);
        //queueBind用于将队列与交换机绑定
        //参数1:队列名 参数2:交互机名  参数三:路由key
        channel.queueBind(RabbitConstant.QUEUE_BAIDU, RabbitConstant.EXCHANGE_WEATHER_ROUTING, "china.jiangsu.wuxi.20201128");
        channel.queueBind(RabbitConstant.QUEUE_BAIDU, RabbitConstant.EXCHANGE_WEATHER_ROUTING, "china.shanghai.qingpu.20201127");
        channel.basicQos(1);
        channel.basicConsume(RabbitConstant.QUEUE_BAIDU , false , new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("百度天气收到气象信息:" + new String(body));
                channel.basicAck(envelope.getDeliveryTag() , false);
            }
        });

    }

}

5.Topics主题模式

Topics模式与Routing模式非常相似,它可以通过通配符的形式匹配不同的信息
#:匹配零个或多个单词
*:匹配一个单词

import com.rabbitmq.client.*;
import com.study.rabbitmq.utils.RabbitConstant;
import com.study.rabbitmq.utils.RabbitUtils;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Sina {
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = RabbitUtils.getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_SINA, false, false, false, null);
        channel.queueBind(RabbitConstant.QUEUE_SINA, RabbitConstant.EXCHANGE_WEATHER_TOPIC, "#.20201127");
        channel.basicQos(1);
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [x] Received '" +
                    delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
        };
        channel.basicConsume(RabbitConstant.QUEUE_SINA , true, deliverCallback, consumerTag -> {});
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值