草庐IT

SpringBoot整合MQTT总结

叶玉恒 2023-08-16 原文

目录

前言:

一、什么是mqtt

二、主要思想

发布/订阅模式

三、MQTT重要概念

3.1 MQTT Client

3.2 MQTT Broker

3.3 MQTT Connection

3.4 MQTT主要参数

四、软件和Apollo

4.1 安装Apollo

4.2 安装Postman

 4.3 安装MQTTBox

五、代码实现

5.1 配置pom.xml

5.2 配置MQTT服务器基本信息

5.3 配置读取yml文件的类MqttConfiguration

5.4  MQTT生产端的Handler处理

5.5  MQTT消费端的Handler处理 

5.6 写个Controller类来进行访问控制测试

六、测试 

6.1测试生产端的Handler

6.2 测试消费端的Handler

后言:


前言:

        这几天在准备面试的过程中做的一个小demo,主要是用通过SpringBoot实现一个与MQTT服务交互通信,也是看着别人的项目改的,这两个技术之前都没有接触过,希望记录一下可以分享给大家,也好久没更新了,借此机会更新一波blog。在正式的开始这个项目前还是学了一下SSM和SpringBoot的基础,上手起来不会这么的无力。期间也是查阅了很多的资料和询问了诸多大佬。

养成习惯先点赞后观看!!!! 

好了话不多说,一步步的搭建项目和原理详解就在下面了

一、什么是mqtt

        MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议,该协议构建于 TCP/IP 协议上,由 IBM 于 1999 年发明。MQTT 协议的主要特征是开放、简单、轻量级和易于实现,这些特征使得它适用于受约束的应用环境,如:

网络受限:网络带宽较低且传输不可靠
终端受限:协议运行在嵌入式设备上,嵌入式终端的处理器、内存等是受限的

        通过 MQTT 协议,目前已经扩展出了数十种 MQTT 服务器端程序,可以通过 PHP、Java、Python、C、C# 等语言向 MQTT 发送消息。由于开放源代码、耗电量小等特点,MQTT 非常适用于物联网领域,如传感器与服务器的通信、传感器信息采集等。

二、主要思想

发布/订阅模式

        订阅发布模式定义了一种一对多的依赖关系,让多个订阅者对象同时监听某一个主题对象。这个主题对象在自身状态变化时,会通知所有订阅者对象,使它们能够自动更新自己的状态。

       将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相应对象间的一致性,这样会给维护、扩展和重用都带来不便。当一个对象的改变需要同时改变其他对象,而且它不知道具体有多少对象需要改变时,就可以使用订阅发布模式了。

       一个抽象模型有两个方面,其中一方面依赖于另一方面,这时订阅发布模式可以将这两者封装在独立的对象中,使它们各自独立地改变和复用。订阅发布模式所做的工作其实就是在解耦合。让耦合的双方都依赖于抽象,而不是依赖于具体,从而使得各自的变化都不会影响另一边的变化。

发布/订阅模式并不是 MQTT 协议特有的模式,像我们很多消息中间件都有使用发布/订阅模式,这里你是不是想说,这不就是我们所说的观察者模式嘛,还真不是,这两个模式很容易混淆。观察者模式只有观察者 + 被观察者两个角色,而发布/订阅模式还有一个经纪人 Broker;往更深层次的讲观察者和被观察者,是松耦合的关系,而发布者和订阅者,则完全不存在耦合。

在我们日常写程序时,经常遇到下面这种情况:

public void 前端业务/硬件业务()
{
    刷新界面();
    更新数据库();
    对界面更新数据();
    ………………………………
}

当有前端和硬件业务产生时,需要依次要去执行:刷新界面()、更新数据库()、对界面更新数据()等操作。表面上看代码写得很工整,其实这里面有很多的问题:

