MQ学习笔记

本文详细介绍了MQ的几种常见实现,包括ActiveMQ的P2P和P/S模式,RabbitMQ的exchange机制及Spring Boot整合,还涉及Kafka的高性能特性和RocketMQ的分布式事务能力。通过对各种MQ的特性讲解,展示了它们在不同场景下的适用性。

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

ActiveMQ

P2P,P/S
jms集成
默认存储 kahadb
P2P:点对点,在这里插入图片描述

P/S : create topic 订阅者都可以收到消息

主备模式, master -> slave(高可用)使用zk协调 配置network
在这里插入图片描述
集群:
master1和master2是一套组合,组成一个集群。 共享一个队列

在这里插入图片描述
共享队列,

RabbitMQ

exchange
在这里插入图片描述
X是作为一个路由器,例如 把 a c 给 c1 ,d b 给c2
队列binding到路由器

如果多个c监听一个mq,将会自动负载均衡。

镜像:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190708165313105.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3MjQzNDcy,size_16,color_FFFFFF,t_70
shovel远程通信,远程复制: 类似于Oracle异地备份
在这里插入图片描述

clustor集群模式
在这里插入图片描述

信息从生产到消费的过程
在这里插入图片描述

Spring Boot整合RabbitMQ

依赖如下

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

接收方使用@RabbitListener注解,后面跟队列名(前提是有这个队列,没有会报找不队列异常)

 //1.@RabbitListener(queues = "MyQue")
 //2.改为 @RabbitListener(queuesToDeclare = @Queue("MyQue")) 如果没有找到则会创建该队列
 //3. 如下会自动创建队列,且会绑定路由
  @RabbitListener(bindings =  @QueueBinding(
            value = @Queue("MyQue"),
            exchange = @Exchange("MyExchange")
    ))
    public void process(String message){
        System.out.println("接收到"+message);
    }
// 绑定路由后设置只接收路由中key为food的消息,并创建MyQueFood队列
// 
@RabbitListener(bindings =  @QueueBinding(
            value = @Queue("MyQueFood"),
            key = "food", // 只接受路由中key为food的消息
            exchange = @Exchange("MyExchangeFood")
    ))
    //自动创建,队列与exchange绑定
    public void processFood(String message){
        System.out.println("MyQueFood接收到"+message);
    }

发送方代码示例

@RestController
public class RabbitMQTest {
    @Autowired
    private AmqpTemplate amqpTemplate;

    @GetMapping("/send")
    public void send(){
        amqpTemplate.convertAndSend("MyQue","new MMMMMM!!!");
    }
    @GetMapping("/sendFood")
    public String sendFood() {
        // 参数1 路由,参数2 key,参数3 消息
        amqpTemplate.convertAndSend("MyExchangeFood","food","new MMMMMM!!!");
        return "发送成功";
    }
}

Spring Cloud Stream 封装

Spring Cloud Stream 就是应用与MQ的粘合剂,就是封装了MQ,目前使用的是RabbitMQ,不知道以后会不会支持其他。

代码示例:
先导依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
            <version>2.1.2.RELEASE</version>
        </dependency>

发送方

public interface StreamClient {

    final static String INPUT = "streamInput";
    final static String OUTPUT = "streamOutput";

    @Input(INPUT)
    public SubscribableChannel input();
    
	//注意input和output不能绑定同一个队列,在sprint boot 2.0.0.M3之后必须绑定不同的队列。
    @Output(OUTPUT)
    public MessageChannel output();

}

接收方

@Slf4j
@Component
@EnableBinding(StreamClient.class)
public class  StreamReceiver {

    @StreamListener(StreamClient.INPUT)
    public void process(Object message){
        log.info("StreamListener: {}",message);
    }

    @StreamListener(StreamClient.OUTPUT)
    public void out(Object message){
        log.info("StreamListener: {}",message);
    }
}

写好了这样两个Bean,注入。

	@Resource
    private StreamClient streamClient;

调用stream的方式

	@GetMapping("/sendMessageByStream")
    public void sendMsgByStream(){
        streamClient.output().send(MessageBuilder.withPayload(new Date()+"now").build());
    }

如果一个服务部署了多个实例的话,为了保证一条消息只被一个服务消费,可以进行以下配置

spring:
  application:
    name: eureka
  cloud:
    stream:
      bindings:
        MyMessage:
          group: food
#          分组,防止多个实例创建多个队列,每个队列都消费消息。
#          分组之后在这个组内,多个实例监听一个队列,一条消息只会被一个实例消费

@SendTo 注解,可以在函数执行完之后,将返回值作为消息发送到另一个队列。注意:在sprint boot 2.0.0.M3之后函数必须有返回值,否则会报以下错误。
在这里插入图片描述

@Slf4j
@Component
@EnableBinding(StreamClient.class)
public class  StreamReceiver {

