访问后端服务负载均衡的方法

在开发过程中,经常需要请求后端某个服务,而这个服务通常会部署在一个集群上,维护这个服务的同事一般都会给我们一个服务器地址列表,让我们请求这个列表中的服务器。

最简单也是最偷懒的方法,就是任选一个服务器,在代码中只使用这个服务器,每次都请求这个服务器。这么做的问题有两个,一是万一这个服务器出故障了,在故障恢复前所有请求都会失败;二是所有的请求都发送到这个服务器上,当请求量小时还好,请求量大时会这把这个服务器压垮。

稍微好一点的做法是每次都随机从列表中选取一个服务器,然后向该服务器发送请求。这种做法可以保证将请求均匀地分布在集群内的各服务器上。但这种做法也存在一定的问题,当集群内的服务器发生故障的数量多于一台时,如果某次请求随机选取到的服务器无法访问,再次随机选取到的服务器也无法访问,这样就会导致这次请求的时间过长,从而影响用户体验。

由于软硬件故障或网络抖动,集群中的服务器随时可能下线,故障修复后又重新上线。因此,我们的代码应当能做到:
1)将请求均匀地分布到集群中的各服务器上(即实现负载均衡);
2)当某台服务器下线时(即无法访问时)能将该服务器从可用服务器列表中剔除,不再向该服务器发送请求;
3)当某台服务器故障修复重新上线后,应当能及时发现,并将请求分发到该服务器上。

为了实现这3个目标,我的基本策略是:
1)每次请求时都随机选取一台服务器;
2)当发现某台服务器无法访问时将其标记为不可用,同时为它设置重试时间,在重试时间到达前不再发送请求到该服务器;
3)被标记为不可用的服务器在重试时间到达后允许再次访问,若发现它已恢复服务,则重新将其标记为可用,否则将重试时间延长。

上述策略的第1点保证了请求可以均匀分布;第2点保证了不会将请求发送到出现故障的服务器上;第3点则保证了当服务器故障修复重新上线后可以及时被发现。第3点是基于这样一个假设,即服务器无法访问是暂时的,等待一段时间后它就可以恢复服务。

从上面的策略可以看出,某次请求可能会向被标记为不可用的服务器发送请求,以探测该服务器是否已经从故障中恢复。为了控制单次请求的延时时间,防止在一次请求中多次尝试访问不可用的服务器,向后端服务器集群发送请求的具体步骤分为以下2步:

第1步:从服务器列表中随机选取一个服务器。
获取到的服务器分3种情况:
a.被标记为可用的服务器,返回该服务器;
b.被标记为不可用的服务器,但是已经到达重试时间,返回该服务器;
c.被标记为不可用的服务器,且还没有到达重试时间,返回空。
第1步获取到服务器后,向其发送请求,若请求成功,则请求结束(若该服务器被标记为不可用,还需重新将其标记为可用);若请求失败,则将其标记为不可用,然后进入第2步。
当然,如果第1步没有获取到服务器,则直接进入第2步。

第2步:从服务器列表中强制获取一个可用的服务器。
到这一步,说明第1步可能已经请求过一次服务了,但是失败了,所以这一步要尽可能地确保请求可以成功。这一步的策略就是,尽可能获取标记为可用的服务器,即只要有可用的服务器,就返回一个可用的服务器;如果没有可用的服务器了,那就退而求其次,返回最早到达重试时间的那个服务器;最后如果所有的服务器都不可用且都还没有到达重试时间,那天王老子也没办法了,只能返回空,表示没有服务器可用。具体实现上来说,为了保证请求尽可能均匀的分布在各服务器上,这一步也是先随机若干次获取可用的服务器,若多次随机获取的都是不可用的服务器,则遍历整个服务器列表,将第一个可用的服务器返回。
第2步获取到服务器后,向其发送请求,若请求成功,则请求结束(若该服务器被标记为不可用,还需重新将其标记为可用);若请求失败,则将其标记为不可用,然后循环执行第2步,直到请求成功,或者获取不到服务器为止。

不论是第1步还是第2步,如果某个服务器本就处于不可用的状态,在重试后发现其仍然无法访问,还需要将其重试时间加倍。

向服务器集群发起请求分成两步走是有必要的,第一步是为了保证被标记为不可用的服务器能有重试的机会,如果每次都只获取可用的服务器,则那些标记为不可用的服务器在故障恢复后将迟迟不能被发现。第二步强制获取可用的服务器,又确保了在有可用服务器的情况下,在一次请求中不会多次尝试访问不可用的服务器,从而保证了请求的响应时间。

对后端服务器集群进行访问是个通用的功能,我使用了2个类来实现这项功能。ServerInfo类用来描述一个服务器,ServerMgr类用来管理服务器列表。以下是具体代码。

public class ServerInfo {
   
    private String ip;                 // 服务器ip
    private int port;                   // 服务器端口
    private boolean isValid;      // 服务器是否可用
    private long lastTryTime;    // 上次尝试访问时间,单位:秒
    private long waitTime;        // 再次尝试访问该服务器前应等待的时间

    public ServerInfo(String ip, int port) {
        this.ip = ip;
        this.port = port;
        isValid = true;
        lastTryTime = 0;
        waitTime = 0;
    }
    public String getIp() {
        return ip;
    }

    
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值