一、引言

随着物联网技术的迅猛发展,大量的设备和传感器产生了海量的数据。本文利用了 MQTT、Kafka 和 MongoDB 各自的优点,满足实时数据处理和大规模数据存储的需求。
如图:

二、总结

优点:

1. 可靠和解耦:

Kafka的复制机制和持久化存储确保了数据在传输过程中的可靠性,即使某个节点发生故障,也不会导致数据丢失,将数据生产者和消费者解耦,各模块可以独立扩展和优化,减少了相互影响。
2. 高可用和灵活性:

MongoDB的复制集和分片机制提供了数据的高可用性和容错能力,保证了数据存储的可靠性和灵活性。

缺点:

1. 复杂度高:

包含多个组件(MQTT、Kafka、MongoDB)配置、部署和维护、各组件之间的协调和集成也增加了实现的复杂性。
2. 延迟:

数据从设备上传到最终存储在MongoDB之间经过多个处理环节,每个环节都可能增加一些延迟。
3. 一致性:

数据在Kafka和MongoDB之间传递时可能需要额外的处理机制来确保一致性。

三、实现

准备工作

使用docker-compose.yml创建Kafka服务和MongoDB,简易代码如下:

version: '3.8'

networks:
app-tier:
driver: bridge services:
kafka:
image: 'bitnami/kafka:latest'
networks:
- app-tier
ports:
- "9092:9092"
environment:
- KAFKA_CFG_NODE_ID=0
- KAFKA_CFG_PROCESS_ROLES=controller,broker
- KAFKA_CFG_LISTENERS=PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://127.0.0.1:9092
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
volumes:
- kafka-data:/bitnami/kafka mongodb:
image: 'mongo:latest'
networks:
- app-tier
container_name: mongodb
ports:
- "27017:27017"
volumes:
- mongo-data:/data/db volumes:
kafka-data:
driver: local
mongo-data:
driver: local

实现步骤

1. 设备数据上传:
服务端代码

var mqttFactory = new MqttFactory();
var mqttServerOptions = new MqttServerOptionsBuilder()
.WithDefaultEndpointPort(1883)//监听的端口
.WithDefaultEndpoint()
.WithoutEncryptedEndpoint()// 不启用tls
.WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(10 * 1000))//10秒超时
.WithPersistentSessions(true)//启用session
.WithConnectionBacklog(1000)//积累的最大连接请求数
.Build();
using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
{
AddMqttEvents(mqttServer); await mqttServer.StartAsync();
Console.WriteLine("Press Enter Ctrl+C to exit.");
Console.ReadLine();
Console.CancelKeyPress += async (sender, e) =>
{
e.Cancel = true; // 防止进程直接终止
await mqttServer.StopAsync();
Environment.Exit(0);
};
} private static void AddMqttEvents(MqttServer mqttServer)
{
MqttServerEvents mqttEvents = new MqttServerEvents();
mqttServer.ClientConnectedAsync += mqttEvents.Server_ClientConnectedAsync;
mqttServer.StartedAsync += mqttEvents.Server_StartedAsync;
mqttServer.StoppedAsync += mqttEvents.Server_StoppedAsync;
mqttServer.ClientSubscribedTopicAsync += mqttEvents.Server_ClientSubscribedTopicAsync;
mqttServer.ClientUnsubscribedTopicAsync += mqttEvents.Server_ClientUnsubscribedTopicAsync;
mqttServer.ValidatingConnectionAsync += mqttEvents.Server_ValidatingConnectionAsync;
mqttServer.ClientDisconnectedAsync += mqttEvents.Server_ClientDisconnectedAsync;
mqttServer.InterceptingInboundPacketAsync += mqttEvents.Server_InterceptingInboundPacketAsync;
mqttServer.InterceptingOutboundPacketAsync += mqttEvents.Server_InterceptingOutboundPacketAsync;
mqttServer.InterceptingPublishAsync += mqttEvents.Server_InterceptingPublishAsync;
mqttServer.ApplicationMessageNotConsumedAsync += mqttEvents.Server_ApplicationMessageNotConsumedAsync;
mqttServer.ClientAcknowledgedPublishPacketAsync += mqttEvents.Server_ClientAcknowledgedPublishPacketAsync;
}

客户端代码

 var mqttFactory = new MqttFactory();
