带有权重的服务器SLB的实现

1)参考了网络上的算法,但是那个算法仅仅是用于展示“权重轮循”的意图,在真正的网络下,因为是并行的,所以不可能单纯一个简单的循环可以解决问题。

2)用lock的话性能显然有损失。

3)想了一阵,结合CAS和volatile等细粒度的锁的方式,一个真正可以用软件描述SLB带有权重的算法大概是这个样子(如下):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace WBasedRobin
{
    /// <summary>
    /// 用于计算WeightRobin的数据结构
    /// </summary>
    public class WeightedRobin
    {
        private readonly int _weight;
        private int _count;
        /// <summary>
        /// 命中次数(累加)
        /// </summary>
        public int ChoosenCount
        {
            get
            {
                return ++_count;
            }
        }
        /// <summary>
        /// 权重
        /// </summary>
        public int Weight
        {
            get
            {
                return _weight;
            }
        }
        /// <summary>
        /// 输出当前的权重
        /// </summary>
        public override string ToString()
        {
            return "Weight:" + Weight.ToString() + "\tCount:" + _count.ToString();
        }
        /// <summary>
        /// 初始化每一个Server的内部值
        /// </summary>
        public WeightedRobin(int weight, int count = 0)
        {
            _weight = weight;
            _count = count;
        }
    }

    public class WeightRobinRule
    {
        private List<WeightedRobin> _servers = null;

        private volatile int _index = -1;
        private volatile int _currentWeight = 0;
        private volatile bool _isServerChanging = false;

        private volatile int _maxWeight = 0;
        private volatile int _gcdWeight = 0;

        private int GetMaxWeight(IEnumerable<WeightedRobin> weights)
        {
            return weights.Max(w => w.Weight);
        }

        private int GetGCDWeight(int big, int small)
        {
            if (big < small)
            {
                big ^= small;
                small ^= big;
                big ^= small;
            }

            if (big % small == 0)
            {
                return small;
            }
            return GetGCDWeight(small, big % small);
        }

        private int GetTotalGCD()
        {
            int gcd = GetGCDWeight(_servers[0].Weight, _servers[1].Weight);

            for (int i = 2; i < _servers.Count; ++i)
            {
                gcd = GetGCDWeight(gcd, _servers[i].Weight);
            }

            return gcd;
        }

        /// <summary>
        /// 初始化权重服务器,至少2台服务器。
        /// </summary>
        public WeightRobinRule(int totalServers = 2)
        {
            Random r = new Random();
            _servers = new List<WeightedRobin>(totalServers);

            for (int i = 0; i < totalServers; i++)
            {
                _servers.Add(new WeightedRobin(r.Next(2, totalServers+1),0));
            }
            _maxWeight = GetMaxWeight(_servers);
            _gcdWeight = GetTotalGCD();
        }

        public void DoRolling()
        {
            int copyIndex = 0;
            int copyIndexNext = 0;
            int copycw = 0;

        //当服务器数量发生变化的时候,锁住该服务直到完毕。
        reloop:   while (_isServerChanging) ;

            for (;;)
            {
                //拷贝本地的index,用做同步
                copyIndex = _index;
                //计算轮询的时候下一个的值
                copyIndexNext = (copyIndex + 1) % _servers.Count;
                //同步作用
                copycw = _currentWeight;

                //假定轮询后的Next=0,说明完成一轮轮询,权重减去最大公约数
                if (copyIndexNext == 0)
                {
                    copycw -= _gcdWeight;

                    //如果权重已经扣完,重新从大的开始
                    if (copycw <= 0)
                    {
                        copycw = _maxWeight;
                    }
                }

                //如果copyIndex和_index相同,说明是同一个线程抢到的,那么直接用本地的替换index进行替换
                if (Interlocked.CompareExchange(ref _index, copyIndexNext, copyIndex) == copyIndex)
                {
                    _currentWeight = copycw;

                    try
                    {
                        //如果轮询的权重大于等于本地权重,选中它即可。
                        if (_servers[copyIndexNext].Weight >= copycw)
                        {
                          int t =  _servers[copyIndexNext].ChoosenCount;
                            break;
                        }
                    }
                    //如果是Index溢出,那么说明服务器数量肯定发生变化了,所以跳过此次轮询,等下一轮,不处理。
                    catch (IndexOutOfRangeException)
                    {
                        goto reloop;
                    }

                }
            }
        }
        /// <summary>
        /// 移除指定的服务器
        /// </summary>
        public WeightedRobin RemoveByIndex(int index)
        {
            _isServerChanging = true;
            var removedServer = _servers[index];
            _servers.RemoveAt(index);
            _gcdWeight = GetTotalGCD();
            _maxWeight = GetMaxWeight(_servers);
            _isServerChanging = false;
            return removedServer;
        }
        /// <summary>
        /// 增加新的服务器
        /// </summary>
        public void AddNewServer(int weight)
        {
            _isServerChanging = true;
            _servers.Add(new WeightedRobin(weight, 0));
            _gcdWeight = GetTotalGCD();
            _maxWeight = GetMaxWeight(_servers);
            _isServerChanging = false;
        }
        /// <summary>
        /// 格式化输出结果
        /// </summary>
        public override string ToString()
        {
            StringBuilder sbu = new StringBuilder(10);

            foreach (WeightedRobin wr in _servers)
            {
                sbu.AppendLine(wr.ToString() + Environment.NewLine);
            }
            return sbu.ToString();
        }
    }
}

调用测试代码如下:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace WBasedRobin
{
    class Program
    {
        static Random r = new Random();

        static void Rounding(WeightRobinRule wr)
        {
            wr.DoRolling();
        }
        static void Main(string[] args)
        {
            WeightRobinRule wr = new WeightRobinRule(5);

            Timer t = new Timer((j) => { var removedS = wr.RemoveByIndex(0); Console.WriteLine("移除了服务器:"+removedS);  }, null, 2050, Timeout.Infinite);

             t = new Timer((o) => { wr.AddNewServer(6); Console.WriteLine("新增加服务器了。"); }, null, 3000, Timeout.Infinite);

            Parallel.For(1, 1001, (num) =>
            {
                Thread.Sleep(r.Next(100, 500));
                Rounding(wr);
            });
            Console.WriteLine(wr);
            Console.ReadLine();
        }
    }
}
时间: 2024-10-13 05:11:07

带有权重的服务器SLB的实现的相关文章

Duilib中带有权重的灵活控件排列实现(一)

在开发播放器软件过程中,因为窗口的大小是可变的,为了让控制栏部分的控件(播放,上一集,下一集,全屏,字幕等)适应窗口的尺寸的变化而显示隐藏,产品经理会定义一系列的规则,好让在任何时候都最核心的功能提供给用户使用. 先列一下产品经理给予的需求: 两边往中间缩,保证左侧LOGO和右侧X最优先显示. 顶部隐藏优先级:搜索栏,换肤,意见反馈,播放记录,最小化,最大化 底部隐藏优先级:全屏,画质增强,无痕,打开文件,播放顺序,音量条 在处理这个需求过程中,前人也尝试了一些方法,比较通过全float绝对布局

MySQL服务器时间同步问题处理

1,调整数据库服务器时间 因为应用app摇一摇活动发现过时15分钟,还可以继续摇一摇,问题根源在于数据库服务器时间比应用服务器时间蛮了18分钟,而app取得是数据库的now(),所以才导致的.2个解决办法,1是修改程序代码,不通过数据库now()去取而是通过应用服务器取:2是直接修改数据库的时间.经过权衡之后采纳第二种方案. 1.1,通过date -s修改时间 修改linux服务器时间 date -s 2015-08-03 修改时间: date -s 10:18:00 1.2,手动调整时间使用n

http服务器--session处理

http服务器--session处理 1. 概述 Session处理是一般带有会话功能服务器必须处理的功能.使用session记录一个会话,能够有效处理短连接或者断线重连的需求.session是存放在服务器的内容,具有如下特性: A. 时间性,创建的session如果长时间没有响应,则服务器需要删除该session,以防止session的无限增长. B. 唯一性,在同一个服务器上,sessionId不能重复,如果重复就会出现歧义. C. 全局性,对于是否为同一会话,服务器上只认sessionId

iOS:搭建本地的服务器

一.介绍 作为一个专业的程序员,不管你是前端还是移动端或者是后台,能够自己试着搭建一个本地的服务器还是很有必要的,有的时候,我们可以自己测试一些数据,很方便开发.其实,mac是自带有本地的服务器的,用命令行开启就行,参考链接:http://www.jianshu.com/p/90d5fa728861. 当然,此处也可以自己试着按照前人的步骤搭建属于自己的服务器,反正我搭建成功了,以后可以拿着测试用了,挺好的....... 注意: 下面的博文转载自@文顶顶http://www.cnblogs.co

在华为服务器 RH 2288H V2上装 windows 2012

1.首先,先查看RAID 是否配置好(按ctrl+H 进入,RAID的具体配置这里就不介绍了) 2.在RAID 1配好的前提下,放入华为SERVICE-CD光盘,按F11选择BOOT启动顺序: 选择第二项:Microsoft  Windows  Server (2008/2012) 现在开始划分C盘.D盘: 下面这一步跳过去,直接点下一步: 装入系统盘后点下一步 选择要安装的操作系统,选择第二项,即Windows Server 2012 R2 Standard(带有GUI的服务器),要选择这项.

微软私有云系列----域服务器准备

安装系统版本要求: 选择:Windows Server 2012 R2 Datacenter (带有GUI的服务器). 选择:"自定义硬盘分区". 设定administrator的密码 打开服务器的系统属性,允许远程连接到此计算机 关闭防火墙:由于是POC环境,我们可以放心的把防火墙关闭. 右键点击HOME网络的属性,对IPv4地址进行设定: 在DC那台服务器上,添加角色,使其变为域控制器: 下图显示,角色添加成功: 在服务器管理器界面中,选择感叹号,并点击"将此服务器提升为

搭建一个三台服务器的Memcached集群

关于memcached的基础知识可以查看博客其他博文,这里只记录了搭建的过程,谢谢! 1.分别在三台服务器上安装Memcached并启动 第一.由于memcached是基于libevent的事件处理,所以需要安装libevent yum install libevent libevent-devel 第二.上传Memcached到三台服务器(192.168.176.129/192.168.176.130/192.168.176.131)解压并安装 tar -zxvf memcached-1.2.

LVS详解及基于LVS实现web服务器负载均衡

前言 LVS(Linux Virtual Server)Linux虚拟服务器,是一个虚拟的服务器集群系统.本项目在1998年5月由章文嵩博士成立,是中国国内最早出现的自由软件项目之一.通过LVS提供的负载均衡技术和Linux操作系统可实现一个高性能.高可用的服务器群集,从而以低成本实现最优的服务性能. 集群基础 集群简介 集群(Cluster)是一组相互独立的.通过高速网络互联的计算机,它们构成了一个组,并以单一系统的模式加以管理.一个客户与集群相互作用时,集群像是一个独立的服务器.集群配置是用

Centos6.5时间服务器NTP搭建

NTP时间服务器安装与配置 第1章 Server端的安装与配置 1.1 查看系统是否已经安装ntp服务组件 rpm -qa | grep "ntp"                #<==查看是否已经安装ntp组件,有如下两个组件说明已经安装 ntpdate-4.2.6p5-1.el6.centos.x86_64 ntp-4.2.6p5-1.el6.centos.x86_64 1.2 yum安装 yum -y install ntp 1.3 ntp服务器端的配置 1.3.1 配置