RabbitMq(一) RabbitMq工作模型

Mq基础

message queue 消息队列
特点:
1、独立部署,解耦
2、数据结构是队列,FIFO
3、具有发布订阅模型

为什么使用MQ:
1、异步
2、解耦
3、削峰
4、能广播通信

带来的问题:
1、增加运维成本
2、系统可用性降低
3、系统复杂性提高

AMQP

AMQP协议,所有的MQ都遵循这个协议

RbbitMq工作模型

在这里插入图片描述

Broker

	服务器

Connection

	生产者和消费者都和Broker建立连接,这是一个TCP长链接

Channel

	消息通道,为了减少TCP长链接的创建和释放

Queue

	消息存放的地方,队列其实有自己的数据库

Consumer

消费者消费有两种模式:
	pull 消费者主动拉取,消息存放咋服务端
	push 推送给消费者,消息存放在消费端
RabbitMq两种都实现了,kafka和RocketMq只实现了pull
消费者-队列 多对多,一般使用时一个消费者只取一个队列的消息

Vhost

虚拟主机,不同的系统可以使用不同的vhost,建自己的用户,交换机和队列
rabitmq安装时有默认的vhost,名字是 "/"

Exchange 交换机

负责分发消息,交换机跟队列有绑定关系
交换机有三种
1、direct 直联,类似于配置写死 basicPublish("交换机名","binding-key",message)
2、topic主题,模糊匹配,#和*,#是不限制,*是代表一个单词
3、fanout广播,不需要binding-key,给所有队列发

RabbitMq基本使用

原生api

交换机有三种类型,channel.exchangeDeclare 时可以创建三种交换机

消费者

	配置ip
	端口:5672
	虚拟机:VHost
	设置用户密码:
	建立链接:
	创建消息通道
	创建交换机:可以创建三种
	声明队列
	绑定交换机和队列
	创建消费者
	获取消息
ConnectionFactory factory = new ConnectionFactory();
        // 连接IP
        factory.setHost("127.0.0.1");
        // 默认监听端口
        factory.setPort(5672);
        // 虚拟机
        factory.setVirtualHost("/");

        // 设置访问的用户
        factory.setUsername("guest");
        factory.setPassword("guest");
        // 建立连接
        Connection conn = factory.newConnection();
        // 创建消息通道
        Channel channel = conn.createChannel();

        // 声明交换机
        // String exchange, String type, boolean durable, boolean autoDelete, Map<String, Object> arguments
        channel.exchangeDeclare(EXCHANGE_NAME,"direct",false, false, null);

        // 声明队列
        // String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        System.out.println(" Waiting for message....");

        // 绑定队列和交换机
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"gupao.best");

        // 创建消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                                       byte[] body) throws IOException
            {
                String msg = new String(body, "UTF-8");
                System.out.println("Received message : '" + msg + "'");
                System.out.println("consumerTag : " + consumerTag );
                System.out.println("deliveryTag : " + envelope.getDeliveryTag() );
            }
        };

        // 开始获取消息
        // String queue, boolean autoAck, Consumer callback
        channel.basicConsume(QUEUE_NAME, true, consumer);

生产者

	配置ip
	端口:5672
	虚拟机:VHost
	设置用户密码:
	建立链接:
	创建消费通道
	发送消息:basicPublish(交换机,bindling-key,消息)
ConnectionFactory factory = new ConnectionFactory();
        // 连接IP
        factory.setHost("127.0.0.1");
        // 连接端口
        factory.setPort(5672);
        // 虚拟机
        factory.setVirtualHost("/");
        // 用户
        factory.setUsername("guest");
        factory.setPassword("guest");

        // 建立连接
        Connection conn = factory.newConnection();
        // 创建消息通道
        Channel channel = conn.createChannel();

        // 发送消息
        String msg = "Hello world, Rabbit MQ,111";

        // String exchange, String routingKey, BasicProperties props, byte[] body
        channel.basicPublish(EXCHANGE_NAME, "gupao.best", null, msg.getBytes());

        channel.close();
        conn.close();

Spring集成

依赖

<!--rabbitmq依赖 -->
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
            <version>1.3.5.RELEASE</version>
        </dependency>

基于xml配置rabbitMq的依赖关系

<!--配置connection-factory,指定连接rabbit server参数 -->
    <rabbit:connection-factory id="connectionFactory" virtual-host="/" username="guest" password="guest" host="127.0.0.1" port="5672" />

    <!--通过指定下面的admin信息,当前producer中的exchange和queue会在rabbitmq服务器上自动生成 -->
    <rabbit:admin id="connectAdmin" connection-factory="connectionFactory" />

    <!--######分隔线######-->
    <!--定义queue -->
    <rabbit:queue name="MY_FIRST_QUEUE" durable="true" auto-delete="false" exclusive="false" declared-by="connectAdmin" />

    <!--定义direct exchange,绑定MY_FIRST_QUEUE -->
    <rabbit:direct-exchange name="MY_DIRECT_EXCHANGE" durable="true" auto-delete="false" declared-by="connectAdmin">
        <rabbit:bindings>
            <rabbit:binding queue="MY_FIRST_QUEUE" key="FirstKey">
            </rabbit:binding>
        </rabbit:bindings>
    </rabbit:direct-exchange>

    <!--定义rabbit template用于数据的接收和发送 -->
    <rabbit:template id="amqpTemplate" connection-factory="connectionFactory" exchange="MY_DIRECT_EXCHANGE" />

    <!--消息接收者 -->
    <bean id="messageReceiver" class="com.gupaoedu.consumer.FirstConsumer"></bean>

    <!--queue listener 观察 监听模式 当有消息到达时会通知监听在对应的队列上的监听对象 -->
    <rabbit:listener-container connection-factory="connectionFactory">
        <rabbit:listener queues="MY_FIRST_QUEUE" ref="messageReceiver" />
    </rabbit:listener-container>

