我正在使用在 Windows 主机操作系统上运行 Linux 容器的 Azure IoT Edge 运行时。 我有两个模块,ModuleA 和 ModuleB。 ModuleA 有一个注册的直接方法,称为“MethodA”,ModuleB 有一个注册的直接方法,称为“MethodB”。
当我调用 MethodA 时,我希望该方法调用位于另一个模块中的 MethodB(但在同一 IoT Edge 运行时中运行)。
我正在使用适用于 C# 的 Azure IoT SDK,并且 在 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));
}
}
我尝试从方法A调用的方法B:
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 调试器中我收到异常
Exception thrown: 'Microsoft.Azure.Devices.Client.Exceptions.IotHubCommunicationException' in System.Private.CoreLib.dll
当我检查 Message/StackTrace-print 日志时,我得到:
An error occurred while sending the request. at Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.d__21.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.d__172.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Azure.Devices.Client.ModuleClient.d__57.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at OrderGateway.Program.d__3.MoveNext() in /app/Program.cs:line 92
我尝试使用 Amqp 和 TCP/WebSocket_Only 以及 Mqtt 和 TCP/WebSocket_Only 来初始化 ModuleClient。 我还尝试在 Ubuntu 18.04 VM 上使用这些模块运行 IoT Edge 运行时,但没有成功。
即使当我尝试使用 ioTHubModuleClient.InvokeMethodAsync() 调用位于同一模块中的方法时,我也会得到相同的异常/堆栈跟踪...
我还尝试从 ModuleA 的 Init() 函数调用 MethodB,以尝试不在回调上下文中调用直接方法,但我确实得到了相同的异常。
github 上有一个 Unresolved 问题,您可以在这里找到:https://github.com/Azure/iotedge/issues/204
但感觉它有点停滞了,我没有得到任何帮助。
根据我的理解,这应该是可能的,但我不知道我错过了什么?
最佳答案
我刚刚使这个场景可行,但它与您的描述有点不同。
所以,这就是我所做的: 1-创建2个模块,ModuleA和ModuleB; 2-在模块A中添加方法A,在模块B中添加方法B; 3-在模块 B(不是模块 A)上,我添加了以下代码:
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":"未找到客户端angelodTransparentGateway/ModuleA","exceptionMessage":"","status":0,"payload": null} 未注册
在 Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.ExecuteAsync(HttpMethod httpMethod, Uri requestUri, Func3modifyRequestMessageAsync, Func
2 isSuccessful, Func3 processResponseMessageAsync, IDictionary
2 errorMappingOverrides , CancellationToken 取消 token )
在 Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.PostAsync[T1,T2](Uri requestUri,T1 实体,IDictionary2 errorMappingOverrides,IDictionary
2 customHeaders,CancellationToken CancellationToken)
在 Microsoft.Azure.Devices.Client.ModuleClient.InvokeMethodAsync(Uri uri,MethodRequest methodRequest,CancellationToken CancellationToken)
在/app/Program.cs 中的 ModuleB.Program.Init() 处:第 74 行
但这是设计使然,只是因为部署后创建模块需要时间,但在不到 1 分钟的时间内我就获得了成功的结果:
方法A已被调用 收到数据:{"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 上更新它)。
关于c# - Azure IoT Edge ModuleClient 调用另一个模块中的直接方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52073548/