负载均衡有多神密?来研究下ShareWAF的开源负载均衡!

栏目: IT技术 · 发布时间: 5年前

内容简介: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 

运行效果:


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

How to Think About Algorithms

How to Think About Algorithms

Jeff Edmonds / Cambridge University Press / 2008-05-19 / USD 38.99

HOW TO THINK ABOUT ALGORITHMS There are many algorithm texts that provide lots of well-polished code and proofs of correctness. Instead, this one presents insights, notations, and analogies t......一起来看看 《How to Think About Algorithms》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具