负载平衡(Load balancing)是一种在多个计算机(网络、CPU、磁盘)之间均匀分配资源,以提高资源利用的技术。使用负载均衡可以最大化服务吞吐量,可能最小化响应时间,同时由于使用负载均衡时,会使用多个服务器节点代单点服务,也提高了服务的可用性。

负载均衡的实现可以软件可以硬件,硬件如大名鼎鼎的 F5 负载均衡设备,软件如 NGINX 中的负载均衡实现,又如 Springcloud Ribbon 组件中的负载均衡实现。

如果看到这里你还不知道负载均衡是干嘛的,那么只能放一张图了,毕竟没图说个啥。

正经的负载均衡示例

负载均衡要做到在多次请求下,每台服务器被请求的次数大致相同。但是实际生产中,可能每台机器的性能不同,我们会希望性能好的机器承担的请求更多一些,这也是正常需求。

如果这样说下来你看不懂,那我就再举个例子好了,一排可爱的小熊(服务器)站好。

一排要被访问的服务器

这时有人(用户)要过来打脸(请求访问)。

用户请求

那么怎么样我们才能让这每一个可爱的小熊被打的次数大致相同呢?

又或者熊 4 比较胖,抗击打能力是别人的两倍,我们怎么提高熊 4 被打的次数也是别人的两倍呢?

又或者每次出手的力度不同,有重有轻,恰巧熊 4 总是承受这种大力度啪啪打脸,熊 4 即将不省熊事,还要继续打它吗?

这些都是值得思考的问题。

说了那么多,口干舌燥,我双手已经饥渴难耐了,迫不及待的想要撸起代码了。

1. 随机访问

上面说了,为了负载均衡,我们必须保证多次出手后,熊 1 到熊 4 被打次数均衡。比如使用随机访问法,根据数学上的概率论,随机出手次数越多,每只熊被打的次数就会越相近。代码实现也比较简单,使用一个随机数,随机访问一个就可以了。

