c# - Azure IoT Edge ModuleClient 在另一个模块中调用直接方法
问题描述
我正在使用 Azure IoT Edge 运行时在 Windows 主机操作系统上运行 Linux 容器。我有两个模块,ModuleA 和 ModuleB。ModuleA 有一个注册的直接方法,称为“MethodA”,ModuleB 有一个注册的直接方法,称为“MethodB”。
当我调用 MethodA 时,我希望该方法调用位于另一个模块中的 MethodB(但在同一 IoT Edge 运行时中运行)。
我正在使用 Azure IoT SDK for c# 和 ModuleA 的 Init() 函数我有:
await ioTHubModuleClient.SetMethodHandlerAsync("MethodA", MethodA, ioTHubModuleClient);
在 ModuleB 的 Init() 函数中,我有:
await ioTHubModuleClient.SetMethodHandlerAsync("MethodB", MethodB, null);
MethodA(类似于代理方法):
static async Task<MethodResponse> MethodA(MethodRequest methodRequest, object moduleClient)
{
try
{
ModuleClient ioTHubModuleClient = (ModuleClient)moduleClient;
// Get deviced id of this device, exposed as a system variable by the iot edge runtime
var deviceId = System.Environment.GetEnvironmentVariable("IOTEDGE_DEVICEID");
// Create the request
MethodRequest request = new MethodRequest("MethodB", Encoding.UTF8.GetBytes("{ \"Message\": \"Hello\" }"));
// Execute request
var resp = await ioTHubModuleClient.InvokeMethodAsync(deviceId, "ModuleB", request);
return resp;
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
return await Task.FromResult(new MethodResponse(500));
}
}
我尝试从 MethodA 调用的 MethodB:
private static Task<MethodResponse> MethodB(MethodRequest methodRequest, object userContext)
{
Console.WriteLine("MethodB has been called");
// Get data but we do not do anything with it
var data = Encoding.UTF8.GetString(methodRequest.Data);
Console.WriteLine("Received data: " + data.ToString());
var methodResponse = new MethodResponse(Encoding.UTF8.GetBytes("{\"status\": \"ok\"}"), 200);
return Task.FromResult(methodResponse);
}
请注意,我可以从 azure 门户或使用 Azure SDK for C# -> ServiceClient 类单独调用这两种方法。
我的程序在线崩溃
var resp = await ioTHubModuleClient.InvokeMethodAsync(deviceId, "ModuleB", request);
在 MethodA 和 VS Code 调试器中,我得到了异常
抛出异常:System.Private.CoreLib.dll 中的“Microsoft.Azure.Devices.Client.Exceptions.IotHubCommunicationException”
当我检查我的 Message/StackTrace-print 的日志时,我得到:
发送请求时出错。在 Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.d__21.MoveNext() --- 从先前引发异常的位置结束堆栈跟踪--- 在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 在 System. Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.d__172.MoveNext() 处的 Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) --- 从先前抛出异常的位置结束堆栈跟踪---在 System.Runtime .ExceptionServices.ExceptionDispatchInfo.Throw() 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 在 Microsoft.Azure.Devices.Client.ModuleClient.d__57。
我尝试使用带有 TCP/WebSocket_Only 的 Amqp 以及带有 TCP/WebSocket_Only 的 Mqtt 来初始化 ModuleClient。我还尝试在 Ubuntu 18.04 VM 上使用这些模块运行 IoT Edge 运行时,但没有成功。
即使我尝试使用 ioTHubModuleClient.InvokeMethodAsync() 调用位于同一模块中的方法,我也会得到相同的异常/堆栈跟踪...
我还尝试从 ModuleA 的 Init() 函数调用 MethodB 以尝试不在回调上下文中调用直接方法,但我确实得到了相同的异常。
github 上有一个未解决的问题,您可以在这里找到:https ://github.com/Azure/iotedge/issues/204
但是感觉它有点停滞了,我没有得到任何帮助。
据我了解,这应该是可能的,但我不知道我错过了什么?
解决方案
我只是让这个场景起作用,但它与你的描述有点不同。
所以,这就是我所做的:1-创建 2 个模块,模块 A 和模块 B;2-在 ModuleA 上添加了 MethodA,在 Module B 中添加了 MethodB;3-On 模块 B(不是 ModuleA),我添加了以下代码:
while(true)
{
try
{
Console.WriteLine($"Invoking method On moduleA");
var deviceId = System.Environment.GetEnvironmentVariable("IOTEDGE_DEVICEID");
MethodRequest request = new MethodRequest("MethodA", Encoding.UTF8.GetBytes("{ \"Message\": \"Hello\" }"));
var response = await ioTHubModuleClient.InvokeMethodAsync(deviceId, "ModuleA", request).ConfigureAwait(false);
Console.WriteLine($"Received response with status {response.Status}");
}
catch (Exception ex)
{
Console.WriteLine($"Error invoking method {ex}");
}
await Task.Delay(TimeSpan.FromSeconds(20)).ConfigureAwait(false);
}
这就对了。一开始你会得到一个像这样的异常:
错误调用方法 Microsoft.Azure.Devices.Client.Exceptions.DeviceNotFoundException: 设备 {"message":"Client angelodTransparentGateway/ModuleA not found","exceptionMessage":"","status":0,"payload":null} not在 Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.PostAsync[ T1 3 modifyRequestMessageAsync, Func
, 3 processResponseMessageAsync, IDictionary
T2](Uri requestUri, T1 entity, IDictionary 2 errorMappingOverrides, IDictionary
2 customHeaders, CancellationToken cancelToken) 在 Microsoft.Azure.Devices.Client.ModuleClient.InvokeMethodAsync(Uri uri, MethodRequest methodRequest, CancellationToken cancelToken) 在 ModuleB。/app/Program.cs:line 74 中的 Program.Init()
但这是设计使然,只是因为在部署后创建模块需要时间,但在不到 1 分钟的时间内,我就获得了成功的结果:
MethodA 已被调用 Received data: {"Message":"Hello"}
如果您从 ModuleA 调用 MethodB,则不会调用 MethodA。但为了确保它正常工作,我还测试了调用模块 B 的模块 A。这是代码:
while(true)
{
try
{
Console.WriteLine($"Invoking method On moduleB");
var deviceId = System.Environment.GetEnvironmentVariable("IOTEDGE_DEVICEID");
MethodRequest request = new MethodRequest("MethodB", Encoding.UTF8.GetBytes("{ \"Message\": \"Hello\" }"));
var response = await ioTHubModuleClient.InvokeMethodAsync(deviceId, "ModuleB", request).ConfigureAwait(false);
Console.WriteLine($"Received response with status {response.Status}");
}
catch (Exception ex)
{
Console.WriteLine($"Error invoking method {ex}");
}
await Task.Delay(TimeSpan.FromSeconds(20)).ConfigureAwait(false);
}
两者都有效。不要忘记使用 .NET 2.1 版(在您的 dockerfile 上更新)。
推荐阅读
- sql - 为什么当数据在表中的后面位置重复时(不在相关行旁边),SQL LAG (HIVE) 给出的差异为 0?
- android - 创建新表时房间迁移查询失败
- html - 如何在网页上发布 HTML 电子邮件?
- javascript - Discord.js SyntaxError: Unexpected Identifier (For command handler)
- sql - 有没有其他方法可以解决涉及员工数据的 SQL 问题?
- mysql - MySQL-Query中的关键字
- python - 附加查询字符串以查看渲染函数 Django
- c - 为什么我的opengl 2D动画代码输出窗口没有响应,只显示动画的最终结果
- c++ - 无法从 C++ 中的 SQLAPI++ 程序连接到我的 Oracle 数据库
- php - 望远镜安装在 laravel-7.25 上失败