Added Regions to Load Balancer Setup.

This commit is contained in:
Derek S 2021-04-07 00:30:38 -05:00
parent 36362af6ba
commit 14ea6243d0
12 changed files with 171 additions and 43 deletions

View file

@ -8,10 +8,10 @@ namespace LightReflectiveMirror.LoadBalancing
[Serializable]
public struct RelayServerInfo
{
public int ConnectedClients;
public int RoomCount;
public int PublicRoomCount;
public TimeSpan Uptime;
public int connectedClients;
public int roomCount;
public int publicRoomCount;
public TimeSpan uptime;
[JsonIgnore]
public List<Room> serversConnectedToRelay;
@ -20,21 +20,22 @@ namespace LightReflectiveMirror.LoadBalancing
[Serializable]
internal struct LoadBalancerStats
{
public int NodeCount;
public TimeSpan Uptime;
public int nodeCount;
public TimeSpan uptime;
public long CCU;
public long TotalServerCount;
public long totalServerCount;
}
// container for relay address info
[JsonObject(MemberSerialization.OptOut)]
public struct RelayAddress
{
public ushort Port;
public ushort EndpointPort;
public string Address;
public ushort port;
public ushort endpointPort;
public string address;
public LRMRegions serverRegion;
[JsonIgnore]
public string EndpointAddress;
public string endpointAddress;
}
[Serializable]
@ -50,4 +51,6 @@ namespace LightReflectiveMirror.LoadBalancing
public RelayAddress relayInfo;
}
public enum LRMRegions { Any, NorthAmerica, SouthAmerica, Europe, Asia, Africa, Oceania }
}

View file

