负载均衡算法

负载均衡算法

加权随机算法

算法解释

例:有四台服务器ip,weight如下,权重越大代表机器性能越好,四台机器被访问的比率应该是权重比率

ipweight
192.168.0.13
192.168.0.27
192.168.0.34
192.168.0.46

如何用随机方法控制机器的访问比率
方法一:新建一个ipList,里面按照权重添加机器ip,例如添加192.168.0.1三次,这样ipList就有20个ip了,就可以用随机算法了,但是缺点是浪费空间
方法二:坐标轴法,例如我把他们的权重当作长度画在坐标轴上如下图:
在这里插入图片描述
我们可以随机生成一个20内的数,落在哪个区间就用哪个ip,比如生成13,落在第三个区间用192.168.0.3

code

public class WeightRandom {

    public static Server getServer(){
        int totalWeight = 0;
        for(Server server:WeightServer.servers){
            totalWeight +=server.getWeight();
        }

        int pos = new Random().nextInt(totalWeight);

        for(Server server:WeightServer.servers){
            if(pos<server.getWeight()){
                return server;
            }
            pos-=server.getWeight();
        }
        return null;
    }

    public static void main(String[] args) {

        for(int i=0;i<1000;i++){
            Server server = getServer();
            server.setCallNum(server.getCallNum()+1);
        }
        for(Server server:WeightServer.servers){
            System.out.println(server);
        }
    }
}

加权轮询算法

算法解释

例:有三台服务器ip,weight如下,权重越大代表机器性能越好,加权轮询的结果应是192.168.0.1,192.168.0.1,192.168.0.2,192.168.0.2,192.168.0.2,192.168.0.3

ipweight
192.168.0.12
192.168.0.23
192.168.0.31

这个很简单,弄个自增变量对totalWeight取模即可

code

public class WeightRoundRobin {
    private static AtomicInteger ai = new AtomicInteger(0);

    public static Server getServer(){
        int totalWeight = 0;
        for(Server server:WeightServer.servers){
            totalWeight +=server.getWeight();
        }
        int pos = ai.getAndIncrement() % totalWeight;

        for(Server server:WeightServer.servers){
            if(pos<server.getWeight()){
                return server;
            }
            pos-=server.getWeight();
        }
        return null;
    }

    public static void main(String[] args) {
        for(int i=0;i<30;i++){
            Server server = getServer();
            server.setCallNum(server.getCallNum()+1);
            System.out.println(server);
        }
        System.out.println("-------------------------------------------------------");

        for(Server server:WeightServer.servers){
            System.out.println(server);
        }
    }

}

平滑加权轮询算法

算法解释

该算法是对加权随机算法的优化,也看到了加权随机算法都是先访问一台机器(权重次数),然后在访问若干次另一台机器,这样的负载均衡其实是很不均衡的,我们希望他不要老是访问一台机器
例如有机器A:1,B:2,C:3,冒号后数字代表权重
算法如下:
初始化两个权重数组
固定权重weight:A:1,B:2,C:3
动态权重curWeight:A:0,B:0,C:0
计算totalWeight=6

curWeight[i]+=weight[i](max curWeight)返回ip(max curWeight)-totalWeight
curWeight1,2,33C1,2,-3
curWeight:2,4,04B2,-2,0
curWeight:3,0,33A-3,0,3
curWeight:-2,2,66C-2,2,0

code

public class SmoothWeightRoundRobin {

    private static int[] curWeight = new int[WeightServer.servers.size()];

    public static Server getServer(){
        int totalWeight = 0;
        for(Server server:WeightServer.servers){
            totalWeight +=server.getWeight();
        }

        for(int i=0;i<curWeight.length;i++){
            curWeight[i]+=WeightServer.servers.get(i).getWeight();
        }

        int maxWeightI = 0;
        int maxWeight = curWeight[0];
        for(int i=1;i<curWeight.length;i++){
            if(curWeight[i]>maxWeight){
                maxWeightI = i;
                maxWeight = curWeight[i];
            }
        }

        curWeight[maxWeightI]-=totalWeight;

        return WeightServer.servers.get(maxWeightI);

    }

