RabbitMQ

在介绍RabbitMQ之前实现要介绍一下MQ,MQ是什么?

MQ全称是Message Queue,可以理解为消息队列的意思,简单来说就是消息以管道的方式进行传递。

RabbitMQ是一个实现了AMQP(Advanced Message Queuing Protocol)高级消息队列协议的消息队列服务,用Erlang语言的。

使用场景

在我们秒杀抢购商品的时候,系统会提醒我们稍等排队中,而不是像几年前一样页面卡死或报错给用户。

像这种排队结算就用到了消息队列机制,放入通道里面一个一个结算处理,而不是某个时间断突然涌入大批量的查询新增把数据库给搞宕机,所以RabbitMQ本质上起到的作用就是削峰填谷,为业务保驾护航。

为什么选择RabbitMQ

现在的市面上有很多MQ可以选择,比如ActiveMQ、ZeroMQ、Appche Qpid,那问题来了为什么要选择RabbitMQ?

  1. 除了Qpid,RabbitMQ是唯一一个实现了AMQP标准的消息服务器;
  2. 可靠性,RabbitMQ的持久化支持,保证了消息的稳定性;
  3. 高并发,RabbitMQ使用了Erlang开发语言,Erlang是为电话交换机开发的语言,天生自带高并发光环,和高可用特性;
  4. 集群部署简单,正是应为Erlang使得RabbitMQ集群部署变的超级简单;
  5. 社区活跃度高,根据网上资料来看,RabbitMQ也是首选;

工作机制

生产者、消费者和代理

在了解消息通讯之前首先要了解3个概念:生产者、消费者和代理。

生产者:消息的创建者,负责创建和推送数据到消息服务器;

消费者:消息的接收方,用于处理数据和确认消息;

代理:就是RabbitMQ本身,用于扮演“快递”的角色,本身不生产消息,只是扮演“快递”的角色。

消息发送原理

首先你必须连接到Rabbit才能发布和消费消息,那怎么连接和发送消息的呢?

你的应用程序和Rabbit Server之间会创建一个TCP连接,一旦TCP打开,并通过了认证,认证就是你试图连接Rabbit之前发送的Rabbit服务器连接信息和用户名和密码,有点像程序连接数据库,使用Java有两种连接认证的方式,后面代码会详细介绍,一旦认证通过你的应用程序和Rabbit就创建了一条AMQP信道(Channel)。

信道是创建在“真实”TCP上的虚拟连接,AMQP命令都是通过信道发送出去的,每个信道都会有一个唯一的ID,不论是发布消息,订阅队列或者介绍消息都是通过信道完成的。

为什么不通过TCP直接发送命令?

对于操作系统来说创建和销毁TCP会话是非常昂贵的开销,假设高峰期每秒有成千上万条连接,每个连接都要创建一条TCP会话,这就造成了TCP连接的巨大浪费,而且操作系统每秒能创建的TCP也是有限的,因此很快就会遇到系统瓶颈。

如果我们每个请求都使用一条TCP连接,既满足了性能的需要,又能确保每个连接的私密性,这就是引入信道概念的原因。

你必须知道的Rabbit

想要真正的了解Rabbit有些名词是你必须知道的。

包括:ConnectionFactory(连接管理器)、Channel(信道)、Exchange(交换器)、Queue(队列)、RoutingKey(路由键)、BindingKey(绑定键)。

**ConnectionFactory(连接管理器):**应用程序与Rabbit之间建立连接的管理器,程序代码中使用;

**Channel(信道):**消息推送使用的通道;

**Exchange(交换器):**用于接受、分配消息;

Queue(队列):用于存储生产者的消息;

RoutingKey(路由键):用于把生成者的数据分配到交换器上;

BindingKey(绑定键):用于把交换器的消息绑定到队列上;

img

消息持久化

Rabbit队列和交换器有一个不可告人的秘密,就是默认情况下重启服务器会导致消息丢失,那么怎么保证Rabbit在重启的时候不丢失呢?答案就是消息持久化。