首先,这完全是面向过程开发,根本不适合大型项目。
第二,代码维护量太大。设想一下,如果产生业务后要执行10多个操作,那这将是个多么大,多少复杂的类呀,时间一长,可能连开发者自己都不知道如何去维护了。
第三,扩展性差。如果产生业务后,要增加一个声音提示()功能,怎么办呢?没错,只能加在前端业务/硬件业务()这个函数中,这样一来,就违反了“开放-关闭原则”。而且修改了原有的函数,那么在测试时,除了要测新增功能外,还要做原功能的回归测试;在一个大型项目中,做一次回归测试可能要花费大约两周左右的时间,而且前提是新增功能没有影响原来功能及产生新的bug。
那么如何把前端业务/硬件业务()函数同其他函数进行解耦合呢?别着急,下面就介绍今天的主角----订阅发布模式。见下图:

上面的流程就是对有告警信息产生()这个函数的描述。我们要做的,就是把产生告警和它需要通知的事件进行解耦,让它们之间没有相互依赖的关系,解耦合图如下:

事件触发者被抽象出来,称为消息发布者,即图中的P。事件接受都被抽象出来,称为消息订阅者,即图中的S。P与S之间通过Broker(即订阅器)连接。这样就实现了P与S的解耦。首先,P就把消息发送到指定的订阅器上,从始至终,它并不知道也不关心要把消息发向哪个S。S如果想接收消息,就要向订阅器进行订阅,订阅成功后,S就可以接收来自Broker的消息了,从始至终,S并不知道也不关心消息来源于哪个具体的P。同理,S还可以向Broker进行退订操作,成功退订后,S就无法接收到来自指定Broker的消息了。这样就完美的解决了P与S之间的解耦。

三、MQTT重要概念

3.1 MQTT Client

publisher 和 subscriber 都属于 MQTT Client,之所以有发布者和订阅者这个概念,其实是一种相对的概念,就是指当前客户端是在发布消息还是在接收消息,发布和订阅的功能也可以由同一个 MQTT Client 实现。

MQTT 客户端是运行 MQTT 库并通过网络连接到 MQTT 代理的任何设备(从微控制器到成熟的服务器)。例如,MQTT 客户端可以是一个非常小的、资源受限的设备,它通过无线网络进行连接并具有一个最低限度的库。基本上,任何使用 TCP/IP 协议使用 MQTT 设备的都可以称之为 MQTT Client。MQTT 协议的客户端实现非常简单直接,易于实施是 MQTT 非常适合小型设备的原因之一。MQTT 客户端库可用于多种编程语言。例如,Android、Arduino、C、C++、C#、Go、iOS、Java、JavaScript 和 .NET。        

3.2 MQTT Broker

与 MQTT Client 对应的就是 MQTT Broker,Broker 是任何发布/订阅协议的核心,根据实现的不同,代理可以处理多达数百万连接的 MQTT Client。

Broker 负责接收所有消息,过滤消息,确定是哪个Client 订阅了每条消息,并将消息发送给对应的 Client,Broker 还负责保存会话数据,这些数据包括订阅的和错过的消息。Broker 还负责客户端的身份验证和授权。

3.3 MQTT Connection

MQTT 协议基于 TCP/IP。客户端和代理都需要有一个 TCP/IP 协议支持。

 MQTT 连接始终位于一个客户端和代理之间。客户端从不直接相互连接。要发起连接,客户端向代理发送 CONNECT 消息。代理使用 CONNACK 消息和状态代码进行响应。建立连接后,代理将保持打开状态,直到客户端发送断开连接命令或连接中断。

3.4 MQTT主要参数

ClientId:ClientId 的长度可以是 1-23 个字符,在一个服务器上 ClientId 不能重复。如果超过 23 个字符,则服务器返回 CONNACK 消息中的返回码为 Identifier Rejected。在 MQTT 3.1.1 中,如果您不需要代理持有状态,您可以发送一个空的 ClientId。空的 ClientId 导致连接没有任何状态。在这种情况下,clean session 标志必须设置为 true,否则代理将拒绝连接。

Clean Session:Clean Session 标志告诉代理客户端是否要建立持久会话。在持久会话 (CleanSession = false) 中,代理存储客户端的所有订阅以及以服务质量(QoS)级别 1 或 2 订阅的客户端的所有丢失消息。 如果会话不是持久的 (CleanSession = true ),代理不为客户端存储任何内容,并清除任何先前持久会话中的所有信息。

