7.21_单机rocketmq+springboot的练习

本文详细介绍了Apache RocketMQ的工作流程,包括nameserver、broker、生产者和消费者的交互。阐述了消息发送的同步、异步、单向和批量方式,以及顺序消息和事务消息的实现。此外,还提供了生产者和消费者的具体Java代码示例,展示了如何进行消息的发送和接收。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

优势

在这里插入图片描述

劣势

在这里插入图片描述

概念

生产者——broker——消费者
broker就是部署了mq的机器就叫broker

工作流程

在这里插入图片描述
· 不论是 broker还是 生产者还是消费者 ,在启动的时候都需要把自己注册到 nameserver上,这个nameserver其实就是一个数据管理中心,只不过记录的都是一些ip等重要数据,具体的消息还是存在broker中的
· 当生产者要往broker中发消息的时候,就会首先问nameserver,应该往哪个broker中发,nameserver则会返回一个broker的IP,此时生产者才可以真正的去发消息
· 消费者也是,消费要要去监听某一个broker,也会去问nameserver,ip是多少,nameserver返回一个ip,此时消费者才能真正的去监听
· broker和消费者之间建立长连接,每当有消息来的时候,broker就会推送给消费者
· 心跳机制,每隔一段时间发送消息给nameserver,让nameserver知道存货与否,不存活的话则踢掉这个ip
· topic和tag的关系,类似于 裤子长裤、短裤的关系

安装

所以说 ,准备工作是,需要 准备
nameserver
broker
生产者
消费者

配置文件

rocketmq.name-server=192.168.222.128:9876
rocketmq.producer.group=group1

这里要替换成自己的 rocketmq 所在机器的 ip 和端口
如果是在自己windows上安装的rocketmq,那基本上就是localhost:9876
如果是在linux上安装的rocketmq,那需要通过 ifconfig 查看自己的ip ,同时关闭linux的防火墙

这里的rocketmq.producer.group=group1 就是说生产者的组名是 group1

生产者

package com.example.controller;

import com.example.controller.domain.User;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("demo")
public class MyController {

    @Autowired
    private RocketMQTemplate template;	//直接拿

/*简单的发送*/
    @GetMapping("/send")
    public String send() {

        /*同步发送————》发送给topic2,内容是syncSend*/
        SendResult sendResult = template.syncSend("topic2", "syncSend");
        System.out.println(sendResult);

        /*异步发送————》需要写回调函数,也就是这个匿名内部类,onSuccess就是发送成功的话xxxxx,onException就是发送失败的话xxxx*/
        template.asyncSend("topic2", "asyncSend", new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.println(sendResult);
            }

            @Override
            public void onException(Throwable throwable) {
                System.out.println(throwable);
            }
        });

        /*单向发送*/
        template.sendOneWay("topic2", "sendOneWay");

        /*批量发送,只能同步发送————》template.syncSend("topic2", stringList, 3000);这里的3000指的是超时时间*/
        List<String> stringList = new ArrayList<>();
        stringList.add("syncSendBatch1");
        stringList.add("syncSendBatch2");
        stringList.add("syncSendBatch3");
        stringList.add("syncSendBatch4");
        template.syncSend("topic2", stringList, 3000);

        return "success";
    }


/*顺序发送*/
    @GetMapping("/sendOrder")
    public String sendOrder() {
    
	/*
	public class User{
    	private Integer id;
    	private String name;
	*/
		/*现在的场景是,想要id一样的都在一个队列中*/
        List<User> users = new ArrayList<>();
        
		users.add(new User(1, "zhangsan1"));
        users.add(new User(2, "wangwu1"));
        users.add(new User(3, "lisi1"));

        users.add(new User(1, "zhangsan2"));
        users.add(new User(2, "wangwu2"));
        users.add(new User(3, "lisi2"));

        users.add(new User(1, "zhangsan3"));
        users.add(new User(2, "wangwu3"));
        users.add(new User(3, "lisi3"));

        users.add(new User(1, "zhangsan4"));
        users.add(new User(2, "wangwu4"));
        users.add(new User(3, "lisi4"));


        /*不是顺序发送*/
        for (User user : users) {
            template.asyncSend("topic2", user.toString(),
                    new SendCallback() {
                        @Override
                        public void onSuccess(SendResult sendResult) {
                            System.out.println("不是顺序发送--"+user.getName()+"的信息被发送到了队列——》"+sendResult.getMessageQueue().getQueueId());
                        }

                        @Override
                        public void onException(Throwable e) {
                            System.out.println(e);
                        }
                    });
        }

        /*
        template.asyncSendOrderly(主题,消息内容,关键字,回调函数);
        关键字是去哪个队列的关键,如果关键字一样,那么就代表着这个消息会被发送到同一个队列中去,
        原理应该是:用关键字去取 hash 值然后再对队列的数量取模,所以一样的关键字就能够 被送往同一个队列
        */
        
        
        /*顺序发送*/
        for (User user : users) {
            template.asyncSendOrderly(
                    "topic2",
                    user.toString(),
                    String.valueOf(user.getId()),//关键字
                    new SendCallback() {
                        @Override
                        public void onSuccess(SendResult sendResult) {
                            System.out.println("顺序发送--"+user.getName()+"的信息被发送到了队列——》"+sendResult.getMessageQueue().getQueueId());
                        }

                        @Override
                        public void onException(Throwable e) {
                            System.out.println(e);
                        }
                    });
        }

        return "success";
    }


    @RequestMapping("/sendInTran")
    public void sendInTran(){
        User user=new User(1,"zhangSan");

        Message message=MessageBuilder.withPayload(user.toString()).build();

        template.sendMessageInTransaction("topic2",message,null);

        System.out.println("fa song cheng gong");

    }
}

