凯亚物联网平台如何通过MQTT网络组件接入设备

一、概述

有人提议我用kestrel代替Dotnetty ,那是不可能的, 物联网平台MQTT,rtmp,rtsp,httpflv,tcp,udp,rpc 都是基于dotnetty实现,压测没有问题,每秒可以达到20w/s,当中因为SingleThreadEventExecutor的问题 导致每天内存会增加的问题,我会通过源码修复,修复完成我会开通MQTT,http,tcp,udp,coap端口提供给大家测试。

凯亚 (Kayak) 是什么?

凯亚(Kayak)是基于.NET6.0软件环境下的surging微服务引擎进行开发的, 平台包含了微服务和物联网平台。支持异步和响应式编程开发,功能包含了物模型,设备,产品,网络组件的统一管理和微服务平台下的注册中心,服务路由,模块,中间服务等管理。还有多协议适配(TCP,MQTT,UDP,CoAP,HTTP,Grpc,websocket,rtmp,httpflv,webservice,等),通过灵活多样的配置适配能够接入不同厂家不同协议等设备。并且通过设备告警,消息通知,数据可视化等功能。能够让你能快速建立起微服务物联网平台系统。

凯亚物联网平台:http://117.72.121.2:3100(用户名:fanly 密码:123456)

链路跟踪Skywalking V8:http://117.72.121.2:8080/

surging 微服务引擎开源地址:https://github.com/fanliang11/surging(后面surging 会移动到microsurging进行维护)

二、网络组件

1.编辑创建MQTT协议的网络组件,可以选择共享配置和独立配置(独立配置是集群模式)

三、自定义协议

  • 如何创建自定义协议模块

如果是网络编程开发,必然会涉及到协议报文的编码解码处理,那么对于平台也是做到了灵活处理,首先是协议模块创建,通过以下代码看出协议模块可以添加协议说明md文档, 身份鉴权处理,HTTP路由,消息编解码,元数据配置。下面一一介绍如何进行编写

复制代码
  public class Demo5ProtocolSupportProvider : ProtocolSupportProvider
    {
             private readonly DefaultConfigMetadata _mqttConfig = new DefaultConfigMetadata(
        "Mqtt认证配置"
        , "secureId以及secureKey在创建设备产品或设备实例时进行配置.\r\n    timestamp为当前时间戳(毫秒), 与服务器时间不能相差5分钟.\r\n        md5为32位, 不区分大小写")
        .Add("secureId", "secureId", "用户唯一标识编号", StringType.Instance)
        .Add("secureKey", "secureKey", "密钥", StringType.Instance);

        public override IObservable<ProtocolSupport> Create(ProtocolContext context) {

              var support = new ComplexProtocolSupport();

            support.Id = "demo5";

            support.Name = "演示协议5";

            support.Description = "演示协议5";          support.AddDocument(MessageTransport.Mqtt, "Document/document-mqtt.md");     support.AddAuthenticator(MessageTransport.Mqtt, new DefaultAuthenticator());

                  support.AddRoutes(MessageTransport.Mqtt, new List<TopicMessageCodec>() {

                     TopicMessageCodec.DeviceOnline,

                      TopicMessageCodec.ReportProperty,

                      TopicMessageCodec.WriteProperty,

                       TopicMessageCodec.ReadProperty,

                         TopicMessageCodec.Event

                  }.Select(p => MqttDescriptor.Instance(p.Pattern)

                      .GroupName(p.Route.GroupName())

                      .Path(p.Pattern)

                      .ContentType(MediaType.ToString(MediaType.ApplicationJson))

                      .Description(p.Route.Description())

                      .Example(p.Route.Example())

                 ).ToList());         support.AddMessageCodecSupport(MessageTransport.Mqtt, () => Observable.Return(new ScriptDeviceMessageCodec(support.Script)));

                           support.AddConfigMetadata(MessageTransport.Mqtt, _mqttConfig); return Observable.Return(support); } } 
  1. 添加协议说明文档如代码: support.AddDocument(MessageTransport.Http, "Document/document-http.md");,文档仅支持 markdown文件,如下所示
复制代码
### 认证说明

CONNECT报文:
```text
clientId: 设备ID
username: secureId+"&"+timestamp
password: md5(secureId+"&"+timestamp+"&"+secureKey)
 ```

说明: secureId以及secureKey在创建设备产品或设备实例时进行配置.
    timestamp为当前时间戳(毫秒), 与服务器时间不能相差5分钟.
        md5为32位, 不区分大小写.
  1. 添加身份鉴权如代码: support.AddAuthenticator(MessageTransport.Http, new Demo5Authenticator()) ,自定义身份鉴权Demo5Authenticator 代码如下:
