Azure Service Bus Relay Hybrid Connections

Azure Service Bus Relay Hybrid Connections

in previous article, we have seen what is a listener and what is a sender in context of relay service. We also have looked at basic concepts related to relay service. If you have not seen the article, I would suggest to read that article.

In this article, we will have look at how to use relay service hybrid connections to send or receive messages. In this article, we will use .NET and Web Sockets.

For following steps in this article, you will need access to Azure Portal.

If you don’t have an Azure subscription, create a free account before you begin.

You can also find the sample console applications at my GitHub repository. Please note that this example is originally based on the samples from Microsoft Azure documentation.

Create Namespace

Login to Azure portal. From the left menu, select “+ Create a resource” and then select Integration -> Relay.

These opens create namespace panel. On this panel, you can enter below details:

  • Namespace name – globally unique name for your namespace
  • Subscription – the azure subscription under which this resources would be created
  • Resource group – logical group where this resource should be placed
  • Location – the physical location / azure data center where this resource should be deployed

Once these details are provided, click on create button.

Get Credentials

Locate your namespace and open it. By default it will show overview panel. You can click on shared access policies and Azure will show you list of all policies.

There is one default policy RootManageSharedAccessKey. Click on this. It would open a panel which shows connection strings and keys. Copy both primary key and primary connection string and keep them at some temporary location. We will use them in the code.

Create Hybrid Connection

From overview blade of relay service namespace, click on add hybrid connection button (highlighted in below snapshot). It would open Create hybrid connection panel.

Enter the name of connection and leave rest of the values to the defaults.

Listener – first application

Listener application has two important parts – establishing connection with relay service is the first part, while handling the received message is the second part. Below section explains the code.

Establish Connection with Relay

First, the below code creates instance of HybridConnectionListener class and then calls OpenAsync method to start listening to the relay service.

The code also subscribes to Connecting, Offline and Online events and writes current status on Console.

var cts = new CancellationTokenSource();        
var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(KeyName, Key);
var listener = new HybridConnectionListener(new Uri(string.Format("sb://{0}/{1}", RelayNamespace, ConnectionName)), tokenProvider);

// Subscribe to the status events.
listener.Connecting += (o, e) => { Console.WriteLine("Connecting"); };
listener.Offline += (o, e) => { Console.WriteLine("Offline"); };
listener.Online += (o, e) => { Console.WriteLine("Online"); };

// Opening the listener establishes the control channel
await listener.OpenAsync(cts.Token);

The code also uses CancellationTokenSource. It checks the console and if anything is entered on Console, then this snippet initiates cancel operation as shown in below snippet.

// Callback for a cancellation token that will close the listener.
cts.Token.Register(() => listener.CloseAsync(CancellationToken.None));

// Start a new thread that will continuously read the console.
// If anything is entered on console. it will shutdown this application
new Task(() => Console.In.ReadLineAsync().ContinueWith((s) => { cts.Cancel(); })).Start();

In below code snippet, the listener gets the relayConnection object. This object and CancellationTokenSource is passed to the method which is supposed to handle the messages received.

var relayConnection = await listener.AcceptConnectionAsync();
if (relayConnection == null)
{
    break;
}         

ProcessMessagesOnConnection(relayConnection, cts);

ProcessMessagesOnConnection Method

In this method, listener creates two streams, reader stream for reading the data from relay service, while writer stream for sending data to the relay service.

var reader = new StreamReader(relayConnection);
var writer = new StreamWriter(relayConnection) { AutoFlush = true };

Then this method simply writes the received messages on Console. After that, it sends the same message (prefixed with text “ECHO:” back to sender – as shown in below snippet.

You can find the complete code of Listener at this GitHub repository.

// Read a line of input until a newline is encountered.
var line = await reader.ReadLineAsync();

if (string.IsNullOrEmpty(line))
{
    await relayConnection.ShutdownAsync(cts.Token);
    break;
}

// Write the line on the console.
Console.WriteLine(line);

// Write the line back to the client, prepended with "Echo:"
await writer.WriteLineAsync($"Echo: {line}");

Sender – the other application

Sender is pretty simple code which creates instance of HybridConnectionClient class using relay namespace URL and connection and token provider.

After connection is established, it gets input from Console and sends it to relay service. Then it waits for ECHO text from listener.

The complete code of Sender application can be found at this GitHub repository.

// Create a new hybrid connection client.
var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(KeyName, Key);
var client = new HybridConnectionClient(new Uri(String.Format("sb://{0}/{1}", RelayNamespace, ConnectionName)), tokenProvider);
            
// Initiate the connection.
var relayConnection = await client.CreateConnectionAsync();
var reader = new StreamReader(relayConnection);
var writer = new StreamWriter(relayConnection) { AutoFlush = true };

while (true)
{
    var messageToSend = Console.ReadLine();
    if (string.IsNullOrEmpty(messageToSend))
    {
        break;
    }

    await writer.WriteLineAsync(messageToSend);
    var textFromListener = await reader.ReadLineAsync();
    Console.WriteLine(textFromListener);
}
           
await relayConnection.CloseAsync(CancellationToken.None);
        

Running the applications

You can clone the repository on your machine, or you can just download the sample from the repository.

You will have to change 4 placeholder values before running the sample:

  • RelayNamespace: then name you provided while creating relay namespace
  • HybridConnectionName: the name you provided while creating a new hybrid connection
  • SASKeyName: When you are on the relay service and you click on shared access policies, you will be able to see list of policies present for the namespace. You can use any of the key name from this list. I have used the default key name which is present after creating the relay namespace (i.e. RootManageSharedAccessKey )
  • SASKey: When you click on selected key name, you can select that key and use the primary key value in this variable.
private const string RelayNamespace = "{RelayNamespace}.servicebus.windows.net";

private const string ConnectionName = "{HybridConnectionName}";

private const string KeyName = "{SASKeyName}";

private const string Key = "{SASKey}";

Then run the Listener and Sender applications. You can enter a text on sender and hit Enter. The listener will print this text on its console and will send back the same text prefixed with “ECHO:” to the sender.

You can run these two from the same machines or you can run them from different physical machines. In any case, the sender will send message via relay service.

In this article, we have seen how easy is to create a relay namespace. We also have seen how easy it is to interact with on-premises applications using the relay service. I hope the article helped you in some way. Please do comment and let me know your thoughts and experiences.

Leave a Reply