Sorted By: Tag (architecture)

Pulling an Old Article From the Coffin: SignalR with Redis Running on a Windows Azure Virtual Machine

Long time ago (about 5 years, at least), I contributed an article to SignalR wiki about scaling SignalR with Redis. You can still find the article here. I also blogged about it here. However, over time, pictures got lost there. I got a few requests from my readers to refresh those images and I was luckily able to find them :) I decided to publish that article here so that I would have a much better control over the content.
2018-08-08 14:32
Tugberk Ugurlu


Long time ago (about 5 years, at least), I contributed an article to SignalR wiki about scaling a SignalR application with Redis. You can still find the article here. I also blogged about it here. However, over time, pictures got lost there. I got a few requests from my readers to refresh those images and I was lucky enough to be able to find them :) I decided to publish that article here so that I would have a much better control over the content. So, here is the post :)

Please keep in mind that this is a really old post and lots of things have evolved since then. However, I do believe the concepts still resonate and it’s valuable to show the ways of how to achieve this within a cloud provider’s context.


SignalR with Redis Running on a Windows Azure Virtual Machine

This wiki article will walk your through on how you can run your SignalR application in multiple machines with Redis as your backplane using Windows Azure Virtual Machines for scale out scenarios.

Creating the Windows Azure Virtual Machines

First of all, we will spin up our virtual machines. What we want here is to have two Windows Server 2008 R2 virtual machines for our SignalR application and we will name them as Web1-08R2 and Web2-08R2. We will have the IIS installed on both of these servers and at the end, we will load balance the request on port 80.

Our third virtual machine will be another Windows Server 2008 R2 only for our Redis server. We will call this server Redis-08R2.

To spin up the VMs, go to new Windows Azure Management Portal and hit New icon at the bottom-right corner.

0-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732

Creating a virtual machine running Windows Server 2008 R2 is explained here in details. We followed the same steps to create our first VM named Web1-08R2.

The second VM we will be creating has a slightly different approach than the first one. Under the hood, every virtual machine is a cloud service instance and we want to put our second VM (Web2-08R2) under the same cloud service that our first web VM is running under. To do that, we need to follow the same steps as explained inside the previously mentioned article but when we come to 3rd step in the creation wizard, we should chose Connect to existing Virtual Machine option this time and we should choose our first VM we have just created.

1-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732d417a7572652d5

As the last step, we now need to create our redis VM which will be named Redis-08R2. We will follow the same steps as we did when we were creating our second web VM (Web2-08R2).

Setting Up Redis as a Windows Service

To use Redis on a Windows machine, we went to Redis on Windows prototype GitHub page and cloned the repository and followed the steps explained under How to build Redis using Visual Studio section.

After you build the project, you will have all the files you need under msvs\bin\release path as zip files. redisbin.zip file will contain the redis server, redis command line interface and some other stuff. rediswatcherbin.zip file will contain the msi file to install redis as a windows service. You can just copy those zip files to your Redis VM and extract redisbin.zip under c:\redis\bin. Then follow the steps:

  • Currently, there is a bug in the RedisWatcher installer and if you don't have Microsoft Visual C++ 2010 Redistributable Package installed on your machine, the service won't start. So, I installed it first.

  • Copy this redis.conf file and put it under c:\redis\bin directory. Open it up and add a password by adding the following line of code:

    requirepass 1234567

    Take this note into considiration when you are setting up your redis password:

    Warning: since Redis is pretty fast an outside user can try up to 150k passwords per second against a good box. This means that you should use a very strong password otherwise it will be very easy to break.

  • Then, extract the rediswatcherbin.zip somewhere and run the InstallWatcher.msito install the service.

  • Navigate to C:\Program Files (x86)\RedisWatcher directory. You will see a file named watcher.conf inside this directory. Open this file up and replace the entire file with the following text. Only difference here is that we are supplying the redis.conf file directory for the server to use:

    exepath c:\redis\bin
    exename redis-server.exe
    
    {
     workingdir c:\redis\inst1
     runmode hidden
     saveout 1
     cmdparms c:\redis\bin\redis.conf
    }
    
  • Create a folder named inst1 under c:\redis because we have specified this folder as working directory for our redis instance.

  • When you do a search against windows services in PowerShell, you will see RedisWatcherSvc service is installed.

2-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732

  • Run the following PowerShell command to start the service for the first time.

    (Get-Service -Name RedisWatcherSvc).Start()
    

Now we have a Redis server running on our VM. To test if it is actually running, open up a windows command window under c:\redis\bin and run the following command (assuming you set your password 1234567):

redis-cli -h localhost -p 6379 -a 1234567

Now, you have a redis client running.

3-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732

Ping the redis to see if you are really authenticated:

4-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732

Now, we are nearly set. As a last step in our redis server, we need to open up TCP port 6379 for external communication. You can do this under Windows Firewall with Advanced Security window as explained here.

5-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732

Communicating Through Internal Endpoints Between Windows Azure Virtual Machines Under Same Cloud Service

When you are inside one of your web VMs, you can simply look up the redis VM by hostname.

6-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732

The hostname will resolve to DIP (Dynamic IP Address) which Windows Azure will use internally. We can configure public endpoints through Windows Azure Management Portal easily but in that case, we would be opening redis to the whole world. Also, if we communicate to our redis server through VIP (Virtual IP Address), we would always go through the load balancer which has its own additional cost.

So, we can easily connect to our redis server from any other connected VM by hostname.

The SignalR Application with Redis

Our SignalR application will not be that much different from a normal SignalR application thanks to SignalR.Redis project. All you need to do is to add the SignalR.Redis nuget package into your application and configure SignalR to use Redis as the message bus inside the Application_Start method in Global.asax.cs file:

protected void Application_Start(object sender, EventArgs e)
{
    // Hook up redis
    string server = ConfigurationManager.AppSettings["redis.server"];
    string port = ConfigurationManager.AppSettings["redis.port"];
    string password = ConfigurationManager.AppSettings["redis.password"];

    GlobalHost.DependencyResolver.UseRedis(server, Int32.Parse(port), password, "SignalR.Redis.Sample");
}

For our demo, the AppSettings should look like as below:

<appSettings>
    <add key="redis.server" value="Redis-08R2" />
    <add key="redis.port" value="6379" />
    <add key="redis.password" value="1234567" />
</appSettings>

I put the application under IIS on our both web servers (Web1-08R2 and Web2-08R2) and configured them to run under .NET Framework 4.0 integrated application pool.

For this demo, I am using the Redis.Sample chat application included inside the SignalR.Redis project.

Let's test them quickly before going public. I fired the both web applications inside the servers and here is the result:

7-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732

Perfectly running! Let's open them up to the world.

Opening up the Port 80 and Load Balancing the Requets

Our requirement here is to make our application reachable over HTTP and at the same time, we want to load balance the request between our two web servers.

To do that, we need to go to Windows Azure Management portal and set up the TCP endpoints for port 80.

First, we navigate to dashboard of our Web1-08R2 VM and hit Endpoints from the dashboard menu:

8-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732

From there, hit the End Endpoint icon at the bottom of the page:

9-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732

A wizard is going to appear on the screen:

10-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732

Click the right-arrow icon and go to next step which is the last one and we will enter the port details there:

11-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732

After that, our endpoint will be created:

12-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732

Follow the same steps of Web2-08R2 VM as well and open the Add Endpoint wizard. This time, we will be able to select Load-balance traffic on an existing port. Chose the previously created port and continue:

13-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732

At the last step, enter the proper details and hit save:

14-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732

We will see our new endpoint is being crated but this time Load Balanced column indicates Yes.

15-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732

As we configured our web applications without a host name and they are exposed through port 80, we can directly run reach our application through the URL or Public Virtual IP Address (VIP) which is provided to us. When we run our application, we should see it running as below:

16-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732

No matter which server it goes, the message will be broadcasted to every client because we will be using Redis as a message bus.

References

Long-Running Asynchronous Operations, Displaying Their Events and Progress on Clients

I want to share a few thoughts that I have been keeping to myself on showing progress for long-running asyncronous operations on a system where individual events can be sent during ongoing operations.
2016-07-30 16:33
Tugberk Ugurlu


With today's modern applications, we end up with lots of asynchronous operations happening and we somehow have to let the user know what is happening. This, of course, depends on the requirements and business needs.

Let's look at the Amazon case. When you buy something on Amazon, Amazon tells you that your order has been taken. However, it doesn't really mean that you have actually purchased it as the process of taking the payment has not been kicked off yet. The payment process probably goes through a few stages and at the end, it has a few outcome options like success, faılure, etc. Amazon only gives the user the change to know about the outcome of this process which is done through e-mail. This is pretty straightforward to implement (famous last words?) as it doesn't require things like real-time process notifications, etc..

On the other hand, creating a VM on Microsoft Azure has different needs. When you kick the VM creation operation through an Azure client like Azure Portal, it can give you real-time notifications on the operation status to show you in what stage the operation is (e.g. provisioning, starting up, etc.).