复制代码
    public class DefaultAuthenticator : IAuthenticator
    {
        public IObservable<AuthenticationResult> Authenticate(IAuthenticationRequest request, IDeviceOperator deviceOperation)
        {
            var result = Observable.Return<AuthenticationResult>(default);
            if (request is DefaultAuthRequest)
            {
                var authRequest = request as DefaultAuthRequest;  
                var username = authRequest.UserName; 
                var password = authRequest.Password;
                String[] arr = username.Split("&");
                if (arr.Length <= 1)
                {
                    return Observable.Return(AuthenticationResult.Failure(StatusCode.CUSTOM_ERROR, "用户名格式错误"));
                }
                var requestSecureId = arr[0];
                long.TryParse(arr[1], out long time);
                if (Math.Abs(Utility.CurrentTimeMillis() - time) > TimeSpan.FromMinutes(10).TotalMilliseconds)
                {
                    return Observable.Return(AuthenticationResult.Failure(StatusCode.CUSTOM_ERROR, "时间不一致"));
                }
                var configs = deviceOperation.GetConfigs("secureId", "secureKey").Subscribe(p =>
                {
                    try
                    {
                        var secureId = p.GetValue("secureId").Convert<string>();
                        var secureKey = p.GetValue("secureKey").Convert<string>();
                        var encryptStr = $"{username}&{secureKey}".GetMd5Hash();
                        if (requestSecureId.Equals(secureId) && encryptStr.Equals(password))
                        {
                            result= result.Publish(AuthenticationResult.Success(deviceOperation.GetDeviceId()));
                        }
                        else
                        {
                            result= result.Publish(AuthenticationResult.Failure(StatusCode.CUSTOM_ERROR, "验证失败,密钥错误"));

                        }
                    }
                    catch (Exception ex)
                    {
                        result = result.Publish(AuthenticationResult.Failure(StatusCode.CUSTOM_ERROR, "请求参数格式错误"));
                    }
                });
            }
            else
            result = Observable.Return<AuthenticationResult>(AuthenticationResult.Failure(StatusCode.CUSTOM_ERROR, "不支持请求参数类型"));
            return result;
        }

        public IObservable<AuthenticationResult> Authenticate(IAuthenticationRequest request, IDeviceRegistry registry)
        {
            var result = Observable.Return<AuthenticationResult>(default);
            var authRequest = request as DefaultAuthRequest;
            registry
              .GetDevice(authRequest.DeviceId)
              .Subscribe( p =>Authenticate(request, p).Subscribe(authResult => result = result.Publish(authResult)));
            return result;
        }
    }
  1. 添加Http路由代码support.AddRoutes,那么如何配置呢,代码如下:
复制代码
    public static BasicMessageCodec ReportProperty =>
 new BasicMessageCodec("/*/properties/report", typeof(ReadPropertyMessage), route => route.GroupName("属性上报")  .Description("上报物模型属性数据") .Example("{\"properties\":{\"属性ID\":\"属性值\"}}"));

4.添加元数据配置代码 support.AddConfigMetadata(MessageTransport.Http, _httpConfig); _httpConfig代码如下

复制代码
        private readonly DefaultConfigMetadata _mqttConfig = new DefaultConfigMetadata(
"Mqtt认证配置"
, "secureId以及secureKey在创建设备产品或设备实例时进行配置.\r\n    timestamp为当前时间戳(毫秒), 与服务器时间不能相差5分钟.\r\n        md5为32位, 不区分大小写")
.Add("secureId", "secureId", "用户唯一标识编号", StringType.Instance)
.Add("secureKey", "secureKey", "密钥", StringType.Instance);
  • 如何加载协议模块,协议模块包含了协议模块支持添加引用加载和上传热部署加载。

引用加载模块

上传热部署协议模块

四、设备网关

创建设备网关

五、产品管理

以下是添加产品。

设备接入

六、设备管理

添加设备

mqtt 认证配置

创建告警阈值

七、测试

利用工具进行连接MQTT

然后可以在平台看到设备日志,看下连接失败成功情况

相关推荐
fanly118 天前
.net clr 8年才修复的BUG,你让我损失太多了
surging microservice
fanly1112 天前
surging 集成SuperSocket预发布版本2.0
surging microservice
fanly1123 天前
通过jmeter压测surging
surging microservice
fanly1124 天前
帮客户解决基于surging的物流速运网关内存泄漏问题
surging microservice
fanly111 个月前
从木舟平台来庖丁解牛微服务
surging microservice
fanly111 个月前
针对于基于surging的dotnetty组件内存泄漏问题
surging microservice
fanly111 个月前
线上测试木舟物联网平台之如何通过HTTP网络组件接入设备
surging microservice
fanly111 个月前
线上测试木舟平台发布
surging microservice
fanly115 个月前
基于surging的木舟平台如何分布式接入设备
surging microservice