Username/Password:MQTT 可以发送用户名和密码进行客户端认证和授权。但是,如果此信息未加密或散列,则密码将以纯文本形式发送。我们强烈建议将用户名和密码与安全传输一起使用。像 HiveMQ 这样的代理可以使用 SSL 证书对客户端进行身份验证,因此不需要用户名和密码。

Will Message:LastWillxxx 表示的是遗愿,client 在连接 broker 的时候将会设立一个遗愿,这个遗愿会保存在 broker 中,当 client 因为非正常原因断开与 broker 的连接时,broker 会将遗愿发送给订阅了这个 topic(订阅遗愿的 topic)的 client。

KeepAlive:keepAlive 是 client 在连接建立时与 broker 通信的时间间隔,通常以秒为单位。这个时间指的是 client 与 broker 在不发送消息下所能承受的最大时长。

QOS:此数字表示消息的服务质量 (QoS)。有三个级别:0、1 和 2。服务级别决定了消息到达预期接收者(客户端或代理)的保证类型。

Payload这个是每条消息的实际内容。MQTT 是数据无关性的。可以发送任何文本、图像、加密数据以及二进制数据。

timeout:MQTT会尝试接收数据,直到timeout时间到后才会退出。

四、软件和Apollo

4.1 安装Apollo

Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。

服务端基于Spring Boot和Spring Cloud开发,打包后可以直接运行,不需要额外安装Tomcat等应用容器。

Java客户端不依赖任何框架,能够运行于所有Java运行时环境,同时对Spring/Spring Boot环境也有较好的支持。

Apollo下载地址http://xn--apollo-np7ii83deeq211d/

相关链接:

Apollo 官方安装教程:https://github.com/ctripcorp/apollo/wiki/Quick-Start
Apollo 分布式部署官方指南:https://github.com/ctripcorp/apollo/wiki/%E5%88%86%E5%B8%83%E5%BC%8F%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97
Apollo Github 地址:https://github.com/ctripcorp/apollo

4.1.1 解压,进入到D:\java\apache-apollo-1.7.1\bin 目录下,执行命令

.\apollo.cmd create mybroker2

4.1.2 进入刚刚创建好的mybroker/bin目录,执行:

.\apollo-broker.cmd run

4.1.3 浏览器打开地址http://127.0.0.1:61680/,默认用户名:admin,密码:password,即可登录主页面

4.2 安装Postman

 4.3 安装MQTTBox

  Microsoft Store里面就有。 

账号密码输入即可

五、代码实现

5.1 配置pom.xml

<dependencies>
    <!--导入起步依赖-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-integration</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.integration</groupId>
			<artifactId>spring-integration-stream</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.integration</groupId>
			<artifactId>spring-integration-mqtt</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.47</version>
		</dependency>
		
</dependencies>

5.2 配置MQTT服务器基本信息

在springBoot配置文件application.yml中配置,添加如下:

#mqtt配置
com:
  mqtt:
    url: tcp://127.0.0.1:61613
    clientId: mqtt_test1234
    topics: topic01,topic02
    username: admin
    password: password
    timeout: 10
    keepalive: 20
    
#指定服务端口 
server:
  port: 8081   #一般没改过tomcat服务器的端口不用修改

5.3 配置读取yml文件的类MqttConfiguration

package com.vcarecity.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;



/**
 * 读取yml
 */
@Component
@ConfigurationProperties(prefix = "com.mqtt")     //对应yml文件中的com下的mqtt文件配置
public class MqttConfiguration {
	private String url;
	private String clientId;
	private String topics;
    private String username;
    private String password;
    private String timeout;
    private String keepalive;
	public String getUrl() {
		return url;
	}
	public void setUrl(String url) {
		this.url = url;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getClientId() {
		return clientId;
	}
	public void setClientId(String clientId) {
		this.clientId = clientId;
	}
	public String getTopics() {
		return topics;
	}
	public void setTopics(String topics) {
		this.topics = topics;
	}
	public String getTimeout() {
		return timeout;
	}
	public void setTimeout(String timeout) {
		this.timeout = timeout;
	}
	public String getKeepalive() {
		return keepalive;
	}
	public void setKeepalive(String keepalive) {
		this.keepalive = keepalive;
	}
}

5.4  MQTT生产端的Handler处理

package com.vcarecity.mqtt;

import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;

import com.vcarecity.config.MqttConfiguration;

/**
 * MQTT生产端
 *
 */
@Configuration
public class MqttOutboundConfiguration {
	@Autowired
	private MqttConfiguration mqttProperties;

