Communicate Realtime on Azure with Web PubSub
Published Mar 15 2022 07:45 PM 5,807 Views
Microsoft

Azure Web PubSub is a PaaS Service on Azure to send real-time messages and notifications between Web and Mobile applications using WebSockets. This uses a Publish/Subscribe model and allows sending notifications, updates, messages between various connected entities.

 

When to use it?

This Link lists down a set of use cases where it can be used; a quick glance:

  • Live Dashboard and Data Updates

  • Real-time Chats, Grouped Chat

  • Collaboration, Screen sharing Apps

  • Broadcasting Advertisements, Offers, Promotions

  • Supply Chain Control Tower - Notify Warehouses, Customers, Transporters - Real-time

  • Apps for People with Disability - Play Music or Videos for someone else

  • Remote Control Apps, Location Tracking Apps, Enterprise Automation Process

  • And many more...

 

What are we going to discuss here?

  • General Overview of Web PubSub components

  • Create Azure Web PubSub instance and configure

  • Connect multiple clients to the PubSub instance (Azure CLI); and then communicate with each other

  • Understand the concept of Upstream server; and how to build and use it

  • Understand programmatic ways of connecting to the PubSub instance (SDKs)

  • Extend these concepts to a Xamarin based Mobile application and build a simple chat app

  • Make the Chat app Intelligent with Azure Cognitive Service (Translator API) to have Multi-lingual Chat

 

Overview

Following are the main Components of the Azure Web PubSub service

  • Connection - An individual WebSocket connection connected to the Web PubSub service. A unique connection ID is assigned to this connection by the Web PubSub service after it is successfully established

  • Hub - A set of client connections. Multiple clients can connect to same Hub; and multiple Hub can be dded within same PubSub instance

  • Group - A subset of connections to the Hub. Thus making is perfect for Group based chat or communication apps. Each Group can be treated as a Chat room containing multiple client connections (Chat participants)

  • User - Connection belongs to one user. A user can have multiple connections

  • Upstream Server - This is Optional and can be used as Controlling element in the entire messaging system. All or designated Connected clients can get messages routed by Upstream server. This implements multiple Web PubSub Events viz. Connected, Disconnected, Login etc. and allow the messaging system to take appropriate decision while routing the messages to designated clients

  • Message - Can be sent to Upstream server which can then be routed to designated Connected clients Or can even be sent by one Connected client to another.

    So, think of a Supply Chain Control Tower scenario where each WareHouse can be a Connected client, each Transporter or even Customers, Upstream server can be the Control Tower itself - using Web PubSub, the entire system can create a messaging Mesh! So, WH1 can send messages to WH2 (WH1 -> WH2). Similarly WH1->WH2->C1->Trans2. This makes Intelligent Risk Analysis, Decision making, Optimised Routing etc. to be built faster and much easier!

 

Let us get into it....

Declare CLI variables

tenantId=""
subscriptionId=""
location=""
resourceGroup="pubsub-workshop-rg"
pubSubName="pubsub-connect-app"
hubName="testhub1"
group1="group1"
userId1="user1"
userId2="user2"

 

Prepare and Configure

# Login through Azure CLI
az login --tenant $tenantId

# Do you have the extension for Web PubSub? Check it!
az version

# If not Add the required extension
az extension add -n webpubsub

# Let us create a resource group for our work
az group create --name $resourceGroup -l $location
az group show --name $resourceGroup

 

Create Web PubSub instance

# Create the Web PubSub instance
az webpubsub create --name $pubSubName --resource-group $resourceGroup --location eastus --sku Free_F1

# Create the Hub which would host our groups, users etc.
az webpubsub hub create --hub-name $hubName --name $pubSubName --resource-group $resourceGroup \
--allow-anonymous true

 

Connect to Web PubSub

# Connect as user1
az webpubsub client start --name $pubSubName --resource-group $resourceGroup \
--hub-name $hubName --user-id $userId1

 

pubsub-connect-user1.png

 

# Connect as user2
az webpubsub client start --name $pubSubName --resource-group $resourceGroup \
--hub-name $hubName --user-id $userId2

 

pubsub-connect-user2.png

 

  • This returns a unique connectionId for the successful connection

  • Users can perform actions as described in usage section

  • Please note, these are options for clients connected through CLI. We will discuss how client connect through other ways can perform additional actions

 

