RabbitMq学习

RabbitMq学习

简介

不同MQ特点

1.ActiveMQ

​ ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。它是一个完全支持JMS规范的的消息中间件。丰富的API,多种集群架构模式让ActiveMQ在业界成为老牌的消息中间件,在中小型企业颇受欢迎!

2.Kafka

​ Kafka是LinkedIn开源的分布式发布-订阅消息系统,目前归属于Apache顶级项目。Kafka主要特点是基于Pull的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输。0.8版本开始支持复制,不支持事务,对消息的重复、丢失、错误没有严格要求,适合产生大量数据的互联网服务的数据收集业务。

3.RocketMQ

​ RocketMQ是阿里开源的消息中间件,它是纯Java开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点。RocketMQ思路起源于Kafka,但并不是Kafka的一个Copy,它对消息的可靠传输及事务性做了优化,目前在阿里集团被广泛应用于交易、充值、流计算、消息推送、日志流式处理、binglog分发等场景。

4.RabbitMQ

​ RabbitMQ是使用Erlang语言开发的开源消息队列系统,基于AMQP协议来实现。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。AMQP协议更多用在企业系统内对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。

RabbitMQ比Kafka可靠,Kafka更适合IO高吞吐的处理,一般应用在大数据日志处理或对实时性(少量延迟),可靠性(少量丢数据)要求稍低的场景使用,比如ELK日志收集。

安装步骤

自行百度,略

代码

pom依赖

<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.7.2</version>
</dependency>

代码

模板代码

public static Connection createFactory() throws IOException, TimeoutException {
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("127.0.0.1");
    connectionFactory.setPort(5672);
    connectionFactory.setUsername("admin");
    connectionFactory.setPassword("admin");
    connectionFactory.setVirtualHost("/admin");
    return connectionFactory.newConnection("testRabbitMq");
}
public static Channel createChannel() throws IOException, TimeoutException {
    Connection connection = RabbitMqConnectFactory.createFactory();
    return connection.createChannel();
}

模型

直连

// publish 
public void testMqPublish() throws IOException, TimeoutException {
    //"hello"->queue name ;是否持久化 ;是否独占 ;是否自动删除; 其他 
    AMQP.Queue.DeclareOk hello = this.channel.queueDeclare("hello", true, false, false, null);
    int consumerCount = hello.getConsumerCount();
    channel.basicPublish("","hello",null,"hello rabbitmq".getBytes());
    channel.close();
}
// consume 
public void testConsume() throws IOException, TimeoutException {
    // "hello"-> consume queue ; 是否自动ACK ;消费函数
    channel.basicConsume("hello",true,new DefaultConsumer(channel){
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            System.out.println(new String(body));
        }
    });
}

工作队列

// publish
public void test2MqPublish() throws IOException {
    AMQP.Queue.DeclareOk work = this.channel.queueDeclare("work", true, false, false, null);
    for (int i = 0; i < 20; i++) {
        channel.basicPublish("", "work", null, (i+"====>:我是消息").getBytes());
    }
}
public void test2Consume() throws IOException {
    //一次只接受一条未确认的消息
    channel.basicQos(1);
    // 关闭自动确认
    channel.basicConsume("work", false, new DefaultConsumer(channel) {
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            System.out.println("消费者1: " + new String(body));
            // 手动确认消息
            channel.basicAck(envelope.getDeliveryTag(),false);
        }
    });
}

public void test2AConsume() throws IOException {
    channel.basicConsume("work", false, new DefaultConsumer(channel) {
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            try {
                Thread.sleep(1000);   //处理消息比较慢 一秒处理一个消息
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("消费者2: " + new String(body));
        }
    });
}

广播模式+路由模式

注意此模式下为推送模式,即消息不会再publish时暂存至队列,队列由消费者创建并消费

