例如现在有两台服务器(server3000
和server7000
)为用户提供服务,如果没有负载均衡就有可能出现95%的用户都去访问server3000
,而只有5%的用户访问server7000
。那样不就出现server3000
快被压死了,而server7000
工作负荷完全不满的情况吗。我们当然希望将用户的访问均衡的分配到这两台服务器上,那样对我们的系统是最有利的,这就是负载均衡最大的意义。
这两台服务器上分别部署了相同的前端静态文件(index.html index.js
)【咱学的服务端都是用node
去做的服务端【部署静态文件就是把前端写好的静态页面放到plugin
目录里放(也就是往静态资源目录里放)】】
为什么要放在两台服务器:
为了分担服务器压力,一般用户量特别大的时候都会分多台服务器,加入了更多的服务器压力肯定就小了这是毋庸置疑的。
最终实现服务器的这个服务中间的细节问题我们得考虑,接下来回去模拟出多台服务器的环境来:
在同一台机器上可以通过端口不一样来区分服务:
npm init
npm i express -S
给两个端口都设置静态资源目录(public
):
假设在public
里面有index.html
文件,这两个html
文件内容都相同,访问它们的域名是不同的:
现在可以去浏览器访问:
server3000:
const express = require('express')
const app = express();
const path = require('path')
app.use(express.static(path.join(__dirname, './public')));
app.listen(3000, () => {
console.log(3000)
})
server7000
const express = require('express')
const app = express();
const path=require('path')
app.use(express.static(path.join(__dirname,'./public')));
app.listen(7000, () => {
console.log(7000)
})
现在就面临着一个问题,就是这个项目要部署在两台服务器上,但是我希望它通过一个域名访问,就是我访问一个地址但是这两台服务器都能访问,只要一个地址两个都能访问。
思路:现在为了减轻目标服务器的压力已经将分流分在两台服务器上,这个时候要再起一台服务器,就不能叫目标服务器了它叫代理服务器。(服务器代理,反向代理)这两个服务器都是目标服务器,资源真正存在它两个身上。
代理服务器,它里面不部署静态资源,它的作用是接收用户请求,根据请求转发到服务器,也就是用户的请求全发给代理服务器(这样的话域名(地址)就统一了),访问代理服务器最终是想要看到3000或7000里面的文件,由代理服务器里面写个逻辑自动的将3000或7000里面内容拿回来,这就解决了两台服务器访问域名统一的问题。
代理服务器还得考虑是从3000取还是从7000取;
将代理服务器启动:
浏览器向服务器发请求:各种资源的url
、ajxs
、script里的src
、表单、jsonP
、link
标签hrf
、a
标签hrf
等;
服务器向服务器发请求:node
端用的是使用http
模块提供的方法get
方法和requset
,还有axios
:
axios这个库它封装的时候里面既包含了ajxs也包含了node端的请求,所以直接用ajxs也能发请求
express
有个中间件express-http-proxy
,它的作用就是把当前请求转发到服务器;
express-http-proxy使用方法
npm i express-http-proxy -S
浏览器运行server9000
,作为用户肯定不知道当前浏览器被代理了,比如去访问其它的网站,它背后有几层代理,用户肯定不知道,这叫反向代理,你不知道背后它代理了。
接下来要实现,如何均衡负载不能全放在一个代理上,要雨露均沾;
server9000
>index.js
:
const express = require('express')
const app = express();
const proxy=require('express-http-proxy')
// 请求转发-目标-3000 / 7000
// proxy(host,options) 接受两个参数
// 第一个是域名就是真正要转发到那个地址
// 第二个是个配置项
// / 代表所有的请求 也可以写abc 那样就是以abc开头的
// 1、127.0.0.1是回送地址,指本地机,一般用来测试使用。
// 2、回送地址(127.x.x.x)是本机回送地址(LoopbackAddress),
// 即主机IP堆栈内部的IP地址,主要用于网络软件测试以及本地机进程间通信,
// 无论什么程序,一旦使用回送地址发送数据,协议软件立即返回,
// 不进行任何网络传输。
app.use('/',proxy('127.0.0.1:3000'))
app.use('/', proxy('127.0.0.1:7000'))
app.listen(9000, () => {
console.log(9000)
})
所有请求过来时都向我们定义的那个服务器走:
发一个请求都有:请求头,请求体,请求地址路径,请求方法(get
/post
);
拿到请求。
server9000
>index.js
:
const express = require('express')
const app = express();
const proxy = require('express-http-proxy')
const url = require('url')
// const axios = require('axios')
let num = 0; // 记录请求的次数
app.use(express.json()); // 处理的是post请求的json格式数据
app.use(express.urlencoded()); // 处理的post请求的 序列化格式数据 key=value&key=value
app.use((req, res) => {
num += 1;
// 得到用户请求的请求路径,请求方法,请求头,请求体
const {
pathname } = url.parse(req.url);//请求路径
const method = req.method;//请求方法
const headers = req.headers;//请求头
const body = req.body;//请求体 body是针对post请求说的
const query = req.query;//拿到地址栏 get请求参数是在地址栏传的
// 去看看是否能拿到请求的东西
console.log(pathname, method, headers, body, query)
res.send('9000')
})
app.listen(9000, () => {
console.log(9000)
})
访问9000:
node index.js
去看看是否能拿到请求的东西:
拿到请求的东西后像目标服务器发起请求(用axios
):
npm i axios -S
const express = require('express')
const app = express();
const proxy = require('express-http-proxy')
const url = require('url')
const axios = require('axios')
// let num = 0; // 记录请求的次数
// let hosts = ['http://127.0.0.1:7000', 'http://127.0.0.1:3000'];
// 请求转发-目标-3000 / 7000
// proxy(host,options) 接受两个参数
// 第一个是域名就是真正要转发到那个地址
// 第二个是个配置项
// / 代表所有的请求 也可以写abc 那样就是以abc开头的
// 1、127.0.0.1是回送地址,指本地机,一般用来测试使用。
// 2、回送地址(127.x.x.x)是本机回送地址(LoopbackAddress),
// 即主机IP堆栈内部的IP地址,主要用于网络软件测试以及本地机进程间通信,
// 无论什么程序,一旦使用回送地址发送数据,协议软件立即返回,
// 不进行任何网络传输。
// express-http-proxy
// app.use('/',proxy('127.0.0.1:3000'))
// app.use('/', proxy('127.0.0.1:7000'))
app.use(express.json()); // 处理的是post请求的json格式数据
app.use(express.urlencoded()); // 处理的post请求的 序列化格式数据 key=value&key=value
app.use((req, res) => {
// num += 1;
// 得到用户请求的请求路径,请求方法,请求头,请求体
const {
pathname } = url.parse(req.url);//请求路径
const method = req.method;//请求方法
const headers = req.headers;//请求头
const body = req.body;//请求体 body是针对post请求说的
const query = req.query;//拿到地址栏 get请求参数是在地址栏传的
// 去看看是否能拿到请求的东西
// console.log(pathname, method, headers, body, query)
axios({
url: `http://localhost:3000${
pathname}`,//需要拼个域名就是绝对路径
method,
headers,
params: query,
data: body,
}).then(data => {
console.log(data);//data就是通过向目标3000请求到的东西
})
res.send('9000')
// 拿到请求的东西后像目标服务器发起请求(用axios):
})
app.listen(9000, () => {
console.log(9000)
})
看:data
这字段是需要的数据
在服务器拿到东西后把拿到的东西给它响应到浏览器端(data就是响应的内容,还需要个响应头响应状态):
响应头,发送给浏览器
9000服务器从3000把东西拿过来之后,获取响应的内容,响应体,响应头,状态码,状态文本;
status 状态码
statusText 状态文本
server9000
>index.js
:
const express = require('express')
const app = express();
const proxy = require('express-http-proxy')
const url = require('url')
const axios = require('axios');
let num = 0; // 记录请求的次数 每次请求都有个用户 建议不要设成固定number类型
// 请求转发-目标-3000 / 7000
// proxy(host,options) 接受两个参数
// 第一个是域名就是真正要转发到那个地址
// 第二个是个配置项
// / 代表所有的请求 也可以写abc 那样就是以abc开头的
// 1、127.0.0.1是回送地址,指本地机,一般用来测试使用。
// 2、回送地址(127.x.x.x)是本机回送地址(LoopbackAddress),
// 即主机IP堆栈内部的IP地址,主要用于网络软件测试以及本地机进程间通信,
// 无论什么程序,一旦使用回送地址发送数据,协议软件立即返回,
// 不进行任何网络传输。
// express-http-proxy
// app.use('/',proxy('127.0.0.1:3000'))
// app.use('/', proxy('127.0.0.1:7000'))
app.use(express.json()); // 处理的是post请求的json格式数据
app.use(express.urlencoded()); // 处理的post请求的 序列化格式数据 key=value&key=value
app.use(async (req, res) => {
// 每次代理请求都要走这个地方 设置num
num += 1;
//console.log('123',url);
// 得到用户请求的请求路径,请求方法,请求头,请求体
const {
pathname } = url.parse(req.url);//请求路径
console.log(pathname)
const method = req.method;//请求方法
const headers1 = req.headers;//请求头
const body = req.body;//请求体 body是针对post请求说的
const query = req.query;//拿到地址栏 get请求参数是在地址栏传的
// 去看看是否能拿到请求的东西
// console.log(pathname, method, headers, body, query)
if(pathname .includes( '/favicon.ico')){
return
}
let host = '';
if (num % 2 === 0) {
host = 'http://127.0.0.1:7000'
} else {
host = 'http://127.0.0.1:3000'
}
// 拿到请求的东西后像目标服务器发起请求(用axios):
let response = await axios({
url: `${
host}${
pathname}`,//需要拼个域名就是绝对路径
method,
headers:headers1,
params: query,
data: body,
})
//console.log('59',response);//data就是通过向目标3000请求到的东西
// 9000服务器从3000把东西拿过来之后,
// 获取响应的内容,响应体,响应头,状态码,状态文本;
// status 状态码
// statusText 状态文本
const {
status, statusText, headers, data} = response;
console.log(status, statusText, headers, data);
res.set(headers)//设置响应头
res.send(data);//发送文本
})
app.listen(9000, () => {
console.log(9000)
})
浏览器运行,在访问代理服务器9000时,成功发送请求访问3000/7000,负载均衡:
总结
举个栗子:
有两个人分别是 甲和乙,甲身材矮小,乙身高马大,乙做的很多事情让甲不痛快,但是碍于打不过乙,只能忍耐。有一天乙做了一件事情实在惹怒了甲,于是,甲花钱请了一个打手,打了乙一顿。在这个过程中真正要打乙的是甲。但是乙不知道。打手在这个过程中充当了一个很重要的角色就是我们所说的代理,也可以说是正向代理。
正向代理:正向代理隐藏的是发起请求的一端,也就是客户端。
反向代理反向代理隐藏的处理请求的一端,也就是服务端。
正向代理隐藏真实的客户端,反向代理隐藏真实的服务器端。
实际上负载均衡就是反向代理服务器,但是它反向代理的目标节点有多个。
- 请求时把相关的逻辑都拿到
- 以服务端为基础想目标服务器发送请求
- 发完请求后在响应过来
真正的代理是两台物理机
负载均衡,就是负责对流量进行分担,以便减少单台设备由于流量过大而形成DDoS(DDOS全名是Distributed Denial of service (分布式拒绝服务),俗称洪水攻击。)
将客户端的请求按照一定的规则分配到一群服务器上,并将处理结果返回给相应的客户端,避免其中某台服务器过载。
使用代理服务器,可以将请求转发给内部的服务器,使用这种加速模式显然可以提升静态网页的访问速度。
拜