	@Bean
	public MessageChannel mqttOutboundChannel() {
		return new DirectChannel();
	}

	@Bean
	public MqttPahoClientFactory mqttClientFactory() {
		DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
		String[] array = mqttProperties.getUrl().split(",");
		MqttConnectOptions options = new MqttConnectOptions();
		options.setServerURIs(array);
		options.setUserName(mqttProperties.getUsername());
		options.setPassword(mqttProperties.getPassword().toCharArray());
		// 接受离线消息
		options.setCleanSession(false); //告诉代理客户端是否要建立持久会话   false为建立持久会话

		factory.setConnectionOptions(options);
		return factory;
	}

	@Bean
	@ServiceActivator(inputChannel = "mqttOutboundChannel")
	public MessageHandler mqttOutbound() {
		MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(
				mqttProperties.getClientId()+"outbound", mqttClientFactory());
		messageHandler.setAsync(true);

		return messageHandler;
	}



}

5.5  MQTT消费端的Handler处理 

实现了对inboundtopic中的主题监听,当有消息推送到inboundtopic主题上时可以接受

package com.vcarecity.mqtt;

import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.IntegrationComponentScan;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.core.MessageProducer;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;

import com.vcarecity.config.MqttConfiguration;

/**
 * MQTT消费端
 *
 */
@Configuration
@IntegrationComponentScan
public class MqttInboundConfiguration {

	@Autowired
	private MqttConfiguration mqttProperties;


	@Bean
	public MessageChannel mqttInputChannel() {
		return new DirectChannel();
	}

	@Bean
	public MqttPahoClientFactory mqttClientFactory() {
		DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
		    String[] array = mqttProperties.getUrl().split(",");
			MqttConnectOptions options = new MqttConnectOptions();
			options.setServerURIs(array);
			options.setUserName(mqttProperties.getUsername());
			options.setPassword(mqttProperties.getPassword().toCharArray());
			options.setKeepAliveInterval(2);

			//接受离线消息
			options.setCleanSession(false);
			factory.setConnectionOptions(options);
		return factory;
	}

	//配置client,监听的topic
	@Bean
	public MessageProducer inbound() {
		String[] inboundTopics = mqttProperties.getTopics().split(",");
		MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(
				mqttProperties.getClientId()+"_inbound",mqttClientFactory(), inboundTopics);  //对inboundTopics主题进行监听
		adapter.setCompletionTimeout(5000);
		adapter.setQos(1);
		adapter.setConverter(new DefaultPahoMessageConverter());
		adapter.setOutputChannel(mqttInputChannel());
		return adapter;
	}




	//通过通道获取数据
	@Bean
	@ServiceActivator(inputChannel = "mqttInputChannel")  //异步处理
	public MessageHandler handler() {
		return new MessageHandler() {
			@Override
			public void handleMessage(Message<?> message) throws MessagingException {
//				System.out.println("message:"+message);
				System.out.println("----------------------");
				System.out.println("message:"+message.getPayload());
				System.out.println("PacketId:"+message.getHeaders().getId());
				System.out.println("Qos:"+message.getHeaders().get(MqttHeaders.QOS));

				String topic = (String) message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC);
				System.out.println("topic:"+topic);
			}
		};
	}
}

5.6 写个Controller类来进行访问控制测试

package com.vcarecity.controller;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.vcarecity.mqtt.MqttGateway;

@RestController
public class MqttPubController {
	
	@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
	@Autowired
	private MqttGateway mqttGateway;


	@RequestMapping("/hello")
	public String hello() {
		return "hello!";
	}
	
	 @RequestMapping("/sendMqtt")
	    public String sendMqtt(String  sendData){
		 	System.out.println(sendData);
	    	System.out.println("进入sendMqtt-------"+sendData);
	        mqttGateway.sendToMqtt("topic01",(String) sendData);
	        return "Test is OK";
	    }



