Geo IP based load balancing (requires external coords)
This commit is contained in:
		
							parent
							
								
									75cefe9956
								
							
						
					
					
						commit
						3e303e0320
					
				
					 1 changed files with 91 additions and 9 deletions
				
			
		| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
#include <stdint.h>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <cmath>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <mist/config.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -19,6 +20,7 @@ bool localMode = false;
 | 
			
		|||
unsigned int weight_cpu = 500;
 | 
			
		||||
unsigned int weight_ram = 500;
 | 
			
		||||
unsigned int weight_bw = 1000;
 | 
			
		||||
unsigned int weight_geo = 1000;
 | 
			
		||||
unsigned int weight_bonus = 50;
 | 
			
		||||
unsigned long hostsCounter = 0; // This is a pointer to guarantee atomic accesses.
 | 
			
		||||
#define HOSTLOOP                                                                                   \
 | 
			
		||||
| 
						 | 
				
			
			@ -61,6 +63,17 @@ class outUrl{
 | 
			
		|||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
inline double toRad(double degree){
 | 
			
		||||
  return degree / 57.29577951308232087684;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double geoDist(double lat1, double long1, double lat2, double long2){
 | 
			
		||||
  double dist;
 | 
			
		||||
  dist = sin(toRad(lat1)) * sin(toRad(lat2)) +
 | 
			
		||||
         cos(toRad(lat1)) * cos(toRad(lat2)) * cos(toRad(long1 - long2));
 | 
			
		||||
  return .31830988618379067153 * acos(dist);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class hostDetails{
 | 
			
		||||
private:
 | 
			
		||||
  tthread::mutex *hostMutex;
 | 
			
		||||
| 
						 | 
				
			
			@ -81,6 +94,9 @@ private:
 | 
			
		|||
public:
 | 
			
		||||
  std::string host;
 | 
			
		||||
  unsigned long long availBandwidth;
 | 
			
		||||
  JSON::Value geoDetails;
 | 
			
		||||
  double servLati, servLongi;
 | 
			
		||||
  std::string servLoc;
 | 
			
		||||
  hostDetails(){
 | 
			
		||||
    hostMutex = 0;
 | 
			
		||||
    cpu = 1000;
 | 
			
		||||
| 
						 | 
				
			
			@ -93,6 +109,8 @@ public:
 | 
			
		|||
    prevTime = 0;
 | 
			
		||||
    total = 0;
 | 
			
		||||
    addBandwidth = 0;
 | 
			
		||||
    servLati = 0;
 | 
			
		||||
    servLongi = 0;
 | 
			
		||||
    availBandwidth = 128 * 1024 * 1024; // assume 1G connections
 | 
			
		||||
  }
 | 
			
		||||
  ~hostDetails(){
 | 
			
		||||
| 
						 | 
				
			
			@ -129,6 +147,11 @@ public:
 | 
			
		|||
    r["streams"] = (long long)streams.size();
 | 
			
		||||
    r["viewers"] = (long long)total;
 | 
			
		||||
    r["bwlimit"] = (long long)availBandwidth;
 | 
			
		||||
    if (servLati || servLongi){
 | 
			
		||||
      r["geo"]["lat"] = servLati;
 | 
			
		||||
      r["geo"]["lon"] = servLongi;
 | 
			
		||||
      r["geo"]["loc"] = servLoc;
 | 
			
		||||
    }
 | 
			
		||||
    if (ramMax && availBandwidth){
 | 
			
		||||
      r["score"]["cpu"] = (long long)(weight_cpu - (cpu * weight_cpu) / 1000);
 | 
			
		||||
      r["score"]["ram"] = (long long)(weight_ram - ((ramCurr * weight_ram) / ramMax));
 | 
			
		||||
| 
						 | 
				
			
			@ -147,7 +170,7 @@ public:
 | 
			
		|||
  }
 | 
			
		||||
  /// Scores a potential new connection to this server
 | 
			
		||||
  /// 0 means not possible, the higher the better.
 | 
			
		||||
  unsigned int rate(std::string &s){
 | 
			
		||||
  unsigned int rate(std::string &s, double lati = 0, double longi = 0){
 | 
			
		||||
    if (!hostMutex){hostMutex = new tthread::mutex();}
 | 
			
		||||
    tthread::lock_guard<tthread::mutex> guard(*hostMutex);
 | 
			
		||||
    if (!ramMax || !availBandwidth){
 | 
			
		||||
| 
						 | 
				
			
			@ -167,16 +190,20 @@ public:
 | 
			
		|||
    unsigned int cpu_score = (weight_cpu - (cpu * weight_cpu) / 1000);
 | 
			
		||||
    unsigned int ram_score = (weight_ram - ((ramCurr * weight_ram) / ramMax));
 | 
			
		||||
    unsigned int bw_score = (weight_bw - (((upSpeed + addBandwidth) * weight_bw) / availBandwidth));
 | 
			
		||||
    unsigned int score = cpu_score + ram_score + bw_score + (streams.count(s) ? weight_bonus : 0);
 | 
			
		||||
    unsigned int geo_score = 0;
 | 
			
		||||
    if (servLati && servLongi && lati && longi){
 | 
			
		||||
      geo_score = weight_geo - weight_geo * geoDist(servLati, servLongi, lati, longi);
 | 
			
		||||
    }
 | 
			
		||||
    unsigned int score = cpu_score + ram_score + bw_score + geo_score + (streams.count(s) ? weight_bonus : 0);
 | 
			
		||||
    // Print info on host
 | 
			
		||||
    MEDIUM_MSG("%s: CPU %u, RAM %u, Stream %u, BW %u (max %llu MB/s) -> %u", host.c_str(),
 | 
			
		||||
    MEDIUM_MSG("%s: CPU %u, RAM %u, Stream %u, BW %u (max %llu MB/s), Geo %u -> %u", host.c_str(),
 | 
			
		||||
               cpu_score, ram_score, streams.count(s) ? weight_bonus : 0, bw_score,
 | 
			
		||||
               availBandwidth / 1024 / 1024, score);
 | 
			
		||||
               availBandwidth / 1024 / 1024, geo_score, score);
 | 
			
		||||
    return score;
 | 
			
		||||
  }
 | 
			
		||||
  /// Scores this server as a source
 | 
			
		||||
  /// 0 means not possible, the higher the better.
 | 
			
		||||
  unsigned int source(std::string &s){
 | 
			
		||||
  unsigned int source(std::string &s, double lati = 0, double longi = 0){
 | 
			
		||||
    if (!hostMutex){hostMutex = new tthread::mutex();}
 | 
			
		||||
    tthread::lock_guard<tthread::mutex> guard(*hostMutex);
 | 
			
		||||
    if (!streams.count(s) || !streams[s].inputs){return 0;}
 | 
			
		||||
| 
						 | 
				
			
			@ -193,11 +220,15 @@ public:
 | 
			
		|||
    unsigned int cpu_score = (weight_cpu - (cpu * weight_cpu) / 1000);
 | 
			
		||||
    unsigned int ram_score = (weight_ram - ((ramCurr * weight_ram) / ramMax));
 | 
			
		||||
    unsigned int bw_score = (weight_bw - (((upSpeed + addBandwidth) * weight_bw) / availBandwidth));
 | 
			
		||||
    unsigned int score = cpu_score + ram_score + bw_score + 1;
 | 
			
		||||
    unsigned int geo_score = 0;
 | 
			
		||||
    if (servLati && servLongi && lati && longi){
 | 
			
		||||
      geo_score = weight_geo - weight_geo * geoDist(servLati, servLongi, lati, longi);
 | 
			
		||||
    }
 | 
			
		||||
    unsigned int score = cpu_score + ram_score + bw_score + geo_score + 1;
 | 
			
		||||
    // Print info on host
 | 
			
		||||
    MEDIUM_MSG("SOURCE %s: CPU %u, RAM %u, Stream %u, BW %u (max %llu MB/s) -> %u", host.c_str(),
 | 
			
		||||
    MEDIUM_MSG("SOURCE %s: CPU %u, RAM %u, Stream %u, BW %u (max %llu MB/s), Geo %u -> %u", host.c_str(),
 | 
			
		||||
               cpu_score, ram_score, streams.count(s) ? weight_bonus : 0, bw_score,
 | 
			
		||||
               availBandwidth / 1024 / 1024, score);
 | 
			
		||||
               availBandwidth / 1024 / 1024, geo_score, score);
 | 
			
		||||
    return score;
 | 
			
		||||
  }
 | 
			
		||||
  std::string getUrl(std::string &s, std::string &proto){
 | 
			
		||||
| 
						 | 
				
			
			@ -353,10 +384,12 @@ int handleRequest(Socket::Connection &conn){
 | 
			
		|||
          if (newVals.isMember("cpu")){weight_cpu = newVals["cpu"].asInt();}
 | 
			
		||||
          if (newVals.isMember("ram")){weight_ram = newVals["ram"].asInt();}
 | 
			
		||||
          if (newVals.isMember("bw")){weight_bw = newVals["bw"].asInt();}
 | 
			
		||||
          if (newVals.isMember("geo")){weight_geo = newVals["geo"].asInt();}
 | 
			
		||||
          if (newVals.isMember("bonus")){weight_bonus = newVals["bonus"].asInt();}
 | 
			
		||||
          ret["cpu"] = weight_cpu;
 | 
			
		||||
          ret["ram"] = weight_ram;
 | 
			
		||||
          ret["bw"] = weight_bw;
 | 
			
		||||
          ret["geo"] = weight_geo;
 | 
			
		||||
          ret["bonus"] = weight_bonus;
 | 
			
		||||
          H.SetBody(ret.toString());
 | 
			
		||||
          H.SendResponse("200", "OK", conn);
 | 
			
		||||
| 
						 | 
				
			
			@ -491,6 +524,22 @@ int handleRequest(Socket::Connection &conn){
 | 
			
		|||
      std::string stream = H.url.substr(1);
 | 
			
		||||
      std::string proto = H.GetVar("proto");
 | 
			
		||||
      H.SetVar("proto", "");
 | 
			
		||||
      double lat = 0;
 | 
			
		||||
      double lon = 0;
 | 
			
		||||
      if (H.GetVar("lat") != ""){
 | 
			
		||||
        lat = atof(H.GetVar("lat").c_str());
 | 
			
		||||
        H.SetVar("lat", "");
 | 
			
		||||
      }
 | 
			
		||||
      if (H.GetVar("lon") != ""){
 | 
			
		||||
        lon = atof(H.GetVar("lon").c_str());
 | 
			
		||||
        H.SetVar("lon", "");
 | 
			
		||||
      }
 | 
			
		||||
      if (H.hasHeader("X-Latitude")){
 | 
			
		||||
        lat = atof(H.GetHeader("X-Latitude").c_str());
 | 
			
		||||
      }
 | 
			
		||||
      if (H.hasHeader("X-Longitude")){
 | 
			
		||||
        lon = atof(H.GetHeader("X-Longitude").c_str());
 | 
			
		||||
      }
 | 
			
		||||
      std::string vars = H.allVars();
 | 
			
		||||
      if (stream == "favicon.ico"){
 | 
			
		||||
        H.Clean();
 | 
			
		||||
| 
						 | 
				
			
			@ -505,7 +554,7 @@ int handleRequest(Socket::Connection &conn){
 | 
			
		|||
      unsigned int bestScore = 0;
 | 
			
		||||
      for (HOSTLOOP){
 | 
			
		||||
        HOSTCHECK;
 | 
			
		||||
        unsigned int score = HOST(i).details->rate(stream);
 | 
			
		||||
        unsigned int score = HOST(i).details->rate(stream, lat, lon);
 | 
			
		||||
        if (score > bestScore){
 | 
			
		||||
          bestHost = &HOST(i);
 | 
			
		||||
          bestScore = score;
 | 
			
		||||
| 
						 | 
				
			
			@ -556,6 +605,31 @@ void handleServer(void *hostEntryPointer){
 | 
			
		|||
  bool down = true;
 | 
			
		||||
 | 
			
		||||
  HTTP::Downloader DL;
 | 
			
		||||
  
 | 
			
		||||
  if (DL.get(HTTP::URL("http://freegeoip.net/json/"+url.host)) && DL.isOk()){
 | 
			
		||||
    JSON::Value &gDet = entry->details->geoDetails;
 | 
			
		||||
    INFO_MSG("Location: %s", DL.data().c_str());
 | 
			
		||||
    gDet = JSON::fromString(DL.data());
 | 
			
		||||
    INFO_MSG("Location: %s", gDet.toString().c_str());
 | 
			
		||||
    if (gDet && gDet.isMember("latitude") && gDet.isMember("longitude")){
 | 
			
		||||
      entry->details->servLati = gDet["latitude"].asDouble();
 | 
			
		||||
      entry->details->servLongi = gDet["longitude"].asDouble();
 | 
			
		||||
      std::stringstream loc;
 | 
			
		||||
      if (gDet.isMember("country_name")){
 | 
			
		||||
        if (gDet.isMember("city") && gDet["city"].asString().size()){
 | 
			
		||||
          entry->details->servLoc = gDet["city"].asString() + ", " + gDet["country_name"].asString();
 | 
			
		||||
        }else{
 | 
			
		||||
          entry->details->servLoc = gDet["country_name"].asString();
 | 
			
		||||
        }
 | 
			
		||||
        INFO_MSG("%s is in %s", url.host.c_str(), entry->details->servLoc.c_str());
 | 
			
		||||
      }else{
 | 
			
		||||
        INFO_MSG("%s is at %f, %f", url.host.c_str(), entry->details->servLati, entry->details->servLongi);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }else{
 | 
			
		||||
    WARN_MSG("Could not reach location server for %s", url.host.c_str());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  while (cfg->is_active && (entry->state != STATE_GODOWN)){
 | 
			
		||||
    if (DL.get(url) && DL.isOk()){
 | 
			
		||||
      JSON::Value servData = JSON::fromString(DL.data());
 | 
			
		||||
| 
						 | 
				
			
			@ -656,6 +730,13 @@ int main(int argc, char **argv){
 | 
			
		|||
  opt["value"].append((long long)weight_bw);
 | 
			
		||||
  conf.addOption("bw", opt);
 | 
			
		||||
 | 
			
		||||
  opt["arg"] = "integer";
 | 
			
		||||
  opt["short"] = "G";
 | 
			
		||||
  opt["long"] = "geo";
 | 
			
		||||
  opt["help"] = "Weight for geo scoring";
 | 
			
		||||
  opt["value"].append((long long)weight_geo);
 | 
			
		||||
  conf.addOption("geo", opt);
 | 
			
		||||
 | 
			
		||||
  opt["arg"] = "integer";
 | 
			
		||||
  opt["short"] = "X";
 | 
			
		||||
  opt["long"] = "extra";
 | 
			
		||||
| 
						 | 
				
			
			@ -675,6 +756,7 @@ int main(int argc, char **argv){
 | 
			
		|||
  weight_ram = conf.getInteger("ram");
 | 
			
		||||
  weight_cpu = conf.getInteger("cpu");
 | 
			
		||||
  weight_bw = conf.getInteger("bw");
 | 
			
		||||
  weight_geo = conf.getInteger("geo");
 | 
			
		||||
  weight_bonus = conf.getInteger("extra");
 | 
			
		||||
  fallback = conf.getString("fallback");
 | 
			
		||||
  localMode = conf.getBool("localmode");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue