消息中间件message queue 消息队列

在单体架构中,所有的代码、模块都放在一份代码中,如果其中一个模块需要升级,哪怕只修改了一点点,整个系统也要一起升级,这样耦合度太高,同时代码管理也比较难。

image-20211102114903351

image-20211102114932238

到了分布式系统架构,这里现在有前台系统、订单系统、会员系统,这三个系统分别独立部署,如果需要升级某个系统,其他系统不需要进行调整。

有些请求不是一个系统就能完成的,比如当在一个页面上面同时想看会员积分和下单的信息,通过一次请求,这个请求会涉及多个系统。这种多个系统协作处理一个请求的系统,就被看作是分布式系统,当某个系统压力大时,可以对单个系统进行扩展。

这时就会用到 RPC 技术,调用远程接口的方式实现系统间的互相调用。 但是这种方式的耦合度比较高,为了实现更强的拓展性架构,所以在分布式系统中引入了消息中间件,通过消息中间件解决系统的耦合。

image-20211102115001326

1.什么是消息队列

消息是指数据,数据的格式通常都使用json,消息中间件的作用就是系统解耦、将同步流程异步化,使用场景:抢购秒杀、订单生成预减库存积分优惠券、流量削峰等等
常用的消息中间件有

  • rabbitMq(几万-10万之间) erlang语言天然支持高并发与集群,提供了事务机制,保证消息生产和消费的安全性
  • avtiveMq (几千-几万) apache下比较老的一款消息中间件产品
  • rocketMq (10万以上) 阿里开源到apache一款消息中间件产品
  • Kafka (百万级) 大规模写入的效率很高,百万qps,主要用于大数据当中的日志收集

不使用消息队列的问题

image-20211102114027460

消息队列的工作原理:防止出现数据不同步,和系统的阻塞

image-20211102114126864

2.在linux上安装rabbitMq

rabbitMq的安装(centos7)
//这种方式 自动

  1. 下载erlang:
    wget http://www.rabbitmq.com/releases/erlang/erlang-18.3-1.el7.centos.x86_64.rpm
    安装erlang:
    rpm -ivh erlang-18.3-1.el7.centos.x86_64.rpm
    下载socat:
    wget http://repo.iotti.biz/CentOS/7/x86_64/socat-1.7.3.2-5.el7.lux.x86_64.rpm
    安装socat:
    rpm -ivh socat-1.7.3.2-5.el7.lux.x86_64.rpm
    下载rabbitmq:
    wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.5/rabbitmq-server-3.6.5-1.noarch.rpm
  2. 安装rabbitmq:
    rpm -ivh rabbitmq-server-3.6.5-1.noarch.rpm
  3. 设置开机启动:
    chkconfig rabbitmq-server on
  4. 开启rabbitmq服务:
    systemctl start rabbitmq-server
    启用web管理插件:
    rabbitmq-plugins enable rabbitmq_management
    [{rabbit, [{loopback_users, []}]}].
  5. 设置远程访问登录:
    cd /etc/rabbitmq
    touch rabbitmq.config
    vi rabbitmq.config
    写入[{rabbit, [{loopback_users, []}]}].
  6. 重启rabbitMq
    systemctl restart rabbitmq-server
  7. 通过浏览器访问 rabbitMq
    ip地址:15672
    使用guest guest来登录
    添加一个用户 admin 密码是admin tags administrator
    设置访问的虚拟路径 /

3.rabbitmq整体简介

https://www.jianshu.com/p/569b8512b14f

优势:RabbitMq的应用场景比较广泛,包括日志管理、应用解耦、流量削峰、异步处理。

其中都是利用了RabbitMq的异步性,响应机制等特性。

rabbitmq采用的amqp协议,该协议有spring的集成。

元素介绍

1、virtual host:为虚拟主机,可以理解为数据库,在定义的库中进行表的创建,进行后续操作,是基础性元素

2、channel:管道,是声明交换机、声明队列、发布消息、消息响应、交换机与队列绑定的载体

3、exchange:交换机,用于传递消息,队列可以绑定到交换机上从而获取转发的消息

4、queue:队列,消息的载体,可以持久化,可以通过不同的响应机制进行状态的标识及补偿消费

5、provider:生产者,消息的提供者,产生消息

6、consumer:消费者,消息的终端,进行消费的最后处理

工作模式

  1. simple模型(简单模式):简单模式,顾名思义,很简单,一个provider,一个queue,一个消费者 ;1对1