	@RequestMapping("/sendMqttTopic")
	public String sendMqtt(String  sendData,String topic){
		//System.out.println(sendData+"   "+topic);
		//System.out.println("进入inbound发送:"+sendData);
		mqttGateway.sendToMqtt(topic,(String) sendData);
		return "Test is OK";
	}
}

六、测试 

直接调用Controller中的URL进行调用测试:

6.1测试生产端的Handler

6.2 测试消费端的Handler

使用Postman:

http://localhost:8081/sendMqttTopic?sendData=this is mq55555&topic=topic01

 可以看见测试台上会出现Message消息,这边实现的是对inboundtopic中的主题监听实现:

刚开始没有出现上图效果,查了好久的bug。结果重启Apollo就好了

如果我要配置多个client,应该怎么处理呢?这个也简单

(1)我们只要配置多个通道即可,简单代码如下:

//通道2
@Bean
public MessageChannel mqttInputChannelTwo() {
    return new DirectChannel();
}
//配置client2,监听的topic:hell2,hello3
@Bean
public MessageProducer inbound1() {
    MqttPahoMessageDrivenChannelAdapter adapter =
            new MqttPahoMessageDrivenChannelAdapter(clientId+"_inboundTwo", mqttClientFactory(),
                    "hello2","hello3");
    adapter.setCompletionTimeout(completionTimeout);
    adapter.setConverter(new DefaultPahoMessageConverter());
    adapter.setQos(1);
    adapter.setOutputChannel(mqttInputChannelTwo());
    return adapter;
}
 
//通过通道2获取数据
@Bean
@ServiceActivator(inputChannel = "mqttInputChannelTwo")
public MessageHandler handlerTwo() {
    return new MessageHandler() {
       @Override
			public void handleMessage(Message<?> message) throws MessagingException {
//				System.out.println("message:"+message);
				System.out.println("----------------------");
				System.out.println("message:"+message.getPayload());
				System.out.println("PacketId:"+message.getHeaders().getId());
				System.out.println("Qos:"+message.getHeaders().get(MqttHeaders.QOS));

				String topic = (String) message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC);
				System.out.println("topic:"+topic);
    };
}

 (2)因为我这个项目用的是读取yml文件的,所以只需要在yml文件中的topics即可,加自己想要的topic。

topics: topic03,topic04,topic01,topic02

 以上测试都可以使用MQTTBox完成

后言:

小白一枚,欢迎交流

资料参考: 

Spring官网对MQTT的支持:MQTT Support (spring.io)

Tackoverflow上面关于MQTT的资料,需要翻阅墙体:

Google上的MQTT论坛

参考文章:

https://blog.csdn.net/tjvictor/article/details/5223309

https://blog.csdn.net/riemann_/article/details/118686072

 No pains No results 

有关SpringBoot整合MQTT总结的更多相关文章

  1. SPI接收数据异常问题总结 - 2

    SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手

  2. 物联网MQTT协议详解 - 2

    一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su

  3. Simulink方法总结和避坑指南(一)——Simulink入门与基本调试方法 - 2

    文章目录一、项目场景二、基本模块原理与调试方法分析——信源部分:三、信号处理部分和显示部分:四、基本的通信链路搭建:四、特殊模块:interpretedMATLABfunction:五、总结和坑点提醒一、项目场景  最近一个任务是使用simulink搭建一个MIMO串扰消除的链路,并用实际收到的数据进行测试,在搭建的过程中也遇到了不少的问题(当然这比vivado里面的debug好不知道多少倍)。准备趁着这个机会,先以一个很基本的通信链路对simulink基础和相关的debug方法进行总结。  在本篇中,主要记录simulink的基本原理和基本的SISO通信传输链路(QPSK方式),计划在下篇记

  4. ruby-on-rails - Rails 和 MQTT : Subscribe to topic in background at server startup? - 2

    我想在服务器启动时在我的Rails应用程序中订阅一个mqtt主题,并保持订阅始终处于事件状态和运行状态。我正在使用这个mqttgem进行mqtt通信:https://github.com/njh/ruby-mqtt这是我现在拥有的:在application.rb中:config.after_initializedomqttSub=BackgroundMQTT.newmqttSub.runend后台MQTT类:classMQTTSubscriberdefrunThread.newdoMQTT::Client.connect(:host=>'localhost',:port=>1883,)

  5. springboot定时任务 - 2

    如果您希望在Spring中启用定时任务功能,则需要在主类上添加 @EnableScheduling 注解。这样Spring才会扫描 @Scheduled 注解并执行定时任务。在大多数情况下,只需要在主类上添加 @EnableScheduling 注解即可,不需要在Service层或其他类中再次添加。以下是一个示例,演示如何在SpringBoot中启用定时任务功能:@SpringBootApplication@EnableSchedulingpublicclassApplication{publicstaticvoidmain(String[]args){SpringApplication.ru

  6. 基于SpringBoot的线上日志阅读器 - 2

    软件特点部署后能通过浏览器查看线上日志。支持Linux、Windows服务器。采用随机读取的方式,支持大文件的读取。支持实时打印新增的日志(类终端)。支持日志搜索。使用手册基本页面配置路径配置日志所在的目录,配置后按回车键生效,下拉框选择日志名称。选择日志后点击生效,即可加载日志。windows路径E:\java\project\log-view\logslinux路径/usr/local/XX历史模式历史模式下,不会读取新增的日志。针对历史文件可以分页读取,配置分页大小、跳转。历史模式下,支持根据关键词搜索。目前搜索引擎使用的是jdk自带类库,搜索速度相对较低,优点是比较简单。2G日志全文搜

  7. springboot使用validator进行参数校验 - 2

    1.依赖导入org.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-validation2.validation常用注解@Null被注释的元素必须为null@NotNull被注释的元素不能为null,可以为空字符串@AssertTrue被注释的元素必须为true@AssertFalse被注释的元素必须为false@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值@D

  8. ruby - 如何将 Interactive Ruby 整合到我的开发过程中? - 2

    我正在尝试找到一种更好的方法将IRB与我的常规ruby​​开发集成。目前我很少在我的代码中使用IRB。我只用它来验证语法或尝试一些小的东西。我知道我可以将我自己的代码加载到ruby​​中作为一个require'mycode'但这通常不符合我的编程风格。有时我要检查的变量超出范围或在循环内。有没有一种简单的方法可以启动我的脚本并在IRB内的某个点卡住?我想我正在寻找一种更简单的方法来调试我的ruby​​代码而不破坏我的F5(编译)键。也许有经验的ruby开发者可以和我分享一个更精简的开发方法。 最佳答案 安装ruby​​-debugg

  9. ruby - 使用 Drupal 和 Ruby。有没有人整合两者? - 2

    我开始了一个小型网络项目并使用Drupal来构建它。到目前为止,还不错:您可以快速建立一个不错的面向CMS的网站,通过模块添加社交功能,并且您有一个广泛的API可以在一个架构良好的平台中进行自定义。现在问题来了:网站的增长超出了最初的计划,我发现自己正处于认真开始为它编写代码的境地。由于Drupal项目,我对PHP有了新的认识,但我想用Ruby来做。我会感觉更舒服,以后维护起来更容易,我可以在其他Ruby/Rails应用程序中重用它。随着时间的推移,我想我会用Ruby重写Drupal中的现有部分。基于此,问题是:是否有人将两者(成功或失败的故事)结合起来?这是一个相当大的决定,但我在G

  10. 【动态规划】背包问题(详细总结,很全) - 2

    【动态规划】一、背包问题1.背包问题总结1)动规四部曲:2)递推公式总结:3)遍历顺序总结:2.01背包1)二维dp数组代码实现2)一维dp数组代码实现3.完全背包代码实现4.多重背包代码实现一、背包问题1.背包问题总结暴力的解法是指数级别的时间复杂度。进而才需要动态规划的解法来进行优化!背包问题是动态规划(DynamicPlanning)里的非常重要的一部分,关于几种常见的背包,其关系如下:在解决背包问题的时候,我们通常都是按照如下五部来逐步分析,把这五部都搞透了,算是对动规来理解深入了。1)动规四部曲:(1)确定dp数组及其下标的含义(2)确定递推公式(3)dp数组的初始化(4)确定遍历顺

随机推荐