SpringBoot集成NSQ消息队列(最最最简单的使用)

SpringBoot集成NSQ消息队列

今天,和一位朋友的谈话让我有很多感触! 朋友说,自己刚开始有点心高气傲,现在回想起来,有一些后悔! 我思虑良久,想起来2022年4月,大专大二的自己,在这口罩事件严重的时候,就要被学校赶出来了,就要面临实习!那时候的自己,没有想过校招,也因为家庭困难,很怕因为不恰当的决策隔离产生昂贵非要,就再天津就近找了一个乱七八糟的活!现在去向,如果自己走校招,那么,自己现在可能会有更好的前途! 可是,仔细回想,在那种情况下,能就业就已经很不错,大厂都在裁员,经济形式一片萎靡,何处高就! 所以,仔细去思考,每一个时间点的每一个决策,都是正确的,因为那个时候的我们,从经验和环境各方面因素而言,我们都只能去走这一步棋,没有对错!即使再次在那个时间点,那个环境,我们再做一次选择,依然如是! 所以,和自己和解吧,和过去和解,面朝大海,春暖花开!

文章概述

nsq.io/deployment/... NSQ下载地址

nsq.io/overview/de... NSQ官方文档

nsqio.cn/design.html NSQ中文文档

NSQ(NSQ Messaging System)是一种实时分布式消息传递平台,由Bitly公司开发。它旨在解决大规模系统中的消息传递和处理问题,特别适用于处理大量数据的微服务架构。

  1. 分布式架构:NSQ采用分布式架构,允许通过多个节点进行消息的生产和消费。这使得系统具有高可用性和容错性。

  2. 去中心化设计:NSQ没有单点故障,消息传递不依赖于中央服务器,而是通过消息队列将消息传递到消费者。

  3. 实时消息传递:NSQ被设计用于实时消息传递,支持高吞吐量和低延迟的消息传递,适用于需要即时响应的应用场景。

  4. 水平扩展:NSQ的节点可以水平扩展,可以根据需求动态地添加或删除节点,以适应不断增长的消息流量。

  5. 消息顺序保证:NSQ保证消息在同一个主题(topic)和通道(channel)中的顺序传递,但不保证不同通道之间的消息顺序。

  6. 容错性:NSQ具有消息重试和失败处理机制,能够处理消息传递过程中的故障,确保消息不会丢失或重复传递。

  7. 监控和管理:NSQ提供了监控和管理工具,可以实时查看消息队列的状态、吞吐量和性能指标,以便进行调优和故障排查。

  8. 支持多种客户端库:NSQ支持多种编程语言的客户端库,包括Go、Python、Java等,使得开发者可以方便地集成NSQ到他们的应用程序中。

NSQ、RabbitMQ、ActiveMQ和Kafka是消息传递系统,每个系统都有自己的特点和优势,这里,我们再对比一下几大流行消息队列做一个大致分析

  1. NSQ vs RabbitMQ

    • 架构差异:NSQ是去中心化的,没有单点故障,而RabbitMQ是集中式的,依赖于中央服务器。这使得NSQ更适合于分布式和高可用性需求。
    • 消息传递保证:RabbitMQ支持丰富的消息传递模式,如点对点、发布/订阅等,而NSQ专注于实时消息传递,不支持像RabbitMQ那样丰富的消息传递模式。
    • 性能差异:NSQ在某些情况下可能具有更好的性能,特别是在高吞吐量和低延迟的场景下,但RabbitMQ具有更多的功能和灵活性。
  2. NSQ vs ActiveMQ

    • 架构差异:NSQ是去中心化的,而ActiveMQ是集中式的,这意味着NSQ更适合于构建分布式系统和微服务架构。
    • 消息处理保证:ActiveMQ支持事务和持久化等高级消息处理特性,而NSQ主要关注于实时消息传递,不提供类似的高级特性。
    • 语言支持:ActiveMQ支持更多的编程语言和协议,如Java、C++、.NET等,而NSQ主要使用Go语言进行开发,并提供了一些其他语言的客户端库。
  3. NSQ vs Kafka

    • 数据处理模型:Kafka是一个分布式事件流平台,支持持久性和高吞吐量的事件流处理。相比之下,NSQ更专注于实时消息传递,适用于即时响应的场景。
    • 消息保证:Kafka提供了严格的消息顺序保证和持久化存储,适合于需要确保消息顺序和可靠性的场景。而NSQ则更注重于高性能和低延迟的消息传递,并不提供严格的消息顺序保证。
    • 存储和消费者管理:Kafka通过分区和复制来实现高可用性和水平扩展,同时支持消费者组和消息回溯等特性。相比之下,NSQ使用多播(multicast)和重新排队(requeue)来实现消息传递和消费者管理。