    @StreamListener(StreamClient.INPUT)
    @SendTo(INPUT2)
    // 注意,默认使用json格式,传Object形式会报错
    public JSONObject process(JSONObject message){
        log.info("StreamListener: input收到了{}",message.toJSONString());
        return message;
    }

    @StreamListener(StreamClient.OUTPUT)
    public void out(Object message){
        log.info("StreamListener: output收到了{}",message.toString());
    }

    @StreamListener(INPUT2)
    public void input2(Object message){
        log.info("StreamListener: input2收到了{}",message.toString());
    }

}

RabbitMQ+websocket实现

stomp介绍

后端代码

public void kickOutByAccount(String account){
        String destination = "/topic/layout_xx_" + account;
        String websocketContent = "您的账号被管理员强制下线";
        JSONObject msg = new JSONObject();
        msg.put("title", "系统通知");
        msg.put("content", websocketContent);
        msg.put("type", "info");
        try {
            stompRabbitMqClient.connectAndSend(destination, msg);
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.yunzainfo.cloud.common.config;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.messaging.simp.stomp.StompHeaders;
import org.springframework.web.socket.WebSocketHttpHeaders;
import org.springframework.web.socket.client.WebSocketClient;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;
import org.springframework.web.socket.sockjs.client.Transport;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;

public class StompRabbitMqClient {
    private static final String URL_TEMPLATE = "http://%s:%s/stomp";
    @Value("${rabbit.stomp.login:guest}")
    private String login;
    @Value("${rabbit.stomp.password:guest}")
    private String passCode;
    @Value("${rabbit.stomp.url}")
    private String url;

    public StompRabbitMqClient() {
    }

    public <T> void connectAndSend(String dest, T toSend) throws ExecutionException, InterruptedException {
        WebSocketClient client = new StandardWebSocketClient();
        List<Transport> transports = new ArrayList(1);
        transports.add(new WebSocketTransport(client));
        WebSocketStompClient stompClient = new WebSocketStompClient(client);
        stompClient.setMessageConverter(new StringMessageConverter());
        CustomStompSessionHandler sessionHandler = new CustomStompSessionHandler(dest, toSend);
        WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
        headers.setSecWebSocketProtocol("13");
        StompHeaders sHeaders = new StompHeaders();
        sHeaders.add("login", this.login);
        sHeaders.add("passcode", this.passCode);
        stompClient.connect(this.url, headers, sHeaders, sessionHandler, new Object[0]);
    }
}

stomp配置

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketStompConfig implements WebSocketMessageBrokerConfigurer {

  @Override
  public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/ws/stomp").setAllowedOrigins("*").withSockJS();
  }

  @Override
  public void configureMessageBroker(MessageBrokerRegistry registry) {
      //订阅广播 Broker(消息代理)名称(前缀)
      // Enables a simple in-memory broker
      registry.enableSimpleBroker("/topic");
      //全局使用的订阅前缀(客户端订阅路径上会体现出来)
      registry.setApplicationDestinationPrefixes("/app/");
      //点对点使用的订阅前缀(客户端订阅路径上会体现出来),不设置的话,默认也是/user/
      registry.setUserDestinationPrefix("/user/");

  }

}

前段代码:Angular

const baseClient: BaseClient = {
  gateway: `${environment.gateway}`,
  ignores: [`${environment.gateway}/cas-proxy/app/validate_full?callback=${window.location.href}`],
  stomp: {
    brokerURL: `${environment.stomp_server_url}`,
    connectHeaders: {login: 'guest', passcode: 'guest'},
    heartbeatIncoming: 5,
    heartbeatOutgoing: 20000,
    reconnectDelay: 200
  },
  layout: {
    show_sider: true,
    show_header: true,
  },
  systemcode: 'message-center-3',
  type: BaseClientType.CAS_SYSTEM,
  dev: false
};
<nz-modal nzTitle="系统通知" nzContent="您的账号被管理员强制下线。"
[(nzVisible)]="visible"
(nzOnCancel)="logOut()"
(nzOnOk)="logOut()"></nz-modal>
ngOnInit() {
    this.getUserInfo().subscribe(res => {
      this.account = res.data.account;
      console.log('获取账号成功'+this.account);
      this.rxStompService.watch('/topic/layout_xx_'+this.account).subscribe(msg =>{
          this.visible = true;
          this.msgList.push(msg.body);
          this.logOut();
      },error1 => {
        console.log('stomp error');
        this.logOut();
      });
    },error1 => {
      console.log('error auth');
    });

  }

Kafka

topic
空中接力,在内存中操作,不走硬盘,所以性能强,但数据容易丢失,适合做推荐,数据分析之类的对数据完整性要求不太严格的功能。

RocketMQ

能承受阿里双十一考验的MQ,分布式事务(两阶段提交)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值