webp

  1. work模式

    1. 轮询模式(round):1个provider,1个队列,多个消费者,消息是一个消费者处理一条,权重相同,你一个我一个

      webp

    2. 公平模式(fair):根据消费者的消费能力,消费完成后,就发下一条消息,多劳多得,但是需要告知mq服务器消费完成,所以要开启消息应答,这里开启自动应答模式

    webp

  2. 订阅模式:订阅模式中,出现了exchange交换机,在消息由生产者发送到消费者的过程中,增加了交换机,可以理解为路由器,作用是判断哪个队列满足条件然后发到哪个队列上

    1. fanout:只要绑定到交换机上的队列都进行发放消息,强调雨露均沾

    webp

    1. direct:需要有rountingKey进行匹配,exchange会定义一个rountingKey,同样消费者也需要定义一个,只有二者相同,才会消费

    2. topic:加入了通配符的rounting绑定,其中*为代替.之间的内容,#代表所有内容

事务机制

生产者发出消息后,需要确认mq服务器是否有收到该消息,两种方式实现

  1. AMQP实现事务机制

    txSelect:用户将当前channel设置成transation模式

    txCommit:用于事务提交

    txRollback:用于事务回滚

  2. confirm模式,进行消息确认

    通过指派唯一的ID进行发送及监听回传

    channel.confirmSelect()进行开启

4. rabbitmq的工作示例

<!--导包-->
    <dependencies>
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.1.2</version>
        </dependency>
    </dependencies>
//消息生产者
public class BasicProductor {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置连接的服务器地址,不设置表示使用本机地址
        factory.setHost("192.168.192.3");
        //设置连接的端口号
        factory.setPort(5672);
        //设置连接的虚拟主机(用来表示权限)
        factory.setVirtualHost("/");
        //建立连接使用的用户名和密码
        factory.setUsername("admin");
        factory.setPassword("admin");
        //建立连接
        Connection connection = factory.newConnection();
        //一切关于mq的操作都是通过channel操作的. 建立通道  Work queues默认模式
        Channel channel = connection.createChannel();
        //队列名称,是否持久化,是否排他(仅对首次声明它的连接可见),是否自动删除,设置队列的一些参数
        channel.queueDeclare("hello", true, false, false, null);
        String json = "{ordersID:xxx}";
        //发布消息,交换机(没有就是空),交换密码(如果没有使用交换机),    ,发送的信息内容
        channel.basicPublish("", "hello", MessageProperties.PERSISTENT_TEXT_PLAIN, json.getBytes());
        channel.close();
        connection.close();
    }
}
//消息消费者
public class BasicConsumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置连接的服务器地址,不设置表示使用本机地址
        factory.setHost("192.168.192.3");
        //设置连接的端口号
        factory.setPort(5672);
        //设置连接的虚拟主机(用来表示权限)
        factory.setVirtualHost("/");
        //建立连接使用的用户名和密码
        factory.setUsername("admin");
        factory.setPassword("admin");
        //建立连接
        Connection connection = factory.newConnection();
        //创建数据管道
        Channel channel=connection.createChannel();
        String queueName="hello";
        //创建消费者,重写处理函数
        Consumer consumer=new DefaultConsumer(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);
            }
        };
        //队列名称,消费者
        channel.basicConsume(queueName,consumer);
    }
}
//使用交换机 路由模式 direct
//消费者
public class ExchangeConsumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory=new ConnectionFactory();
        factory.setHost("192.168.192.3");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("admin");
        factory.setPassword("admin");
        Connection connection=factory.newConnection();
        //建立通道
        Channel channel=connection.createChannel();
        //自定义队列
        channel.queueDeclare("q1", true, false, false, null);
        //存储数据的队列,交换机名称,交换密码
        channel.queueBind("q1", "test", "zhimakaimen");
        Consumer consumer=new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(new String(body,"utf-8"));
            }
        };
        channel.basicConsume("q1", consumer);
    }
}
//生产者
public class ExchangeProductor {
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory=new ConnectionFactory();
        factory.setHost("192.168.192.3");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("admin");
        factory.setPassword("admin");
        Connection connection=factory.newConnection();
        //建立通道
        Channel channel=connection.createChannel();
        //假如当前mqServer没有交换机,这里需要创建一个交换机
        //交换机名称,交换机类型
        channel.exchangeDeclare("test","direct");
        String json = "hello 2";
        //direct模式下不需要创建队列,直接发布,由消费者创建队列接收
        //交换机名称,交换密码             ,交换信息
        channel.basicPublish("test", "zhimakaimen", MessageProperties.PERSISTENT_TEXT_PLAIN, json.getBytes());
        channel.close();
        connection.close();
    }
}

posted @ 2021-11-06 13:06  2333gyh  阅读(214)  评论(0编辑  收藏  举报