内容简介:ShareWAF有一款开源的负载均衡,名为ShareWAF-Blance(后文也简称其为Blance),本文通过解析这款负载均衡工具,来揭开负载均衡的神秘面纱,了解它的原理、了解它的工作方式,最后奉上干货:ShareWAF-Blance的完整源码。简单的来说,可以说ShareWAF负载均衡其实是一个反向代理服务器,访问数据先到达负载,负载再转发给ShareWAF(我们在应用它时,当然可以不转发给ShareWAF,而是转发数据给我们的web什么的),其工作模式很简洁,如下图:
ShareWAF有一款开源的负载均衡,名为ShareWAF-Blance(后文也简称其为Blance),本文通过解析这款负载均衡工具,来揭开负载均衡的神秘面纱,了解它的原理、了解它的工作方式,最后奉上干货:ShareWAF-Blance的完整源码。
ShareWAF-Blance的特点
1、反向代理模式
简单的来说,可以说ShareWAF负载均衡其实是一个反向代理服务器,访问数据先到达负载,负载再转发给ShareWAF(我们在应用它时,当然可以不转发给ShareWAF,而是转发数据给我们的web什么的),其工作模式很简洁,如下图:
2、服务注册表式的动态负载
服务注册表有点高端,可以实现动态负载,即:我们可以动态的添加、删除负载,实时调整负载数量和目标,该技术的示意图如下:
3、支持有状态通信
即:负载均衡器总是会将所有的与会话关联的请表路由到应用程序(web)的同一个实例。这种技术也称为黏性负载均衡。该技术主要处理如下图所示的问题:
上图是无状态通信,如果不加以解决,负载有可能会将已经认证过的会话转发给不同的应用目标,造成会话状态丢失,影响有认证流程的业务功能。
Blance会通过会话池,将一个访问者总是定向到同一个应用程序(WEB)实例。
随机负载
负载均衡有多种负载方式,如轮询、权重、随机,Blance采用的是随机的方式。
以上介绍了Blance的关键特征,下面是源码:
上图,是ShareWAF-Blance的项目文件,
blance.JS是核心文件; Config.JS是配置文件; Blance.Html是动态添加、删除、负载目标的操作页面; Log.TXT是日志文件,先透露个彩蛋,源码中日志的记录顾颇有技巧,使用的是API HOOK技术。
Blance源码:
//*****************************************/ // Blance // ShareWAF.com 负载均衡模块 // Auther: WangLiwen //*****************************************/ /** * 使用方法: * 打开Config.JS,进行配置 * port为负载端口,接受Web访问 * admin_port为负载管理端口,用于管理负载,添加、删除、查看负载 * password为管理密码,进行管理操作时,要校验此密码 * blance_pool为负载池,即多个负载目标,可以为ip或域名 * (需最少添加一个负载目标,方可正常工作,但要达到负载效果,则至少需添加两个) * (可以在这里直接配置好,也可以启动后通过管理端口号访问进行动态添加、删除) * Ready,可以开始使用! * * 说明:同一访问者,会访问到同一负载目标,即:可负载有状态通信 */ //三方模块 var express = require("express")(); var http_proxy = require("http-proxy"); var body_parser = require("body-parser"); var admin_express = require("express")(); var fs = require("fs"); //调试信息 var debug = require("./config.js").debug; //日志 var log = require("./config.js").log; //端口 var port = require("./config.js").port; //管理密码 var password = require("./config.js").admin_password; //管理端口 var admin_port = require("./config.js").admin_port; //调试开关 var debug = true; //代理 var proxy = http_proxy.createProxyServer({}); //存放目标 var pool = require("./config.js").blance_pool; //特征池,实现同一人访问同一目标 var signatures = []; //监听 express.listen(port); admin_express.listen(admin_port); console.info("ShareWAF-Blance v1.0.2"); console.info("Blance server at port:",port); console.info("Blance admin server at port:",admin_port); console.info("Copyright (c) 2020 ShareWAF.com"); //管理后台 admin_express.get("/",function(req,res){ fs.readFile("./blance.html",function(err,std_out,std_err){ res.writeHead(200,{'Content-type':"text/html"}); if(!err){ res.end(std_out); }else{ res.end("Error while read blance.html"); } }) }); proxy.on("error",function(err,req,res){ try{ res.end("error"); }catch(e){ console.log(e.message); } }); //body-parser express.use(body_parser.urlencoded({extended: true})); //注册 express.post("/register_blance",function(req,res,next){ //密码,用于校验 if(req.body.password == password){ //添加到负载均衡池 pool.push(req.body.target); console.log("add blance:" + req.body.target); res.end("blance added!"); }else{ console.log("register blance error: password error!"); res.end("error!"); } return; }); //获取列表 express.post("/get_blance_list",function(req,res,next){ //密码,用于校验 if(req.body.password == password){ console.log("get_blance_list" + pool.toString()); res.end(pool.toString()); }else{ console.log("register blance error: password error!"); res.end("error!"); } return; }); //反注册 express.post("/unregister_blance",function(req,res,next){ //密码,用于校验 if(req.body.password == password){ var remove_flag = 0; //遍历 for(i=0; i<pool.length; i++){ //匹配 if(pool[i] == req.body.target){ //删除 delete pool[i]; pool.splice(i,1); console.log("remove blance:" + req.body.target); res.end("blance removed!"); remove_flag = 1; } } if(remove_flag == 0){ res.end("unregister blance error:blance not exist!"); console.log("error,blance not exist") } }else{ console.log("unregister blance error: password error!"); res.end("error!") } return; }); //随机访问负载 express.use(function(req,res,next){ if(pool.length == 0){ console.log("error: blance pool is null.") res.end("Error:No blance! Config first,Please!"); return; } //随机数 var rnd = random_number(0,pool.length - 1); //访问者特征:IP+AGENT var req_signature = get_req_ip(req) + req.headers["user-agent"]; //从特征库中获取负载目标 for(i=0; i<signatures.length; i++){ if(signatures[i].signature == req_signature){ rnd = signatures[i].index; console.log("get blance from signature pool:" + i + "."); signatures[i].time = (new Date).getTime(); } } //访问 proxy.web(req, res, {target: pool[rnd], selfHandleResponse : false, changeOrigin:true} ); console.log("blance visit: " + rnd + " " + pool[rnd] + ",url:" + req.url); //遍历,检查特存是否已存入特征池 for(i=0; i<signatures.length; i++){ if(signatures[i].signature == req_signature){ return; } } //保存到特征池 signatures.push({signature:req_signature, index:rnd, time:(new Date).getTime()}); }) //10秒检查一次,将特征池中超时的特征移除 setInterval(function(){ //遍历特征池 for(i=0; i<signatures.length; i++){ if(signatures[i].time * 1 + 1000 * 60 * 10 <= (new Date).getTime()){ console.log("remove signature:" + signatures[i]); delete signatures[i]; signatures.splice(i,1); } } },1000 * 10) //获取访问者ip var get_req_ip = function(req) { try{ var ip = req.headers["x-forwarded-for"] || req.ip || req.connection.remoteAddress || req.socket.remoteAddress || req.connection.socket.remoteAddress || ""; if(ip.split(",").length > 0){ ip = ip.split(",")[0]; } return ip.replace("::ffff:", ""); }catch(e){ console.log("error while get client ip." + e.message); return "127.0.0.1"; } }; //范围内随机数 function random_number(min,max){ var range = max - min; var rand = Math.random(); var num = min + Math.round(rand * range); return num; } //API hook,处理console.log var old_console_log = console.log; console.log = function(msg){ if(debug == 1){ old_console_log("\u001b[32m" + msg +"\u001b[0m"); } if(log == 1){ fs.appendFile("log.txt", new Date() + " " + msg + "\r\n",function(e){ if(e){ console.error("Error while write to log.txt:",e.message); } }); } }
代码量不大,而且注释很清晰,细细口味很快便可取得其精华。
Config.JS源码:
exports.port = 8090;
exports.admin_port = 9000;
exports.admin_password = "pass";
exports.blance_pool = [" http://www.sharewaf.com "," http://www.jshaman.com "];
exports.debug = 1;
exports.log = 1;
这是个单纯的配置文件,内容一目了然。
Blance.HTML代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>ShareWAF Blance</title>
<style>
.blance_div{
border:1px solid #cccccc;
background: #f4f5f8;
padding: 10px;
margin: 10px;
}
</style>
</head>
<body>
<h2>ShareWAF-Blance</h2>
<div class="blance_div">
添加负载:<br>
<form action=" http://localhost:8090/register_blance " method="post">
<div>密码:<input type="password" name="password"></div>
<div>目标:<input type="text" name="target"></div>
<div><input type="submit" name="" value="提交"></div>
</form>
</div>
<div class="blance_div">
删除负载:<br>
<form action=" http://localhost:8090/unregister_blance " method="post">
<div>密码:<input type="password" name="password"></div>
<div>目标:<input type="text" name="target"></div>
<div><input type="submit" name="" value="提交"></div>
</form>
</div>
<div class="blance_div">
负载列表
<form action=" http://localhost:8090/get_blance_list " method="post">
<div>密码:<input type="password" name="password"></div>
<div><input type="submit" name="" value="查询"></div>
</form>
</div>
</body>
</html>
以上便是该负载均衡的全部实现。
完整的代码也可以从ShareWAF官网下载获取。
使用:
由上述代码可知,ShareWAF-Blance是Node.JS开发的。需先安装Node再运行,
启动:
Node blance
运行效果:
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 开源软负载均衡 HAProxy 使用及配置
- 重磅!GitHub 开源负载均衡组件 GLB Director
- iQiYi 高性能开源负载均衡器及应用
- Heptio 开源 Gimbal,用于 Kubernetes 的负载均衡器
- Facebook 宣布开源 Katran,高性能第4层负载均衡器
- 开源Bloom是一个REST API缓存中间件,充当负载均衡器和REST API工作者之间的反向代理。
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。