• RabbitMQ和Springboot集成RabbitMQ知识点


    有关RabbitMQ的知识点

    1.linux安装rabbitmq

    rpm-ivh openssl-libs-1.0.2k-19.el7.x86_64.rpm --force 
    rpm-ivh erlang-22.1.8-1.el7.x86_64.rpm --force
    然后安装rabbitmq
    rpm -ivh rabbitmg-server-3.8.1-1.el7.noarch.rpm
    主节点∶ hostnamectl --transient --static set-hostname mq1
    从节点∶ hostnamectl --transient --static set-hostname mq2
    主从节点都执行∶ vi/etc/hosts 追加192.168.1.2 mq1 192.168.1.3 mq2
    主从都执行∶ chkconfig rabbitmq-server on
    主从都执行∶ rabbitmq-plugins enable rabbitmq_management
    主从都执行∶ vim /etc/security/limits.conf 追加rabbitmq-nofile 65536
    主节点执行∶ scp /var/lib/rabbitmq/.erlang.cookie 从节点用户名@IP∶/var/lib/rabbitmq/
    主从都退出终端重新登录,使主机名更改生效。
    主节点执行∶ rabbitmqctl stop_app 
    rabbitmqctl join_cluster --ram rabbit@mq2
    主从都执行∶ /sbin/service rabbitmq-server start
    主节点执行
    rabbitmqctl add user admin admin
    rabbitmqctl set_user tags admin administrator 
    rabbitmqctl add_vhost /myHost
    rabbitmqctl set_permissions -p /myHost admin "*" "*" ".*" 

    2.Springboot整合RabbitMQ

    (1).pom.xml引用RabbitMQ包

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

    如果需要强制覆盖版本,则在<properties>加入以下代码,然后在引用amqp的地方将版本设置为${spring-boot-starter-amqp}

    <spring-boot-starter-amqp>2.0.6.RELEASE</spring-boot-starter-amqp>

      (2).创建配置类

    package com.controler;
    
    import org.springframework.amqp.core.Binding;
    import org.springframework.amqp.core.BindingBuilder;
    import org.springframework.amqp.core.DirectExchange;
    import org.springframework.amqp.core.Queue;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class RabbitMQConfig {
        private static final String DIRECT_CHANGE_NAME = "CHX_CHANGE";
        private static final String DIRECT_QUEUE_NAME = "CHX_QUEUE";
    
        @Bean
        public Queue DirectQueue() {
            return new Queue(DIRECT_QUEUE_NAME, true);
        }
    
        @Bean
        DirectExchange DirectExchange() {
            return new DirectExchange(DIRECT_CHANGE_NAME, true, false);
        }
    
        @Bean
        Binding bindingDirect() {
            return BindingBuilder.bind(DirectQueue()).to(DirectExchange()).with("routingkey");
        }
    }

      (3).监听RabbitMQ

    package com.controler;
    
    import com.rabbitmq.client.Channel;
    import org.springframework.amqp.core.Message;
    import org.springframework.amqp.rabbit.annotation.RabbitHandler;
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.stereotype.Component;
    import java.io.IOException;
    
    @Component
    @RabbitListener(queues = RabbitMQConfig.DIRECT_QUEUE_NAME)
    public class DirectReceiver {
        @RabbitHandler
        public void process(Channel channel, Message msg) throws IOException {
            try {
                String tmp = new String(msg.getBody());
                System.out.println("消费者收到消息  : " + tmp);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                channel.basicAck(msg.getMessageProperties().getDeliveryTag(),false);
            }
        }
    }

      需要注意的是,这个finally体里的方法要加上,否则有异常时会循环消费死信。这里也可以在application.yml里进行配置。

    rabbitmq:
        host: 127.0.0.1
        port: 5672
        username: guest
        password: guest
        virtual-host: /chxhost
        listener:
            default-requeue-rejected: true #意思是,消息被拒后(即未消费),重新(true)放入队列
            retry:
              enabled: true #是否开启消费者重试(为false时关闭消费者重试,这时消费端代码异常会一直重复收到消息)

      (4).以上是单机版,如果需要配置集群,可用以下代码,即只更换配置类即可。

    package com.chx.util;
    
    import com.rabbitmq.client.ConnectionFactory;
    import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class RabbitmqConfig {
        private String hosts = "127.0.0.1:5672,127.0.0.2:5672";
    
        @Bean
        public ConnectionFactory connectionFactory(){
            CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
            connectionFactory.setAddresses(hosts);
            connectionFactory.setUsername("");
            connectionFactory.setPassword("");
            connectionFactory.setVirtualHost("");
        }
    }

      (5).JAVA直接调用RabbitMQ API的代码,更多信息可查看RabbitMQ的web管理页ip:15672/api查看

    @scheduled(cron = "* 0/2 * * * ?")
        public void startMonitor() throws CLientProtocolException, IOException {
            String rabbitmqMsg = getRabbitmqMsg("/api/queues", username.password, host, port);
            List<JSONObject> parseArray = JSONArray.parseArray(rabbitmqMsg, JSONObject.class);
            if (parseArray == null || parseArray.size() == 0) {
                return;
            }
            for (int i = 0; i < parseArray.size(); i++) {
                //获取队列里的消息数量
                BigDecimal messages = parseArray.get(i).getBigDecimal("messages");
                //获取虚拟机名
                String vhost = parseArray.get(i).getString("vhost");
                //获取队列名
                String name = parseArray.get(i).getString("name");
                System.out.println(vhost + name + "消息数量" + messages);
            }
        }
    
        /**
         * @param tailUrl  尾缀
         * @param userName 较大权限的用户名(可查看队列信息)
         * @param password 较大权限的用户的密码
         * @param hostIp   访问的主机IP
         * @param port     访问的rabbitmq端口(一定是web管理端口,默认15672,不是连接端口5672)
         * @return
         * @throws CLientProtocolException
         * @throws IOException
         */
        public String getRabbitmqMsg(String tailUrl, String userName, String password, String hostIp, int port)
                throws CLientProtocolException, IOException {
            HostPost host = new HostPost(hostIp, port);
            HttpGet httGet = new HttpGet(tailUrl);
            //开始基于Bisc模式认证用户名密码。basic由于是明文传输所以不安全,但是和tls/ssl协同仍然可以接受
            BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
            credentialsProvider.setCredentials(new AuthScope(hostIp.port),
                    new UsernamePasswordCredentials(userName, password));
            DefaultHttpClient client = new DefaultHttpClient();
            client.setCredentialsProvider(credentialsProvider);
            HttpResponse execute = client.execute(host, httGet);
            HttpEntity entity = execute.getEntity();
            return EntityUtils.toString(entity);
        }

    3.常见问题解决办法

      (1).springboot连接rabbitmq发生socket closed异常。

    1.检查配置文件端口是否是5672,因为rabbitmq连接的不是web管理端,很多时候粗心出现这个错误。只有访问Web API时才设定15672.

    2.不管是集群还是单机模式,一般都会配置下hosts文件的主机名。检查下host是否被篡改导致的。

    3.在web管理页检查配置的用户是否有操作当前队列的权限。如未绑定虚拟机、交换机权限。设置如图所示,进入15672端口的页面管理。

    以下图片从网络寻找。

    4.扩展知识点

    1.如何保证消息队列的顺序性:

    首先要保证生产者发送消息到队列时,消息是有序的,这个可以通过发送时设置时间戳或者通过序号进行控制。

    消息队列接收消息向下做分发是有序的,消费者接收消息这一块最容易乱序。比如A/B/C三条消息,向下分发:

    (1).一个消息队列对多个消费者,此时消费者执行这三条消息的时间是不固定的也就是不受控制的。

    (2).一个消息队列对一个消费者,但是消费者进行了多线程消费,理由和上述相似。

    解决办法是:一个队列对应一个消费者,严格按照串行执行。

    嫌弃串行慢或者只需要保证部分有序的话,可以进行业务分析,通过特定策略,把部分有序的消息发送到同一个队列里,并单线程消费。就类似于打包在一起,最后打包消费。

    2.处理消费重复:只能通过业务代码进行筛查。或者通过消息内容计算唯一主键,消费端进行判断。

     

  • 相关阅读:
    [好好学习]在VMware中安装Oracle Enterprise Linux (v5.7)
    [冲昏头脑]IDEA中的maven项目中学习log4j的日志操作
    [烧脑时刻]EL表达式1分钟完事
    Sublime2 破解教程
    全脑瘫IT时代(八)
    全脑瘫IT时代(九)
    迁移完成
    USB Debug Cable (一)
    一个不是很常见的.Net Interop问题
    全脑瘫IT时代(十二)
  • 原文地址:https://www.cnblogs.com/chxwkx/p/14629502.html
Copyright © 2020-2023  润新知