var mqttClient = mqttFactory.CreateMqttClient(); var mqttOptions = new MqttClientOptionsBuilder()
.WithClientId("MqttServiceClient")
.WithTcpServer("127.0.0.1", 1883)
.Build();
mqttClient.ConnectedAsync+=(e =>
{
Console.WriteLine("MQTT连接成功");
return Task.CompletedTask;
}); mqttClient.DisconnectedAsync+=(e =>
{
Console.WriteLine("MQTT连接断开");
return Task.CompletedTask;
});
await mqttClient.ConnectAsync(mqttOptions, CancellationToken.None);
//发送消息
MqttApplicationMessage applicationMessage = new MqttApplicationMessage
{
Topic = "mqtttest",
PayloadSegment = new ArraySegment<byte>(System.Text.Encoding.UTF8.GetBytes(input))
}; var res = await mqttClient.PublishAsync(applicationMessage);
2. Kafka消息处理:

生产者代码

        var config = new ProducerConfig
{
BootstrapServers = "localhost:9092"
};
using var producer = new ProducerBuilder<string, string>(config).Build();
try
{
var message = new Message<string, string>
{
Key = e.ClientId,
Value = JsonConvert.SerializeObject(e.Packet)
};
var deliveryResult = await producer.ProduceAsync("mqttMsg-topic", message);
Console.WriteLine($"Delivered '{deliveryResult.Value}' to '{deliveryResult.TopicPartitionOffset}'");
}
catch (ProduceException<string, string> ke)
{
Console.WriteLine($"Delivery failed: {ke.Error.Reason}");
}

消费者代码

var config = new ConsumerConfig
{
GroupId = "my-consumer-group",
BootstrapServers = "127.0.0.1:9092",
AutoOffsetReset = AutoOffsetReset.Earliest
};
using var consumer = new ConsumerBuilder<string, string>(config).Build();
consumer.Subscribe("mqttMsg-topic");
//消费消息并保存到mongodb
var client = new MongoClient("mongodb://127.0.0.1:27017");
var collection = client.GetDatabase("mqtttest").GetCollection<BsonDocument>($"history_{DateTime.UtcNow.Year}_{DateTime.UtcNow.Month}");
while (true)
{
try
{
var consumeResult = consumer.Consume(cancellationToken.Token);
Console.WriteLine($"收到Kafka消息 '{consumeResult.Message.Value}' at: '{consumeResult.TopicPartitionOffset}'.");
var document = new BsonDocument
{
{ "clientId", consumeResult.Message.Key },
{ "JsonData", MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>(consumeResult.Message.Value) },//不同设备上报数据格式不一定一样
{ "created", DateTime.UtcNow }
};
await collection.InsertOneAsync(document);
}
catch (ConsumeException e)
{
Console.WriteLine($"处理Kafka消息异常: {e.Error.Reason}");
}
}

源码地址:https://github.com/jclown/MqttPersistence

使用mongodb、Kafka保存mqtt消息的更多相关文章

  1. MQTT 消息 发布 订阅

    当连接向一个mqtt服务器时,clientId必须是唯一的.设置一样,导致client.setCallback总是走到 connectionLost回调.报connection reset.调查一天才 ...

  2. Kafka介绍与消息队列

    消息队列的好处: 消息队列(Message Queue) 消息: 网络中的两台计算机或者两个通讯设备之间传递的数据.例如说:文本.音乐.视频等内容. 队列:一种特殊的线性表(数据元素首尾相接),特殊之 ...

  3. Kafka设计解析(十六)Kafka 0.11消息设计

    转载自 huxihx,原文链接 [原创]Kafka 0.11消息设计 目录 一.Kafka消息层次设计 1. v1格式 2. v2格式 二.v1消息格式 三.v2消息格式 四.测试对比 Kafka 0 ...

  4. Kafka中的消息是否会丢失和重复消费(转)

    在之前的基础上,基本搞清楚了Kafka的机制及如何运用.这里思考一下:Kafka中的消息会不会丢失或重复消费呢?为什么呢? 要确定Kafka的消息是否丢失或重复,从两个方面分析入手:消息发送和消息消费 ...

  5. Kafka作为分布式消息系统的系统解析

    Kafka概述 Apache Kafka由Scala和Java编写,基于生产者和消费者模型作为开源的分布式发布订阅消息系统.它提供了类似于JMS的特性,但设计上又有很大区别,它不是JMS规范的实现,如 ...

  6. RabbitMQ,RocketMQ,Kafka 事务性,消息丢失和消息重复发送的处理策略

    消息队列常见问题处理 分布式事务 什么是分布式事务 常见的分布式事务解决方案 基于 MQ 实现的分布式事务 本地消息表-最终一致性 MQ事务-最终一致性 RocketMQ中如何处理事务 Kafka中如 ...

  7. vue中使用stompjs实现mqtt消息推送通知

    最近在研究vue+webAPI进行前后端分离,在一些如前端定时循环请求后台接口判断状态等应用场景用使用mqtt进行主动的消息推送能够很大程度的减小服务端接口的压力,提高系统的效率,而且可以利用mqtt ...

  8. 百万级开源MQTT消息服务器 搭建

    下载地址:http://emqtt.com/downloads 文档地址:http://emqtt.com/docs/v2/index.html 开始使用EMQ 2.0 消息服务器简介EMQ (Erl ...

  9. Kafka与常见消息队列的对比

    Kafka与常见消息队列的对比 RabbitMQ Erlang编写 支持很多的协议:AMQP,XMPP, SMTP, STOMP 非常重量级,更适合于企业级的开发 发送给客户端时先在中心队列排队.对路 ...

  10. [3] MQTT,mosquitto,Eclipse Paho---怎样使用 Eclipse Paho MQTT工具来发送订阅MQTT消息?

    在上两节,笔者主要介绍了 MQTT,mosquitto,Eclipse Paho的基本概念已经怎样安装mosquitto. 在这个章节我们就来看看怎样用 Eclipse Paho MQTT工具来发送接 ...

