Nginx学习笔记2-负载均衡算法

首先要了解为什么使用负载均衡

Nginx 实现了动静分离 反向代理

这里再强调一下正向代理和反向代理。下面通俗的讲一下---

正向代理:就是用户找了个代理,例如vpn之类的,把用户自己藏起来了。

反向代理:服务器找了个代理藏起来了,外界看不到其真实的地址。

 

当然 如果使用硬件实现负载均衡的话是 非常快的,且可靠性高,并发量大。当然缺点是太贵成本高。

常用的实现负载均衡的软件Nginx和apache http service。但是apache http service 效率和Nginx比起来效率一般。

所以着重使用Nginx。

Nginx的下载

https://nginx.org/

进入后选择一个版本,一般来说选择稳定的版本(stable version),点击windows版本的则开始下载,解压开始使用。

当然 使用前需要进行一定的配置 conf里的nginx.conf, 一般来说做以下修改。

这样启动服务输入url就可以访问了。

负载均衡算法

1.轮询:这是比较常用的算法。     先模拟集群中ip的集合---

package distributed;

import java.util.HashMap;

public class IPMap {

   public static HashMap<String, Integer> serverWeightMap = new HashMap<String, Integer>();

    static {
        //第一个参数是IP地址,第二个是权重.
        serverWeightMap.put("192.168.1.100", 1);
        serverWeightMap.put("192.168.1.101", 2);
        serverWeightMap.put("192.168.1.102", 3);
        serverWeightMap.put("192.168.1.103", 4);
        serverWeightMap.put("192.168.1.104", 3);
        serverWeightMap.put("192.168.1.105", 2);
        serverWeightMap.put("192.168.1.106", 1);
        serverWeightMap.put("192.168.1.107", 2);
        serverWeightMap.put("192.168.1.108", 1);
        serverWeightMap.put("192.168.1.109", 4);
    }
}

而轮询其实就是按顺序均等分配请求,代码如下。

public class RoundRobin {

    private static Integer pos=0;

    public static String getServerIP(){

        //重新在线程本地copy一份IPMap, 避免服务器上线下线导致的并发问题
        Map<String, Integer>  serverMap = new HashMap<String, Integer>();
        serverMap.putAll(IPMap.serverWeightMap);

        //取的IP地址的Set
        Set<String> ips = serverMap.keySet();

        List<String> iplist = new ArrayList<String>();
        iplist.addAll(ips);

        String server = null;

        synchronized (pos){
            if(pos > iplist.size())
                pos=0;

            server = iplist.get(pos);

            pos++;
        }
        return server;
    }
}

由于serverWeightMap中的地址列表是动态的,随时可能有机器上线、下线或者宕机,因此为了避免可能出现的并发问题,方法内部要新建局部变量serverMap,现将serverMap中的内容复制到线程本地,以避免被多个线程修改。这样可能会引入新的问题,复制以后serverWeightMap的修改无法反映给serverMap,也就是说这一轮选择服务器的过程中,新增服务器或者下线服务器,负载均衡算法将无法获知。新增无所谓,如果有服务器下线或者宕机,那么可能会访问到不存在的地址。因此,服务调用端需要有相应的容错处理,比如重新发起一次server选择并调用。

轮询法的优点在于:试图做到请求转移的绝对均衡。

轮询法的缺点在于:为了做到请求转移的绝对均衡,必须付出相当大的代价,因为为了保证pos变量修改的互斥性,需要引入重量级的悲观锁synchronized,这将会导致该段轮询代码的并发吞吐量发生明显的下降。

2.随机轮询

public class RandomIP {

    public static String getServerIP() {

        //重新在线程本地copy一份IPMap, 避免服务器上线下线导致的并发问题
        Map<String, Integer> serverMap = new HashMap<String, Integer>();
        serverMap.putAll(IPMap.serverWeightMap);

        //取的IP地址的Set
        Set<String> ips = serverMap.keySet();

        List<String> iplist = new ArrayList<String>();
        iplist.addAll(ips);

        String server = null;

        //获取IP的策略
        Random random = new Random();
        int pos = random.nextInt(iplist.size());
        return iplist.get(pos);
    }

}

随着调用量的增大,这种算法也就接近于上面所说的轮询了。