joingroup
joingroup group1
{"type":"ack","ackId":1,"success":true}

 

pubsub-join-group1.png

 

sendtogroup
sendtogroup group1 "hello from user2"          
{"type":"ack","ackId":2,"success":true}

 

pubsub-group-message-user2.pngpubsub-group-message-user1.png

 

  • user1 joined group1

  • user2 joined group1

  • user2 sends message to the group group1; received by user1 as well since being connected to group1

 

leavegroup
leavegroup group1
{"type":"ack","ackId":2,"success":true}

 

Connect to a specific Connection

az webpubsub service connection send --connection-id "<connectionId>" \
--hub-name $hubName --name $pubSubName --resource-group $resourceGroup --payload "hello connect from server"

 

Broadcast Message to every Connected client

az webpubsub service broadcast --hub-name $hubName --name $pubSubName \
--resource-group $resourceGroup --payload "hello broadcast from server"

 

pubsub-broadcast-user1.pngpubsub-broadcast-user2.png

 

Send Message to a specific User

az webpubsub service user send --hub-name $hubName --name $pubSubName --resource-group $resourceGroup \
--user-id $userId1 --payload "hello from server for:$userId1"

 

Upstream Server

pubsub-overview.png

 

  • Upstream server is built as Serverless Azure Function; as we need this to be active only when some Web PubSub event occurs viz. Login, Connected, Disconnected etc.

  • User1 can send message directly to User2 with out doing a roundtrip to the Upstream server

  • Both User1 and User2 connects to Azure Web PubSub with multiple SubProtocols; requesting permission for specific actions e.g. joingroup, leavegroup, sendtogroup etc.

 

Build Serverless Upstream
[FunctionName("validate")]
public static HttpResponseMessage Validate([HttpTrigger(AuthorizationLevel.Anonymous, "options")]
HttpRequest req, [WebPubSubContext] WebPubSubContext wpsReq)
{
return wpsReq.Response;
}

