Files
src/engine/server/application/TaskManager/src/shared/Locator.cpp
T
2014-01-16 20:06:39 -07:00

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);
}
// ======================================================================