Abp vNext 集成 MQTT

发布于 2022年 05月 19日 18:30

Abp vNext 集成 MQTT

Broker

ApsNet Core 集成 MQTT

引用包

    <PackageReference Include="MQTTnet.AspNetCore" Version="3.1.2" />

添加服务

代码清单:Artisan.IotHub.HttpApi.Host/IotHubHttpApiHostModule.cs

public override void ConfigureServices(ServiceConfigurationContext context)
{
    // ......
     ConfigureMqttService(context, configuration);
}

private void ConfigureMqttService(ServiceConfigurationContext context, IConfiguration configuration)
{
    var settings = configuration.GetSection("MqttServer").Get<MqttServerSettings>();
    context.Services.ConfigureMqttService(settings);
}

appsettings.json 中的配置:

  "MqttServer": {
    "IpAddress": "localhost",
    "DomainName": "",
    "Port": 2883,
    "EnableTls": false,
    "TlsPort": 8883
  },

其中:

方法 ConfigureMqttServic() 是扩展方法,如下所示:

代码清单:Artisan.IotHub.Application/Mqtts/Extensions/MqttConfiguration.cs

  public static class MqttConfiguration
  {
            public static void ConfigureMqttService(
                this IServiceCollection services, 
                MqttServerSettings settings)
            {
                services.AddHostedMqttServerWithServices(options =>
                {
                    options.WithDefaultEndpointPort(settings.Port).WithDefaultEndpoint();
                    var mqttService = services.GetRequiredService<MqttServerService>();
                    mqttService.ConfigureMqttServerOptions(options);
                });

                services.AddMqttConnectionHandler();
                services.AddMqttWebSocketServerAdapter();
                services.AddConnections();
            }
      
         // .....
    }

添加 MQTT Endpoint

代码清单:Artisan.IotHub.HttpApi.Host/IotHubHttpApiHostModule.cs

public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
        app.UseAbpRequestLocalization();
        app.UseCorrelationId();
        app.UseStaticFiles();
        app.UseRouting();
        app.UseCors();
        app.UseAuthentication();
        app.UseAuthorization();
        // 添加 MQTT 中间件
        app.UseMqttServer();
        app.UseSwagger();
}

其中:方法UseMqttServe()是自定义的扩展方法,如下所示:

代码清单:Artisan.IotHub.Application/Mqtts/Extensions/MqttConfiguration.cs

  public static class MqttConfiguration
    {
        //......
      
        public static void UseMqttServer(this IApplicationBuilder app)
        {
            app.UseMqttServer(mqttServer =>
            {
                app.ApplicationServices
                    .GetRequiredService<MqttServerService>()
                    .ConfigureMqttServer(mqttServer);
            });

            app.UseEndpoints(endpoint =>
            {
                endpoint.MapMqtt("/mqtt");
            });
        }
    }

ConfigureMqttServer()调用:

代码清单:Artisan.IotHub.Application/Mqtts/MqttServerService.cs

public class MqttServerService : IMqttServerService, ISingletonDependency
{
    private readonly IMqttConnectionService  _mqttConnectionService;
    private readonly IMqttSubscriptionService _mqttSubscriptionService;
    private readonly IMqttPublishingService _mqttPublishingService;

    public MqttServerService(
        IMqttConnectionService mqttConnectionService,
        IMqttSubscriptionService mqttSubscriptionService,
        IMqttPublishingService mqttPublishingService)
    {
        _mqttConnectionService = mqttConnectionService;
        _mqttSubscriptionService = mqttSubscriptionService;
        _mqttPublishingService = mqttPublishingService;
    }

    public void ConfigureMqttServer(IMqttServer mqttServer)
    {
        _mqttConnectionService.ConfigureMqttServer(mqttServer);
        _mqttSubscriptionService.ConfigureMqttServer(mqttServer);
        _mqttPublishingService.ConfigureMqttServer(mqttServer);
    }

    public void ConfigureMqttServerOptions(AspNetMqttServerOptionsBuilder options)
    {
        _mqttConnectionService.ConfigureMqttServerOptions(options);
        _mqttSubscriptionService.ConfigureMqttServerOptions(options);
        _mqttPublishingService.ConfigureMqttServerOptions(options);
        options.WithoutDefaultEndpoint();
    }
}

设置 MQTT监听端口

通过ConfigureKestrel()方法在 Kestrel 上设置MQTT的监听端口,如下代码所示:

代码清单:Artisan.IotHub.HttpApi.Host/Program.cs

            var builder = WebApplication.CreateBuilder(args);
            // ......
            builder.WebHost.ConfigureKestrel(option =>
            {
                option.ConfigureMqttServer();
            });

其中:方法 ConfigureMqttServer()是扩展方法,如下所示:

代码清单:Artisan.IotHub.HttpApi.Host/KestrelServerOptionsExtensions.cs

public static class KestrelServerOptionsExtensions
{
        /// <summary>
        /// 在 KestrelServer 中设置 MqttServer 的监听端口
        /// </summary>
        /// <param name="option"></param>
        public static void ConfigureMqttServer(this KestrelServerOptions option)
        {
            var configuration = option.ApplicationServices.GetRequiredService<IConfiguration>();
            var mqttSettings = configuration.GetSection("MqttServer").Get<MqttServerSettings>();
            
            //option.ListenAnyIP(mqttSettings.Port, opt => opt.UseMqtt());
            if (mqttSettings.IpAddress.ToLower() == "localhost")
            {
                option.ListenLocalhost(mqttSettings.Port, opt => opt.UseMqtt());
            }
            else
            {
                option.Listen(
                    IPAddress.Parse(mqttSettings.IpAddress), 
                    mqttSettings.Port, opt => opt.UseMqtt()
                );
            }
        }
}

修复 Http和Https 的监听端口

通过ConfigureKestrel()方法在 Kestrel 上设置MQTT的监听端口,如下代码所示:

          builder.WebHost.ConfigureKestrel(option =>
            {
                option.ConfigureMqttServer();
            });

 =>           option.Listen(
                    IPAddress.Parse(mqttSettings.IpAddress), 
                    mqttSettings.Port, opt => opt.UseMqtt()
                );

但是这样做,会出现一个BugHttp和Https 的监听端口将会失效 !!!
通过Http和Https 监听端口(比如:https://localhost:44359)是无法访问 Api的。

原因:

通过 ConfigureKestrel() 方法在 Kestrel 上设置的Http和Https 监听端口后,其它地方设置(例如:在launchSettings.json 文件)的监听方式会被覆盖而失效!

而我们又没有在ConfigureKestrel()的方法中重新设置Http和Https 的监听端口。

如何修复呢?

可通过修改配置文件appsettings.json来设置Http和Https的监听端口,具体操作如下:

在配置文件appsettings.json中添加如下 Kestrel 配置节点:

{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://localhost:7059",
        "Protocols": "Http1AndHttp2" 
      },
      "Https": {
        "Url": "https://localhost:44359",
        "Protocols": "Http1AndHttp2" 
      },
    }
  }
  // .....   
}

这样就通过配置文件的方式为Kestrel 设置了Http和Https的监听端口

推荐文章