Skip to content

Commit

Permalink
Normailize the DefaultEndpointRouter weight using unit
Browse files Browse the repository at this point in the history
  • Loading branch information
bjqian committed Oct 25, 2023
1 parent ea96052 commit 6fa8775
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 114 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,21 @@
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.SignalR.Common;
using Microsoft.Extensions.Options;

namespace Microsoft.Azure.SignalR
{
internal class DefaultEndpointRouter : DefaultMessageRouter, IEndpointRouter
{
private readonly EndpointRoutingMode _mode;

public DefaultEndpointRouter(IOptions<ServiceOptions> options)
{
_mode = options?.Value.EndpointRoutingMode ?? EndpointRoutingMode.Weighted;
}

/// <summary>
/// Select an endpoint for negotiate request according to the mode
/// Select an endpoint for negotiate request
/// </summary>
/// <param name="context">The http context of the incoming request</param>
/// <param name="endpoints">All the available endpoints</param>
public ServiceEndpoint GetNegotiateEndpoint(HttpContext context, IEnumerable<ServiceEndpoint> endpoints)
{
// get primary endpoints snapshot
var availableEndpoints = GetNegotiateEndpoints(endpoints);
return _mode switch
{
EndpointRoutingMode.Random => GetEndpointRandomly(availableEndpoints),
EndpointRoutingMode.LeastConnection => GetEndpointWithLeastConnection(availableEndpoints),
_ => GetEndpointAccordingToWeight(availableEndpoints),
};
return GetEndpointAccordingToWeight(availableEndpoints);
}

/// <summary>
Expand Down Expand Up @@ -69,7 +56,7 @@ private ServiceEndpoint GetEndpointAccordingToWeight(ServiceEndpoint[] available
if (availableEndpoints.Any(endpoint => endpoint.EndpointMetrics.ConnectionCapacity == 0) ||
availableEndpoints.Length == 1)
{
return GetEndpointRandomly(availableEndpoints);
return availableEndpoints[StaticRandom.Next(availableEndpoints.Length)];
}

var we = new int[availableEndpoints.Length];
Expand All @@ -89,38 +76,5 @@ private ServiceEndpoint GetEndpointAccordingToWeight(ServiceEndpoint[] available

return availableEndpoints[Array.FindLastIndex(we, x => x <= index) + 1];
}

/// <summary>
/// Choose endpoint with least connection count
/// </summary>
private ServiceEndpoint GetEndpointWithLeastConnection(ServiceEndpoint[] availableEndpoints)
{
//first check if weight is available or necessary
if (availableEndpoints.Any(endpoint => endpoint.EndpointMetrics.ConnectionCapacity == 0) ||
availableEndpoints.Length == 1)
{
return GetEndpointRandomly(availableEndpoints);
}

var leastConnectionCount = int.MaxValue;
var index = 0;
for (var i = 0; i < availableEndpoints.Length; i++)
{
var endpointMetrics = availableEndpoints[i].EndpointMetrics;
var connectionCount = endpointMetrics.ClientConnectionCount + endpointMetrics.ServerConnectionCount;
if (connectionCount < leastConnectionCount)
{
leastConnectionCount = connectionCount;
index = i;
}
}

return availableEndpoints[index];
}

private static ServiceEndpoint GetEndpointRandomly(ServiceEndpoint[] availableEndpoints)
{
return availableEndpoints[StaticRandom.Next(availableEndpoints.Length)];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class EndpointRouterDecorator : IEndpointRouter

public EndpointRouterDecorator(IEndpointRouter router = null)
{
_inner = router ?? new DefaultEndpointRouter(null);
_inner = router ?? new DefaultEndpointRouter();
}

public virtual ServiceEndpoint GetNegotiateEndpoint(HttpContext context, IEnumerable<ServiceEndpoint> endpoints)
Expand Down
6 changes: 0 additions & 6 deletions src/Microsoft.Azure.SignalR/ServiceOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,5 @@ public int ConnectionCount
/// Gets or sets a function which accepts <see cref="HttpContext"/> and returns a bitmask combining one or more <see cref="HttpTransportType"/> values that specify what transports the service should use to receive HTTP requests.
/// </summary>
public Func<HttpContext, HttpTransportType> TransportTypeDetector { get; set; } = null;

/// <summary>
/// Gets or sets the default endpoint routing mode when using multiple endpoints.
/// <see cref="EndpointRoutingMode.Weighted"/> by default.
/// </summary>
public EndpointRoutingMode EndpointRoutingMode { get; set; } = EndpointRoutingMode.Weighted;
}
}
57 changes: 26 additions & 31 deletions test/Microsoft.Azure.SignalR.Tests/EndpointRouterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

namespace Microsoft.Azure.SignalR.Tests
Expand All @@ -14,7 +12,7 @@ public class EndpointRouterTests
[Fact]
public void TestDefaultEndpointRouterWeightedMode()
{
var drt = GetEndpointRouter(EndpointRoutingMode.Weighted);
var drt = new DefaultEndpointRouter();

const int loops = 20;
var context = new RandomContext();
Expand All @@ -37,46 +35,43 @@ public void TestDefaultEndpointRouterWeightedMode()
context.Reset();
}

[Fact]
public void TestDefaultEndpointRouterLeastConnectionMode()
[Theory]
[InlineData(200)]
[InlineData(300)]
[InlineData(400)]
[InlineData(500)]
public void TestDefaultEndpointRouterWeightedModeWhenAutoScaleIsEnabled(int quotaOfScaleUpInstance)
{
var drt = GetEndpointRouter(EndpointRoutingMode.LeastConnection);
var drt = new DefaultEndpointRouter();

const int loops = 10;
var loops = 100 + (quotaOfScaleUpInstance / 5);
var context = new RandomContext();
const double quotaBarForScaleUp = 0.8;

const string small = "small_instance", large = "large_instance";
var uSmall = GenerateServiceEndpoint(100, 0, 90, small);
var uLarge = GenerateServiceEndpoint(1000, 0, 200, large);
var el = new List<ServiceEndpoint>() { uLarge, uSmall };
var endpointA = GenerateServiceEndpoint(quotaOfScaleUpInstance, 0, 80, "a");
var endpointB = GenerateServiceEndpoint(100, 0, 70, "b");
var endpointC = GenerateServiceEndpoint(100, 0, 70, "c");
var el = new List<ServiceEndpoint>() {endpointA, endpointB, endpointC};
context.BenchTest(loops, () =>
{
var ep = drt.GetNegotiateEndpoint(null, el);
ep.EndpointMetrics.ClientConnectionCount++;
var percent = (ep.EndpointMetrics.ClientConnectionCount + ep.EndpointMetrics.ServerConnectionCount) /
(double)ep.EndpointMetrics.ConnectionCapacity;
if (percent > quotaBarForScaleUp)
{
ep.EndpointMetrics.ConnectionCapacity += 100;
}
return ep.Name;
});
var uLargeCount = context.GetCount(large);
var uSmallCount = context.GetCount(small);
Assert.Equal(0, uLargeCount);
Assert.Equal(10, uSmallCount);
context.Reset();
}

private static IEndpointRouter GetEndpointRouter(EndpointRoutingMode mode)
{
var config = new ConfigurationBuilder().Build();
var serviceProvider = new ServiceCollection()
.AddSignalR()
.AddAzureSignalR(
o =>
{
o.EndpointRoutingMode = mode;
})
.Services
.AddSingleton<IConfiguration>(config)
.BuildServiceProvider();
Assert.Equal(context.GetCount("a") + context.GetCount("b") + context.GetCount("c"), loops);
Assert.Equal(quotaOfScaleUpInstance, endpointA.EndpointMetrics.ConnectionCapacity);
Assert.Equal(200, endpointB.EndpointMetrics.ConnectionCapacity);
Assert.Equal(200, endpointC.EndpointMetrics.ConnectionCapacity);

return serviceProvider.GetRequiredService<IEndpointRouter>();
context.Reset();
}

private static ServiceEndpoint GenerateServiceEndpoint(int capacity, int serverConnectionCount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ public async Task TestContainerWithOneEndpointWithAllDisconnectedConnectionThrow
{
var endpoint = new ServiceEndpoint(ConnectionString1);
var sem = new TestServiceEndpointManager(endpoint);
var router = new DefaultEndpointRouter(null);
var router = new DefaultEndpointRouter();

var container = new TestMultiEndpointServiceConnectionContainer("hub",
e => new TestServiceConnectionContainer(new List<IServiceConnection> {
Expand Down

0 comments on commit 6fa8775

Please sign in to comment.