@ -18,16 +18,30 @@ namespace LightReflectiveMirror.LoadBalancing
[RestResource]
public class Endpoint
{
public static string cachedServerList = "[]";
public static string allCachedServers = "[]";
public static string NorthAmericaCachedServers = "[]";
public static string SouthAmericaCachedServers = "[]";
public static string EuropeCachedServers = "[]";
public static string AsiaCachedServers = "[]";
public static string AfricaCachedServers = "[]";
public static string OceaniaCachedServers = "[]";
private static List<Room> northAmericaServers = new();
private static List<Room> southAmericaServers = new();
private static List<Room> europeServers = new();
private static List<Room> africaServers = new();
private static List<Room> asiaServers = new();
private static List<Room> oceaniaServers = new();
private static List<Room> allServers = new();
private LoadBalancerStats _stats
{
get => new()
{
NodeCount = Program.instance.availableRelayServers.Count,
Uptime = DateTime.Now - Program.startupTime,
nodeCount = Program.instance.availableRelayServers.Count,
uptime = DateTime.Now - Program.startupTime,
CCU = Program.instance.GetTotalCCU(),
TotalServerCount = Program.instance.GetTotalServers(),
totalServerCount = Program.instance.GetTotalServers(),
};
}
@ -45,12 +59,14 @@ namespace LightReflectiveMirror.LoadBalancing
string endpointPort = req.Headers["x-EndpointPort"];
string gamePort = req.Headers["x-GamePort"];
string publicIP = req.Headers["x-PIP"];
string region = req.Headers["x-Region"];
int regionId = 1;
string address = context.Request.RemoteEndPoint.Address.ToString();
Logger.WriteLogMessage("Received auth req [" + receivedAuthKey + "] == [" + Program.conf.AuthKey + "]");
// if server is authenticated
if (receivedAuthKey != null && address != null && endpointPort != null && gamePort != null && receivedAuthKey == Program.conf.AuthKey)
if (receivedAuthKey != null && region != null && int.TryParse(region, out regionId) && address != null && endpointPort != null && gamePort != null && receivedAuthKey == Program.conf.AuthKey)
{
Logger.WriteLogMessage($"Server accepted: {address}:{gamePort}");
@ -58,7 +74,7 @@ namespace LightReflectiveMirror.LoadBalancing
{
var _gamePort = Convert.ToUInt16(gamePort);
var _endpointPort = Convert.ToUInt16(endpointPort);
await Program.instance.AddServer(address, _gamePort, _endpointPort, publicIP);
await Program.instance.AddServer(address, _gamePort, _endpointPort, publicIP, regionId);
}
catch
{
@ -82,20 +98,66 @@ namespace LightReflectiveMirror.LoadBalancing
// Dont allow unauthorizated access waste computing resources.
string auth = context.Request.Headers["Authorization"];
if(!string.IsNullOrEmpty(auth) && auth == Program.conf.AuthKey)
if (!string.IsNullOrEmpty(auth) && auth == Program.conf.AuthKey)
{
var relays = Program.instance.availableRelayServers.ToList();
List<Room> masterList = new();
ClearAllServersLists();
List<Room> requestedRooms;
for(int i = 0; i < relays.Count; i++)
for (int i = 0; i < relays.Count; i++)
{
masterList.AddRange(await Program.instance.RequestServerListFromNode(relays[i].Key.Address, relays[i].Key.EndpointPort));
requestedRooms = await Program.instance.RequestServerListFromNode(relays[i].Key.address, relays[i].Key.endpointPort);
allServers.AddRange(requestedRooms);
switch (relays[i].Key.serverRegion)
{
case (LRMRegions.NorthAmerica):
northAmericaServers.AddRange(requestedRooms);
break;
case (LRMRegions.SouthAmerica):
southAmericaServers.AddRange(requestedRooms);
break;
case (LRMRegions.Europe):
europeServers.AddRange(requestedRooms);
break;
case (LRMRegions.Africa):
africaServers.AddRange(requestedRooms);
break;
case (LRMRegions.Asia):
asiaServers.AddRange(requestedRooms);
break;
case (LRMRegions.Oceania):
oceaniaServers.AddRange(requestedRooms);
break;
}
}
cachedServerList = JsonConvert.SerializeObject(masterList);
CacheAllServers();
}
}
void CacheAllServers()
{
allCachedServers = JsonConvert.SerializeObject(allServers);
NorthAmericaCachedServers = JsonConvert.SerializeObject(northAmericaServers);
SouthAmericaCachedServers = JsonConvert.SerializeObject(southAmericaServers);
EuropeCachedServers = JsonConvert.SerializeObject(europeServers);
AsiaCachedServers = JsonConvert.SerializeObject(asiaServers);
AfricaCachedServers = JsonConvert.SerializeObject(africaServers);
OceaniaCachedServers = JsonConvert.SerializeObject(oceaniaServers);
}
void ClearAllServersLists()
{
northAmericaServers.Clear();
southAmericaServers.Clear();
europeServers.Clear();
asiaServers.Clear();
africaServers.Clear();
oceaniaServers.Clear();
allServers.Clear();
}
/// <summary>
/// Hooks into from unity side, client will call this to
/// find the least populated server to join
@ -119,7 +181,7 @@ namespace LightReflectiveMirror.LoadBalancing
for (int i = 0; i < servers.Count; i++)
{
if (servers[i].Value.ConnectedClients < lowest.Value.ConnectedClients)
if (servers[i].Value.connectedClients < lowest.Value.connectedClients)
{
lowest = servers[i];
}
@ -127,7 +189,7 @@ namespace LightReflectiveMirror.LoadBalancing
// respond with the server ip
// if the string is still dummy then theres no servers
if (lowest.Key.Address != "Dummy")
if (lowest.Key.address != "Dummy")
{
await context.Response.SendResponseAsync(JsonConvert.SerializeObject(lowest.Key));
}
@ -145,7 +207,40 @@ namespace LightReflectiveMirror.LoadBalancing
[RestRoute("Get", "/api/masterlist/")]
public async Task GetMasterServerList(IHttpContext context)
{
await context.Response.SendResponseAsync(cachedServerList);
string region = context.Request.Headers["x-Region"];
if(int.TryParse(region, out int regionID))
{
switch ((LRMRegions)regionID)
{
case LRMRegions.Any:
await context.Response.SendResponseAsync(allCachedServers);
break;
case LRMRegions.NorthAmerica:
await context.Response.SendResponseAsync(NorthAmericaCachedServers);
break;
case LRMRegions.SouthAmerica:
await context.Response.SendResponseAsync(SouthAmericaCachedServers);
break;
case LRMRegions.Europe:
await context.Response.SendResponseAsync(EuropeCachedServers);
break;
case LRMRegions.Africa:
await context.Response.SendResponseAsync(AfricaCachedServers);
break;
case LRMRegions.Asia:
await context.Response.SendResponseAsync(AsiaCachedServers);
break;
case LRMRegions.Oceania:
await context.Response.SendResponseAsync(OceaniaCachedServers);
break;
}
return;
}
// They didnt submit a region header, just give them all servers as they probably are viewing in browser.
await context.Response.SendResponseAsync(allCachedServers);
}
/// <summary>
@ -218,7 +313,7 @@ namespace LightReflectiveMirror.LoadBalancing
bool hasLocal = false;
for(int i = 0; i < bindableIPv4Addresses.Count; i++)
for (int i = 0; i < bindableIPv4Addresses.Count; i++)
{
if (bindableIPv4Addresses[i] == "127.0.0.1")
hasLocal = true;

View file

@ -71,9 +71,9 @@ namespace LightReflectiveMirror.LoadBalancing
/// <param name="endpointPort"></param>
/// <param name="publicIP"></param>
/// <returns></returns>
public async Task AddServer(string serverIP, ushort port, ushort endpointPort, string publicIP)
public async Task AddServer(string serverIP, ushort port, ushort endpointPort, string publicIP, int regionId)
{
var relayAddr = new RelayAddress { Port = port, EndpointPort = endpointPort, Address = publicIP, EndpointAddress = serverIP };
var relayAddr = new RelayAddress { port = port, endpointPort = endpointPort, address = publicIP, endpointAddress = serverIP.Trim(), serverRegion = (LRMRegions)regionId };
if (availableRelayServers.ContainsKey(relayAddr))
{
@ -187,13 +187,14 @@ namespace LightReflectiveMirror.LoadBalancing
for (int i = 0; i < keys.Count; i++)
{
if(!await HealthCheckNode(keys[i].EndpointAddress, keys[i].EndpointPort))
if(!await HealthCheckNode(keys[i].endpointAddress, keys[i].endpointPort))
{
Logger.ForceLogMessage($"Server {keys[i].Address}:{keys[i].Port} failed a health check, removing from load balancer.", ConsoleColor.Red);
Logger.ForceLogMessage($"Server {keys[i].address}:{keys[i].port} failed a health check, removing from load balancer.", ConsoleColor.Red);
availableRelayServers.Remove(keys[i]);
}
}
GC.Collect();
await Task.Delay(_pingDelay);
}
}
@ -229,5 +230,4 @@ namespace LightReflectiveMirror.LoadBalancing
Logger.ForceLogMessage(load, ConsoleColor.Cyan);
}
}
}

View file

@ -35,5 +35,6 @@ namespace LightReflectiveMirror
public string LoadBalancerAuthKey = "AuthKey";
public string LoadBalancerAddress = "127.0.0.1";
public ushort LoadBalancerPort = 7070;
public LRMRegions LoadBalancerRegion = LRMRegions.NorthAmerica;
}
}

View file

@ -22,4 +22,19 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View file

@ -225,13 +225,14 @@ namespace LightReflectiveMirror
var uri = new Uri($"http://{conf.LoadBalancerAddress}:{conf.LoadBalancerPort}/api/auth");
string endpointPort = conf.EndpointPort.ToString();
string gamePort = 7777.ToString();
string gamePort = "7777";
HttpWebRequest authReq = (HttpWebRequest)WebRequest.Create(uri);
authReq.Headers.Add("Authorization", conf.LoadBalancerAuthKey);
authReq.Headers.Add("x-EndpointPort", endpointPort);
authReq.Headers.Add("x-GamePort", gamePort);
authReq.Headers.Add("x-PIP", publicIP); // Public IP
authReq.Headers.Add("x-Region", ((int)conf.LoadBalancerRegion).ToString());
var res = await authReq.GetResponseAsync();

View file

@ -34,4 +34,6 @@ namespace LightReflectiveMirror
private readonly string CONFIG_PATH = System.Environment.GetEnvironmentVariable("LRM_CONFIG_PATH") ?? "config.json";
}
public enum LRMRegions { Any, NorthAmerica, SouthAmerica, Europe, Asia, Africa, Oceania }
}

View file

@ -132,10 +132,10 @@ namespace LightReflectiveMirror
serverData = serverData,
clients = new List<int>(),
// hard coded for now REMEMBER TO UN-HARDCODE RETARD
// hard coded for now REMEMBER TO UN-HARDCODE
// this is needed for load balancer to know which server this room
// belongs to
relayInfo = new RelayAddress { Address = Program.publicIP, Port = 7777, EndpointPort = Program.conf.EndpointPort },
relayInfo = new RelayAddress { address = Program.publicIP, port = 7777, endpointPort = Program.conf.EndpointPort },
serverId = GetRandomServerID(),
hostIP = hostIP,

View file

@ -33,8 +33,8 @@ namespace LightReflectiveMirror
[Serializable]
public struct RelayAddress
{
public ushort Port;
public ushort EndpointPort;
public string Address;
public ushort port;
public ushort endpointPort;
public string address;
}
}

View file

@ -152,22 +152,23 @@ namespace LightReflectiveMirror
}
}
}
else if(lrm.NATPunchtroughPort < 0)
else if (lrm.NATPunchtroughPort < 0)
{
// NAT Punchthrough configuration.
EditorGUILayout.HelpBox("Do you wish to use NAT Punchthrough? This can reduce load by up to 80% on your LRM nodes, but exposes players IP's to other players.", MessageType.None);
if(GUILayout.Button("Use NAT Punchthrough"))
if (GUILayout.Button("Use NAT Punchthrough"))
{
lrm.NATPunchtroughPort = 1;
lrm.useNATPunch = true;
lrm.gameObject.AddComponent<LRMDirectConnectModule>();
}
if(GUILayout.Button("Do NOT use NAT Punchthrough"))
if (GUILayout.Button("Do NOT use NAT Punchthrough"))
lrm.NATPunchtroughPort = 1;
}else if(directModule != null && directModule.directConnectTransport == null)
}
else if (directModule != null && directModule.directConnectTransport == null)
{
// NAT Punchthrough setup.
EditorGUILayout.HelpBox("To use direct connecting, we need a transport to communicate with the other clients. Please select a transport to use.", MessageType.None);
@ -217,7 +218,7 @@ namespace LightReflectiveMirror
break;
case 1:
// NAT punch tab.
if(directModule == null)
if (directModule == null)
{
EditorGUILayout.HelpBox("If you wish to use NAT punch, you will need to add a \"Direct Connect Module\" to this gameobject.", MessageType.Info);
}
@ -230,8 +231,14 @@ namespace LightReflectiveMirror
case 2:
// Load balancer tab
lrm.useLoadBalancer = EditorGUILayout.Toggle("Use Load Balancer", lrm.useLoadBalancer);
if (!lrm.useLoadBalancer)
GUI.enabled = false;
lrm.loadBalancerAddress = EditorGUILayout.TextField("Load Balancer Address", lrm.loadBalancerAddress);
lrm.loadBalancerPort = (ushort)Mathf.Clamp(EditorGUILayout.IntField("Load Balancer Port", lrm.loadBalancerPort), ushort.MinValue, ushort.MaxValue);
lrm.region = (LRMRegions)EditorGUILayout.EnumPopup("Node Region", lrm.region);
if (!lrm.useLoadBalancer)
GUI.enabled = true;
break;
case 3:
// Other tab...

View file

@ -148,6 +148,7 @@ namespace LightReflectiveMirror
using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
{
webRequest.SetRequestHeader("x-Region", ((int)region).ToString());
// Request and wait for the desired page.
yield return webRequest.SendWebRequest();
var result = webRequest.downloadHandler.text;

View file

@ -42,6 +42,7 @@ namespace LightReflectiveMirror
private LRMDirectConnectModule _directConnectModule;
public LRMRegions region = LRMRegions.NorthAmerica;
private byte[] _clientSendBuffer;
private bool _connectedToRelay = false;
private bool _isClient = false;
@ -61,4 +62,6 @@ namespace LightReflectiveMirror
private BiDictionary<int, int> _connectedRelayClients = new BiDictionary<int, int>();
private BiDictionary<int, int> _connectedDirectClients = new BiDictionary<int, int>();
}
public enum LRMRegions { Any, NorthAmerica, SouthAmerica, Europe, Asia, Africa, Oceania }
}