    public static void main(String[] args) {
        for(int i=0;i<30;i++){
            Server server = getServer();
            server.setCallNum(server.getCallNum()+1);
            System.out.println(server);
        }
        System.out.println("-------------------------------------------------------");

        for(Server server:WeightServer.servers){
            System.out.println(server);
        }
    }

}

一致性hash算法

算法解释

hash环原理
在这里插入图片描述
hash环上只有数字,数字从00:00的位置从零开始顺时针自增1,加到23:59:59(大约),加到最后位置就是int的最大数值。假设有三台服务器的ip地址经过hash算法映射到int的值如图,假设此时有一个客户端ip来访问机器集群,映射的clientHash在ip1和ip2之间,他就选择ip2作为访问地址,如果clientHash在ip2和ip3之间,就选ip3作为访问地址,即选择比clientHash大的第一个ip地址,如果clientHash落在ip3和ip1之间呢?找不到比clientHash大的ip,就选择所有ip中最小的那个,这样就是一个完整的hash环。
hash环如何保证映射的稳定性?
作为分布式集群,我们希望同一个用户尽可能的的访问同一台机器,即要确保clientHash到服务集群映射的稳定性,这一点hash环也很好的保证了,由于客户端ip不常变化,经过hash的数值也都不变,所以只要集群机器不变,映射性就不会改变,试想如下情形:
ip2挂了
这种情况只会影响到之前落在ip1和ip2之间的客户端,他们会被委托给ip3,这也只不过影响了1/3
新增一台ip4在ip2和ip3之间
这种情况只会把ip2和ip4之间的客户端委托给新机器ip4,影响更小了
综上所述无论新增还是减少机器,对于映射稳定性都是影响不大的,不像普通的clientHash%集群机器数这种算法,新增减少机器影响是巨大的

hash环也不总是均衡的
在这里插入图片描述
如果机器分布成这样,就机器不均衡了,大量请求会落在ip1,而且对于只有几台机器来说映射成这样是极有可能的,解决办法是加机器,但是机器很贵,几十台也不一定可以映射均衡,这就衍生出虚拟机器的概念
在这里插入图片描述
这样就让集群重新分布均衡

code

public class ConsistentHash {
    //服务器列表
    private static List<String> servers = new ArrayList();
    //虚拟节点
    private static TreeMap<Integer,String> VNode = new TreeMap();
    //为每一个真实节点加的虚拟节点个数
    private static int V_NODE = 160;
    static{
        servers.add("192.168.0.1");
        servers.add("192.168.0.2");
        servers.add("192.168.0.3");
        servers.add("192.168.0.4");
        //为每一个节点添加虚拟节点
        for(String ip:servers){
            for(int i=0;i<V_NODE;i++){
                VNode.put(getHash(ip+"VN"+i),ip);
            }
        }
    }

    private static String getServer(String clientIp){
        int hash = getHash(clientIp);
        //拿到比hash大的第一个节点
        Integer nodeIndex = VNode.tailMap(hash).firstKey();
        //节点不存在
        if(null==nodeIndex){
            //拿所有节点第一个
            nodeIndex = VNode.firstKey();
        }
        return VNode.get(nodeIndex);
    }

    private static int getHash(String str) {
        final int p = 16777619;
        int hash = (int) 2166136261L;
        for (int i = 0; i < str.length(); i++)
            hash = (hash ^ str.charAt(i)) * p;
        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;
        if (hash < 0)
            hash = Math.abs(hash);
        return hash;
    }

    public static void main(String[] args) {
        for(int i=0;i<10;i++){
            System.out.println(getServer("client" + i));
        }
    }
}

公共代码

public class Server {

    private String ip;
    private int weight;
    private int callNum;//被调用次数
    private int callOK;
    private int callFail;
    private double longestResponseTime;
    private double shortestResponseTime;
    private double averageResponseTime;
    public Server(){}
    public Server(String ip){
        this.ip = ip;
    }
    public Server(String ip,int weight){
        this.ip = ip;
        this.weight = weight;
    }
	//get set toString 省略
public class WeightServer {
    public static List<Server> servers = new ArrayList();
    static {
        servers.add(new Server("192.168.0.1",3));
        servers.add(new Server("192.168.0.2",7));
        servers.add(new Server("192.168.0.3",6));
        servers.add(new Server("192.168.0.4",4));
        servers.add(new Server("192.168.0.5",10));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值