工作者角色和Web角色之间的Azure通信

时间:2015-04-28 09:19:14

标签: c# azure azure-web-roles azure-worker-roles

您好我正在构建一个Cloud Service,它目前拥有一个Web和一个Worker角色。我想要的工作流程是:浏览器调用Web角色上的webApi控制器,该控制器将消息发送到队列(或服务总线),然后由工作者角色处理。到现在为止还挺好。现在,当worker角色完成处理消息时,我想在web角色上调用一个方法,然后谁将向浏览器发出处理已完成的信号(通过SignalR)。如果这不是一个正确的问题,请原谅我,因为这更像是一个“最佳实践”问题,而不是一个真正的问题。到目前为止,我已经考虑了两种方法:

  1. 辅助角色使用进度和任务完成更新表行(在表存储中)。 Web角色没有信号。浏览器直接读取表存储(通过REST api),因此知道任务何时完成。虽然我不喜欢常规轮询的方法,但我希望有一个“基于事件”的解决方案,但这种方法效果很好(我已经测试过了)。此外,一旦客户端获得进程已完成的信息,它必须执行对web api方法的附加调用,以便向其他客户端(通过SignalR)广播操作已完成。

  2. Interrole communication与SignalR一起使用(请参阅下面的代码示例)也有效(已经过测试)

  3. 代码示例:

    var protocol = "http";      
    var ipAddress = RoleEnvironment.Roles["XXX.YYY.Web"]
            .Instances[0]
            .InstanceEndpoints.ToArray()
            .Where(ep => ep.Value.Protocol == protocol)
            .First()
            .Value.IPEndpoint.ToString();
    
    var stringEndpoint = string.Format("{0}://{1}", protocol, ipAddress.ToString());                
    Trace.WriteLine("Retrieved endpoint address: " + stringEndpoint, "Information");            
    HubConnection connection = new HubConnection(stringEndpoint);
    IHubProxy proxy = connection.CreateHubProxy("MyWebHub");
    connection.Start().Wait();
    
    //later...
    
    proxy.Invoke("ProgressUpdated", clientId, progress);
    

    我的问题是:是否还有其他(更好)的方式来沟通方向工作者角色 - >网络角色?也就是说,当一个辅助角色完成处理时,触发Web角色的方法?然后,Web角色上的方法将通过SignalR将更新广播到所有客户端。我还看了一下Event Hubs,但根据我的理解,事件消费者仍将继续担任工作角色。

4 个答案:

答案 0 :(得分:2)

好的经过一些额外的尝试和一些研究后,我想出了一个可能的解决方案......我正在使用这段代码

AzureServiceBus.QueueClient.OnMessage((message) =>
            {
                try
                {
                    // Process message from queue
                    Trace.WriteLine("Message received: " + ((BrokeredMessage)message).Label);

                    var breezeController = new BreezeController();
                    breezeController.TestSignal();

                    // Remove message from queue
                    message.Complete();
                }
                catch (Exception)
                {
                    // Indicates a problem, unlock message in queue
                    message.Abandon();
                }
            });

在Web角色的OnStart方法(不是Worker角色)中,以便我可以在Web角色中引用我的方法(在这种情况下为TestSignal()),但事实证明,当从内部调用时,IHubContext始终为null这个OnMessage事件处理程序,因为它很可能属于(很可能)不同的AppDomain,因此甚至不共享signalR的静态Hub。因此,我在Global.asax.cs中移动了相同的整个代码,以便它将共享相同的AppDomain,现在它可以工作。我想我会坚持这种方法,因为我比连续民意更喜欢它。

答案 1 :(得分:0)

我认为您已经拥有足够的知识和实施经验来实现它。使用SignalR是很好的方法,我向你学习。

另一种有点不同的方法,我使用Azure Scheduler进行常量轮询,以便向WebRole发送GET消息。

IMO,作为webapi服务器的本质,轮询是遵循Web服务器设计的最合适和最可靠的方法。

答案 2 :(得分:0)

问题有点晚了:)但我们实现的是指定在将消息发送到辅助角色后Web角色等待的响应队列和会话ID。您可以调整它以避免在Web角色等待工作者角色回复时阻止响应(在我们的场景中我们要特别等待)

WebRole

string sessionId = Guid.NewGuid().ToString(); 
[...]
// put message in sync queue
var message = new BrokeredMessage(request)
{
   ReplyToSessionId = sessionId
};
await ServiceBusConnector.Instance.SyncClient.SendAsync(message);

// now listen to reply on session response queue (only accepts message on same session id)
MessageSession session = await ServiceBusConnector.Instance.SyncResponseClient.AcceptMessageSessionAsync(sessionId);

BrokeredMessage responseMessage = await session.ReceiveAsync(TimeSpan.FromMinutes(5));
await responseMessage.CompleteAsync();
await session.CloseAsync();

Response response = responseMessage.GetBody<Response>();
// process Worker Role's response

工人角色

    // if a ReplyToSessionId has been specified, it means that the sender is
    // actively waiting for a response
    if (!String.IsNullOrEmpty(receivedMessage.ReplyToSessionId))
    {
        // now respond on sync response queue
        var responseMessage = new BrokeredMessage(response)
        {
            SessionId = receivedMessage.ReplyToSessionId
        };

        // consider allowing client to specify a ReplyTo response queue (not needed for now)
        await ServiceBusConnector.Instance.SyncResponseClient.SendAsync(responseMessage);
    }

答案 3 :(得分:0)

另请在此处查看Jessie's approach,以便通过HttpClient直接与应用进行通信。

public class Functions
{
    public static async Task ProcessQueueMessage([QueueTrigger("jobqueue")] Guid jobId, TextWriter log)
    {
        for (int i = 10; i <= 100; i+=10)
        {
            Thread.Sleep(400);

            await CommunicateProgress(jobId, i);
        }
    }

    private static async Task CommunicateProgress(Guid jobId, int percentage)
    {
        var httpClient = new HttpClient();

        var queryString = String.Format("?jobId={0}&progress={1}", jobId, percentage);
        var request = ConfigurationManager.AppSettings["ProgressNotificationEndpoint"] + queryString;

        await httpClient.GetAsync(request);
    }
}