V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
fsneak
V2EX  ›  服务器

如何改进负载均衡的实现策略

  •  
  •   fsneak · 2016-03-06 00:40:06 +08:00 · 2593 次点击
    这是一个创建于 3214 天前的主题,其中的信息可能已经有所发展或是发生改变。

    目前服务器的情况是这样的。

    有若干台逻辑服务器(数量较多)和若干台选择服务器(数量较少)。
    运行时,选择服务器根据一定的负载均算法选择一台逻辑服务器,远程调用在其中创建一个房间。
    然后再把这个房间的逻辑服务器 IP 保存下来,需要的时候发送给用户,用户拿到 IP 后再去连接逻辑服务器,加入房间执行操作。这个房间的用户上限为 12 。

    之前的做法是,每台逻辑服务器定时(大概 3 秒)将自己服务器连接的用户数写入数据库。选择服务器同时也定时(大概 5 秒)从数据库中读取逻辑服务器的人数。然后当要选择逻辑服务器的时候,就选择人数最少的逻辑服务器。

    而这样的做法在人数多的时候有很大的问题。

    因为选择服务器的人数数据是上一个定时周期更新的,在下一次更新之前如果有大量的请求涌入的话,选择服务器会将这些请求导向同一个逻辑服务器,最终导致服务器不堪重负。同时选择服务器在选择逻辑服务器创建房间的时候,并不会知道有多少人最终会进入这个房间。而且这样的选择服务器还存在多台(各自有一些不同的业务逻辑)。

    最简单的做法就是加快人数数据的写入和读取速度了吧。。。
    大家有什么好的方法吗?

    12 条回复    2016-03-07 09:06:15 +08:00
    lecher
        1
    lecher  
       2016-03-06 00:58:52 +08:00 via Android
    设计一个令牌做分发,我的思路是。如果逻辑服务器能够获得用户退出的事件。
    那么令牌服务器维护一个空闲位置的队列。当逻辑服务器的房间创建时,主动将空闲位置添加到选择服务器的空闲位置队列中。这样只要有用户申请连接就从队列取空闲位置参数返回。
    当逻辑服务器获得用户退出事件,则说明房间又有新的空位,就再推送空位参数到分发服务器的队列中。

    这样可以保证一个位置一个用户,避免频繁读写数据库,扩展新逻辑服务器也比较容易。
    xiaogui
        2
    xiaogui  
       2016-03-06 01:16:23 +08:00   ❤️ 1
    感觉你的问题是“负载算法”,以及“选择服务器”不知道“逻辑服务器”的状态:
    1 、“服务器不堪重负”时,该“逻辑服务器”有多少房间、多少玩家?
    2 、是否可以对原有的日志数据进行一定的处理,用于了解创建的“房间”的平均存活时间,“逻辑服务器”当前房间数和玩家数与时间的相关性?从而对“负载算法”进行升级?
    3 、是否已使用内存数据库等对服务器状态数据的读写进行加速,从而让“选择服务器”及时了解“逻辑服务器”的状态。
    4 、是否可以将“逻辑服务器”划分为不同的组,分别指定给一台“选择服务器”。将“选择服务器”与“逻辑服务器”处理成一对多的关系,从而降低“选择服务器”的选择逻辑?(取决于你们的业务逻辑)
    fsneak
        3
    fsneak  
    OP
       2016-03-06 01:35:12 +08:00 via iPhone
    @lecher 用户是创建新的房间还是进入已有的某件房间是有一套业务逻辑去判断的。并不是只要房间有空位就行了。换句话说用户进入哪个房间需要通过用户的操作和用户自身的数据去判断,这些选择服务器无法控制。
    fsneak
        4
    fsneak  
    OP
       2016-03-06 01:50:20 +08:00 via iPhone
    @xiaogui 4 比较有启发性。谢谢回复。
    cevincheung
        5
    cevincheung  
       2016-03-06 02:03:22 +08:00
    觉得不是单一负载均衡的问题,更像是描述一个架构方案的问题。

    dns 轮询+haproxy*n -> compute node * n -> redis

    用户直接连接服务器,创建房间就好,后端计算节点由前端的 haproxy 做负载均衡。
    用户信息、房间信息存在 redis ,由 token ( hash )做唯一性验证,消息的广播、点对点消息由计算节点查询 redis 取得对方正在连接计算节点的消息直接发送消息。

    不知道这样理解对不对。
    lecher
        6
    lecher  
       2016-03-06 09:10:32 +08:00
    @fsneak
    如果有那么精细的参数。按房间类型开设多个队列,队列为空则由选择服务器计算是否需要创建。

    逻辑服务器将房间信息按房间类型推送到不同的队列。
    选择服务器按照用户数据判断从哪个队列取房间信息给用户。如果队列为空再取服务器的负载信息判断在哪一台服务器创建新的房间。
    fsneak
        7
    fsneak  
    OP
       2016-03-06 17:21:27 +08:00
    @cevincheung 嗯~。。。感觉你说的比较偏向 web 服务器吧。我讲述的是应用服务器。选择服务器需要对玩家信息进行匹配,决定哪些玩家进到同一个房间去。所以没办法直接由玩家连接服务器创建房间。
    fsneak
        8
    fsneak  
    OP
       2016-03-06 17:31:59 +08:00
    @lecher 嗯~。。。当然选择服务器会根据自己的业务需要,把可用的房间存下来。在一定条件下也是优先让用户进入已有的房间。而我的问题就是针对你这句话:
    “如果队列为空再取服务器的负载信息判断在哪一台服务器创建新的房间”
    按照之前的做法,由于服务器的负载信息获取有一定的滞后性,比如 5 秒更新一次。那么这 5 秒钟内,如果有大量用户请求涌入需要创建大量房间,就会优先在负载较低的服务器上创建房间,将用户导入这台服务器,结果就是下一个周期,这台服务器的人数会远高于其他服务器。
    cevincheung
        9
    cevincheung  
       2016-03-06 17:58:47 +08:00
    @fsneak
    那还是一样的啊。服务器创建房间,所有线上用户连接信息存 redis ,标记当前连接服务器的 IP 再然后就是互相间的消息推送啊。感觉这样还更清晰点,所有服务器都能“均衡”的扛起线上用户,还能相互间的消息互通。
    lecher
        10
    lecher  
       2016-03-06 19:23:45 +08:00 via Android   ❤️ 1
    如果房间是由选择服务器发起创建的,确实会出现一段时间内的新用户请求都集中在同一台逻辑服务器上。

    这个属于峰值抖动的问题,应该可以通过其它判断处理平缓抖动,比如创建保存所有同类型的逻辑服务器的信息到一个环形数组。
    每一次需要创建房间时,取数组游标指向的逻辑服务器,做个判断:去数据库取服务器负载检测是否满足创建房间的负载,不满足则修改数组游标到下一个继续判断,满足则创建之后修改数组游标到下一个。这样可以平缓峰值的抖动,将创建房间的需求按数组的顺序是依次在不同服务器创建。
    fsneak
        11
    fsneak  
    OP
       2016-03-06 21:59:51 +08:00 via iPhone
    @lecher 哦哦,这的确也是一个思路。 thank you 。
    fsneak
        12
    fsneak  
    OP
       2016-03-07 09:06:15 +08:00 via iPhone
    @cevincheung 唔…你说的架构是用户和房间信息都存在 redis ,然后让用户根据负载连接不同的逻辑服务器,执行操作时从 redis 拿数据这样吗?
    是的话感觉跟我这里的业务需要不大符合…我这里一个房间的数据可能比较杂和多,操作也可能比较频密,这种架构存取数据不方便,有网络开销而且同步问题比较难处理=_=
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3724 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 00:59 · PVG 08:59 · LAX 16:59 · JFK 19:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.