[FunctionName("connect")]
public static object Connect([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
[WebPubSubContext] WebPubSubContext wpsReq)
{
 if (wpsReq.Request is PreflightRequest || wpsReq.ErrorMessage != null)
{
   return wpsReq.Response;
}
 var request = wpsReq.Request as ConnectEventRequest;
 return request.CreateResponse(request.ConnectionContext.UserId, null, null, null);
}

[FunctionName("message")]
public static async Task<HttpResponseMessage> Broadcast([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req, [WebPubSubContext] WebPubSubContext wpsReq, [WebPubSub(Connection = "PubSubConn", Hub = "PubSubHub")] IAsyncCollector<WebPubSubAction> actions)
{
 if (wpsReq.Request is PreflightRequest || wpsReq.ErrorMessage != null)
{
   return wpsReq.Response;
}
 if (wpsReq.Request is UserEventRequest request)
{                
   await actions.AddAsync(WebPubSubAction.CreateSendToAllAction(request.Data, request.DataType));
}

 return null;
}

[FunctionName("connected")]
public static async Task Connected(
[HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
[WebPubSubContext] WebPubSubContext wpsReq,
[WebPubSub(Connection = "PubSubConn", Hub = "PubSubHub")] IAsyncCollector<WebPubSubAction> actions)
{

 await actions.AddAsync(new SendToAllAction
{
Data = BinaryData.FromString($"{wpsReq.Request.ConnectionContext.UserId} connected."),
   DataType = WebPubSubDataType.Text
});
}

[FunctionName("disconnected")]
[return: WebPubSub(Connection = "PubSubConn", Hub = "PubSubHub")]
public static WebPubSubAction Disconnect([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
                                        [WebPubSubContext] WebPubSubContext wpsReq)
{
 Console.WriteLine("Disconnected.");
 return new SendToAllAction
{
   Data = BinaryData.FromString($"{wpsReq.Request.ConnectionContext.UserId} disconnect."),
   DataType = WebPubSubDataType.Text
};
}

 

Environment Variables
{
   "IsEncrypted": false,
   "Values": {
       "AzureWebJobsStorage": "<strogae account or Azure Function>",
       "FUNCTIONS_WORKER_RUNTIME": "dotnet",
       "PubSubConn": "<pubsub connection string",
       "PubSubHub":  "<Hub name>"
  }
}

 

Build PubSub Client

using Azure.Messaging.WebPubSub;
using System.Net.WebSockets;

// initialize PubSub client to conenct to Azure Web PubSub
WebPubSubServiceClient kWebPubSubServiceClient = new WebPubSubServiceClient(kConnectionString,
                                                                           kHubNameString,
                                                                           new string[]
                                                                        {"webpubsub.joinLeaveGroup.group1",
                                                                          "webpubsub.sendToGroup.group1" });

// Authentication: Get Client Access Uri with Access Token
// Please note: Token is created for a 5 minutes window
var uri = kWebPubSubServiceClient.GetClientAccessUri(new TimeSpan(0, 5, 0), kUser1NameString);

// initialize Websocket client created for User1 with above permissions
var clientWebSocket = new ClientWebSocket();

// This will ensure that clients can directly communicate with each other
clientWebSocket.Options.AddSubProtocol("json.webpubsub.azure.v1");

// Let us connect the Websocket client to the Azure Web PubSub
await clientWebSocket.ConnectAsync(uri, CancellationToken.None);

 

Sending Messages from Web PubSub

// Web PubSub service sending Message to User1
await kWebPubSubServiceClient.SendToUserAsync(kUser1NameString, kTestMessageString);

// Web PubSub service sending Message to all Users connected to Group1
await kWebPubSubServiceClient.SendToGroupAsync(kGroupNameString, kTestMessageString);

// Web PubSub service Adding User1 to Group1
var resp = await kWebPubSubServiceClient.AddUserToGroupAsync(kGroupNameString, kUser1NameString);

// Web PubSub service Removing User2 from Group1
var resp = await kWebPubSubServiceClient.RemoveUserFromGroupAsync(kGroupNameString, kUser2NameString);

// Web PubSub service Closing connection for User1
await kWebPubSubServiceClient.CloseUserConnectionsAsync(kUser1NameString);

 

Sending Messages P2P

var uri = kWebPubSubServiceClient.GetClientAccessUri(new TimeSpan(0, 5, 0), kUser1NameString);

// Create Websocket instance with SubProtocol
var clientWebSocket = new ClientWebSocket();
clientWebSocket.Options.AddSubProtocol("json.webpubsub.azure.v1");

// Connect Websocket instance to the User1 connection
await clientWebSocket.ConnectAsync(uri, CancellationToken.None);

// User1 joins Group1
var joinMessage = JsonSerializer.Serialize(new
{
type = "joinGroup",
group = kGroupNameString,
ackId = 1000
});
await clientWebSocket.SendAsync(Encoding.UTF8.GetBytes(joinMessage), WebSocketMessageType.Text, true,
                               CancellationToken.None);

// User1 sends message to all users of Group1
var groupMessage = JsonSerializer.Serialize(new
{
   type = "sendToGroup",
   group = kGroupNameString,
   data = kTestMessageString,
   ackId = 1001
});
await clientWebSocket.SendAsync(Encoding.UTF8.GetBytes(groupMessage), WebSocketMessageType.Text, true,
                               CancellationToken.None);

 

Receiving Messages


var message = new ArraySegment<byte>(new byte[4096]);
await clientWebSocket.ReceiveAsync(message, CancellationToken.None);
Console.WriteLine(Encoding.UTF8.GetString(message));

 

Examples...

Multi lingual Chat App

pubsub-translator.png

 

  • Each Mobile client creates a WebSocket connection to Azure Web PubSub

  • Each client can send chat messages directly through Azure Web PubSub

  • An Upstream server exists, built with Azure Function, to handle client events - Login, Connected, Disconnected,

  • Each Mobile client can call Azure Cognitive Service to Translate the received chat message in any language and update its own UI

 

Supply Chain Control Tower App

pubsub-control-tower.png

 

  • Multiple business entities act as Web PubSub client and Control Tower App as Upstream server

  • Each client can send chat messages directly through Azure Web PubSub e.g. Warehouse <-> WareHouse, Warehouse <-> Customer, Customers <-> Factories etc.

  • Each Business client can be made more intelligent using Azure Cognitive Services and can take important decisions and share with other clients; resulting in a vibrant Control Tower system

 

References

Co-Authors
Version history
Last update:
‎Mar 15 2022 07:48 PM
Updated by: