May 2012 - Posts

API Improvements made in SignalR 0.5

We made lots of breaking changes in 0.5 in order to gain more API consistency across the board. Some of the changes may not affect you if you weren’t doing anything with lower level APIs. These types of breaking changes will obviously slow down as we get closer to a 1.0 release.

API comments

This release, we spent some time writing API comments that should be helpful during development.

Breaking changes

A bunch of APIs were removed from PersistentConnection including the following:

  • All overloads of Send
  • SendToGroup
  • AddToGroup
  • RemoveFromGroup

Sending Data

All data is now sent through an IConnection (Connection property). Now, whether you’re within a PersistentConnection or outside of one, the logic to send data over that connection will look exactly the same.

SignalR 0.4

public class MyConnection : PersistentConnection
{
   
protected override Task OnConnectedAsync(IRequest request, IEnumerable<string> groups, string
connectionId)
    {
       
return Connection.Broadcast("A new connection was made!"
);
    }

   
protected override Task OnReceivedAsync(string connectionId, string
data)
    {
       
return Send(data);
    }
}

SignalR 0.5

public class MyConnection : PersistentConnection
{
   
protected override Task OnConnectedAsync(IRequest request, string
connectionId)
    {
       
return Connection.Broadcast("A new connection was made!"
);
    }

   
protected override Task OnReceivedAsync(string connectionId, string
data)
    {
       
return Connection.Send(connectionId, data);
    }
}

Groups

Group management is now centralized and consistent across Hubs and PersistentConnections. Each of those types have a property Groups of type IGroupManager that are scoped to a specific PersistentConnection or Hub. Here’s an example of adding a group in 0.4 and 0.5:

SignalR 0.4

public class MyConnection : PersistentConnection
{
   
protected override Task OnConnectedAsync(IRequest request, IEnumerable<string> groups, string
connectionId)
    {
       
return AddToGroup(connectionId, "foo"
);
    }

   
protected override Task OnReceivedAsync(string connectionId, string
data)
    {
       
return SendToGroup("foo", data);
    }
}
public class MyHub : Hub
{
   
public Task Join(string
group)
    {
       
return
AddToGroup(group);
    }

   
public Task Send(string group, string
message)
    {
       
return Clients[group].addMessage(message);
    }
}

SignalR 0.5

public class MyConnection : PersistentConnection
{
   
protected override Task OnConnectedAsync(IRequest request, string
connectionId)
    {
       
return Groups.Add(connectionId, "foo"
);
    }

   
protected override Task OnReceivedAsync(string connectionId, string
data)
    {
       
return Groups.Send("foo", data);
    }
}
public class MyHub : Hub
{
   
public Task Join(string
group)
    {
       
return
Groups.Add(Context.ConnectionId, group);
    }

   
public Task Send(string group, string
message)
    {
       
return Clients[group].addMessage(message);
    }
}

Dependency Injection

We’ve removed the AspNetHost type that from SignaR 0.4 and added a type that is purely optional to use, GlobalHost. The idea behind this type is to allow easy access to items in the container that will be used most commonly in any SignalR application, as well as a host agnostic default dependency resolver. This type exists in a new assembly SignalR.Hosting.Common.dll.

Replacing the resolver

SignalR 0.4

public class Global : System.Web.HttpApplication
{
   
protected void Application_Start(object sender, EventArgs
e)
    {
       
AspNetHost.SetResolver(new CustomResolver());
    }
}

SignalR 0.5

public class Global : System.Web.HttpApplication
{
   
protected void Application_Start(object sender, EventArgs
e)
    {
       
GlobalHost.DependencyResolver = new CustomResolver
();

       
RouteTable.Routes.MapHubs();
    }
}

Here, we’re changing the global resolver and remapping the default hub route to pick it up. The hub route is automatically added at a very early stage in the application (PreApplicationStart) so we need to remap it to use the new default resolver or we can specify a new one for the hub route directly:

public class Global : System.Web.HttpApplication
{
   
protected void Application_Start(object sender, EventArgs
e)
    {
       
RouteTable.Routes.MapHubs(new CustomResolver());
    }
}

The above logic doesn’t replace the global resolver but instead creates a self contained SignalR world using the specified resolver. If you go with this more “pure” approach, you’re opting out of using the GlobalHost.

Changing the Hub route

In 0.4, the magical signalr/hubs url was implemented as a dynamic http module. Now it’s just a regular route in the route table that can be replaced. Here’s an example of changing it to signalr2:

public class Global : System.Web.HttpApplication
{
   
protected void Application_Start(object sender, EventArgs
e)
    {
       
RouteTable.Routes.MapHubs("~/signalr2");
    }
}

Obviously this change needs to be accompanied by a change to your JavaScript include:

<!DOCTYPE html>
<
html
>
<
head>
    <title></title>
    <script src="Scripts/jquery-1.6.4.js" type="text/javascript"></script>
    <script src="Scripts/jquery.signalR-0.5rc.js" type="text/javascript"></script>
    <script src="signalr2/hubs" type="text/javascript"></script
>
</
head
>
<
body>

</body
>
</
html
>

Getting a reference to the connection/hub outside of a connection or hub

This is an extremely popular pattern for integrating with other frameworks. Here’s an example of broadcasting to all clients from an mvc controller:

SignalR 0.4

public class HomeController : Controller
{
   
public void
PerformLongRunningOperation()
    {
       
var connection = AspNetHost.DependencyResolver.Resolve<IConnectionManager>().GetConnection<MyConnection
>();

        connection.Broadcast(
"Called from an mvc controller"
);
    }

   
public void
PerformLongRunningHubOperation()
    {
       
var clients = AspNetHost.DependencyResolver.Resolve<IConnectionManager>().GetClients<MyHub
>();

        clients.notify(
"Hello world");
    }
}

SignalR 0.5

public class HomeController : Controller
{
   
public void
PerformLongRunningOperation()
    {
       
IPersistentConnectionContext context = GlobalHost.ConnectionManager.GetConnectionContext<MyConnection
>();

        context.Connection.Broadcast(
"Called from an mvc controller"
);
    }

   
public void
PerformLongRunningHubOperation()
    {
       
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MyHub
>();

        context.Clients.notify(
"Hello world");
    }
}

With this, you can inject these new context objects into your mvc controller or whatever framework you want to combine with SignalR. For example:

public class HomeController : Controller
{
   
private readonly IHubContext
_hubContext;

   
public HomeController(IHubContext
hubContext)
    {
        _hubContext = hubContext;
    }
       
   
public void
PerformLongRunningHubOperation()
    {
        _hubContext.Clients.notify(
"Hello world");
    }
}

Configuring SignalR

On each host (Self host and GlobalHost) there’s a Configuration property. This gives you access to the server configuration for things like setting timeouts, the keep alive interval and the heart beat interval:

public class Global : System.Web.HttpApplication
{
   
protected void Application_Start(object sender, EventArgs
e)
    {
       
GlobalHost.Configuration.KeepAlive = TimeSpan.FromSeconds(20);
    }
}

I’ll be blogging about more of these changes as we get closer to the release of 0.5.

Posted by davidfowl with 9 comment(s)

SignalR 0.5

This is the rebirth of my blog. I’ve been busy over the last few months with projects like JabbR, SignalR and other stuff. With the upcoming release of SignalR 0.5, I thought it’d be good to write a blog post that touches on some of the things we did.

Webfarm Support

There’s a lot of confusion as to what webfarm support means in SignalR. This is an FYI, SignalR 0.5 will not automatically make your application work on azure with zero effort. SignalR needs a way to communicate between instances of your webserver, in 0.5 we implemented 2 of those providers on NuGet (message buses):

  1. SignalR.WindowsAzureServiceBus - http://vasters.com/clemensv/2012/02/13/SignalR+Powered+By+Service+Bus.aspx (blog post slightly outdated but information is relevant).
  2. SignalR.Redis – Redis message bus implementation for SignalR - https://github.com/SignalR/SignalR.Redis

When deciding to make SignalR work in a webfarm, you’ll have these 2 options. We’re going to continue improving them as well as implement solutions on top of popular message buses like NServiceBus and lower fidelity solutions built on top of SQL QNS and possibly SQL Server Service Broker.

This wasn’t the only hurdle to making SignalR work in webfarms, there were some nasty bugs that caused some bad behavior that we fixed:

  1. Disconnect notifications across webfarms
  2. Load balancer idle timeout in azure.

Fixing disconnect in webfarms was the most important issue to fix. Before SignalR 0.5rc, disconnect in a webfarm (using service bus or redis) could fire for the same client multiple times. This is more obvious when using longPolling and load balancing happens between web server instances. If you had control of your LB, you could affinitize clients to a server so you’d never see this behavior, but I’m glad to say we got this fixed.

For load balancer idle timeouts (especially on azure), we support keep-alive. You can now tell SignalR to send an empty packet on a specific time interval. This will fend off nosy network devices that like to kill idle connections waiting for data. This is especially important for things like server sent events and forever frame transports.

Mono Support

In the 0.4 release, SignalR only “worked” when self hosted on mono so I spent time making SignalR work across the board (ASP.NET and all). When I say work, I mean both the client and the server. You can now literally clone SignalR onto your OSX (I didn’t try linux) box and run make and get a successful build (see the README for more details). This wasn’t a simple task as I had to make changes to the NuGet targets file so that package restore worked on mono.

If you’re using Mono develop, open SignalR.Mono.sln. You can run the samples as you would normally. Here’s a glimpse of the chat demo running:

551939760

 

Not much time was spent optimizing and tuning specifically for mono but in the future we’ll have time to do that.

JsonP Longpolling support (XDomain)

This feature wasn’t planned for 0.5 but thanks to an awesome contribution from codeputty, this became a reality.

Dynamic Hubs Implementation

Contributor pszmyd was trying to build better integration between SignalR and Orchard and ended up adding infrastructure to SignalR to decouple Hubs from types (System.Type). This is a great example of why I love OSS development (scratch your own itch).

Continuous Integration and NuGet Server

We finally got a CI server setup on codebetter’s teamcity instance. You can view SignalR here http://teamcity.codebetter.com/project.html?projectId=project188. The CI also builds packages and pushes them to our myget feed. If you want to live on the bleeding edge but don’t want to build from source, you can configure nuget to point to http://www.myget.org/F/signalr/ (make sure you use the –Pre flag or select the Include Prerelease in the dialog).

 

0.5 Release

Help us test the RC now on NuGet! (http://signalr.net/releases/v0584.html). We’re reserving this week for any bugs that we consider ship stoppers.

What’s Next

We’re going to continue improving SignalR. Future releases will focus on some of the following:

  • Performance Improvements
  • In the box WebSockets support for Windows8
  • Windows 8 .NET Client
  • .NET Micro Framework Client
  • Web API Host
  • Build time hubs.js generation (instead of runtime).
  • Better network availability handling
  • Better Mobile support (optimized for battery life)
  • Web Forms control

… And much more! Check the issue backlog for details on what else we've got planned for upcoming releases.

Come Talk with us

We’re always around in the signalr room on jabbr. Come say hi!

Posted by davidfowl with 7 comment(s)
More Posts