3.源地址哈希法

public class HashIP {

    public static String getServerIP() {

        //重新在线程本地copy一份IPMap, 避免服务器上线下线导致的并发问题
        Map<String, Integer> serverMap = new HashMap<String, Integer>();
        serverMap.putAll(IPMap.serverWeightMap);

        //取的IP地址的Set
        Set<String> ips = serverMap.keySet();

        List<String> iplist = new ArrayList<String>();
        iplist.addAll(ips);

        //获取IP的策略
        //获取远端请求的IP地址
        String remoteIP = "127.0.0.11";
        int hashCode = remoteIP.hashCode();
        hashCode = Math.abs(hashCode);//确保hash值是正数. 如果hash值是负数
        int ipSize = iplist.size();
        int pos = hashCode % ipSize;

        return iplist.get(pos);
    }

}

源地址哈希法的优点在于:保证了相同客户端IP地址将会被哈希到同一台后端服务器,直到后端服务器列表变更。根据此特性可以在服务消费者与服务提供者之间建立有状态的session会话。

源地址哈希算法的缺点在于:除非集群中服务器的非常稳定,基本不会上下线,否则一旦有服务器上线、下线,那么通过源地址哈希算法路由到的服务器是服务器上线、下线前路由到的服务器的概率非常低,如果是session则取不到session,如果是缓存则可能引发”雪崩”。(缓存雪崩指:某些原因导致缓存全部失效,意味着所有的数据请求都会到达数据库,导致数据库请求暴增,导致数据库挂掉。)

4.加权轮询

这个算法是受到服务器配置以及负载情况影响的。就是能者多劳的意思。但是实际情况中服务器一般配置都会保持一样,所以这种算法用的也并不多。

public class WeightRoundRobin {

    private static Integer pos=0;

    public static String getServerIP(){

        //重新在线程本地copy一份IPMap, 避免服务器上线下线导致的并发问题
        Map<String, Integer>  serverMap = new HashMap<String, Integer>();
        serverMap.putAll(IPMap.serverWeightMap);

        //取的IP地址的Set
        Set<String> ips = serverMap.keySet();
        Iterator<String> iterator = ips.iterator();

        List<String> iplist = new ArrayList<String>();
        while (iterator.hasNext()){
            String server = iterator.next();
            int weight = serverMap.get(server);
            //按照权重来添加比例.
            for(int i=0; i<weight; i++){
                iplist.add(server);
            }
        }

        String server=null;
        synchronized (pos){
            if(pos > iplist.size())
                pos=0;

            server = iplist.get(pos);

            pos++;
        }
        return server;
    }
}

5.加权随机

这个也很好理解,就是随机到服务器的概率有所增大。

public class WeightRandom {

    public static String getServerIP() {

        //重新在线程本地copy一份IPMap, 避免服务器上线下线导致的并发问题
        Map<String, Integer>  serverMap = new HashMap<String, Integer>();
        serverMap.putAll(IPMap.serverWeightMap);

        //取的IP地址的Set
        Set<String> ips = serverMap.keySet();
        Iterator<String> iterator = ips.iterator();

        List<String> iplist = new ArrayList<String>();
        while (iterator.hasNext()){
            String server = iterator.next();
            int weight = serverMap.get(server);
            //按照权重来添加比例.
            for(int i=0; i<weight; i++){
                iplist.add(server);
            }
        }

        //获取IP的策略
        Random random = new Random();
        int pos = random.nextInt(iplist.size());
        return iplist.get(pos);
    }
}

6.最小连接数法

前面几种方法费尽心思来实现服务消费者请求次数分配的均衡,当然这么做是没错的,可以为后端的多台服务器平均分配工作量,最大程度地提高服务器的利用率,但是实际情况是否真的如此?实际情况中,请求次数的均衡真的能代表负载的均衡吗?这是一个值得思考的问题。

上面的问题,再换一个角度来说就是:以后端服务器的视角来观察系统的负载,而非请求发起方来观察。最小连接数法便属于此类。

最小连接数算法比较灵活和智能,由于后端服务器的配置不尽相同,对于请求的处理有快有慢,它正是根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前请求,尽可能地提高后端服务器的利用效率,将负载合理地分流到每一台机器。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章