// DIRECT路由 FANOUT 广播
// 路由:按照路由去找对应的通道以及队列 广播:所有通道以及队列都可以接受到消息
public void test2MqPublish() throws IOException, TimeoutException {
    channel.exchangeDeclare("exchange2", BuiltinExchangeType.DIRECT);
    for (int i = 0; i < 40; i++) {
        channel.basicPublish("exchange2", "", null, (i + "====>:我是消息").getBytes());
    }
    channel.close();
}
public void test2MqConsume() throws IOException {
    String queue = channel.queueDeclare().getQueue();
    channel.queueBind(queue, "exchange2", "");
    channel.basicQos(1);
    channel.basicConsume(queue, false, new DefaultConsumer(channel) {
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            System.out.println("消费者1:" + new String(body));
            channel.basicAck(envelope.getDeliveryTag(), false);
        }
    });
}
// 路由模式下无法接受到消息 广播模式下可以接受到
public void test2AMqConsume() throws IOException {
    String queue = channel.queueDeclare().getQueue();
    channel.queueBind(queue, "exchange2", "key");
    channel.basicQos(1);
    channel.basicConsume(queue, false, new DefaultConsumer(channel) {
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            System.out.println("消费者2:" + new String(body));
            channel.basicAck(envelope.getDeliveryTag(), false);
        }
    });
}

TOPIC订阅模式(路由模式+通配符)

例子略

# 统配符
* (star) can substitute for exactly one word.    匹配不多不少恰好1个词
# (hash) can substitute for zero or more words.  匹配一个或多个词
# 如:
audit.#    匹配audit.irs.corporate或者 audit.irs 等
audit.*   只能匹配 audit.irs

SpringBoot中使用rabbitmq

依赖

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

配置文件

spring:
  profiles:
    active: local
  application:
    name: test-learn
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: admin
    password: admin
    virtual-host: /admin

模型

普通模型略

广播模型

@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class TestLearnDemoApplication {
     @Test
    public void testFanout() throws InterruptedException {
        rabbitTemplate.convertAndSend("logs","","这是日志广播");
    }
}
// 自动触发
@RabbitListener(bindings = @QueueBinding(
    value = @Queue,
    exchange = @Exchange(name="logs",type = ExchangeTypes.FANOUT)
))
public void receive1(String message){
    System.out.println("message1 = " + message);
}

@RabbitListener(bindings = @QueueBinding(
    value = @Queue, //创建临时队列
    exchange = @Exchange(name="logs",type = ExchangeTypes.FANOUT)  //绑定交换机类型
))
public void receive2(String message){
    System.out.println("message2 = " + message);
}

路由模型

@Test
public void testFanout() throws InterruptedException {
    rabbitTemplate.convertAndSend("route","error","这是日志路由");
}
// 如果同时匹配多个路由,默认会消费多次
@RabbitListener(bindings = @QueueBinding(
    value = @Queue,
    key = {"error","info"},
    exchange = @Exchange(name = "route",type = ExchangeTypes.DIRECT)
))
public void routeModel1(String message){
    System.out.println("route message1 = " + message);
}

@RabbitListener(bindings = @QueueBinding(
    value = @Queue,
    key = {"error","warn"},
    exchange = @Exchange(name = "route",type = ExchangeTypes.DIRECT)
))
public void routeModel2(String message){
    System.out.println("route message2 = " + message);
}

订阅模型略(简单示例)

@RabbitListener(bindings = {
    @QueueBinding(
        value = @Queue,
        key = {"user.*"},//route is user.save.findAll
        exchange = @Exchange(type = "topic",name = "topics")
    )
})
public void receive1(String message){
    System.out.println("message1 = " + message);
}

后续补充

@RabbitListener(bindings = @QueueBinding(
    value = @Queue(name ="test-final" ,durable = "true",autoDelete = "false",exclusive = "true"),
    key = {"error","info"},
    exchange = @Exchange(name = "route",type = ExchangeTypes.DIRECT)
	),ackMode = "MANUAL")
public void routeModel1(String message, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, Channel channel) throws IOException {
    // RabbitMQ的ack机制中,第二个参数返回true,表示需要将这条消息投递给其他的消费者重新消费
    System.out.println(deliveryTag);
    // 第三个参数true,表示这个消息会重新进入队列
    channel.basicAck(deliveryTag,false);
    //        channel.basicNack(deliveryTag,false,true);
    System.out.println("route message1 = " + message);
}
posted @ 2021-03-10 13:21  WheelCode  阅读(119)  评论(0)    收藏  举报