消费者

package com.example.consumer;

import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;



@Component
@RocketMQMessageListener(topic = "topic2",consumerGroup = "group1")//监听topic2的所有tag,消费者组是group1,这里和生产者组是两套体系,不要误认为生产者是group1,消费者这里就必须是group1,他俩不相关
//@RocketMQMessageListener(topic = "topic1",consumerGroup = "group1",
//                         selectorExpression = "tag1||tag2",
//                         messageModel = MessageModel.BROADCASTING )
public class Consumer implements RocketMQListener<String> {

    @Override
    public void onMessage(String s) {
        System.out.println("接收到消息--》"+s);
    }
}

事务类

package com.example.consumer.listener;

import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
import org.springframework.messaging.Message;

@RocketMQTransactionListener//记得加这个注解,在之前的版本可以设置具体的去监听哪一个topic,但是新的版本中删除了这个设置
public class MyListener implements RocketMQLocalTransactionListener {
    /*事务执行*/
    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message message, Object o) {
        /*一般来说就是 把要发送的消息存储到数据库,做一次备份*/
        /*就比如一个  insert()
        * 这里只做演示*/

        int i=100/20;
        if (i<2){
            /*COMMIT 就是通知 broker可以把这条数据放到队列里面了*/
            System.out.println("message是"+message);
            System.out.println("RocketMQLocalTransactionState.COMMIT --- 提交");
            return RocketMQLocalTransactionState.COMMIT;
        }else {
            
            System.out.println("message是"+message);
            System.out.println("RocketMQLocalTransactionState.UNKNOWN --- 未知");
            return RocketMQLocalTransactionState.UNKNOWN;
            /*UNKNOWN 就是不知道对数据库的操作有没有成功 就会执行下面的事务补偿*/
            /*ROLLBACK 就是通知 broker 把消息不要放到队列里面了,直接丢弃*/
        }

    }

    /*事务补偿*/
    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message message) {

        /*那么就是说,生产者会去数据库中查询,刚才的插入操作到底成功没有 也就是
        * select 操作 有的话就返回 commit
        * 没有的话就是 rollback
        * 如果事务补偿里面还是 Unkown的话 就需要 人工干预 了*/


        if (true){
            System.out.println("事务补偿---message是"+message);
            System.out.println("RocketMQLocalTransactionState.COMMIT --- 事务补偿-- 提交");
            return RocketMQLocalTransactionState.COMMIT;
        }else {
            System.out.println("message是"+message);
            System.out.println("RocketMQLocalTransactionState.ROLLBACK --- 事务补偿-- 回滚");
            return RocketMQLocalTransactionState.ROLLBACK;
        }
    }
}

这里有几个需要注意的点:

消费者加了一个 Component 注解,意思是在程序启动的时候就随之启动了,因为消费者会一直监听某一个topic,而生产者我们采用调用的模式,调用一次发送一次消息

启动查看结果

1. 简单的发送

在这里插入图片描述

结果
接收到消息--》convertAndSend
SendResult [sendStatus=SEND_OK, msgId=7F000001468018B4AAC26924AE060019, offsetMsgId=C0A8DE8000002A9F000000000007AD7A, messageQueue=MessageQueue [topic=topic2, brokerName=localhost.localdomain, queueId=0], queueOffset=15]
接收到消息--》syncSend
SendResult [sendStatus=SEND_OK, msgId=7F000001468018B4AAC26924AE09001A, offsetMsgId=C0A8DE8000002A9F000000000007AE88, messageQueue=MessageQueue [topic=topic2, brokerName=localhost.localdomain, queueId=0], queueOffset=16]
接收到消息--》asyncSend
接收到消息--》sendOneWay
接收到消息--["syncSendBatch1","syncSendBatch2","syncSendBatch3","syncSendBatch4"]

2.顺序发送

在这里插入图片描述

