没想到我也是一个标题党
写在前面
去年,利用业余时间以及所学知识建了人生中第一个网站,这个网站也就我用来平时写写东西。当然前期就是写后端交互代码,把后端代码写好的话,免不了要放在服务器上,既然放在服务器上,我们肯定也要部署Web服务器,目前市面上常用的两款Web服务器就是apache和NginX,在我了解了这两款服务器后,我坚决的选择了后者,那就是鼎鼎大名的NginX。
按F12可以查看到我网站的请求头里就有我的代理服务器版本号。当然博思平台也用的是此款服务器。
总之,我们不妨先记住Nginx的几个优点,高并发,轻量级,部署简单快捷,百万级的访问量下,Nginx能够还能够保持它的性能,这也得益于它的代理策略。
关于Nginx(Engine X)
nginx [engine x] is an HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server, originally written by Igor Sysoev. For a long time, it has been running on many heavily loaded Russian sites including Yandex, Mail.Ru, VK, and Rambler. According to Netcraft, nginx served or proxied 25.54% busiest sites in April 2020. Here are some of the success stories: Dropbox, Netflix, Wordpress.com, FastMail.FM.
上面这段话出自Nginx官网,大致意思差不多就是Nginx这个服务器很多牛逼的互联网公司在用,
不过说到Nginx,那就要说到 伊戈尔·赛索耶夫 这个人,他是一名俄罗斯软件工程师,也就是Nginx的发明者,不得不说,毛子的编程水平也是一流。他在工作闲暇之余用C语言开发了这款闻名全球的轻量级高并发的Web服务器,Nginx不仅可以用作网站的服务器,还可以作为支持电子邮件的代理服务器。早期的淘宝就是用的Nginx来部署的(厉害吧),不过随着淘宝网后期的业务调整之类的种种原因,淘宝叫来了一群工程师,把Nginx的一些组件封装好,再加入一些特性,开发出了一款全新的Web服务器Tengine,估计淘宝现在用的是自家的服务器吧。
不过悲惨的是,伊戈尔·赛索耶夫在去年被俄罗斯警方逮捕,随着一起被逮捕的有另外一个人,也是Nginx的开发者之一,具体原因大家可以百度一下,为了节省篇幅就不废话了。
说到这,Nginx官网介绍的是,这款服务器是一个反向代理服务器,那么什么是反向代理?说反向代理之前,我先来说一下正向代理吧!此文的重点来了
正向代理
先看一个直观图(图片来源网络)
Proxy server就是我们的代理服务器,Server就是我们需要访问的服务器
举个例子:某天小明在写代码的时候发现了一个Bug,他想上谷歌搜一下这个Bug是怎么回事,但是无论怎么搜只能搜到如下界面
后来,小明知道了学校的服务器是可以访问Google的,他就向学校的服务器发送了一个请求,学校的服务器收到了请求后,再去访问Google,随后再把Google返回的数据返回给小明,就这样小明成功的访问到了Google。
例子中很容易就理解了正向代理服务器的过程,学校的服务器就作为一个代理服务器,而Google就是我们想要访问的目标服务器,而我们是无法直接访问,只能通过代理服务器去访问,然后再把数据返回给我们。
正向代理我们是需要手动去设置代理服务器的ip地址的,在Win下就可以直接设置代理服务器,有兴趣的可以百度一下如何设置。我就不讲如何设置了。
那说完了正向代理,下面就要讲Nginx所用到的反向代理模式了。
累死我了,喝口茶~~~~~
反向代理
说到反向代理,我们先讲一下服务器群组,玩过饥荒的同学知道,如果我们自己开房间玩的话,房主所在的电脑将被饥荒作为一个服务器,所有玩家都将连接到你的电脑上来进行联机游玩,要知道当初我电脑性能并不是很好,记得一个房间最多5人还是8人来着,刚开始还好,游戏正常有序的进行下去,越到后面,随着加入游戏的人数增多,后期资源,地图的扩大,导致了我的电脑宕机了,没错直接就崩了,从那之后我就再也没有自己开过房间玩,到后面就游戏就吃灰了(哈哈,我玩游戏玩不长,总想着玩新游戏)。
不够这也说明电脑以及服务器的性能是执行程序效率的好坏关键之一。话不多说,还是先来看一图
我们再思考一个问题,现实生活中,无论你的服务器性能再好,你终究只是一台服务器,然而我们却要处理数以千万记的请求,就像www.baidu.com一样,每天都会有千万ip在进行着访问服务器的过程,可一个服务器怎么可能处理的过来,这个时候反向代理服务器就出来了,例如上图,当我们去访问baidu的时候,www.baidu.com这个域名指向的服务就作为一个代理服务器,代理服务器利用一种算法(负载均衡算法,下文会讲)将每一个请求转发到其LAN下的某一台具体的服务器,接下来这台服务器处理相关的请求,随后返回给客户端。就这样简单。
正/反向代理的使用场景
其实正向代理估计很多人都已经知晓他的用途了,没错,那就是去墙外的世界。咳咳~,剩下的我就不说了。
那反向代理主要就是用来为一群服务器提供负载均衡,保持性能资源利用最大化。
负载均衡
本来负载均衡我是打算另开一个文章来详细说的,眼看开学在即,我也没有多少时间收集资料,这里我就简单的描述一下,以及用Java简单模拟一下负载均衡算法。
负载均衡的英语是 load balance,目的是用来使每一台服务器负载量保持均衡,比如说,我有十台服务器,我不可能让第一台服务器处理一百个请求,第二台服务器处理1个请求吧,所以就有了算法来处理这些问题。
我这里只说两种常用的负载均衡算法
1. 轮询法(Round Robin)
所谓轮询法,轮询,轮询,光听名字就知道,轮着来呗,假如我们有10台服务器,一台代理服务器,突然有10个ip发来了请求,代理服务器就把这10个请求依次交给这10台服务器,每个服务器都达到了负载均衡,只需处理一个请求即可。光听描述简单吧。我相信你能够懂,代码我就不写了,一个循环而已。
方法总是伴随着问题而来,负载是达到了均衡,可是真的达到了吗?我再举一个例子,假如有10个人,其中有高有矮,有壮有瘦,在他们的面前有1000斤的东西要搬到仓库去,如果按照轮询法的策略的话,每个人只需要搬100斤的东西,可是又高又壮的人明明可以般200斤,矮的人最多只能般50斤。这样的话就造成了资源浪费以及超负荷负载。那么接下来讲的就是另一种负载均衡算法了。
2. 加权轮询法(Weight Round Robin)
没错,我要写代码了,之所以上面不写,主要是这两个算法不难,但是在服务器内部实现的话要考虑很多因素,待会儿再说这个问题。
其实我们只需要把每一台服务器加上一个权值,这个权的大小代表着这个服务器的性能好坏。
我们先实现一个类,该类的签名如下:
public class RoundRobin {}
然后还有一些类属性:
// 服务器群的ip地址 private static ArrayList<String> ips; // 每台服务器对应的权值 private static ArrayList<Integer> weight; // 每台服务器的负载量 private static TreeMap<String, Integer> map; // 服务器的个数 private static int count; // 打印服务器的ip地址 public void getIps(){} // 模仿负载均衡算法 public void loadBalancing(int req){} // 打印每台服务器的负载量 public void getServerChargeNumber(){}
最重要的就是负载均衡算法方法了,我们先来看一下是如何实现的
public void loadBalancing(int req) {
int pos = 0;
while (req > 0){
// 当pos轮询到最后一台服务器的时候,又从0开始轮询
if (pos == count) pos = 0;
// 当前转发的服务器ip地址
String currServerIp = ips.get(pos);
// 当前权值
int currServerWeight = weight.get(pos);
/**
* 如果当前服务器的权为5的话,必须给当前服务器分配5个请求才能给下一份服务器分配
*/
for (int currServerChargeNumber = 0;currServerChargeNumber <= currServerWeight; ++currServerChargeNumber){
// 模拟转发请求。这里记录一下服务器收到的请求数
map.put(currServerIp, map.get(currServerIp) + 1);
// 请求减一
req--;
}
// 对应加一
pos++;
}
}
其实加权轮询法比轮询法多加了一个权值而已,假如我们有三台服务器,有10个ip发来请求,这三台服务器的权值分别为5, 3, 2,根据权值大小,第一台服务器要处理5个请求,在轮询到下一个服务器,第二台就要处理3个请求,接下来第三台肯定就要处理两个请求了,就这样我们才能做到真正的负载均衡。根据服务器的硬件性能分配不同的权值,在根据权值大小去分配请求。
多说无益,我们直接模拟发起请求吧。
先看完整的算法代码:
package com.round_robin;
import java.util.ArrayList;
import java.util.TreeMap;
import java.util.Set;
public class RoundRobin {
private static ArrayList<String> ips;
private static ArrayList<Integer> weight;
private static TreeMap<String, Integer> map;
private static int count;
static {
// 初始化 ip地址
ips = new ArrayList<>();
map = new TreeMap<>();
weight = new ArrayList<>();
count = 0;
for (int i = 1; i < 10; ++i) {
String ip = "192.168.1." + i;
ips.add(ip);
map.put(ip, 0);
count++;
}
for (int i = 9; i >= 1; --i)
weight.add(i);
}
public RoundRobin() {}
public void getIps() {
for (int i = 0; i < count; ++i) {
System.out.println("ip:" + ips.get(i) + " 权重:" + weight.get(i));
}
}
/**
* @param req 向请求服务器发起请求次数
* @return 返回代理的ip地址
*/
public void loadBalancing(int req) {
int pos = 0;
while (req > 0){
if (pos == count) pos = 0;
String currServerIp = ips.get(pos);
int currServerWeight = weight.get(pos);
/**
* 如果当前服务器的权为5的话,必须给当前服务器分配5个请求才能给下一份服务器分配
*/
for (int currServerChargeNumber = 0; currServerChargeNumber <= currServerWeight; ++currServerChargeNumber) {
map.put(currServerIp, map.get(currServerIp) + 1);
req--;
}
pos++;
}
}
/**
* 打印每台服务器的负载量
*/
public void getServerChargeNumber() {
Set<String> set = map.keySet();
for (String ip : set)
System.out.println(ip + " : " + map.get(ip));
}
}
代码较长,其实自己写一遍更好理解。
模拟发起请求就放在主函数了。
public class Main {
public static void main(String[] args) {
RoundRobin roundRobin = new RoundRobin();\
// roundRobin.getIps();
// 模拟发起10000次请求
roundRobin.loadBalancing(10000);
roundRobin.getServerChargeNumber();
}
}
每台服务器处理的请求如下:
192.168.1.1 : 1860
192.168.1.2 : 1665
192.168.1.3 : 1480
192.168.1.4 : 1295
192.168.1.5 : 1110
192.168.1.6 : 925
192.168.1.7 : 740
192.168.1.8 : 555
192.168.1.9 : 370
ok,很简单吧,是不是瞬间感觉自己也能开发出一台服务器了。哈哈哈,不过正式服务器内部代码肯定不像我这样,还要处理客户端发来的数据,还要检测每台服务器是否能够正常通讯,总之神功不是一天就练成了,了解其内部处理原理才能够帮助我们写出更好的代码。
加油吧骚年!!!
软件
链接:https://pan.baidu.com/s/1ujgOh_EGhyUakAOCBEIgDA 提取码:ydi7
如何使用就自己琢磨了。
微信公众号:NonCover