如何使用Azure IoT Hub Device SDK管理连接状态
分类: Azure物联网 ◆ 标签: #Azure #IoT Hub # #入门 #指南 ◆ 发布于: 2023-06-13 22:27:51

我们前面介绍了如何使用设备SDK
连入Azure IoT Hub
,我们这一篇来介绍一下如何识别设备的连接状态和管理连接。
我们先来介绍一下可能出现的连接状态:
- 瞬时网络问题:一般出现瞬时网络问题,SDK会自动重试, 连接状态变为
Disconnected_Retrying
,SDK
显示的改变原因变为Communication_Error
。 - 认证使用的对称密钥或者证书过期:这种情况下连接状态会变为
Disconnected
,SDK
显示的原因为变为Bad_Credential
, 需要注意这种情况下SDK
不会重试。 - 设备标识在
Azure IoT Hub
被删除或者被禁止,连接状态会变为Disconnected
,SDK
显示原因为DeviceNotFoundException
。 这种情况SDK
也不会自动重试。 - 连接过程中由于认证成功之后生成的
sas token
过期,连接状态会变为Disconnected_Retrying
,SDK
显示的原因会变为Communication_Error
,这种情况下SDK
会自动重试。
如上是我们列举的可能的例子,实际上你可以参考下表对于连接状态的改变做一个详细的了解:
连接状态 | 改变原因 | 连接由谁处理 | 说明 | 需要采取的动作 |
---|---|---|---|---|
Connected | Connection_Ok | SDK | SDK和服务之间的连接已经建立 | 客户端已经准备好使用 |
Disconnected_Retrying | Communication_Error | SDK | 由于一些瞬时的问题(例如网络,瞬时连接丢失,sas token过期)等发生的时候,连接状态会改变Disconnected_Retrying , SDK会尝试进行重试,用户可以设计重试规则,当重试的最大次数达到了,才会抛出异常,并出错。 | 当在这个状态时,不要dispose 或者重新初始化设备客户端实例,所有其他的操作会被queue起来,等到重试成功或者失败继续进行 |
Disconnected | Device_Disabled | 应用 | 设备或者模块标识被删除或者被禁止 | Dispose 设备SDK实例,报异常退出应用 |
Disconnected | Bad_Credential | 应用 | 认证使用的标识过期或者无效 | Dispose 设备SDK实例,报异常,退出应用 |
Disconnected | Communication_Error | 应用 | 连接遇到无法修复的通讯错误 | Dispose 设备SDK`,报异常,退出应用, 也可以考虑安排一个指数增长级别的重试,等候网络恢复。 |
Disconnected | Retry_Expired | 应用 | 重试超过了最大次数 | Dispose 设备SDK实例,报异常,退出应用 |
Disabled | Client_Close | 应用 | 客户端明确关闭连接 | 如果应用想进一步做其他的操作,需要Dispose SDK实例,并重新初始化客户端 |
设备连接管理的最佳实践
大多数的物联网设备都运行在网络受限的环境下,所以连接问题是一个常态,为了更好的管理连接,建议使用Azure
提供的设备SDK
来进行连接的管理,重试等。
Azure SDK
提供了两个方面特性供用户使用:
SDK
监控的错误
连接失败可能发生在各个层面:
- 网络错误:
Socket
连接错误或者DNS
解析错误。 - 协议级别的错误: 例如
MQTT
的sas token过期,AMQP
的linked deattach等等。 - 应用错误。
SDK
会检测上述三个层面的错误,但是针对操作系统级别或者硬件级别的错误,SDK
是不管的。
SDK
的重试机制
SDK
通过如下的步骤进行重试:
SDK
侦测发生的错误,这些错误可能是来自网络,协议或者应用。SDK
使用错误过滤甄别错误的类型,决定是否进行重试。- 如果
SDK
检测到该错误是不可恢复的错误,SDK
通知用户,并报错退出。 - 如果
SDK
侦测到该错误是可恢复错误,SDK
会根据指定的重试机制,安排重试。SDK
默认使用Exponential back-off with jitter
重试规则。 - 重试达到了最大次数或者最长事件,
SDK
报错退出。
针对于连接的状态变化,SDK
允许用户注册一个回调函数用于处理连接状态改变的机制。
SDK
有三种重试机制类型:
Exponential back-off with jitter
Custom retry
No Retry
连接管理Demo
我们上面讨论了很多理论,现在我们开始动手实现上述理论,基本原则是:
- 注册一个回调函数(委托),监控连接状态的变化。
- 根据错误的基本信息,对照表1, 确认那种错误是可恢复的,那种是不可恢复的。可恢复的安排重试。
- 重试根据场景选择默认的
Exponential back-off with jitter
机制,还是自定义重试机制。
我先创建一个简单的例子,在这个例子我们测试一下连接的各种变化。
dotnet new console -o ConnectionChange
cd .\ConnectionChange\
dotnet add package Microsoft.Extensions.Hosting
dotnet add package Microsoft.Azure.Devices.Client
本例中为了简便,我们使用Symmetric Key
认证设备。请先使用Azure Portal
创建好一个使用Symmetric Key
认证的设备,并记录下连接字符串,同样使用Security Manager
来管理密钥。
dotnet user-secrets init
dotnet user-secrets set "Device:ConnectString" "<你的设备连接字符串>"
然后使用VS Code
打开项目目录,编辑Program.cs
:
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Azure.Devices.Client; using System.Text; using System.Text.Json; using IHost host = Host.CreateDefaultBuilder(args).Build(); var ConnectionString = host.Services.GetRequiredService<IConfiguration>().GetValue<string>("Device:ConnectString"); var deviceClient = DeviceClient.CreateFromConnectionString(ConnectionString, TransportType.Mqtt, new ClientOptions { SasTokenTimeToLive = TimeSpan.FromSeconds(5) } ); IRetryPolicy retryPolicy = new ExponentialBackoff(3, TimeSpan.FromMicroseconds(100), TimeSpan.FromSeconds(3), TimeSpan.FromMicroseconds(100)); deviceClient.SetRetryPolicy(retryPolicy); deviceClient.SetConnectionStatusChangesHandler(ConnectionStatusChangeHandlerAsync); using var cts = new CancellationTokenSource(); await SendDeviceToCloudMessagesAsync(deviceClient, cts.Token); await host.RunAsync();
我们注意到这个代码里有三个部分:
var deviceClient = DeviceClient.CreateFromConnectionString(ConnectionString, TransportType.Mqtt, new ClientOptions { SasTokenTimeToLive = TimeSpan.FromSeconds(5) } );
需要注意的是我们给客户端创建时指定了一个ClientOptions
, 这里指定认证成功之后的SAS Token
多长时间过期,我们为了测试,这里仅仅给了5秒,默认情况是一个小时。
IRetryPolicy retryPolicy = new ExponentialBackoff(3, TimeSpan.FromMicroseconds(100), TimeSpan.FromSeconds(3), TimeSpan.FromMicroseconds(100)); deviceClient.SetRetryPolicy(retryPolicy);
这个部分设置当可以retry的时候,最多重试3次,最小的间隔是100毫秒,最大的简单是2秒,指数级增长。
deviceClient.SetConnectionStatusChangesHandler(ConnectionStatusChangeHandlerAsync);
用于设置连接状态变化的监控函数,该函数现阶段很简单:
void ConnectionStatusChangeHandlerAsync(ConnectionStatus status, ConnectionStatusChangeReason reason) { Console.WriteLine($"Connection status changed: status={status}, reason={reason}"); }
就打印状态的变化方便我们观察。
最后我们定义方法:SendDeviceToCloudMessagesAsync
, 用于发送遥测数据,这个方法的定义我就不贴了,大家可以去前面几章拷贝一下,或者本章的代码库里找一下。
注意
上述简单的例子代码库在:https://github.com/hylinux/azure-iot-hub-examples/tree/main/ConnectionChange
然后我们开始运行:
$env:DOTNET_ENVIRONMENT = "Development"
dotnet run
我们开始测试。
测试网络断开
测试时将计算机网络临时屏蔽:
临时屏蔽网络,看到重试消息之后,立即打开网络:
从图上可以看到,红1表示连接建立,红2处表示网络断开,进行重试。红3网络恢复,连接继续。临时网络断开,等候重试次数结束:
从图上可以看到,红1连接建立,红2网络断开重试,红3自动重试次数达到最大,立即报异常退出。
测试Disabled
设备
我们打开Azure Portal
-> Device Management
-> Device
, 在设备列表里找到你的设备,打开设备,如下可以Disabled
和Enabled
设备:
如图上所示,我们可以利用红1 Disabled
设备,红2 Enable
设备, 红3 重新生成连接字符串,红4 保存结果。
我们先运行应用,然后使用红1 Disabled
设备,并保存: Save
:
从运行结果我们可以看到,Disabled
设备之后,SDK
也会进入重试,并且在重试次数结束之后,你可以看到报的异常和之前都不一样的。
测试重新生成连接字符串
我们先将设备恢复,然后运行应用,然后使用Azure Portal
上的Manage Keys
重新生成字符串:
发现重新生成连接字符串之后,连接没有中断的情况下居然不影响。
但是如果重新运行应用:
就报异常了。
本篇介绍了如何监听连接状态变化的基本方法,以及如何设置重试的基本方法,我们下一次一起来学习一下由微软官方提供的一个重新连接的实例,这个实例的代码完全可以应用在产线上,非常值得认证的研究和学习。