结果——》可以看到zhangsan的信息在不是顺序发送的时候,可能会被放到各个队列,顺序发送的话,zhangsan的信息只会在一个队列中。
不是顺序发送--zhangsan1的信息被发送到了队列——》3
不是顺序发送--wangwu1的信息被发送到了队列——》2
不是顺序发送--lisi1的信息被发送到了队列——》1
不是顺序发送--zhangsan2的信息被发送到了队列——》3
不是顺序发送--wangwu2的信息被发送到了队列——》0
不是顺序发送--lisi2的信息被发送到了队列——》1
不是顺序发送--zhangsan3的信息被发送到了队列——》0
不是顺序发送--wangwu3的信息被发送到了队列——》1
不是顺序发送--lisi3的信息被发送到了队列——》3
不是顺序发送--zhangsan4的信息被发送到了队列——》0
不是顺序发送--wangwu4的信息被发送到了队列——》3
不是顺序发送--lisi4的信息被发送到了队列——》0
顺序发送--zhangsan1的信息被发送到了队列——》1
顺序发送--wangwu1的信息被发送到了队列——》2
顺序发送--lisi1的信息被发送到了队列——》3
顺序发送--zhangsan2的信息被发送到了队列——》1
顺序发送--wangwu2的信息被发送到了队列——》2
顺序发送--lisi2的信息被发送到了队列——》3
顺序发送--zhangsan3的信息被发送到了队列——》1
顺序发送--wangwu3的信息被发送到了队列——》2
顺序发送--lisi3的信息被发送到了队列——》3
顺序发送--zhangsan4的信息被发送到了队列——》1
顺序发送--wangwu4的信息被发送到了队列——》2
顺序发送--lisi4的信息被发送到了队列——》3
接收到消息--》User{id=1, name='zhangsan1'}
接收到消息--》User{id=3, name='lisi3'}
接收到消息--》User{id=2, name='wangwu4'}
接收到消息--》User{id=2, name='wangwu3'}
接收到消息--》User{id=3, name='lisi2'}
接收到消息--》User{id=2, name='wangwu2'}
接收到消息--》User{id=3, name='lisi4'}
接收到消息--》User{id=3, name='lisi2'}
接收到消息--》User{id=2, name='wangwu1'}
接收到消息--》User{id=1, name='zhangsan1'}
接收到消息--》User{id=1, name='zhangsan2'}
接收到消息--》User{id=2, name='wangwu1'}
接收到消息--》User{id=2, name='wangwu2'}
接收到消息--》User{id=1, name='zhangsan3'}
接收到消息--》User{id=1, name='zhangsan2'}
接收到消息--》User{id=1, name='zhangsan4'}
接收到消息--》User{id=3, name='lisi4'}
接收到消息--》User{id=1, name='zhangsan3'}
接收到消息--》User{id=1, name='zhangsan4'}
接收到消息--》User{id=2, name='wangwu3'}
接收到消息--》User{id=3, name='lisi1'}
接收到消息--》User{id=3, name='lisi3'}
接收到消息--》User{id=2, name='wangwu4'}
接收到消息--》User{id=3, name='lisi1'}

3.事务

在这里插入图片描述

结果——》事务补偿机制是需要间隔一段时间的,事务执行返回Unknow之后,并不是马上执行事务补偿,而是等一会再去执行事务补偿。

在这里插入图片描述

原始的发送和消费模式

依赖
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>4.8.0</version>
        </dependency>

使用这一个就可以

在这里插入图片描述

消费者

package com.hyl.producer;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListener;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;

public class Consumer1 {
    public static void main(String[] args) throws Exception {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
        consumer.setNamesrvAddr("localhost:9876");
        consumer.subscribe("topic1","*");
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                for (MessageExt messageExt : list) {
                    System.out.println("messageExt = " + messageExt);
                    System.out.println("new String(messageExt.getBody()) = " + new String(messageExt.getBody()));
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
        System.out.printf("consumer start");
    }
}

生产者

package com.hyl.producer;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import java.time.LocalDateTime;

public class Producer1 {
    public static void main(String[] args) throws Exception {
//        谁来发?
//        发给谁?
//        怎么发?
//        发什么?
//        发的结果是什么?
//        收尾
        DefaultMQProducer produccer1 = new DefaultMQProducer("group1");
        produccer1.setNamesrvAddr("localhost:9876");
        produccer1.start();
        SendResult sendResult = null;
        for (int i = 0; i < 100; i++) {
            sendResult = produccer1.send(new Message("topic1", "tag1", (LocalDateTime.now() + "_____helloworld").getBytes()));
        }
        System.out.println("sendResult = " + sendResult);
        produccer1.shutdown();
    }
}

如果生产者发送到的地方是 topic1 那么消费者需要监听topic1才行
如果有两个消费者 同属group1 而且都监听topic1 那么生产者发送10个消息,这两个消费者各拿五个
如果生产者生产10个消息,但是消费者属于不同的组,也就是一个属于 group1 一个属于group2,虽然都监听 topic1,但是这两个消费者都会拿到10条数据 , 这也就是 广播模式
那么如果说 两个消费者就是要在一个组里面,并且就是要都拿到10个消息,怎么弄?
可以 consumer.setMessageModel(MessageModel.BROADCASTING); 即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值