总的来说,SQ适合于构建实时、高吞吐量的分布式系统,特别是微服务架构;RabbitMQ和ActiveMQ提供了丰富的消息处理特性,适用于更复杂的消息传递场景;而Kafka则专注于事件流处理和大数据处理,适合于构建实时数据流应用。选择合适的系统取决于具体的需求和场景。

NSQ安装

  1. 在一个 shell 中,开始 nsqlookupd:

    ruby 复制代码
    $ nsqlookupd
  2. 再开启一个 shell ,运行 nsqd:

    ini 复制代码
    $ nsqd --lookupd-tcp-address=127.0.0.1:4160
  3. 再开启第三个 shell ,运行 nsqadmin:

    ini 复制代码
    $ nsqadmin --lookupd-http-address=127.0.0.1:4161
  4. 发布一条初始消息 (并且在集群中创建一个 topic):

    ruby 复制代码
    $ curl -d 'hello world 1' 'http://127.0.0.1:4151/pub?topic=test'
  5. 最后,在第五个 shell 中,运行 nsq_to_file:

    css 复制代码
    $ nsq_to_file --topic=test --output-dir=/tmp --lookupd-http-address=127.0.0.1:4161
  6. 推送更多的数据到 nsqd:

    ruby 复制代码
    $ curl -d 'hello world 2' 'http://127.0.0.1:4151/pub?topic=test'
    $ curl -d 'hello world 3' 'http://127.0.0.1:4151/pub?topic=test'
  7. 为了验证事情是否按预期进行,请在打开的网络浏览器 http://127.0.0.1:4171/ 中查看 nsqadmin 用户界面并查看统计信息。另外,检查 (test.*.log) 写入的日志文件内容从 /tmp的目录.

这里重要的是 nsq_to_file (客户端)没有明确告知 test 主题的产生地,它从 nsqlookupd 获取信息,即使在消息推送之后才开始连接 nsqd,消息也并没有消失。

注:如果win系统中,curl命令无法执行,可参考如下命令

sh 复制代码
Invoke-WebRequest -Uri "http://127.0.0.1:4151/pub?topic=test" -Method POST -Body "hello world 1"

注意:

这里,此处涉及到我们再yml中的相关配置,尤其是端口号,此处切不可弄错,如此部署,yml中对应的端口如下:

xml 复制代码
nsq:
  host: 127.0.0.1
  port:
    produce: 4150
    lookup: 4161

系统集成

首先,pom引入如下依赖

xml 复制代码
<!-- https://mvnrepository.com/artifact/com.github.brainlag/nsq-client -->
<dependency>
    <groupId>com.github.brainlag</groupId>
    <artifactId>nsq-client</artifactId>
    <version>1.0.0.RC4</version>
</dependency>

然后我们创建一个Java类 NsqConsume

java 复制代码
package com.nsqclient.config;

import com.github.brainlag.nsq.NSQConsumer;
import com.github.brainlag.nsq.lookup.DefaultNSQLookup;
import com.github.brainlag.nsq.lookup.NSQLookup;