消费者

public class ThirdConsumer implements MessageListener {
    private Logger logger = LoggerFactory.getLogger(ThirdConsumer.class);

    public void onMessage(Message message) {
        logger.info("The third cosumer received message : " + message);
    }
}

发送消息

@Autowired
    @Qualifier("amqpTemplate")
    private AmqpTemplate amqpTemplate;

	.....

	// Exchange 为 direct 模式,直接指定routingKey
    amqpTemplate.convertAndSend("FirstKey", "[Direct,FirstKey] "+message);
    amqpTemplate.convertAndSend("SecondKey", "[Direct,SecondKey] "+message);

Springboot集成

依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

配置

@Configuration
public class RabbitConfig {

    // 两个交换机
    @Bean("topicExchange")
    public TopicExchange getTopicExchange(){
        return new TopicExchange("TOPIC_EXCHANGE");
    }

    @Bean("fanoutExchange")
    public FanoutExchange getFanoutExchange(){
        return  new FanoutExchange("FANOUT_EXCHANGE");
    }

    // 三个队列
    @Bean("firstQueue")
    public Queue getFirstQueue(){
        Map<String, Object> args = new HashMap<String, Object>();
        args.put("x-message-ttl",6000);
        Queue queue = new Queue("FIRST_QUEUE", false, false, true, args);
        return queue;
    }

    @Bean("secondQueue")
    public Queue getSecondQueue(){
        return new Queue("SECOND_QUEUE");
    }

    @Bean("thirdQueue")
    public Queue getThirdQueue(){
        return new Queue("THIRD_QUEUE");
    }

    // 两个绑定
    @Bean
    public Binding bindSecond(@Qualifier("secondQueue") Queue queue,@Qualifier("topicExchange") TopicExchange exchange){
        return BindingBuilder.bind(queue).to(exchange).with("#.gupao.#");
    }

    @Bean
    public Binding bindThird(@Qualifier("thirdQueue") Queue queue,@Qualifier("fanoutExchange") FanoutExchange exchange){
        return BindingBuilder.bind(queue).to(exchange);
    }

}

消费者

@Component
@RabbitListener(queues = "FIRST_QUEUE")
public class FirstConsumer {

    @RabbitHandler
    public void process(String msg){
        System.out.println(" first queue received msg : " + msg);
    }
}

生产消息

@Component
public class MyProvider {

    @Autowired
    AmqpTemplate amqpTemplate;

    public void send(){
        // 发送4条消息

        amqpTemplate.convertAndSend("","FIRST_QUEUE","-------- a direct msg");

        amqpTemplate.convertAndSend("TOPIC_EXCHANGE","shanghai.gupao.teacher","-------- a topic msg : shanghai.gupao.teacher");
        amqpTemplate.convertAndSend("TOPIC_EXCHANGE","changsha.gupao.student","-------- a topic msg : changsha.gupao.student");

        amqpTemplate.convertAndSend("FANOUT_EXCHANGE","","-------- a fanout msg");

    }

}

RabbitMq进阶知识

订单延迟关闭

1、入库,定时扫描,太low

2、利用rabbitMq的死信队列 TTL
	下单的时候,同时发一条消息到mq,30分钟后消费这条消息
	1、消息设置过期时间,死性交换机,消息过期了,把消息转到死性交换机-死性队列上,一个队列只有一个死性交换机
	2、队列设置过期时间,如果队列跟消息过期时间都有,取小的一个
	3、延迟投递交换机
	
	流程:生产者-原交换机-原队列(超时之后)-----死信交换机----死信队列----最终消费者

死信交换机缺点
	1、如果用队列设置死信时间,存在不同时间的消息,需要很多的交换机和队列
	2、如果设置消息的TTL,可能会造成阻塞,前一条是10秒,后一条是3秒,第二条无法投递
	3、存在时间偏差

使用插件时间延迟队列功能,声明x-delayed-message的类型,所以交换机可以有4种

队列满了

	队列有俩属性控制长度
		x-max-length:存储最大消息数,超过数量,消息被丢弃
		x-max-length-bytes:存储最大消息容量,超过容量,消息丢弃
	内存控制
		检测物理内存数值,超过设置比例后,报警
	磁盘超过设置容量后,触发流控措施
	消费端限流
		basciQos(2)超过2条消息没有发送ACK,不再接受消息

总结

支持多客户端,主流语言都支持客户端实现
灵活的路由:通过交换机实现消息的灵活路由
权限管理:用户和虚拟机
支持插件拓展
与spring集成
高可靠性
集群与拓展性
高可用队列
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值