现在的位置: 首页 > 云计算 > 正文

负载均衡是什么?ShareWAF-Blance的特点有哪些

2020年02月17日 云计算 ⁄ 共 6562字 ⁄ 字号 评论关闭

  ShareWAF有一款开源的负载均衡,名为ShareWAF-Blance(后文也简称其为Blance),本文通过解析这款负载均衡工具,来揭开负载均衡的神秘面纱,了解它的原理、了解它的工作方式,最后奉上干货:ShareWAF-Blance的完整源码。

  ShareWAF-Blance的特点

  1、反向代理模式

  简单的来说,可以说ShareWAF负载均衡其实是一个反向代理服务器,访问数据先到达负载,负载再转发给ShareWAF(我们在应用它时,当然可以不转发给ShareWAF,而是转发数据给我们的web什么的),其工作模式很简洁。

  2、服务注册表式的动态负载

  服务注册表有点高端,可以实现动态负载,即:我们可以动态的添加、删除负载,实时调整负载数量和目标。

  3、支持有状态通信

  即:负载均衡器总是会将所有的与会话关联的请表路由到应用程序(web)的同一个实例。这种技术也称为黏性负载均衡。该技术主要处理如下图所示的问题:

  无状态通信,如果不加以解决,负载有可能会将已经认证过的会话转发给不同的应用目标,造成会话状态丢失,影响有认证流程的业务功能。

  Blance会通过会话池,将一个访问者总是定向到同一个应用程序(WEB)实例。

  随机负载

  负载均衡有多种负载方式,如轮询、权重、随机,Blance采用的是随机的方式。

  以上介绍了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

  //匹配

  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

  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

  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

  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-Blance是Node.JS开发的。需先安装Node再运行启动。

抱歉!评论已关闭.