当你把消息发送到Rabbit服务器的时候,你需要选择你是否要进行持久化,但这并不能保证Rabbit能从崩溃中恢复,想要Rabbit消息能恢复必须满足3个条件:

  1. 投递消息的时候durable设置为true,消息持久化,代码:channel.queueDeclare(x, true, false, false, null),参数2设置为true持久化;
  2. 设置投递模式deliveryMode设置为2(持久),代码:channel.basicPublish(x, x, MessageProperties.PERSISTENT_TEXT_PLAIN,x),参数3设置为存储纯文本到磁盘;
  3. 消息已经到达持久化交换器上;
  4. 消息已经到达持久化的队列;

持久化工作原理

Rabbit会将你的持久化消息写入磁盘上的持久化日志文件,等消息被消费之后,Rabbit会把这条消息标识为等待垃圾回收。

持久化的缺点

消息持久化的优点显而易见,但缺点也很明显,那就是性能,因为要写入硬盘要比写入内存性能较低很多,从而降低了服务器的吞吐量,尽管使用SSD硬盘可以使事情得到缓解,但他仍然吸干了Rabbit的性能,当消息成千上万条要写入磁盘的时候,性能是很低的。

虚拟主机

每个Rabbit都能创建很多vhost,我们称之为虚拟主机,每个虚拟主机其实都是mini版的RabbitMQ,拥有自己的队列,交换器和绑定,拥有自己的权限机制。

vhost特性

  1. RabbitMQ默认的vhost是“/”开箱即用;
  2. 多个vhost是隔离的,多个vhost无法通讯,并且不用担心命名冲突(队列和交换器和绑定),实现了多层分离;
  3. 创建用户的时候必须指定vhost;

环境搭建

安装wget命令

#rpm -ivh http://mirrors.163.com/centos/7/os/x86_64/Packages/wget-1.14-18.el7.x86_64.rpm
#yum -y install wget

下载erlang solution

rabbitMQ是erlang语言进行开发,所以安装RabbitMQ前需要安装erlang的依赖

#wget https://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.rpm

安装erlang

#yum install epel-release 
#rpm -Uvh erlang-solutions-1.0-1.noarch.rpm
#yum install erlang

下载rabbitMQ

#wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.7.12/rabbitmq-server-3.7.12-1.el6.noarch.rpm

安装rabbitMQ

#yum install rabbitmq-server-3.7.12-1.el6.noarch.rpm

开启web端管理访问(非必须,如果要开启,需要先开启允许远程访问)

#rabbitmq-plugins enable rabbitmq_management rabbitmq_management_agent  rabbitmq_web_dispatch

安装消息延迟插件

#cd /usr/lib/rabbitmq/lib/rabbitmq_server-3.7.12/plugins
#wget wget https://dl.bintray.com/rabbitmq/community-plugins/rabbitmq_delayed_message_exchange-0.0.1.ez
#rabbitmq-plugins enable rabbitmq_delayed_message_exchange

注:查看插件情况

rabbitmq-plugins list 

启动rabbitMQ

service rabbitmq-server start ##启动rabbitMQ
service rabbitmq-server status ##查看rabbitMQ状态
service rabbitmq-server restart ##重启rabbitMQ
service rabbitmq-server stop ##停止rabbitMQ

使用web访问

http://xxx.xxx.xxx.xxx:15672

RabbitMQ命令

创建VirtualHost

rabbitmqctl add_vhost 虚拟服务器名称
rabbitmqctl add_vhost my_test

创建用户并配置权限

rabbitmqctl add_user 用户名 密码
rabbitmqctl add_user testuser testuser
rabbitmqctl set_permissions -p / 用户名 ".*" ".*" ".*"
rabbitmqctl set_permissions -p my_test testuser ".*" ".*" ".*"

配置角色

rabbitmqctl set_user_tags 用户名 角色类型
rabbitmqctl set_user_tags testuser policymaker
角色类型有:none、management、policymaker、monitoring、administrator