In this post, I will look at the later option and try to make a few points to prove that the implementation is a bit tricky. However, we will see a potential solution at the end and that will hopefully be helpful to you as well.

Problematic Flow

Let's look at a problematic flow:

  • User starts an operation by sending a POST/PUT/PATCH request to an HTTP endpoint.
  • Server sends the 202 (Accepted) response and includes a some sort of operation identifier on the response body.
  • Client subscribes to a notification channel based on this operation identifier.
  • Client starts receiving events on that notification channel and it decides on how to display them on the user interface (UI).
  • There are special event types which give an indication that the operation has ended (e.g. completed, failed, abandoned, etc.).
    • The client needs to know about these event types and handle them specially.
    • Whenever the client receives an event which corresponds to operation end event, it should unsubscribe from the notification channel for that specific operation.

We have a few problems with the above flow:

  1. Between the operation start and the client subscribing to a notification channel, there could be events that potentially could happen and the client is going to miss those.
  2. If the user disconnects from the notification channel for a while (for various reasons), the client will miss the events that could happen during that disconnect period.
  3. Similar to above situation, the user might entirely shut its client and opens it up again at some point later to see the progress. In that case, we lost the events that has happened before subscribing to the notification channel on the new session.

Possible Solution

The solution to all of the above problems boils down to persisting the events and opening up an HTTP endpoint to pull all the past events per each operation. Let's assume that we are performing payment transactions and want to give transparent progress on this process. Our events endpoint would look similar to below:

GET /payments/eefcb363/events

[
     {
          "id": "c12c432c",
          "type": "ProcessingStarted",
          "message": "The payment 'eefcb363' has been started for processing by worker 'h8723h7d'.",
          "happenedAt": "2016-07-30T11:05:26.222Z"
          "details": {
               "paymentId": "eefcb363",
               "workerId": "h8723h7d"
          }
     },

     {
          "id": "6bbb1d50",
          "type": "FraudCheckStarted",
          "message": "The given credit card details are being checked against fraud for payment 'eefcb363' by worker 'h8723h7d'.",
          "happenedAt": "2016-07-30T11:05:28.779Z"
          "details": {
               "paymentId": "eefcb363",
               "workerId": "h8723h7d"
          }
     },

     {
          "id": "f9e09a83",
          "type": "ProgressUpdated",
          "message": "40% of the payment 'eefcb363' has been processed by worker 'h8723h7d'.",
          "happenedAt": "2016-07-30T11:05:29.892Z"
          "details": {
               "paymentId": "eefcb363",
               "workerId": "h8723h7d",
               "percentage": 40
          }
     }
]

This endpoint allows the client to pull the events that have already happened, and the client can mix these up with the events that it receives through the notification channel where the events are pushed in the same format.

With this approach, suggested flow for the client is to follow the below steps in order to get 100% correctness on the progress and events:

  • User starts an operation by sending a POST/PUT/PATCH request to an HTTP endpoint.
  • Server sends the 202 (Accepted) response and includes a some sort of operation identifier (which is 'eefcb363' in our case) on response body.
  • Client subscribes to a notification channel based on that operation identifier.
  • Client starts receiving events on that channel and puts them on a buffer list.
  • Client then sends a GET request based on the operation id to get all the events which have happened so far and puts them into the buffer list (only the ones that are not already in there).
  • Client can now start displaying the events from the buffer to the user (optionally in chronological order) and keeps receiving events through the notification channel; updates the UI simultaneously.

Obviously, at any display stage, the client needs to honor that there are special event types which give an indication that the operation has ended. The client needs to know about these types and handle them specially (e.g. show the completed view, etc.). Besides this, whenever the client receives an event which corresponds to operation end event, it should unsubscribe from the notification channel for that specific operation.

However, the situation can be a bit easier if you only care about percentage progress. If that's case, you may still want to persist all events but you only care about the latest event on the client. This makes your life easier and if you decide that your want to be more transparent about showing the progress, you can as you already have all the data for this. It is just a matter of exposing them with the above approach.

Conclusion

Under any circumstances, I would suggest you to persist operation events (you can even go further with event sourcing and make this a natural process). However, your use case may not require extensive and transparent progress reporting through the client. If that's the case, it will certainly make your implementation a lot simpler. However, you can change your mind later and go with a different progress reporting approach since you have been already persisting all the events.

Finally, please share your thoughts on this if you see a better way of handling this.

Tags