RabbitMQ

1、什么是MQ

问题1:

系统的注册功能:注册到数据库中、发邮件、发短信

如果串行执行的话,需要耗时3秒

改进: 注册数据库成功,即可,

注册成功后,并行进行发邮件、发短信 ,仅需1秒

Redis数据缓存,它只供获取数据使用,不能发送数据,所以不能使用redis

问题2:

大量的写数据库的请求,如果请求直接到达数据库,将会把数据库写死?

改进:把写数据的请求都写到MQ中,消息的消费者按照队列的顺序逐一取出写入的数据,写入数据库中

流量削峰

某一时间段会有大量的数据发送,达到一定的峰值,针对于这种情况,我们就将消除峰值的操作,称之为,流量削峰

微服务之间的通讯方式:

1、调用

2、MQ

市面上比较火爆的几款MQ:

ActiveMQ,RocketMQ,Kafka,RabbitMQ。

  • 语言的支持:ActiveMQ,RocketMQ只支持Java语言,Kafka可以支持多们语言,RabbitMQ支持多种语言。

  • 效率方面:ActiveMQ,RocketMQ,Kafka效率都是毫秒级别,RabbitMQ是微秒级别的。

  • 消息丢失,消息重复问题: RabbitMQ针对消息的持久化,和重复问题都有比较成熟的解决方案。

  • 学习成本:RabbitMQ非常简单。

RabbitMQ是由Rabbit公司去研发和维护的,最终是在Pivotal。

RabbitMQ严格的遵循AMQP协议,高级消息队列协议,帮助我们在进程之间传递异步消息。


2、搭建环境

1、安装erang依赖环境,一路下一步即可

2、配置环境变量

新建环境变量ERLANG_HOME

编辑系统环境变量Path

3、安装rabbitmq软件,一路下一步即可

4、配置rabbitmq的环境变量

新建环境变量

5、启动RabbitMQ的监视器:

6、启动rabbitmq的服务

7、url地址中访问 localhost:15672

8、账号密码均为mgest


3、RabbitMQ的工作原理

  • Publisher - 生产者:发布消息到RabbitMQ中的Exchange

  • Consumer - 消费者:监听RabbitMQ中的Queue中的消息

  • Exchange - 交换机:和生产者建立连接并接收生产者的消息

  • Queue - 队列:Exchange会将消息分发到指定的Queue,Queue和消费者进行交互

  • Routes - 路由:交换机以什么样的策略将消息发布到Queue


4、RabbitMQ的通讯方式

Hello-World通讯方式

特点:生产环境无法使用

一个生产者、一个消费者、一个交换机、一个队列

1、新建生产者微服务

2、添加依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>com.rabbitmq</groupId>
        <artifactId>amqp-client</artifactId>
        <version>5.6.0</version>
    </dependency>
</dependencies>

3、创建启动程序

4、编写配置文件

server:
  port: 8091
spring:
  application:
    name: APPLYPUBLISHERSERVER
eureka:
  client:
    service-url:
      defaultZone: http://root:root@localhost:8080/eureka

5、工具类,在pda-common

package com.qf.pdacommon.util;

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

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

public class RabbitMQUtil {
    //声明连接工厂,static的
    private static ConnectionFactory factory = null;
    //在静态代码块中初始化工厂信息
    static{
        //创建工厂
        factory = new ConnectionFactory();
        //配置连接信息
        factory.setHost("127.0.0.1");
        //设置端口号
        factory.setPort(5672);
//        //以下可以省略,当自建的用户必须做出如下的配置
//        factory.setUsername("zhangsan");  //登陆账号
//        factory.setPassword("abc123");    //密码
//        factory.setVirtualHost("/pda");   //操作目录
    }

    //编写获取连接对象的方法
    public static Connection getConnection(){
        Connection connection=null;
        try {
            connection = factory.newConnection();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
        return connection;
    }
}

6、生产者写入消息到队列中

package com.qf.applypublisher.service.impl;

import com.qf.applypublisher.service.PublisherService;
import com.qf.pdacommon.util.RabbitMQUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import org.springframework.stereotype.Service;

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

@Service
public class PublisherServiceImpl implements PublisherService {