import com.nsqclient.service.TestTopicHanlder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class NsqConsume implements ApplicationRunner {

    @Value("${nsq.host}")
    private String nsqHost;

    @Value("${nsq.port.lookup}")
    private Integer nsqLookupPort;

    @Value("${nsq.topic}")
    private String topic;

    @Value("${nsq.channel}")
    private String channel;
    private final TestTopicHanlder topicHanlder;

    public NsqConsume(TestTopicHanlder topicHanlder) {
        this.topicHanlder = topicHanlder;
    }

    @Override
    public void run(ApplicationArguments args) {
        NSQLookup lookup = new DefaultNSQLookup();
        lookup.addLookupAddress(nsqHost, nsqLookupPort);
        log.info("=========================NSQ消息通信地址:【{}】=========================", nsqHost);
        log.info("=========================NSQ消息监听端口:【{}】=========================", nsqLookupPort);

        NSQConsumer acsConsumer = new NSQConsumer(lookup, topic, channel, topicHanlder);
        acsConsumer.start();
    }
}

它实现了ApplicationRunner接口。该类用于配置和启动NSQ消息队列的消费者。

在代码中,使用了Lombok库提供的@Slf4j注解来自动生成日志对象log。通过@Value注解从配置文件中获取了NSQ相关的配置信息,包括主机地址、查找端口、主题和通道等。

run方法中,创建了一个DefaultNSQLookup对象,并使用addLookupAddress方法将NSQ主机地址和查找端口添加到查找器中。然后,打印出NSQ消息通信地址和监听端口的信息。

接下来,创建了一个NSQConsumer对象,传入查找器、主题、通道和处理程序(TestTopicHanlder)作为参数。最后,调用start方法启动消费者。

总结起来,这段代码的作用是配置和启动一个NSQ消息队列的消费者,用于接收和处理特定主题的消息。 那么,我们就得配置一个生产者

java 复制代码
package com.nsqclient.config;

import com.github.brainlag.nsq.NSQProducer;
import com.github.brainlag.nsq.exceptions.NSQException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeoutException;
@Slf4j
@Component(value = "NsqProduce")
public class NsqProduce implements ApplicationRunner {
    @Value("${nsq.host}")
    private String nsqHost;
    @Value("${nsq.port.produce}")
    private Integer port;
    @Value("${nsq.port.lookup}")
    private Integer nsqLookupPort;

    @Value("${nsq.topic}")
    private String topic;

    @Value("${nsq.channel}")
    private String channel;

    private NSQProducer producer;
    private static boolean is_start = false;
    private static final byte[] LOCK = new byte[0];

    public void run(ApplicationArguments args) {
        producer = new NSQProducer();
        producer.addAddress(nsqHost, port).start();
        is_start = true;
    }

    public NSQProducer getProducer() {
        if (!is_start) {
            log.info("========================NSQProduce no start====================");
        }
        if (producer == null) {
            synchronized (LOCK) {
                if (producer == null) {
                    producer = new NSQProducer();
                    producer.addAddress(nsqHost, port).start();
                }
            }
        }
        return producer;
    }
    public void sendMsgToTestTopic(String msg) {
        try {
            this.getProducer().produce(topic, msg.getBytes());
        } catch (NSQException | TimeoutException e) {
            log.error(e.getMessage());
        }
    }
}

这段代码是用于配置 NSQ 生产者(Producer)。在分布式消息系统中,生产者负责向消息队列发送消息,而消费者负责从队列中接收并处理消息。根据代码中的功能和命名,可以看出这段代码是用于配置 NSQ 的生产者,并提供了发送消息的功能。

  1. NSQProducer 初始化与配置: 该类使用了第三方库 com.github.brainlag.nsq 中的 NSQProducer 类,用于与 NSQ(一个实时分布式消息平台)交互。在 run 方法中,通过读取配置文件中的 nsqHost 和 port 属性,初始化 NSQProducer,并连接到指定的 NSQ 服务器上。

  2. 消息发送功能: 通过 sendMsgToTestTopic 方法,可以向指定的 NSQ topic 发送消息。消息以字节数组的形式传递,并在发送过程中处理可能抛出的 NSQException 或 TimeoutException。

  3. 单例模式实现: 为了确保 NSQProducer 的唯一性,使用了双重检查锁(double-checked locking)的单例模式实现。这种方式在多线程环境下保证了对象的唯一性,避免了多次创建 NSQProducer 的开销。

  4. 日志记录: 使用了 SLF4J 日志框架,通过 @Slf4j 注解实现日志记录,便于在代码中输出日志信息,以便于调试和监控。

总体来说,这段代码实现了与 NSQ 交互的功能,并且在 Spring Boot 应用程序启动时初始化 NSQProducer。 然后,我就得再继续配置一个TestTopicHanlder用来处理消息

java 复制代码
package com.nsqclient.service;

import com.github.brainlag.nsq.NSQMessage;
import com.github.brainlag.nsq.callbacks.NSQMessageCallback;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class TestTopicHanlder implements NSQMessageCallback {
    @Override
    public void message(NSQMessage nsqMessage) {
        log.info("=========================接收到消息===========================");
        log.info(new String(nsqMessage.getMessage()));
    }

}

这段代码是一个用于处理 NSQ 消息的服务类。它实现了 com.github.brainlag.nsq.callbacks.NSQMessageCallback 接口,用于定义处理接收到的 NSQ 消息的逻辑。

主要功能包括:

  1. 消息处理方法: 在 message 方法中,对接收到的 NSQ 消息进行处理。在这个示例中,它简单地打印了接收到的消息内容到日志中,使用了 SLF4J 日志框架。

  2. 日志记录: 使用了 @Slf4j 注解,以便在代码中使用日志记录功能,方便查看程序运行时的状态信息。

总体来说,这段代码是一个用于处理接收到的 NSQ 消息的处理器类。 然后,肯定需要有人发消息,我们再写一个发送消息的服务

java 复制代码
package com.nsqclient.dao;

import lombok.Data;

@Data
public class NsqMsg {
    //消息ID
    private String msgId;
    //消息内容
    private String msgText;
    //消息标识
    private String msgFlag;
}
java 复制代码
package com.nsqclient.service;

import com.nsqclient.config.NsqProduce;
import com.nsqclient.dao.NsqMsg;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.UUID;

@Service
public class TestTopicSender {
	@Autowired
	private NsqProduce nsqProduce;
	public void sendMsgToTopic(){
	    NsqMsg nsqMsg=new NsqMsg();
	    nsqMsg.setMsgId(UUID.randomUUID().toString());
	    nsqMsg.setMsgText("测试");
	    nsqMsg.setMsgFlag("1");
	    nsqProduce.sendMsgToTestTopic(nsqMsg.toString());
	}
}

我们再写一个控制层去调用

java 复制代码
package com.nsqclient.controller;

import com.alibaba.fastjson.JSONObject;
import com.nsqclient.config.NsqProduce;
import com.nsqclient.service.TestTopicSender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/nsq")
public class NSQClientController {
    @Autowired
    private TestTopicSender topicSender;

    @GetMapping("/sendMsg")
    public JSONObject sendMsgToTopic() {
        topicSender.sendMsgToTopic();
        JSONObject result = new JSONObject();
        result.put("status", 200);
        result.put("msg", "发送成功");
        return result;
    }
}

测试效果

相关推荐
捂月20 分钟前
Spring Boot 深度解析:快速构建高效、现代化的 Web 应用程序
前端·spring boot·后端
瓜牛_gn1 小时前
依赖注入注解
java·后端·spring
Estar.Lee1 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
喜欢猪猪1 小时前
Django:从入门到精通
后端·python·django
一个小坑货1 小时前
Cargo Rust 的包管理器
开发语言·后端·rust
bluebonnet271 小时前
【Rust练习】22.HashMap
开发语言·后端·rust
uhakadotcom2 小时前
如何实现一个基于CLI终端的AI 聊天机器人?
后端
Iced_Sheep2 小时前
干掉 if else 之策略模式
后端·设计模式