前言

经常使用nginx来做web服务器,你对它了解有多少,这篇文章将带领你简单的触碰它。


代理服务器

代理服务器都是存在Client(客户端)与Server(服务端)之间,但是性质不同,所以为了两种。

  1. 正向代理服务器(客户端)
  2. 反向代理服务器(服务端)

正向代理服务器

写前端的小伙伴就会触碰到,后端xxx不会弄跨域只能用代理解决,哈哈哈。

直接偷图,正向代理就是直接在客户端进行代理,服务器不知道实际发送请求的客户端

手动实现代理服务器(仿nginx)_服务器

反向代理服务器

也是偷图,反向代理就是直接在服务器进行代理,客户端不知道实际提供服务的服务器

手动实现代理服务器(仿nginx)_代理服务器_02

好了,也介绍完了,我们实现的代理服务器就是反向代理。


轮询

轮询是一种CPU决策如何提供周边设备服务的方式,又称《程控输入输出》

普通轮询

将请求依次发给服务器,周而复始

加权轮询

主要解决:效率偏低,无法满足服务器配置不同的情况

例子: 现在有三台服务器,A=5、B=3、C=1,数字代表权重。将三台服务器权重值相加值为9,也就是说每9个请求会分给 5个给A、3个给B、1个给C,依次循环 AAAAABBBC .....

代码实现

/***
* 加权轮询
*/
let server = []
function init() {
let A = {
ip: '211.xxx.xxx.xxx',
weight: 5
}
let B = {
ip: '192.xxx.xxx.xxx',
weight: 3
}
let C = {
ip: '166.xxx.xxx.xxx',
weight: 1
}
server.push(A)
server.push(B)
server.push(C)
// console.log(server)
}
function getServer(requestId) {
// 计算总权重
total = 0
for(let i = 0; i < server.length; i++) {
let itemI = server[i]
total += itemI.weight
}
// console.log(`总权重是 ${total}`)

// 第几次请求 % 权重总数 + 1 = (2 % 9) + 1 = 2 + 1 = 3
// +1是因为怕余数等于0
let s = requestId % total + 1
// console.log(s)

// 计算返回结果
for(let n = 0; n < server.length; n++) {
let itemN = server[n]
if (s <= itemN.weight) {
return itemN
}
s = s - itemN.weight
}
return null
}
function run() {
// 模拟请求
for(let i=1; i <= 9; i++) {
let item = getServer(i)
console.log(`当前访问的服务器ip是 ${item.ip}`)
}
}
init()
run()

/**
当前访问的服务器ip是 211.xxx.xxx.xxx
当前访问的服务器ip是 211.xxx.xxx.xxx
当前访问的服务器ip是 211.xxx.xxx.xxx
当前访问的服务器ip是 211.xxx.xxx.xxx
当前访问的服务器ip是 192.xxx.xxx.xxx
当前访问的服务器ip是 192.xxx.xxx.xxx
当前访问的服务器ip是 192.xxx.xxx.xxx
当前访问的服务器ip是 166.xxx.xxx.xxx
当前访问的服务器ip是 211.xxx.xxx.xxx
*
*/

那这样是不是也会有问题呢,这样是不是会导致权重高的服务器会一直被请求呢,一系列问题,所以就出现了 平滑加权轮询

平滑加权轮询

主要解决加权轮询的问题(短时间内权重大的服务器得到过多的请求数,不是一种均匀的分配方式)

例子:现在有三台服务器,A=5、B=3、C=1,数字代表权重。将三台服务器权重值相加值为9,也就是说每9个请求会分给 5个给A、3个给B、1个给C,依次循环 ABCABABAA....

代码实现

/**
* 平滑加权轮询
*/
let server = []
function init() {
let A = {
ip: '211.xxx.xxx.xxx',
weight: 5,
dynamicWeight: 0
}
let B = {
ip: '192.xxx.xxx.xxx',
weight: 3,
dynamicWeight: 0
}
let C = {
ip: '166.xxx.xxx.xxx',
weight: 1,
dynamicWeight: 0
}
server.push(A)
server.push(B)
server.push(C)
// console.log(server)
}
function getServer() {
// 计算总权重
total = 0
for(let i = 0; i < server.length; i++) {
let itemI = server[i]
total += itemI.weight
}
// console.log(`总权重是 ${total}`)

// 给每台服务器加上动态权重 = 动态权重 + 权重
for(let a = 0; a < server.length; a++) {
let itemA = server[a]
itemA.dynamicWeight = itemA.dynamicWeight + itemA.weight
}

// 选出动态权重最大的服务器
let maxServerItem = server[0]
for(let b = 0; b < server.length; b++) {
let itemB = server[b]
if (maxServerItem.dynamicWeight < itemB.dynamicWeight) {
maxServerItem = itemB
}
}

// 选中的动态权重最大的服务器的动态权重 - 总权重
maxServerItem.dynamicWeight = maxServerItem.dynamicWeight - total

// console.log(server)

return maxServerItem
}
function run() {
// 模拟请求
for(let i=1; i <= 9; i++) {
let item = getServer()
console.log(`当前访问的服务器ip是 ${item.ip}`)
}
}
init()
run()

/**
当前访问的服务器ip是 211.xxx.xxx.xxx
当前访问的服务器ip是 192.xxx.xxx.xxx
当前访问的服务器ip是 211.xxx.xxx.xxx
当前访问的服务器ip是 166.xxx.xxx.xxx
当前访问的服务器ip是 211.xxx.xxx.xxx
当前访问的服务器ip是 192.xxx.xxx.xxx
当前访问的服务器ip是 211.xxx.xxx.xxx
当前访问的服务器ip是 192.xxx.xxx.xxx
当前访问的服务器ip是 211.xxx.xxx.xxx
*
*/

那我们轮询算法就完毕了,开始手动实现一个,不过实现的话 啥语言都可以实现但是性能差距肯定是不一样的,所以我们使用了go语言实现


实现功能

效果如下

手动实现代理服务器(仿nginx)_代理服务器_03

文件结构

    
└─src
│ go.mod
│ go.sum
│ main.go 代理服务器

├─config
│ env 配置目录
│ init.go 解析配置目录

├─util
│ httpChecker.go 检查当前服务器状态
│ loadBalance.go 实现轮询的几种方式

└─web
web1.go
web2.go
web3.go

 

​项目地址​


总结

我们介绍了代理服务器,也介绍了轮询并实现了几种轮询的方式,然后使用了go语言写了一个小案例。