    @Override
    public String addMq(String meg) {
        //在此将消息写入消息队列
        //1、获取RabbitMQ服务的连接
        Connection connection = RabbitMQUtil.getConnection();
        //通过连接对象获取管道
        Channel channel = null;
        String result ="";

        try {
            channel= connection.createChannel();

            //生产者把消息通过管道,写入队列中
            //参数1:""使用默认的交换机
            //参数2:队列的名字,也可以作为路由规则,【注意】在一个MQ服务中,队列的名字是唯一的
            //参数3:写入消息时,携带的附加信息,如果没有就传null
            //参数4:写入的消息
            channel.basicPublish("","apply",null,meg.getBytes());
            System.out.println("写入消息队列成功!");
            result="success";
        } catch (IOException e) {
            System.out.println("写入消息失败,发生有异常:"+e.getMessage());
            result = "failed";
            e.printStackTrace();
        }finally {
            try {
                channel.close();
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }

        }

        return result;
    }


}

7、新建消息的消费者微服务

package com.qf.applyconsumer.service.impl;

import com.qf.applyconsumer.service.ConsumerService;
import com.qf.pdacommon.util.RabbitMQUtil;
import com.rabbitmq.client.*;
import org.springframework.stereotype.Service;

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

@Service
public class ConsumerServiceImpl implements ConsumerService {
    @Override
    public String getMeg() {
        //到队列中获取消息
        //1、获取连接
        Connection connection = RabbitMQUtil.getConnection();
        //2、创建管道
        Channel channel = null;
        String result = "";
        try {
            channel = connection.createChannel();

            //定义队列
            //参数1:获取消息队列的名称
            //参数2:是否给队列中的消息进行持久化,true-进行持久化
            //参数3:当消费者的连接connection对象关闭后(消费者宕机后),是否删除队列中的消息
            //参数4:当队列中没有消费者消费的时候,是否自动删除队列
            //参数5:指定的队列的附加消息
            channel.queueDeclare("apply",true,false,false,null);
            //声明监听:监听会一直运行,只要监听到队列中有消息就去获取消息
            //通过handleDelivery方法获取消息
            DefaultConsumer consumer = new DefaultConsumer(channel){
                public void handleDelivery(String consumerTeg, Envelope envelope, AMQP.BasicProperties properties,byte[] body){
                    //参数body就是从队列中获取到的消息
                    //service-> dao  调用其他微服务消息处理
                    String meg = new String(body);
                    System.out.println("获取到的消息是" + meg);
                }
            };

            //自动消息确认:从队列里取出新消息后,自动通知队列,可以删除刚才取出的消息  ACK
            //参数1:队列的名字,告知哪个队列在取出消息后,就自动删除多列中的消息
            //参数2:true-取出消息后,队列就自动删除消息
            //参数3:监听对象
            channel.basicConsume("apply",true,consumer);
            System.out.println("消费者的监听程序开始工作......");
            System.in.read();
            result = "success";
        } catch (IOException e) {
            result = "faileld";
            e.printStackTrace();
        }finally {
            try {
                channel.close();
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}

 

 

Work工作队列通讯方式

特点:一个生产者、一个交换机、一个队列、多个消费者

新建了一个消费者:确保两个消费者从同一队列中获取消息

【注意】一个消息只能被消费一次,有一个消费者进行消费了,另一个就等到消费完了,删除之后才能取下一个

同时定义两个消费者,除了yml文件中的name名字不一样之外,其他地方一样,生产者不用变,队列名相同就可以

package com.qf.applyconsumerwork2.service.impl;

import com.qf.applyconsumerwork2.service.ConsumerWorkService;
import com.qf.pdacommon.util.RabbitMQUtil;
import com.rabbitmq.client.*;
import org.springframework.stereotype.Service;

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

@Service
public class ConsumerWorkServiceImpl implements ConsumerWorkService {
    @Override
    public String getMeg() {

        //获取连接
        Connection connection = RabbitMQUtil.getConnection();
        //创建管道
        Channel channel = null;
        String result = "";
        try {
            channel = connection.createChannel();
            channel.queueDeclare("apply",true,false,false,null);

            //指定消费者的消费能力:一个消费者一次只能最多消费一个消息
            channel.basicQos(1);

            //定义channel1,接收外部类的channel,因为内部类中 外部类定义的channel不适用
            Channel channel1 = channel;
            DefaultConsumer consumer = new DefaultConsumer(channel){
                public void handleDelivery(String consumerTeg, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    String meg = new String(body);
                    System.out.println("获取到的消息是:" + meg);
                    //改为false只是队列不自动删除消息
                    //需要我们手动删除
                    //判断:消息处理成功了,手动通知队列删除消费者的消息
                    channel1.basicAck(envelope.getDeliveryTag(), false);
                }
            };
            //改成手动确认,将true改成false
            //从队列里取出消息后,队列不删除取出的消息
            channel.basicConsume("apply",false,consumer);
            System.out.println("8094消费者的监听程序开始工作......");
            System.in.read();
            result =  "Success";

        } catch (IOException e) {
            result = "failed";
            e.printStackTrace();
        }finally {
            try {
                channel.close();
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }


        return result;
    }
}

 

 

发布订阅通讯方式 --- 广播模式

特点:一个生产者、一个交换机、多个队列,每个队列对应多个消费者

定义的所有消费者每一个都会接收到消息

生产者

    //广播模式
    @Override
    public String addMqFanOut(String meg) {
        String result = "";
        Connection connection = RabbitMQUtil.getConnection();
        Channel channel = null;
        try {
            channel = connection.createChannel();
            //定义交换机的类型
            channel.exchangeDeclare("exchange2105", BuiltinExchangeType.FANOUT);
            //定义队列
            //参数1:消息队列的名字
            //参数2:交换机的名字,使用对用的交换机写入到队列里
            //路由器规则
            channel.queueBind("queue1","exchange2105","");
            channel.queueBind("queue2","exchange2105","");

            //写入到队列中
            channel.basicPublish("exchange2105","",null,meg.getBytes());
            result = "success";
            System.out.println("广播方式消息写入队列成功");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                channel.close();
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }

        }
        return result;
    }


}

消费者queue1

package com.qf.applyconsumerwork.service.impl;

import com.qf.applyconsumerwork.service.ConsumerWorkService;
import com.qf.pdacommon.util.RabbitMQUtil;
import com.rabbitmq.client.*;
import org.springframework.stereotype.Service;

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

@Service
public class ConsumerWorkServiceImpl implements ConsumerWorkService {
    @Override
    public String getMeg() {

        //获取连接
        Connection connection = RabbitMQUtil.getConnection();
        //创建管道
        Channel channel = null;
        String result = "";
        try {
            channel = connection.createChannel();
            //queue1进行修改,和生产者一致
            channel.queueDeclare("queue1",true,false,false,null);

            //指定消费者的消费能力:一个消费者一次只能最多消费一个消息
            channel.basicQos(1);

            //定义channel1,接收外部类的channel,因为内部类中 外部类定义的channel不适用
            Channel channel1 = channel;
            DefaultConsumer consumer = new DefaultConsumer(channel){
                public void handleDelivery(String consumerTeg, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                   String meg = new String(body);
                    System.out.println("8093获取到的消息是:" + meg);
                    //改为false只是队列不自动删除消息
                    //需要我们手动删除
                    //判断:消息处理成功了,手动通知队列删除消费者的消息
                    channel1.basicAck(envelope.getDeliveryTag(), false);
                }
            };
            //改成手动确认,将true改成false
            //从队列里取出消息后,队列不删除取出的消息
            //queue1进行修改,和生产者一致
            channel.basicConsume("queue1",false,consumer);
            System.out.println("消费者的监听程序开始工作......");
            System.in.read();
            result =  "Success";

        } catch (IOException e) {
            result = "failed";
            e.printStackTrace();
        }finally {
            try {
                channel.close();
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }


        return result;
    }
}

消费者queue2

package com.qf.applyconsumerwork2.service.impl;

import com.qf.applyconsumerwork2.service.ConsumerWorkService;
import com.qf.pdacommon.util.RabbitMQUtil;
import com.rabbitmq.client.*;
import org.springframework.stereotype.Service;

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

@Service
public class ConsumerWorkServiceImpl implements ConsumerWorkService {
    @Override
    public String getMeg() {

        //获取连接
        Connection connection = RabbitMQUtil.getConnection();
        //创建管道
        Channel channel = null;
        String result = "";
        try {
            channel = connection.createChannel();
            //queue2进行修改,和生产者一致
            channel.queueDeclare("queue2",true,false,false,null);

            //指定消费者的消费能力:一个消费者一次只能最多消费一个消息
            channel.basicQos(1);

            //定义channel1,接收外部类的channel,因为内部类中 外部类定义的channel不适用
            Channel channel1 = channel;
            DefaultConsumer consumer = new DefaultConsumer(channel){
                public void handleDelivery(String consumerTeg, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    String meg = new String(body);
                    System.out.println("获取到的消息是:" + meg);
                    //改为false只是队列不自动删除消息
                    //需要我们手动删除
                    //判断:消息处理成功了,手动通知队列删除消费者的消息
                    channel1.basicAck(envelope.getDeliveryTag(), false);
                }
            };

            //队列名字是消费者中定义的队列名字
            //queue2进行修改,和生产者一致
            channel.basicConsume("queue2",false,consumer);
            System.out.println("8094消费者的监听程序开始工作......");
            System.in.read();
            result =  "Success";

        } catch (IOException e) {
            result = "failed";
            e.printStackTrace();
        }finally {
            try {
                channel.close();
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }


        return result;
    }
}

 

Routing路由通讯方式

生产者:交换机类型是Direct

               定义队列时指定了路由规则

生产者

    //路由方式
    @Override
    public String addMqDirect(String meg, String route) {
        Connection connection = RabbitMQUtil.getConnection();
        Channel channel = null;
        String result = "";
        try {
            channel = connection.createChannel();

            //定义交换机的类型
            channel.exchangeDeclare("exchange210502",BuiltinExchangeType.DIRECT);
            //绑定队列,同时指定路由规则
            channel.queueBind("queue3","exchange210502","goods");
            channel.queueBind("queue4","exchange210502","score");

            //写入消息
            channel.basicPublish("exchange210502",route,null,meg.getBytes());
            System.out.println("Direct消息写入成功");
            result = "success";
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

消费者queue3  ---- queue4中把3改为4

package com.qf.applyconsumerwork.service.impl;

import com.qf.applyconsumerwork.service.ConsumerWorkService;
import com.qf.pdacommon.util.RabbitMQUtil;
import com.rabbitmq.client.*;
import org.springframework.stereotype.Service;

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

@Service
public class ConsumerWorkServiceImpl implements ConsumerWorkService {
    @Override
    public String getMeg() {

        //获取连接
        Connection connection = RabbitMQUtil.getConnection();
        //创建管道
        Channel channel = null;
        String result = "";
        try {
            channel = connection.createChannel();
            channel.queueDeclare("queue3",true,false,false,null);

            //指定消费者的消费能力:一个消费者一次只能最多消费一个消息
            channel.basicQos(1);

            //定义channel1,接收外部类的channel,因为内部类中 外部类定义的channel不适用
            Channel channel1 = channel;
            DefaultConsumer consumer = new DefaultConsumer(channel){
                public void handleDelivery(String consumerTeg, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                   String meg = new String(body);
                    System.out.println("8093获取到的消息是:" + meg);
                    //改为false只是队列不自动删除消息
                    //需要我们手动删除
                    //判断:消息处理成功了,手动通知队列删除消费者的消息
                    channel1.basicAck(envelope.getDeliveryTag(), false);
                }
            };
            //改成手动确认,将true改成false
            //从队列里取出消息后,队列不删除取出的消息
            channel.basicConsume("queue3",false,consumer);
            System.out.println("8093消费者的监听程序开始工作......");
            System.in.read();
            result =  "Success";

        } catch (IOException e) {
            result = "failed";
            e.printStackTrace();
        }finally {
            try {
                channel.close();
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }


        return result;
    }
}

Topic通讯方式


 

生产者

    @Override
    public String addTopic(String meg, String route) {
        Connection connection = RabbitMQUtil.getConnection();
        Channel channel = null;
        String result = "";

        try {
            channel = connection.createChannel();

            //定义交换机的类型
            channel.exchangeDeclare("exchange210504",BuiltinExchangeType.TOPIC);

            //绑定队列:交换机按照什么路由规则,写入哪个队列
            channel.queueBind("queue5","exchange210504","liaoning.dalian.*");
            channel.queueBind("queue6","exchange210504","heilongjiang.#");

            //写入消息
            channel.basicPublish("exchange210504",route,null,meg.getBytes());
            System.out.println("Topic消息写入成功");
            result = "success";
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

消费者

package com.qf.applyconsumerwork.service.impl;

import com.qf.applyconsumerwork.service.ConsumerWorkService;
import com.qf.pdacommon.util.RabbitMQUtil;
import com.rabbitmq.client.*;
import org.springframework.stereotype.Service;

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

@Service
public class ConsumerWorkServiceImpl implements ConsumerWorkService {
    @Override
    public String getMeg() {

        //获取连接
        Connection connection = RabbitMQUtil.getConnection();
        //创建管道
        Channel channel = null;
        String result = "";
        try {
            channel = connection.createChannel();
            channel.queueDeclare("queue5",true,false,false,null);

            //指定消费者的消费能力:一个消费者一次只能最多消费一个消息
            channel.basicQos(1);

            //定义channel1,接收外部类的channel,因为内部类中 外部类定义的channel不适用
            Channel channel1 = channel;
            DefaultConsumer consumer = new DefaultConsumer(channel){
                public void handleDelivery(String consumerTeg, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                   String meg = new String(body);
                    System.out.println("8093获取到的消息是:" + meg);
                    //改为false只是队列不自动删除消息
                    //需要我们手动删除
                    //判断:消息处理成功了,手动通知队列删除消费者的消息
                    channel1.basicAck(envelope.getDeliveryTag(), false);
                }
            };
            //改成手动确认,将true改成false
            //从队列里取出消息后,队列不删除取出的消息
            channel.basicConsume("queue5",false,consumer);
            System.out.println("8093消费者的监听程序开始工作......");
            System.in.read();
            result =  "success";

        } catch (IOException e) {
            result = "failed";
            e.printStackTrace();
        }finally {
            try {
                channel.close();
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }


        return result;
    }
}

如果没有问题但是报错就看一下http://localhost:15672/#/exchanges

 更改交换机的名字


5、消息的可靠性---重要

从如下方面考虑可靠性:

1、生产者要把消息成功的写入交换机 ----- confirm机制

2、交换机要成功的把消息写入队列

3、队列要可靠 ---- 本身具有的持久化机制

4、确保消费者成功消费消息后,才把消息从队列中删除 --- 手动ACK

Confirm 普通消息确认机制

@Override
public String addMqTopic(String meg, String route) {
    String result="";
    Connection connection=RabbitMQUtil.getConnection();
    Channel channel=null;
    try {
        channel=connection.createChannel();
        //开启Confirm机制---确保生产者把消息成功的写入到交换机中
        channel.confirmSelect();
        //定义交换机
        channel.exchangeDeclare("exchange210503",BuiltinExchangeType.TOPIC);

        //绑定队列:交换机按照什么路由规则,写入哪个队列
        channel.queueBind("queue5","exchange210503","liaoning.dalian.*");
        channel.queueBind("queue6","exchange210503","heilongjiang.#");

        //写入队列
        channel.basicPublish("exchange210503",route,null,meg.getBytes());

        //判断消息写入交换机是否成功!
        if(channel.waitForConfirms()){
            result="写入交换机成功!";
        }else {
            result="写入交换机失败!";
        }
    } catch (IOException | InterruptedException e) {
        e.printStackTrace();
    } finally {
        try {
            channel.close();
            connection.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
    return result;
}

批量消息确认机制(Confirm)

@Override
public String addMq(String meg) {
    //在此处将消息写入消息队列
    //1、获取RabbitMQ服务的连接
    Connection connection= RabbitMQUtil.getConnection();
    //2、通过连接对象获取管道
    Channel channel=null;
    String reslult="";
    try {
        channel=connection.createChannel();
        //开启消息确认机制
        channel.confirmSelect();
        //批量写入消息
        //生产者把消息通过管道,写入队列中
        //参数1:""使用默认的交换机
        //参数2:队列的名字,也可以作为路由规则,【注意】在一个MQ服务中,队列的名字是唯一的
        //参数3:写入消息时,携带的附加信息,如果没有就传null
        //参数4:写入的消息
        for (int i=1; i<=10; i++){
            meg=meg+i;
            channel.basicPublish("","apply",null,meg.getBytes());
        }
        //批量确认,如果有一个消息写入交换机失败,则之前写过的所有消息都失败,失败后会抛出异常
        channel.waitForConfirmsOrDie();
        System.out.println("写入消息队列成功!");
        reslult="批量将消息写入交换机成功!";
    } catch (IOException | InterruptedException e) {
        System.out.println("写入消息失败,发生有异常:"+e.getMessage());
        reslult="批量写入交换机失败!";
        e.printStackTrace();
    }finally {
        try {
            channel.close();
            connection.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
    return reslult;
}

异步Confirm消息确认

@Override
public String addMqFanOut(String meg){
    String result="";
    Connection connection=RabbitMQUtil.getConnection();
    Channel channel=null;
    try {
        channel=connection.createChannel();
        //开启Confirm消息确认机制
        channel.confirmSelect();
        //定义交换机的类型
        channel.exchangeDeclare("exchange2105", BuiltinExchangeType.FANOUT);
        //定义队列
        //参数1:队列名字,不能重复
        //参数2:交换机名字,使用对应没名字的交换机写入到该队列中
        channel.queueBind("queue1","exchange2105","");
        channel.queueBind("queue2","exchange2105","");

        //写入到队列中
        channel.basicPublish("exchange2105","",null,meg.getBytes());
        //异步消息确认
        channel.addConfirmListener(new ConfirmListener() {
            @Override
            public void handleAck(long l, boolean b) throws IOException {
                //消息写入成功时,会调用
                System.out.println("写入成功!消息的标识:"+l+",是否为批量?"+b);
            }

            @Override
            public void handleNack(long l, boolean b) throws IOException {
                //消息写入失败时回调
                System.out.println("写入失败!消息的标识:"+l+",是否为批量?"+b);

            }
        });

        result="success";
        System.out.println("发布、订阅方式将消息写入队列成功!");

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            channel.close();
            connection.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
    return null;
}

Return机制---确保消息从交换机写入队列成功


6、避免消息的重复消费

产生的原因:没有及时 进行手动ACK

解决思路:

1、在Redis中设置一个值 set mqKey 0 0---没有程序去消费这个消息

2、消费者在到队列中取消息之前,先读取mqKey

如果读取到的是0,则允许读取消息,否则不允许

3、在读取过程中,设置为1 1---此消息正在被某个消费者消费        

当一个消费者进入消息队列之后,将其key值进行改变,正常情况之下,在执行完流程,将信息存储进数据库,然后正常出队列之后进行删除,将key改回去,但是有一种情况,在没有执行完就离开队列key没有改回去,这种情况下,谁都进不来,产生了死锁现象

解决的办法就是设置有效时间

public void getMeg() {
    //到队列中获取消息
    //1、获取连接
    Connection connection= RabbitMQUtil.getConnection();
    //2、创建管道
    Channel channel=null;
    String result="";
    try {
        channel=connection.createChannel();

        //定义队列
        //参数1:获取消息的队列的名称
        //参数2:是否给队列中的消息进行持久化  true-进行持久化
        //参数3:当消费者的连接connection对象关闭后(消费者宕机后),是否删除队列中的消息
        //参数4:当队列中没有消费者消费的时候,是否自动删除队列
        //参数5:指定的队列的附加消息
        channel.queueDeclare("queue5",true,false,false,null);

        //指定消费者的消费能力:一个消费者一次只能最多消费一个消息
        channel.basicQos(1);

        //声明监听:监听会一直运行,只要监听到队列中有消息,就去获取消息
        //通过handleDelivery方法获取消息
        Channel channel1=channel;
        DefaultConsumer consumer=new DefaultConsumer(channel1){
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //获取Redis的连接
                Jedis jedis= JedisUtil.getJedis();
                if(jedis!=null){
                    jedis.select(5);
                    //读取锁里的信息
                    String redisLock=jedis.get("mqKey");
                    if(redisLock==null){
                        //设置只当时间,时间到自动将key改回去,赋值
                        jedis.setex("mqKey",10,"1");
                        //参数body就是从队列中获取到的消息
                        //service->dao   调用其他微服务处理消息
                        String meg=new String(body);
                        System.out.println("1号消费者获取到消息:"+meg);
                        //判断,消息处理成功了,手动通知队列删除消费的消息
                        channel1.basicAck(envelope.getDeliveryTag(),false);
                        //消费完成之后,设置为0
                        jedis.set("mqKey","0");
                    }else{
                        int intRedisLock=Integer.parseInt(redisLock);
                        if(intRedisLock==0){
                            //赋值
                            jedis.setex("mqKey",10,"1");
                            //参数body就是从队列中获取到的消息
                            //service->dao   调用其他微服务处理消息
                            String meg=new String(body);
                            System.out.println("1号消费者获取到消息:"+meg);
                            //判断,消息处理成功了,手动通知队列删除消费的消息
                            channel1.basicAck(envelope.getDeliveryTag(),false);
                            //消费完成之后,设置为0
                            jedis.set("mqKey","0");
                        }
                    }
                }
            }
        };

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值