public virtual Server Choose(LoadBalancerRoute route) { if (route == null || route.ServerGroups.IsNullOrEmpty()) { return(null); } InternalBuildLoadBalanceItems(route); ServerGroup serverGroup = null; for (int i = 0; i < serverGroups.Count; i++) { serverGroup = roundRobinAlgorithm.Choose(serverGroups); if (serverGroup.AvailableServers.Length > 0) { break; } } if (serverGroup == null) { return(null); } var serverContext = serverContexts.GetOrAdd(serverGroup.GroupId, key => new RoundRobinContext(serverGroup)); return(serverContext.Choose()); }
private bool CheckHealth(LoadBalancerRoute route) { bool statusChanged = false; Stopwatch watch = Stopwatch.StartNew(); var additionInfo = _loadBalancerContext.AdditionalInfo.Copy().With("RouteId", route.RouteId); var servers = route.Servers; if (servers.Length == 0) { return(false); } if (route.AvailableServers.Length == 0) { string message = "There is no available server in the list. Need check health."; _logger.Warn(message, additionInfo); } int checkedCount = 0, availableCount = 0; foreach (Server server in servers) { try { bool isAlive = server.IsAlive; if (isAlive != CheckHealth(route, server)) { statusChanged = true; } if (server.IsAlive) { ++availableCount; } ++checkedCount; } catch (Exception ex) { _logger.Warn("Error occurred while check server", ex, additionInfo); } } watch.Stop(); var checkHealthLatencyMetric = GetCheckHealthLatencyMetric(route); checkHealthLatencyMetric.AddValue(watch.ElapsedMilliseconds); bool hasUnavailableServers = checkedCount != servers.Length || availableCount != servers.Length; if (hasUnavailableServers || _lastHasUnavailableServers) { string messageFormat = "{0} servers checked ({1}/{2}) in route:{3}"; string message = string.Format(messageFormat, checkedCount, availableCount, servers.Length, route.RouteId); _logger.Info(message, additionInfo); } _lastHasUnavailableServers = hasUnavailableServers; return(statusChanged); }
private IEventMetric GetCheckHealthEventMetric(LoadBalancerRoute route, string serverId) { var metricId = _loadBalancerContext.GetMetricId(route.RouteId + "." + serverId + "." + CheckHealthEventMetricSuffix); var distributionMetricName = _loadBalancerContext.GetDistributionMetricName(CheckHealthEventMetricSuffix); Dictionary <string, string> metadata = new Dictionary <string, string>(); metadata["metric_name_distribution"] = distributionMetricName; metadata["loadbalancerid"] = _loadBalancerContext.LoadBalancerId; metadata["routeid"] = route.RouteId; metadata["serverid"] = serverId; return(_checkHealthEventMetrics.GetOrAdd(route.RouteId, key => _loadBalancerContext.EventMetricManager.GetMetric(metricId, new MetricConfig(metadata)))); }
private void InitAvailableServerCountMetric(LoadBalancerRoute route) { var metricId = _loadBalancerContext.GetMetricId(route.RouteId + "." + ServerAvailableCountMetricSuffix); var metricName = _loadBalancerContext.GetMetricName(ServerAvailableCountMetricSuffix); Dictionary <string, string> metadata = new Dictionary <string, string>(); metadata["metric_name_audit"] = metricName; metadata["loadbalancerid"] = _loadBalancerContext.LoadBalancerId; metadata["routeid"] = route.RouteId; var metricConfig = new StatusMetricConfig <double>(() => route.AvailableServers.Length, metadata); _availableServerCountMetrics.GetOrAdd(route.RouteId, key => _statusMetricManager.GetMetric(metricId, metricConfig)); }
public ILoadBalancerRequestContext GetRequestContext(LoadBalancerRequestConfig requestConfig) { LoadBalancerRoute route = LoadBalancerContext.ServerSourceFilter.GetLoadBalancerRoute(requestConfig); if (route == null) { _logger.Warn("No matching route for \n" + requestConfig, LoadBalancerContext.AdditionalInfo); return(null); } Server server = LoadBalancerContext.GetLoadBalancerRule(route.RouteId).Choose(route); return(server == null ? null : new DefaultLoadBalancerRequestContext(server, LoadBalancerContext)); }
protected void InternalBuildLoadBalanceItems(LoadBalancerRoute route) { if (!Object.ReferenceEquals(loadBalancerRoute, route)) { lock (this) { if (!Object.ReferenceEquals(loadBalancerRoute, route)) { serverGroups = BuildLoadBalanceItems(route); serverContexts = new ConcurrentDictionary <string, RoundRobinContext>(); loadBalancerRoute = route; } } } }
private void PingServers(LoadBalancerRoute route) { PingUtil pingUtil = new PingUtil(_loadBalancerContext, _logger); if (pingUtil.HasPing) { foreach (ServerGroup serverGroup in route.ServerGroups) { foreach (Server server in serverGroup.Servers) { server.IsAlive = pingUtil.IsAlive(server); } } } }
protected override List <ServerGroup> BuildLoadBalanceItems(LoadBalancerRoute route) { int weightGCD = GetWeightGCD(route.ServerGroups); var newLoadBalanceItems = new List <ServerGroup>(); foreach (var serverGroup in route.ServerGroups) { int weight = serverGroup.Weight / weightGCD; for (int i = 0; i < weight; i++) { newLoadBalanceItems.Add(serverGroup); } } newLoadBalanceItems.Shuffle(); return(newLoadBalanceItems); }
public void WeightedRoundRobinWithSpecificWeightTest() { int serverCount = 10; int repeatTimes = 100; int repeatInterval = 0; int threadCount = 20; int serverSumCount = 0; string loadBalancerId = MethodInfo.GetCurrentMethod().Name; IConfiguration configuration = ObjectFactory.CreateAppSettingConfiguration(); var configurationSource = ObjectFactory.CreateDefaultConfigurationSource(0, "App.config", configuration); var configurationManager = ObjectFactory.CreateDefaultConfigurationManager(configurationSource); Dictionary <int, AtomicInteger> indexCountMapping = new Dictionary <int, AtomicInteger>(); List <ServerGroup> serverGroups = new List <ServerGroup>(); List <Server> servers = new List <Server>(); for (int i = 0; i < serverCount; i++) { var metadata = new Dictionary <string, string>(); metadata["Index"] = i.ToString(); var server = new Server("Server_" + i, metadata); servers.Add(server); serverGroups.Add(new ServerGroup(server.ServerId, i, new List <Server>() { server })); indexCountMapping[i] = 0; serverSumCount += i; } LoadBalancerRoute route0 = new LoadBalancerRoute("default", serverGroups); ServerGroup route1Group0 = new ServerGroup("group0", 2, new List <Server>() { servers[0] }); ServerGroup route1Group1 = new ServerGroup("group1", 1, new List <Server>() { servers[1] }); LoadBalancerRoute route1 = new LoadBalancerRoute("route1", new List <ServerGroup>() { route1Group0, route1Group1 }); ServerGroup route2Group0 = new ServerGroup("group0", 2, new List <Server>() { servers[0] }); ServerGroup route2Group1 = new ServerGroup("group1", 3, new List <Server>() { servers[1] }); LoadBalancerRoute route2 = new LoadBalancerRoute("route2", new List <ServerGroup>() { route2Group0, route2Group1 }); LoadBalancerRequestConfig route1Config = new LoadBalancerRequestConfig(new List <LoadBalancerRouteConfig>() { new LoadBalancerRouteConfig("route1", 1, false) }); LoadBalancerRequestConfig route2Config = new LoadBalancerRequestConfig(new List <LoadBalancerRouteConfig>() { new LoadBalancerRouteConfig("route2", 1, false) }); int server0Count = route1Group0.Weight + route2Group0.Weight; int server1Count = route1Group1.Weight + route2Group1.Weight; int route1RequestCount = route1Group0.Weight + route1Group1.Weight; int route2RequestCount = route2Group0.Weight + route2Group1.Weight; var ping = new TruePing(); var serverSource = new DefaultDynamicServerSource(new List <LoadBalancerRoute>() { route0, route1, route2 }); var factory = LoadBalancerManager.GetManager("soa", new LoadBalancerManagerConfig(configurationManager)); var loadBalancerConfig = new LoadBalancerConfig(ping, serverSource); var loadBalancer = factory.GetLoadBalancer(loadBalancerId, loadBalancerConfig); var defaultLoadBalancer = ((DefaultLoadBalancer)loadBalancer).LoadBalancerContext; Action action = () => { for (int i = 0; i < repeatTimes; i++) { for (int j = 0; j < serverSumCount; j++) { var requestContext = loadBalancer.GetRequestContext(requestConfig); int index = int.Parse(requestContext.Server.Metadata["Index"]); indexCountMapping[index].IncrementAndGet(); if (repeatInterval > 0) { Thread.Sleep(repeatInterval); } } for (int j = 0; j < route1RequestCount; j++) { var requestContext = loadBalancer.GetRequestContext(route1Config); int index = int.Parse(requestContext.Server.Metadata["Index"]); indexCountMapping[index].IncrementAndGet(); if (repeatInterval > 0) { Thread.Sleep(repeatInterval); } } for (int j = 0; j < route2RequestCount; j++) { var requestContext = loadBalancer.GetRequestContext(route2Config); int index = int.Parse(requestContext.Server.Metadata["Index"]); indexCountMapping[index].IncrementAndGet(); if (repeatInterval > 0) { Thread.Sleep(repeatInterval); } } } }; List <Task> tasks = new List <Task>(); for (int i = 0; i < threadCount; i++) { tasks.Add(Task.Factory.StartNew(action)); } Task.WaitAll(tasks.ToArray()); foreach (var item in indexCountMapping) { if (item.Key == 0) { Assert.AreEqual((item.Key + server0Count) * repeatTimes * threadCount, item.Value.Value, item.Key.ToString()); } else if (item.Key == 1) { Assert.AreEqual((item.Key + server1Count) * repeatTimes * threadCount, item.Value.Value, item.Key.ToString()); } else { Assert.AreEqual(item.Key * repeatTimes * threadCount, item.Value.Value, item.Key.ToString()); } } }
protected virtual List <ServerGroup> BuildLoadBalanceItems(LoadBalancerRoute route) { return(new List <ServerGroup>(route.ServerGroups)); }
private bool CheckHealth(LoadBalancerRoute route, Server server) { if (!server.IsAlive) { if (!_pingUtil.HasPing) { return(server.IsAlive); } bool pingSuccess = _loadBalancerContext.Ping.IsAlive(server); if (pingSuccess) { server.IsAlive = true; string message = string.Format("Server({0}) is available due to ping success", server); _logger.Info(message, _loadBalancerContext.AdditionalInfo.Copy().With(AvailabilityKey, bool.TrueString)); var checkHealthEventMetric = GetCheckHealthEventMetric(route, server.ServerId); checkHealthEventMetric.AddEvent("PullIn"); } return(pingSuccess); } else { if (route.AvailableServers.Length <= _loadBalancerContext.MinAvailableServerCount) { return(true); } int successCount, failureCount, totalCount, failurePercent; bool failureRateExceeded, pingSuccess; var serverStats = _loadBalancerContext.GetServerStats(server); successCount = serverStats.GetAvailableCount(); failureCount = serverStats.GetUnavailableCount(); totalCount = successCount + failureCount; if (totalCount == 0) { return(server.IsAlive); } failurePercent = (int)(failureCount * 100.0 / totalCount); failureRateExceeded = failurePercent >= FailureThresholdPercentage; if (!failureRateExceeded) { return(true); } if (!_pingUtil.HasPing) { server.IsAlive = false; string message = string.Format("Server({0}) is unavailable due to high failure rate({1}%)", server, failurePercent); _logger.Warn(message, _loadBalancerContext.AdditionalInfo.Copy().With(AvailabilityKey, bool.FalseString)); var checkHealthEventMetric = GetCheckHealthEventMetric(route, server.ServerId); checkHealthEventMetric.AddEvent("PullOut"); return(false); } pingSuccess = _pingUtil.IsAlive(server); if (!pingSuccess) { server.IsAlive = false; string message = string.Format("Server({0}) is unavailable due to high failure rate({1}%) and ping failure", server, failurePercent); _logger.Warn(message, _loadBalancerContext.AdditionalInfo.Copy().With(AvailabilityKey, bool.FalseString)); var checkHealthEventMetric = GetCheckHealthEventMetric(route, server.ServerId); checkHealthEventMetric.AddEvent("PullOut"); } return(pingSuccess); } }
internal SeekRouteEventArgs(LoadBalancerRoute route) { Route = route; }