mirror of
https://github.com/SWG-Source/src.git
synced 2026-07-28 22:15:49 -04:00
446 lines
14 KiB
C++
446 lines
14 KiB
C++
// ======================================================================
|
|
//
|
|
// Locator.cpp
|
|
//
|
|
// Copyright 2000-04 Sony Online Entertainment
|
|
//
|
|
// ======================================================================
|
|
|
|
#include "FirstTaskManager.h"
|
|
#include "Locator.h"
|
|
|
|
#include "ConfigTaskManager.h"
|
|
#include "ManagerConnection.h"
|
|
#include "TaskManager.h"
|
|
#include "serverNetworkMessages/TaskUtilization.h"
|
|
#include "sharedFoundation/ConfigFile.h"
|
|
#include "sharedLog/Log.h"
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
// ======================================================================
|
|
|
|
namespace LocatorNamespace
|
|
{
|
|
float getConfigSetting(const char *section, const char *key, const float defaultValue)
|
|
{
|
|
const ConfigFile::Section * sec = ConfigFile::getSection(section);
|
|
if (sec == NULL)
|
|
return defaultValue;
|
|
|
|
const ConfigFile::Key * ky = sec->findKey(key);
|
|
if (ky == NULL)
|
|
return defaultValue;
|
|
|
|
return ky->getAsFloat(ky->getCount()-1, defaultValue);
|
|
}
|
|
|
|
struct PreferredNode
|
|
{
|
|
std::string m_nodeLabel;
|
|
std::string m_processName;
|
|
std::vector<std::string> m_nodeOptions;
|
|
|
|
bool match(std::string const &processName, std::string const &options) const
|
|
{
|
|
if (m_processName != processName)
|
|
return false;
|
|
for (std::vector<std::string>::const_iterator i = m_nodeOptions.begin(); i != m_nodeOptions.end(); ++i)
|
|
if (options.find(*i) == std::string::npos)
|
|
return false;
|
|
return true;
|
|
}
|
|
};
|
|
|
|
struct ServerEntry
|
|
{
|
|
ServerEntry(ManagerConnection *conn, std::string const &label, float load);
|
|
|
|
bool operator==(std::string const &label) const
|
|
{
|
|
return m_label == label;
|
|
}
|
|
bool operator<(ServerEntry const & rhs) const
|
|
{
|
|
// don't change this; the code requires that the nodes
|
|
// list be ordered by the current load on the node
|
|
return m_load < rhs.m_load;
|
|
}
|
|
bool hasAvailableLoad(float cost) const
|
|
{
|
|
return ((m_load + cost) < m_maximumLoad);
|
|
}
|
|
|
|
ManagerConnection *m_connection;
|
|
std::string m_label;
|
|
float m_load;
|
|
float m_maximumLoad;
|
|
};
|
|
|
|
ServerEntry::ServerEntry(ManagerConnection *conn, std::string const &label, float load) :
|
|
m_connection(conn),
|
|
m_label(label),
|
|
m_load(load),
|
|
m_maximumLoad(0.0f)
|
|
{
|
|
// see if there is a maximum load specified for this particular node
|
|
char key[128];
|
|
snprintf(key, sizeof(key)-1, "maximumLoad.%s", m_label.c_str());
|
|
key[sizeof(key)-1] = '\0';
|
|
|
|
m_maximumLoad = getConfigSetting("TaskManager", key, ConfigTaskManager::getMaximumLoad());
|
|
}
|
|
|
|
std::vector<ServerEntry> s_serverList;
|
|
const char s_masterNodeLabel[] = "node0";
|
|
float s_myLoad;
|
|
std::vector<PreferredNode> s_preferredNodes;
|
|
std::map<std::string, std::string> s_closedConnections; // to keep track of TaskManager that we have lost connection to which has not yet reconnected
|
|
|
|
ManagerConnection *getPreferredServer(std::string const &processName, std::string const &options, float cost);
|
|
ManagerConnection *getUnpreferredServer(float cost);
|
|
void addPreferredNode(std::string const &preferredNodeSpec);
|
|
}
|
|
|
|
using namespace LocatorNamespace;
|
|
|
|
// ======================================================================
|
|
|
|
void Locator::install()
|
|
{
|
|
int index = 0;
|
|
char const * result = 0;
|
|
|
|
do
|
|
{
|
|
result = ConfigFile::getKeyString("TaskManager", "preferredNode", index++, 0);
|
|
if (result)
|
|
addPreferredNode(result);
|
|
}
|
|
while (result);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Locator::closed(std::string const &label, ManagerConnection const *oldConnection)
|
|
{
|
|
if (oldConnection)
|
|
{
|
|
std::vector<ServerEntry>::iterator i = std::find(s_serverList.begin(), s_serverList.end(), label);
|
|
if (i != s_serverList.end())
|
|
{
|
|
DEBUG_REPORT_LOG(true, ("Removing connection %s on %s from locator map\n", label.c_str(), oldConnection->getRemoteAddress().c_str()));
|
|
IGNORE_RETURN(s_serverList.erase(i));
|
|
s_closedConnections[label] = oldConnection->getRemoteAddress();
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
ManagerConnection *LocatorNamespace::getPreferredServer(std::string const &processName, std::string const &options, float cost)
|
|
{
|
|
// Find a node other than the master node that can afford to run the process
|
|
// and is a preferred node for the process and option set.
|
|
for (std::vector<ServerEntry>::iterator i = s_serverList.begin(); i != s_serverList.end(); ++i)
|
|
{
|
|
ServerEntry &e = *i;
|
|
if ((e.m_label != s_masterNodeLabel) && (e.hasAvailableLoad(cost)))
|
|
{
|
|
for (std::vector<PreferredNode>::const_iterator j = s_preferredNodes.begin(); j != s_preferredNodes.end(); ++j)
|
|
if ((*j).m_nodeLabel == e.m_label && (*j).match(processName, options))
|
|
return e.m_connection;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
ManagerConnection *LocatorNamespace::getUnpreferredServer(float cost)
|
|
{
|
|
// Find a node other than the master node that can afford to run the process
|
|
// and is a not marked as a preferred node for anything; if more than one
|
|
// node qualifies, return the node with the lowest load
|
|
for (std::vector<ServerEntry>::iterator i = s_serverList.begin(); i != s_serverList.end(); ++i)
|
|
{
|
|
ServerEntry &e = *i;
|
|
if ((e.m_label != s_masterNodeLabel) && (e.hasAvailableLoad(cost)))
|
|
{
|
|
bool foundPreferred = false;
|
|
for (std::vector<PreferredNode>::const_iterator j = s_preferredNodes.begin(); j != s_preferredNodes.end(); ++j)
|
|
{
|
|
if ((*j).m_nodeLabel == e.m_label)
|
|
{
|
|
foundPreferred = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// s_serverList is ordered by load, so this will
|
|
// return the available node with the lowest load
|
|
if (!foundPreferred)
|
|
return e.m_connection;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
ManagerConnection *Locator::getBestServer(std::string const &processName, std::string const &options, float cost)
|
|
{
|
|
if (s_serverList.empty())
|
|
return 0;
|
|
|
|
// first, look for a preferred node for this process
|
|
{
|
|
ManagerConnection * const connection = getPreferredServer(processName, options, cost);
|
|
if (connection)
|
|
return connection;
|
|
}
|
|
|
|
// there is not a preferred node available, so look for a node which is not preferred in some form
|
|
{
|
|
ManagerConnection * const connection = getUnpreferredServer(cost);
|
|
if (connection)
|
|
return connection;
|
|
}
|
|
|
|
// there is not a non-preferred node available, so look for any node other than the command node;
|
|
// If more than one node qualifies, we want to choose the one who currently has the lowest load
|
|
{
|
|
for (std::vector<ServerEntry>::iterator i = s_serverList.begin(); i != s_serverList.end(); ++i)
|
|
{
|
|
ServerEntry &e = *i;
|
|
if ((e.m_label != s_masterNodeLabel) && (e.hasAvailableLoad(cost)))
|
|
{
|
|
// s_serverList is ordered by load, so this will
|
|
// return the available node with the lowest load
|
|
return e.m_connection;
|
|
}
|
|
}
|
|
|
|
WARNING(true, ("No hosts are available to spawn a process costing %f without exceeding the host's load limit", cost));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
ManagerConnection *Locator::getServer(std::string const &label)
|
|
{
|
|
for (std::vector<ServerEntry>::const_iterator i = s_serverList.begin(); i != s_serverList.end(); ++i)
|
|
if (i->m_label == label || i->m_connection->getRemoteAddress() == label)
|
|
return i->m_connection;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
float Locator::getServerLoad(std::string const &label)
|
|
{
|
|
std::vector<ServerEntry>::const_iterator i = std::find(s_serverList.begin(), s_serverList.end(), label);
|
|
if (i != s_serverList.end())
|
|
return i->m_load;
|
|
return 0.f;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
float Locator::getServerMaximumLoad(std::string const &label)
|
|
{
|
|
std::vector<ServerEntry>::const_iterator i = std::find(s_serverList.begin(), s_serverList.end(), label);
|
|
if (i != s_serverList.end())
|
|
return i->m_maximumLoad;
|
|
|
|
// see if there is a maximum load specified for this particular node
|
|
char key[128];
|
|
snprintf(key, sizeof(key)-1, "maximumLoad.%s", label.c_str());
|
|
key[sizeof(key)-1] = '\0';
|
|
|
|
return getConfigSetting("TaskManager", key, ConfigTaskManager::getMaximumLoad());
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Locator::incrementMyLoad(float amount)
|
|
{
|
|
s_myLoad += amount;
|
|
std::sort(s_serverList.begin(), s_serverList.end());
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Locator::decrementMyLoad(float amount)
|
|
{
|
|
s_myLoad -= amount;
|
|
if (s_myLoad < 0.0f)
|
|
s_myLoad = 0.0f;
|
|
std::sort(s_serverList.begin(), s_serverList.end());
|
|
updateAllLoads(-amount);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Locator::incrementServerLoad(std::string const &label, float amount)
|
|
{
|
|
std::vector<ServerEntry>::iterator i = std::find(s_serverList.begin(), s_serverList.end(), label);
|
|
if (i != s_serverList.end())
|
|
{
|
|
i->m_load += amount;
|
|
std::sort(s_serverList.begin(), s_serverList.end());
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Locator::decrementServerLoad(std::string const &label, float amount)
|
|
{
|
|
std::vector<ServerEntry>::iterator i = std::find(s_serverList.begin(), s_serverList.end(), label);
|
|
if (i != s_serverList.end())
|
|
{
|
|
i->m_load -= amount;
|
|
if (i->m_load < 0.0f)
|
|
{
|
|
DEBUG_WARNING(true, ("%s dropped less than 0", label.c_str()));
|
|
i->m_load = 0.0f;
|
|
}
|
|
std::sort(s_serverList.begin(), s_serverList.end());
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
float Locator::getMyLoad()
|
|
{
|
|
return s_myLoad;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
float Locator::getMyMaximumLoad()
|
|
{
|
|
static float myMaximumLoad = -1.0f;
|
|
|
|
// get the maximum load for this particular node
|
|
if (myMaximumLoad <= 0.0f)
|
|
{
|
|
// see if there is a maximum load specified for this particular node
|
|
char key[128];
|
|
snprintf(key, sizeof(key)-1, "maximumLoad.%s", TaskManager::getNodeLabel().c_str());
|
|
key[sizeof(key)-1] = '\0';
|
|
|
|
myMaximumLoad = getConfigSetting("TaskManager", key, ConfigTaskManager::getMaximumLoad());
|
|
}
|
|
|
|
return myMaximumLoad;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Locator::opened(std::string const &label, ManagerConnection *newConnection)
|
|
{
|
|
if (newConnection)
|
|
{
|
|
std::vector<ServerEntry>::const_iterator i = std::find(s_serverList.begin(), s_serverList.end(), label);
|
|
if (i == s_serverList.end())
|
|
{
|
|
ServerEntry s(newConnection, label, 0.0f);
|
|
s_serverList.push_back(s);
|
|
std::sort(s_serverList.begin(), s_serverList.end());
|
|
IGNORE_RETURN(s_closedConnections.erase(label));
|
|
|
|
LOG("TaskManagerConnect", ("Connection with remote task manager %s established with label %s, maximum load %f", newConnection->getRemoteAddress().c_str(), label.c_str(), s.m_maximumLoad));
|
|
}
|
|
else
|
|
{
|
|
WARNING_STRICT_FATAL(true, ("Tried to add %s on %s to server list but it already exsists", label.c_str(), newConnection->getRemoteAddress().c_str()));
|
|
}
|
|
DEBUG_REPORT_LOG(true, ("Connection with remote task manager %s established with label %s\n", newConnection->getRemoteAddress().c_str(), label.c_str()));
|
|
TaskManager::runSpawnRequestQueue();
|
|
}
|
|
else
|
|
WARNING_STRICT_FATAL(true, ("Passed null connection to Locator::opened"));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Locator::updateServerLoad(std::string const &label, float load)
|
|
{
|
|
std::vector<ServerEntry>::iterator i = std::find(s_serverList.begin(), s_serverList.end(), label);
|
|
if(i != s_serverList.end())
|
|
{
|
|
i->m_load += load;
|
|
std::sort(s_serverList.begin(), s_serverList.end());
|
|
LOG("TaskManager", ("updateServerLoad(%s, %f)", label.c_str(), load));
|
|
}
|
|
else
|
|
{
|
|
DEBUG_WARNING(true, ("Told to update load on %s which didn't exist\n", label.c_str()));
|
|
}
|
|
TaskManager::runSpawnRequestQueue();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Locator::updateAllLoads(float delta)
|
|
{
|
|
if (!s_serverList.empty())
|
|
{
|
|
TaskUtilization const util(TaskUtilization::SYSTEM_AVG, delta);
|
|
sendToAllTaskManagers(util);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
std::map<std::string, std::string> const & Locator::getClosedConnections()
|
|
{
|
|
return s_closedConnections;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Locator::sendToAllTaskManagers(GameNetworkMessage const &msg)
|
|
{
|
|
if (!s_serverList.empty())
|
|
for (std::vector<ServerEntry>::const_iterator i = s_serverList.begin(); i != s_serverList.end(); ++i)
|
|
i->m_connection->send(msg);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void LocatorNamespace::addPreferredNode(std::string const &preferredNodeSpec)
|
|
{
|
|
// preferredNodeSpec is "nodeLabel:processName:option=value:option=value:..."
|
|
|
|
PreferredNode n;
|
|
std::string::size_type lastPos = 0;
|
|
std::string::size_type pos;
|
|
|
|
pos = preferredNodeSpec.find(':', lastPos);
|
|
n.m_nodeLabel = preferredNodeSpec.substr(lastPos, pos-lastPos);
|
|
lastPos = pos == std::string::npos ? preferredNodeSpec.size() : pos+1;
|
|
|
|
pos = preferredNodeSpec.find(':', lastPos);
|
|
n.m_processName = preferredNodeSpec.substr(lastPos, pos-lastPos);
|
|
lastPos = pos == std::string::npos ? preferredNodeSpec.size() : pos+1;
|
|
|
|
while (lastPos != std::string::npos)
|
|
{
|
|
pos = preferredNodeSpec.find(':', lastPos);
|
|
n.m_nodeOptions.push_back(preferredNodeSpec.substr(lastPos, pos-lastPos));
|
|
lastPos = pos == std::string::npos ? std::string::npos : pos+1;
|
|
}
|
|
|
|
if (!n.m_nodeLabel.empty() && !n.m_processName.empty())
|
|
s_preferredNodes.push_back(n);
|
|
}
|
|
|
|
// ======================================================================
|
|
|