随机推荐

  1. AtCoder Beginner Contest 379

    Contest Link C 妙妙贪心题,居然需要高斯求和公式. Submission D 妙妙套路题,维护全局 lazytag,easy to solve. Submission E 妙妙拆贡献题, ...

  2. App中第三方登录和分享模块的实现

    @ 目录 1 流程 2 设计与实现 3 优化 本文目的:" 实现一套易于使用.维护的第三方登录和分享模块" 我们开发App有时为了吸引用户,会引入三方的授权登录降低用户的注册和登录 ...

  3. C# 之 Int16 Int32 Int64 的区别-迷恋自留地

    int16 值类型表示值介于 -32768 到 +32767 之间的有符号整数. Int32 值类型表示值介于 -2,147,483,648 到 +2,147,483,647 之间的有符号整数. In ...

  4. manim边做边学--淡入淡出

    本篇介绍Manim中的淡入和淡出动画效果. 淡入FadeIn 主要用于让对象以渐变的方式在场景中显现. 它的特点是视觉上柔和过渡,能自然地引导观众注意新出现的元素. 淡出FadeOut 则是使对象逐渐 ...

  5. django静态文件、form表单和request对象

    目录 一.静态文件 1.静态文件概念 2.资源访问 3.静态文件资源访问 二.静态文件相关配置 1.接口前缀 2.接口前缀动态匹配 三.form表单 action属性 method属性 四.reque ...

  6. Qt音视频开发36-超时检测和自动重连的设计

    一.前言 如果网络环境正常设备正常,视频监控系统一般都是按照正常运行下去,不会出现什么问题,但是实际情况会很不同,奇奇怪怪七七八八的问题都会出现,就比如网络出了问题都有很多情况(交换机故障.网线故障. ...

  7. Qt编写地图综合应用6-百度在线地图

    一.前言 百度在线地图的应用老早就做过,后面经过不断的完善才到今天的这个程序,除了基本的可以载入地图并设置一些相关的属性以外,还增加了各种js函数直接异步加载数据比如动态添加点.矩形.圆形.行政区划等 ...

  8. C# – XmlSerializer:字符串“2020-07-31 09:29:16”不是有效的 AllXsd 值。

    C#中使用XmlSerializer对字符串进行反序列化时,提示:字符串"2020-07-31 09:29:16"不是有效的AllXsd值. 原因: 先说下为什么会出现这样的错误, ...

  9. 从新手到专家:如何设计一套亿级消息量的分布式IM系统

    本文原作者Chank,原题"如何设计一个亿级消息量的 IM 系统",为了提升内容质量,本次有修订和改动. 1.写有前面 本文将在亿级消息量.分布式IM系统这个技术前提下,分析和总结 ...

  10. 『AutoHotkey』 效率提升「脚本集」

    AutoHotkey 效率提升脚本集 一些实用的 AutoHotkey 脚本示例,这些可以显著提升工作效率. #Requires AutoHotkey v2.0 ; 1. 快速启动常用程序 ^!n:: ...