Azure IoT Edge模块客户端在另一个模块中调用直接方法

时间:2018-08-29 09:04:29

标签: c# azure azure-iot-hub azure-iot-sdk azure-iot-edge

我正在使用Azure IoT Edge运行时,该运行时在Windows主机OS上运行Linux容器。 我有两个模块,即ModuleA和ModuleB。 ModuleA具有注册的直接方法称为“ MethodA”,而ModuleB具有注册的直接方法称为“ MethodB”。

当我调用MethodA时,我希望该方法调用位于另一个模块(但在同一IoT Edge运行时中运行)的MethodB。

我正在将Azure IoT SDK用于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.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)      在Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.d__172.MoveNext()   ---从之前引发异常的位置开始的堆栈结束跟踪---      在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()      在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)      在Microsoft.Azure.Devices.Client.ModuleClient.d__57.MoveNext()   ---从之前引发异常的位置开始的堆栈结束跟踪---      在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()      在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)      在System.Runtime.CompilerServices.TaskAwaiter1.GetResult()      在/app/Program.cs:第92行的OrderGateway.Program.d__3.MoveNext()中

我尝试使用带有TCP / WebSocket_Only的Amqp以及带有TCP / WebSocket_Only的Mqtt来初始化ModuleClient。 我还尝试在Ubuntu 18.04 VM上使用这些模块运行IoT Edge运行时,但没有成功。

即使当我尝试使用ioTHubModuleClient.InvokeMethodAsync()调用位于同一模块中的方法时,也会得到相同的异常/ stacktrace ...

我还尝试从ModuleA的Init()函数调用MethodB,以尝试在回调的上下文中不调用直接方法,但是我确实得到了相同的异常。

在github上有一个未解决的问题,您可以在这里找到:https://github.com/Azure/iotedge/issues/204

但是它感觉有点停滞了,我没有得到任何帮助。

以我的理解,这应该可行,但我不知道自己缺少什么?

1 个答案:

答案 0 :(得分:2)

我只是使这种情况起作用,但这与您的描述有些不同。

所以,这是我所做的: 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,Func 3 modifyRequestMessageAsync, Func 2成功,Func 3 processResponseMessageAsync, IDictionary 2 errorMappingOverrides,CancellationToken cancelledToken)处    在Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.PostAsync [T1,T2](Uri requestUri,T1实体,IDictionary 2 errorMappingOverrides, IDictionary 2 customHeaders,CancellationToken cancelledToken)    在Microsoft.Azure.Devices.Client.ModuleClient.InvokeMethodAsync(Uri uri,MethodRequest methodRequest,CancellationToken cancelleToken)    在/app/Program.cs:第74行的ModuleB.Program.Init()中

但这是设计使然,这只是因为部署后需要花费一些时间来创建模块,但是不到一分钟,我就能获得成功的结果:

MethodA已被调用 收到的数据:{“消息”:“你好”}

如果从ModuleA调用MethodB,则不会调用MethodA。但是为了确保它能正常工作,我还测试了模块A调用模块B的代码。

        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上更新它)。