Springboot应用

pom配置文件

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
            <!--<version>1.5.2.RELEASE</version>-->
            <!--排除这个slf4j-log4j12-->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

application配置文件

rabbitmq.host=192.168.163.131,192.168.163.130
rabbitmq.port=5672,5672
rabbitmq.username=haoweiGuest,haowei
rabbitmq.password=hw123456,1Qaz2wsx
rabbitmq.virtual-host=/vasky,/radap
rabbitmq.exchangeName=/realtime/face
rabbitmq.topic=haowei.face,haowei-lanlingyuan

启动注入bean(注入两个MQ)

package intellif.custom.mqtt.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

/**
 * @author weijinzhi
 * @project IFaceServer-shanghaiputuo
 * @datetime 2019/7/22 13:34
 */
@Configuration
public class RabbitConfig {

    @Value("${rabbitmq.host}")
    private String mqHost;

    @Value("${rabbitmq.port}")
    private String mqPort;

    @Value("${rabbitmq.username}")
    private String mqUserName;

    @Value("${rabbitmq.password}")
    private String mqPassword;

    @Value("${rabbitmq.virtual-host}")
    private String mqVirtualHost;

    @Bean(name = "firstConnectionFactory")
    @Primary
    public ConnectionFactory firstConnectionFactory() {
        String[] host = mqHost.split(",");
        String[] port = mqPort.split(",");
        String[] username = mqUserName.split(",");
        String[] password = mqPassword.split(",");
        String[] virtualHost = mqVirtualHost.split(",");
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost(host[0]);
        connectionFactory.setPort(Integer.valueOf(port[0]));
        connectionFactory.setUsername(username[0]);
        connectionFactory.setPassword(password[0]);
        connectionFactory.setVirtualHost(virtualHost[0]);
        return connectionFactory;
    }

    @Bean(name = "firstRabbitTemplate")
    @Primary
    public RabbitTemplate firstRabbitTemplate(@Qualifier("firstConnectionFactory") ConnectionFactory connectionFactory) {
        RabbitTemplate firstRabbitTemplate = new RabbitTemplate(connectionFactory);
        firstRabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
        return firstRabbitTemplate;
    }

    @Bean(name = "secondConnectionFactory")
    public ConnectionFactory secondConnectionFactory() {
        String[] host = mqHost.split(",");
        String[] port = mqPort.split(",");
        String[] username = mqUserName.split(",");
        String[] password = mqPassword.split(",");
        String[] virtualHost = mqVirtualHost.split(",");
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost(host[1]);
        connectionFactory.setPort(Integer.valueOf(port[1]));
        connectionFactory.setUsername(username[1]);
        connectionFactory.setPassword(password[1]);
        connectionFactory.setVirtualHost(virtualHost[1]);
        return connectionFactory;
    }

    @Bean(name = "secondRabbitTemplate")
    public RabbitTemplate secondRabbitTemplate(@Qualifier("secondConnectionFactory") ConnectionFactory connectionFactory) {
        RabbitTemplate secondRabbitTemplate = new RabbitTemplate(connectionFactory);
        secondRabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
        return secondRabbitTemplate;
    }

    @Bean(name="firstFactory")
    public SimpleRabbitListenerContainerFactory firstFactory(@Qualifier("firstConnectionFactory") ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
//        factory.setMessageConverter(new Jackson2JsonMessageConverter());
        return factory;
    }

    @Bean(name="secondFactory")
    public SimpleRabbitListenerContainerFactory secondFactory(@Qualifier("secondConnectionFactory") ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
//        factory.setMessageConverter(new Jackson2JsonMessageConverter());
        return factory;
    }
}

监听

@Component
public class QueneConsemer {
    private final Logger logger = LogManager.getLogger(QueneConsemer.class);

    @RabbitListener(queues = "haowei.face", containerFactory = "firstFactory")
    public void process(Object content) {
        logger.info("接收处理队列A当中的消息: " + JSONObject.toJSONString(content));
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值