/** 服务器列表 */private static List serverList = new ArrayList<>();static {    serverList.add("192.168.1.2");    serverList.add("192.168.1.3");    serverList.add("192.168.1.4");    serverList.add("192.168.1.5");}/** * 随机路由算法 */public static String random() {    // 复制遍历用的集合,防止操作中集合有变更    List tempList = new ArrayList<>(serverList.size());    tempList.addAll(serverList);    // 随机数随机访问    int randomInt = new Random().nextInt(tempList.size());    return tempList.get(randomInt);}

因为使用了非线程安全的集合,所以在访问操作时操作的是集合的拷贝,下面几种轮询方式中也是这种思想。

写一个模拟请求方法,请求10w次,记录请求结果。

public static void main(String[] args) {    HashMap serverMap = new HashMap<>();    for (int i = 0; i  entry : serverMap.entrySet()) {        System.out.println("IP:" + entry.getKey() + ",次数:" + entry.getValue());    }}

运行得到请求结果。

IP:192.168.1.3,次数:24979IP:192.168.1.2,次数:24896IP:192.168.1.5,次数:25043IP:192.168.1.4,次数:25082

每台服务器被访问的次数都趋近于 2.5w,有点负载均衡的意思。但是随机毕竟是随机,是不能保证访问次数绝对均匀的。

2. 轮询访问

轮询访问就简单多了,拿上面的熊1到熊4来说,我们一个接一个的啪啪 - 打脸,熊1打完打熊2,熊2打完打熊3,熊4打完打熊1,最终也是实现了被打均衡。但是保证均匀总是要付出代价的,随机访问中需要随机,轮询访问中需要什么来保证轮询呢?

/** 服务器列表 */private static List serverList = new ArrayList<>();static {    serverList.add("192.168.1.2");    serverList.add("192.168.1.3");    serverList.add("192.168.1.4");    serverList.add("192.168.1.5");}private static Integer index = 0;/** * 随机路由算法 */public static String randomOneByOne() {    // 复制遍历用的集合,防止操作中集合有变更    List tempList = new ArrayList<>(serverList.size());    tempList.addAll(serverList);    String server = "";    synchronized (index) {        index++;        if (index == tempList.size()) {            index = 0;        }        server = tempList.get(index);;    }    return server;}

由代码里可以看出来,为了保证轮询,必须记录上次访问的位置,为了让在并发情况下不出现问题,还必须在使用位置记录时进行加锁,很明显这种互斥锁增加了性能开销。

依旧使用上面的测试代码测试10w次请求负载情况。

IP:192.168.1.3,次数:25000IP:192.168.1.2,次数:25000IP:192.168.1.5,次数:25000IP:192.168.1.4,次数:25000

3. 轮询加权

上面演示了轮询方式,还记得一开始提出的熊4比较胖抗击打能力强,可以承受别人2倍的挨打次数嘛?上面两种方式都没有体现出来熊 4 的这个特点,熊 4 窃喜,不痛不痒。但是熊 1 到 熊 3 已经在崩溃的边缘,不行,我们必须要让胖着多打,能者多劳,提高整体性能。

/** 服务器列表 */private static HashMap serverMap = new HashMap<>();static {    serverMap.put("192.168.1.2", 2);    serverMap.put("192.168.1.3", 2);    serverMap.put("192.168.1.4", 2);    serverMap.put("192.168.1.5", 4);}private static Integer index = 0;/** * 加权路由算法 */public static String oneByOneWithWeight() {    List tempList = new ArrayList();    HashMap tempMap = new HashMap<>();    tempMap.putAll(serverMap);    for (String key : serverMap.keySet()) {        for (int i = 0; i 

这次记录下了每台服务器的整体性能,给出一个数值,数值越大,性能越好。可以承受的请求也就越多,可以看到服务器 192.168.1.5 的性能为 4,是其他服务器的两倍,依旧 10 w 请求测试。

IP:192.168.1.3,次数:20000IP:192.168.1.2,次数:20000IP:192.168.1.5,次数:40000IP:192.168.1.4,次数:20000

192.168.1.5 承担了 2 倍的请求。

4. 随机加权

随机加权的方式和轮询加权的方式大致相同,只是把使用互斥锁轮询的方式换成了随机访问,按照概率论来说,访问量增多时,服务访问也会达到负载均衡。

/** 服务器列表 */private static HashMap serverMap = new HashMap<>();static {    serverMap.put("192.168.1.2", 2);    serverMap.put("192.168.1.3", 2);    serverMap.put("192.168.1.4", 2);    serverMap.put("192.168.1.5", 4);}/** * 加权路由算法 */public static String randomWithWeight() {    List tempList = new ArrayList();    HashMap tempMap = new HashMap<>();    tempMap.putAll(serverMap);    for (String key : serverMap.keySet()) {        for (int i = 0; i 

依旧 10 w 请求测试,192.168.1.5 的权重是其他服务器的近似两倍,

IP:192.168.1.3,次数:19934IP:192.168.1.2,次数:20033IP:192.168.1.5,次数:39900IP:192.168.1.4,次数:20133

5. IP-Hash

上面的几种方式要么使用随机数,要么使用轮询,最终都达到了请求的负载均衡。但是也有一个很明显的缺点,就是同一个用户的多次请求很有可能不是同一个服务进行处理的,这时问题来了,如果你的服务依赖于 session ,那么因为服务不同, session 也会丢失,不是我们想要的,所以出现了一种根据请求端的 ip 进行哈希计算来决定请求到哪一台服务器的方式。这种方式可以保证同一个用户的请求落在同一个服务上。

private static List serverList = new ArrayList<>();static {    serverList.add("192.168.1.2");    serverList.add("192.168.1.3");    serverList.add("192.168.1.4");    serverList.add("192.168.1.5");}/** * ip hash 路由算法 */public static String ipHash(String ip) {    // 复制遍历用的集合,防止操作中集合有变更    List tempList = new ArrayList<>(serverList.size());    tempList.addAll(serverList);    // 哈希计算请求的服务器    int index = ip.hashCode() % serverList.size();    return tempList.get(Math.abs(index));}

6. 总结

上面的四种方式看似不错,那么这样操作下来真的体现了一开始说的负载均衡吗?答案是不一定的。就像上面的最后一个提问。

又或者每次出手的力度不同,有重有轻,恰巧熊 4 总是承受这种大力度啪啪打脸,熊 4 即将不省熊事,还要继续打它吗?

服务器也是这个道理,每次请求进行的操作对资源的消耗可能是不同的。比如说某些操作它对 CPU 的使用就是比较高,也很正常。所以负载均衡有时不能简单的通过请求的负载来作为负载均衡的唯一依据。还可以结合服务的当前连接数量、最近响应时间等维度进行总体均衡,总而言之,就是为了达到资源使用的负载均衡。

2台服务器负载均衡后synchronized_一篇有趣的负载均衡算法实现相关推荐

  1. 两台linux服务器负载均衡代码实现,nginx实现负载均衡,nginx负载均衡确保两台服务器数据保...

    nginx实现负载均衡,nginx负载均衡确保两台服务器数据保 一.准备篇: Nginx 负载服务器: Centos 6.2 IP:192.168.1.93 WEB服务器: Web1:192.168. ...

  2. nginx负载均衡两台服务器文件,Nginx之负载均衡 :两台服务器均衡(填坑)

    第一步,两台服务器都要安装好Nginx和Tomcat,我这边的安装的是Nginx 1.16.1 Tomcat9: 第二步,安装完成之后,选择你要做均衡的那台服务器,,打开其Nginx 配置文件,在se ...

  3. 服务器装系统后没有移动硬盘盘符

    以server2003 为例 今天装过一台服务器 装完后竟然不能使用移动硬盘 解决方法如下: 1.开始 ----运行----输入compmgmt.msc---回车 2.    存储----  磁盘管理 ...

  4. scp 传输文件到另一台服务器

    文章目录 一.scp 命令简介 二.简单测试 scp 命令 1.启动 ssh 服务 3.scp 命令格式 2.运行 scp 命令 三.scp 免密码传输文件 四.docker 备份 MySQL 到另一 ...

  5. 使用两台服务器做负载均衡(nginx版)

    使用两台服务器做负载均衡(nginx版) 一.环境准备 在使用两台服务器做负载均衡前,首先要把环境配置好. 两台服务器上启动的项目都是一样的. 用到的项目包:前端的dist打包文件,后端的jar包: ...

  6. Nginx多台服务器负载均衡

    一 操作步骤: 1.服务器IP 45.114.124.215   //主服务器(安装Nginx) 45.114.124.99     //从服务器(安装Nginx或Apache都可以) 2.保证2台服 ...

  7. weblogic集群部署与负载均衡_集群,负载均衡,分布式的讲解和多台服务器代码同步...

    集群 我们的项目如果跑在一台机器上,如果这台机器出现故障的话,或者用户请求量比较高,一台机器支撑不住的话.我们的网站可能就访问不了.那怎么解决呢?就需要使用多台机器,部署一样的程序,让几个机器同时的运 ...

  8. 运维企业专题(8)LVS高可用与负载均衡后篇——LVS健康检查与高可用详解

    实验准备 1.下面的实验使用的是rhel6系列(rhel6.5)的虚拟机,因此你需要有对应的镜像和yum源 2.准备三台虚拟机,为了区分主机名与IP分别为 server1 172.25.6.1 ser ...

  9. js轮询导致服务器瘫痪_面试官:讲一下什么是负载均衡,什么是轮询策略随机策略哈希策略...

    什么是负载均衡? 先举个例子吧.以超市收银为例,假设现在只有一个窗口.一个收银员: 一般情况下,收银员平均 2 分钟服务一位顾客,10 分钟可以服务 5 位顾客:到周末高峰期时,收银员加快收银,平均 ...

最新文章

  1. 用NVIDIA A100 GPUs提高计算机视觉
  2. [Java拾遗四]JavaWeb基础之Servlet_RequestResponse
  3. window 查找 java 进程中占用cpu比较高的线程
  4. react把表格渲染好ui_《RSUITE》React企业级UI框架实战评测
  5. 无法获得锁 /var/lib/dpkg/lock
  6. [算法笔记] 爬楼梯
  7. java md5算法_JAVA实现MD5算法
  8. React项目中第三方使用微信扫码登录
  9. Matlab颜色对照(RGB三元组、十六进制表示)
  10. Sublime Text 2 注册码/破解方法
  11. CleanMyMac最新4.10.5版本 智能一键扫描清理工具
  12. 如何系统学习经济学 -- 来自知乎建议
  13. C++17之std::any
  14. unapp Error: Unbalanced delimiter found in string
  15. 魔兽mdx文件导出为Ogre Mesh的小进展
  16. RTC实时时钟实验学习笔记一
  17. 使用 html2canvas 生成图片
  18. tableau-计算一个月内的第几周
  19. Undirected Vertex Geography
  20. [宋史学习] 王全斌功过

热门文章

  1. python去掉html标签_python 去除html标签的几种方法
  2. matlab求偏微分方程程序,用MATLAB解偏微分方程.pdf
  3. Java Date hashCode()方法与示例
  4. Java PrintWriter close()方法与示例
  5. Java SimpleTimeZone toString()方法与示例
  6. 图解:为什么非公平锁的性能更高?
  7. 如何让mysql索引更快一点
  8. 最小拍控制系统详细解读(阶跃输入+速度输入2个案例)【Simulink仿真】
  9. linux localhost的修改
  10. Django代码编写规范