diff --git a/.gitignore b/.gitignore index 87a711097..dc5119205 100644 --- a/.gitignore +++ b/.gitignore @@ -163,7 +163,6 @@ plugins/l2tp plugins/openlitespeed plugins/tamper_proof plugins/cryptocurrency_trade -plugins/op_load_balance plugins/gdrive plugins/mtproxy plugins/zimg diff --git a/plugins/op_load_balance/conf/load_balance.conf b/plugins/op_load_balance/conf/load_balance.conf new file mode 100644 index 000000000..0dff636c3 --- /dev/null +++ b/plugins/op_load_balance/conf/load_balance.conf @@ -0,0 +1,2 @@ +## 指定共享内存 +lua_shared_dict healthcheck 10m; \ No newline at end of file diff --git a/plugins/op_load_balance/conf/rewrite.tpl.conf b/plugins/op_load_balance/conf/rewrite.tpl.conf new file mode 100644 index 000000000..1554bf2c7 --- /dev/null +++ b/plugins/op_load_balance/conf/rewrite.tpl.conf @@ -0,0 +1,64 @@ +location / { + proxy_pass http://{$UPSTREAM_NAME}; + + client_max_body_size 10m; + client_body_buffer_size 128k; + + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $proxy_host; + proxy_connect_timeout 90; + proxy_send_timeout 90; + proxy_read_timeout 90; + proxy_buffer_size 4k; + proxy_buffers 4 32k; + proxy_busy_buffers_size 64k; + proxy_temp_file_write_size 64k; +} + + +#location /upstream_status { +# allow 127.0.0.1; +# deny all; +# access_log off; +# default_type text/plain; +# content_by_lua_block { +# local hc = require "resty.upstream.healthcheck" +# ngx.say("OpenResty Worker PID: ", ngx.worker.pid()) +# ngx.print(hc.status_page()) +# } +#} + +location /upstream_status_{$UPSTREAM_NAME} { + allow 127.0.0.1; + deny all; + access_log off; + default_type text/plain; + content_by_lua_block { + local json = require "cjson" + local ok, upstream = pcall(require, "ngx.upstream") + if not ok then + ngx.print("[]") + return + end + + local get_primary_peers = upstream.get_primary_peers + local get_backup_peers = upstream.get_backup_peers + local upstream_name = "{$UPSTREAM_NAME}" + + peers, err = get_primary_peers(upstream_name) + if not peers then + ngx.print("[]") + return + end + + peers_backup, err = get_backup_peers(upstream_name) + if peers_backup then + for k, v in pairs(peers_backup) do + table.insert(peers,v) + end + end + + ngx.print(json.encode(peers)) + } +} \ No newline at end of file diff --git a/plugins/op_load_balance/conf/upstream.tpl.conf b/plugins/op_load_balance/conf/upstream.tpl.conf new file mode 100644 index 000000000..711bbec01 --- /dev/null +++ b/plugins/op_load_balance/conf/upstream.tpl.conf @@ -0,0 +1,5 @@ +upstream {$UPSTREAM_NAME} +{ + {$NODE_ALGO} + {$NODE_SERVER_LIST} +} \ No newline at end of file diff --git a/plugins/op_load_balance/ico.png b/plugins/op_load_balance/ico.png new file mode 100644 index 000000000..57850d8d4 Binary files /dev/null and b/plugins/op_load_balance/ico.png differ diff --git a/plugins/op_load_balance/index.html b/plugins/op_load_balance/index.html new file mode 100755 index 000000000..c789b699c --- /dev/null +++ b/plugins/op_load_balance/index.html @@ -0,0 +1,21 @@ +
+
+
+

服务

+

负载均衡

+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/plugins/op_load_balance/index.py b/plugins/op_load_balance/index.py new file mode 100755 index 000000000..59a646fb2 --- /dev/null +++ b/plugins/op_load_balance/index.py @@ -0,0 +1,474 @@ +# coding:utf-8 + +import sys +import io +import os +import time +import subprocess +import json +import re + +sys.path.append(os.getcwd() + "/class/core") +import mw + + +app_debug = False +if mw.isAppleSystem(): + app_debug = True + + +def getPluginName(): + return 'op_load_balance' + + +def getPluginDir(): + return mw.getPluginDir() + '/' + getPluginName() + + +def getServerDir(): + return mw.getServerDir() + '/' + getPluginName() + + +def getArgs(): + args = sys.argv[2:] + tmp = {} + args_len = len(args) + + if args_len == 1: + t = args[0].strip('{').strip('}') + if t.strip() == '': + tmp = [] + else: + t = t.split(':') + tmp[t[0]] = t[1] + tmp[t[0]] = t[1] + elif args_len > 1: + for i in range(len(args)): + t = args[i].split(':') + tmp[t[0]] = t[1] + return tmp + + +def checkArgs(data, ck=[]): + for i in range(len(ck)): + if not ck[i] in data: + return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!')) + return (True, mw.returnJson(True, 'ok')) + + +def getConf(): + path = getServerDir() + "/cfg.json" + + if not os.path.exists(path): + mw.writeFile(path, '[]') + + c = mw.readFile(path) + return json.loads(c) + + +def writeConf(data): + path = getServerDir() + "/cfg.json" + mw.writeFile(path, json.dumps(data)) + + +def contentReplace(content): + service_path = mw.getServerDir() + content = content.replace('{$ROOT_PATH}', mw.getRootDir()) + content = content.replace('{$SERVER_PATH}', service_path) + content = content.replace('{$APP_PATH}', app_path) + return content + + +def restartWeb(): + mw.opWeb('stop') + mw.opWeb('start') + + +def loadBalanceConf(): + path = mw.getServerDir() + '/web_conf/nginx/vhost/load_balance.conf' + return path + + +def initDreplace(): + + dst_conf_tpl = getPluginDir() + '/conf/load_balance.conf' + dst_conf = loadBalanceConf() + + if not os.path.exists(dst_conf): + con = mw.readFile(dst_conf_tpl) + mw.writeFile(dst_conf, con) + + +def status(): + if not mw.getWebStatus(): + return 'stop' + + dst_conf = loadBalanceConf() + if not os.path.exists(dst_conf): + return 'stop' + + return 'start' + + +def start(): + initDreplace() + restartWeb() + return 'ok' + + +def stop(): + dst_conf = loadBalanceConf() + os.remove(dst_conf) + + deleteLoadBalanceAllCfg() + restartWeb() + return 'ok' + + +def restart(): + restartWeb() + return 'ok' + + +def reload(): + restartWeb() + return 'ok' + + +def installPreInspection(): + check_op = mw.getServerDir() + "/openresty" + if not os.path.exists(check_op): + return "请先安装OpenResty" + return 'ok' + + +def deleteLoadBalanceAllCfg(): + cfg = getConf() + upstream_dir = mw.getServerDir() + '/web_conf/nginx/upstream' + lua_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_worker_by_lua_file' + rewrite_dir = mw.getServerDir() + '/web_conf/nginx/rewrite' + vhost_dir = mw.getServerDir() + '/web_conf/nginx/vhost' + + for conf in cfg: + upstream_file = upstream_dir + '/' + conf['upstream_name'] + '.conf' + if os.path.exists(upstream_file): + os.remove(upstream_file) + + lua_file = lua_dir + '/' + conf['upstream_name'] + '.lua' + if os.path.exists(lua_file): + os.remove(lua_file) + + rewrite_file = rewrite_dir + '/' + conf['domain'] + '.conf' + mw.writeFile(rewrite_file, '') + + path = vhost_dir + '/' + conf['domain'] + '.conf' + + content = mw.readFile(path) + content = re.sub('include ' + upstream_file + ';' + "\n", '', content) + mw.writeFile(path, content) + + mw.opLuaInitWorkerFile() + + +def makeConfServerList(data): + slist = '' + for x in data: + slist += 'server ' + slist += x['ip'] + ':' + x['port'] + + if x['state'] == '0': + slist += ' down;\n\t' + continue + + if x['state'] == '2': + slist += ' backup;\n\t' + continue + + slist += ' weight=' + x['weight'] + slist += ' max_fails=' + x['max_fails'] + slist += ' fail_timeout=' + x['fail_timeout'] + "s;\n\t" + return slist + + +def makeLoadBalanceAllCfg(row): + # 生成所有配置 + cfg = getConf() + + upstream_dir = mw.getServerDir() + '/web_conf/nginx/upstream' + rewrite_dir = mw.getServerDir() + '/web_conf/nginx/rewrite' + vhost_dir = mw.getServerDir() + '/web_conf/nginx/vhost' + upstream_tpl = getPluginDir() + '/conf/upstream.tpl.conf' + rewrite_tpl = getPluginDir() + '/conf/rewrite.tpl.conf' + + if not os.path.exists(upstream_dir): + os.makedirs(upstream_dir) + + conf = cfg[row] + + # replace vhost start + vhost_file = vhost_dir + '/' + conf['domain'] + '.conf' + vcontent = mw.readFile(vhost_file) + + vhost_find_str = 'upstream/' + conf['upstream_name'] + '.conf' + vhead = 'include ' + mw.getServerDir() + '/web_conf/nginx/' + \ + vhost_find_str + ';' + + vpos = vcontent.find(vhost_find_str) + if vpos < 0: + vcontent = vhead + "\n" + vcontent + mw.writeFile(vhost_file, vcontent) + # replace vhost end + + # make upstream start + upstream_file = upstream_dir + '/' + conf['upstream_name'] + '.conf' + content = '' + if len(conf['node_list']) > 0: + content = mw.readFile(upstream_tpl) + slist = makeConfServerList(conf['node_list']) + content = content.replace('{$NODE_SERVER_LIST}', slist) + content = content.replace('{$UPSTREAM_NAME}', conf['upstream_name']) + if conf['node_algo'] != 'polling': + content = content.replace('{$NODE_ALGO}', conf['node_algo'] + ';') + else: + content = content.replace('{$NODE_ALGO}', '') + mw.writeFile(upstream_file, content) + # make upstream end + + # make rewrite start + rewrite_file = rewrite_dir + '/' + conf['domain'] + '.conf' + rcontent = '' + if len(conf['node_list']) > 0: + rcontent = mw.readFile(rewrite_tpl) + rcontent = rcontent.replace('{$UPSTREAM_NAME}', conf['upstream_name']) + mw.writeFile(rewrite_file, rcontent) + # make rewrite end + + # health check start + lua_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_worker_by_lua_file' + lua_init_worker_file = lua_dir + '/' + conf['upstream_name'] + '.lua' + if conf['node_health_check'] == 'ok': + lua_dir_tpl = getPluginDir() + '/lua/health_check.lua.tpl' + content = mw.readFile(lua_dir_tpl) + content = content.replace('{$UPSTREAM_NAME}', conf['upstream_name']) + content = content.replace('{$DOMAIN}', conf['domain']) + mw.writeFile(lua_init_worker_file, content) + else: + if os.path.exists(lua_init_worker_file): + os.remove(lua_init_worker_file) + + mw.opLuaInitWorkerFile() + # health check end + return True + + +def add_load_balance(args): + + data = checkArgs( + args, ['domain', 'upstream_name', 'node_algo', 'node_list', 'node_health_check']) + if not data[0]: + return data[1] + + domain_json = args['domain'] + + tmp = json.loads(domain_json) + domain = tmp['domain'] + + cfg = getConf() + cfg_len = len(cfg) + tmp = {} + tmp['domain'] = domain + tmp['data'] = args['domain'] + tmp['upstream_name'] = args['upstream_name'] + tmp['node_algo'] = args['node_algo'] + tmp['node_list'] = args['node_list'] + tmp['node_health_check'] = args['node_health_check'] + cfg.append(tmp) + writeConf(cfg) + + import site_api + sobj = site_api.site_api() + domain_path = mw.getWwwDir() + '/' + domain + + ps = '负载均衡[' + domain + ']' + data = sobj.add(domain_json, '80', ps, domain_path, '00') + + makeLoadBalanceAllCfg(cfg_len) + mw.restartWeb() + return mw.returnJson(True, '添加成功', data) + + +def edit_load_balance(args): + data = checkArgs( + args, ['row', 'node_algo', 'node_list', 'node_health_check']) + if not data[0]: + return data[1] + + row = int(args['row']) + + cfg = getConf() + tmp = cfg[row] + tmp['node_algo'] = args['node_algo'] + tmp['node_list'] = args['node_list'] + tmp['node_health_check'] = args['node_health_check'] + cfg[row] = tmp + writeConf(cfg) + + makeLoadBalanceAllCfg(row) + mw.restartWeb() + return mw.returnJson(True, '修改成功', data) + + +def loadBalanceList(): + cfg = getConf() + return mw.returnJson(True, 'ok', cfg) + + +def loadBalanceDelete(): + args = getArgs() + data = checkArgs(args, ['row']) + if not data[0]: + return data[1] + + row = int(args['row']) + + cfg = getConf() + data = cfg[row] + + import site_api + sobj = site_api.site_api() + + sid = mw.M('sites').where('name=?', (data['domain'],)).getField('id') + + if type(sid) == list: + del(cfg[row]) + writeConf(cfg) + return mw.returnJson(False, '已经删除了!') + + status = sobj.delete(sid, data['domain'], 1) + status_data = json.loads(status) + + if status_data['status']: + del(cfg[row]) + writeConf(cfg) + + upstream_dir = mw.getServerDir() + '/web_conf/nginx/upstream' + rewrite_dir = mw.getServerDir() + '/web_conf/nginx/rewrite' + + upstream_file = upstream_dir + '/' + data['upstream_name'] + '.conf' + if os.path.exists(upstream_file): + os.remove(upstream_file) + + rewrite_file = rewrite_dir + '/' + data['domain'] + '.conf' + if os.path.exists(rewrite_file): + mw.writeFile(rewrite_file, '') + + return mw.returnJson(status_data['status'], status_data['msg']) + + +def http_get(url): + ret = re.search(r'https://', url) + if ret: + try: + from gevent import monkey + monkey.patch_ssl() + import requests + ret = requests.get(url=str(url), verify=False, timeout=10) + status = [200, 301, 302, 404, 403] + if ret.status_code in status: + return True + else: + return False + except: + return False + else: + try: + if sys.version_info[0] == 2: + import urllib2 + rec = urllib2.urlopen(url, timeout=3) + else: + import urllib.request + rec = urllib.request.urlopen(url, timeout=3) + status = [200, 301, 302, 404, 403] + if rec.getcode() in status: + return True + return False + except: + return False + + +def checkUrl(): + args = getArgs() + data = checkArgs(args, ['ip', 'port', 'path']) + if not data[0]: + return data[1] + + ip = args['ip'] + port = args['port'] + path = args['path'] + + if port == '443': + url = 'https://' + str(ip) + ':' + str(port) + str(path.strip()) + else: + url = 'http://' + str(ip) + ':' + str(port) + str(path.strip()) + ret = http_get(url) + if not ret: + return mw.returnJson(False, '访问节点[%s]失败' % url) + return mw.returnJson(True, '访问节点[%s]成功' % url) + + +def getHealthStatus(): + args = getArgs() + data = checkArgs(args, ['row']) + if not data[0]: + return data[1] + + row = int(args['row']) + + cfg = getConf() + data = cfg[row] + + url = 'http://' + data['domain'] + \ + '/upstream_status_' + data['upstream_name'] + + url_data = mw.httpGet(url) + return mw.returnJson(True, 'ok', json.loads(url_data)) + + +def getLogs(): + args = getArgs() + data = checkArgs(args, ['domain']) + if not data[0]: + return data[1] + + domain = args['domain'] + logs = mw.getLogsDir() + '/' + domain + '.log' + return logs + +if __name__ == "__main__": + func = sys.argv[1] + if func == 'status': + print(status()) + elif func == 'start': + print(start()) + elif func == 'stop': + print(stop()) + elif func == 'restart': + print(restart()) + elif func == 'reload': + print(reload()) + elif func == 'install_pre_inspection': + print(installPreInspection()) + elif func == 'add_load_balance': + print(addLoadBalance()) + elif func == 'load_balance_list': + print(loadBalanceList()) + elif func == 'load_balance_delete': + print(loadBalanceDelete()) + elif func == 'check_url': + print(checkUrl()) + elif func == 'get_logs': + print(getLogs()) + elif func == 'get_health_status': + print(getHealthStatus()) + else: + print('error') diff --git a/plugins/op_load_balance/info.json b/plugins/op_load_balance/info.json new file mode 100755 index 000000000..185572878 --- /dev/null +++ b/plugins/op_load_balance/info.json @@ -0,0 +1,16 @@ +{ + "title":"OP负载均衡", + "tip":"soft", + "name":"op_load_balance", + "type":"其他插件", + "install_pre_inspection":true, + "ps":"基于OpenResty的负载均衡", + "shell":"install.sh", + "checks":"server/op_load_balance", + "path":"server/op_load_balance", + "author":"midoks", + "home":"https://github.com/midoks", + "date":"2023-02-02", + "pid": "1", + "versions": ["1.0"] +} \ No newline at end of file diff --git a/plugins/op_load_balance/install.sh b/plugins/op_load_balance/install.sh new file mode 100755 index 000000000..d78bf73fb --- /dev/null +++ b/plugins/op_load_balance/install.sh @@ -0,0 +1,45 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + +install_tmp=${rootPath}/tmp/mw_install.pl + +action=$1 +version=$2 +sys_os=`uname` + +if [ -f ${rootPath}/bin/activate ];then + source ${rootPath}/bin/activate +fi + +if [ "$sys_os" == "Darwin" ];then + BAK='_bak' +else + BAK='' +fi + +Install_App(){ + echo '正在安装脚本文件...' > $install_tmp + mkdir -p $serverPath/op_load_balance + echo "${version}" > $serverPath/op_load_balance/version.pl + cd ${rootPath} && python3 ${rootPath}/plugins/op_load_balance/index.py start + echo 'install ok' > $install_tmp +} + +Uninstall_App(){ + cd ${rootPath} && python3 ${rootPath}/plugins/op_load_balance/index.py stop + rm -rf $serverPath/op_load_balance +} + + +action=$1 +if [ "${1}" == 'install' ];then + Install_App +else + Uninstall_App +fi diff --git a/plugins/op_load_balance/js/app.js b/plugins/op_load_balance/js/app.js new file mode 100644 index 000000000..3ffca2292 --- /dev/null +++ b/plugins/op_load_balance/js/app.js @@ -0,0 +1,606 @@ +function ooPost(method,args,callback){ + var _args = null; + if (typeof(args) == 'string'){ + _args = JSON.stringify(toArrayObject(args)); + } else { + _args = JSON.stringify(args); + } + + var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); + $.post('/plugins/run', {name:'op_load_balance', func:method, args:_args}, function(data) { + layer.close(loadT); + if (!data.status){ + layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']}); + return; + } + + if(typeof(callback) == 'function'){ + callback(data); + } + },'json'); +} + +function ooAsyncPost(method,args){ + var _args = null; + if (typeof(args) == 'string'){ + _args = JSON.stringify(toArrayObject(args)); + } else { + _args = JSON.stringify(args); + } + return syncPost('/plugins/run', {name:'op_load_balance', func:method, args:_args}); +} + +function ooPostCallbak(method, args, callback){ + var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); + + var req_data = {}; + req_data['name'] = 'op_load_balance'; + req_data['func'] = method; + args['version'] = '1.0'; + + if (typeof(args) == 'string'){ + req_data['args'] = JSON.stringify(toArrayObject(args)); + } else { + req_data['args'] = JSON.stringify(args); + } + + $.post('/plugins/callback', req_data, function(data) { + layer.close(loadT); + if (!data.status){ + layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']}); + return; + } + + if(typeof(callback) == 'function'){ + callback(data); + } + },'json'); +} + +function addNode(){ + layer.open({ + type: 1, + area: ['450px','580px'], + title: '添加节点', + closeBtn: 1, + shift: 5, + shadeClose: true, + btn:['提交','关闭'], + content: "
\ +
\ + IP地址\ +
\ + \ +
\ +
\ +
\ + 端口\ +
\ + \ +
\ +
\ +
\ + 验证文件路径\ +
\ + \ +
\ +
\ +
\ + 节点状态\ +
\ + \ +
\ +
\ +
\ + 权重\ +
\ + \ +
\ +
\ +
\ + 阈值\ +
\ + 次\ +
\ +
\ +
\ + 恢复时间\ +
\ + 秒\ +
\ +
\ + \ +
", + success:function(){ + }, + yes:function(index) { + + var ip = $('input[name="ip"]').val(); + var port = $('input[name="port"]').val(); + var path = $('input[name="path"]').val(); + var state = $('select[name="state"]').val(); + var weight = $('input[name="weight"]').val(); + var max_fails = $('input[name="max_fails"]').val(); + var fail_timeout = $('input[name="fail_timeout"]').val(); + + ooPost('check_url', {ip:ip,port:port,path:path},function(rdata){ + var rdata = $.parseJSON(rdata.data); + showMsg(rdata.msg, function(){ + if (rdata.status){ + layer.close(index); + $('#nodecon .nulltr').hide(); + + var tbody = ''; + tbody +=''+ip+''; + tbody +=''+port+''; + tbody +=''+path+''; + + tbody +=""; + + tbody +=''; + tbody +=''; + tbody +=''; + tbody +='删除'; + tbody += ''; + $('#nodecon').append(tbody); + + $('#nodecon .delete').click(function(){ + $(this).parent().parent().remove(); + if ($('#nodecon tr').length == 1 ){ + $('#nodecon .nulltr').show(); + } + }); + } + },{ icon: rdata.status ? 1 : 2 }, 2000); + }); + } + }); +} + +function addBalance(){ + layer.open({ + type: 1, + area: ['750px','460px'], + title: '创建负载', + closeBtn: 1, + shift: 5, + shadeClose: true, + btn:['提交','关闭'], + content: "
\ +
\ + 域名\ +
\ +
\ +
\ + 负载名称\ +
\ + \ +
\ +
\ +
\ + 节点调度\ +
\ + \ +
\ +
\ +
\ + 节点健康检查\ +
\ + \ +
\ +
\ +
\ + 节点\ +
\ +
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
IP地址端口验证路径状态权重阀值恢复时间操作
当前节点为空,请至少添加一个普通节点
\ +
\ + 添加节点\ +
\ +
\ +
", + success:function(){ + $('textarea[name="load_domain"]').attr('placeholder','每行填写一个域名,默认为80端口。\n泛解析添加方法 *.domain.com\n如另加端口格式为 www.domain.com:88'); + var rval = getRandomString(6); + $('input[name="upstream_name"]').val('load_balance_'+rval); + + $('.add_node').click(function(){ + addNode(); + }); + }, + yes:function(index) { + var data = {}; + + var upstream_name = $('input[name="upstream_name"]').val(); + if (upstream_name == ''){ + layer.msg('负载名称不能为空!',{icon:0,time:2000,shade: [0.3, '#000']}); + return; + } + + var domain = $('textarea[name="load_domain"]').val().replace('http://','').replace('https://','').split("\n"); + if (domain[0] == ''){ + layer.msg('域名不能为空!',{icon:0,time:2000,shade: [0.3, '#000']}); + return; + } + + var domainlist = ''; + for(var i=1; i\ +
\ + 负载名称\ +
\ +
\ +
\ + 节点调度\ +
\ + \ +
\ +
\ +
\ + 节点健康检查\ +
\ + \ +
\ +
\ +
\ + 节点\ +
\ +
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
IP地址端口验证路径状态权重阀值恢复时间操作
当前节点为空,请至少添加一个普通节点
\ +
\ + 添加节点\ +
\ +
\ + ", + success:function(){ + $('input[name="upstream_name"]').val(data['upstream_name']); + $('select[name="node_algo"]').val(data['node_algo']); + + $('input[name="node_health_check"]').prop('checked',false); + if (data['node_health_check'] == 'ok'){ + $('input[name="node_health_check"]').prop('checked',true); + } + + var node_list = data['node_list']; + if (node_list.length>0){ + $('#nodecon .nulltr').hide(); + } + + var state_option_list = { + '1':'参与者', + '2':'备份', + '0':'停用', + } + + for (var n in node_list) { + + var tbody = ''; + tbody +=''+node_list[n]['ip']+''; + tbody +=''+node_list[n]['port']+''; + tbody +=''+node_list[n]['path']+''; + + tbody +=""; + + tbody +=''; + tbody +=''; + tbody +=''; + tbody +='删除'; + tbody += ''; + $('#nodecon').append(tbody); + } + + $('#nodecon .delete').click(function(){ + $(this).parent().parent().remove(); + if ($('#nodecon tr').length == 1 ){ + $('#nodecon .nulltr').show(); + } + }); + + $('.add_node').click(function(){ + addNode(); + }); + }, + yes:function(index) { + var data = {}; + + data['node_algo'] = $('select[name="node_algo"]').val(); + data['node_health_check'] = 'fail'; + if ($('input[name="node_health_check"]').prop('checked')){ + data['node_health_check'] = 'ok'; + } + + var node_list = []; + $('#nodecon tr').each(function(){ + + var ip = $(this).find('td').eq(0).text(); + var port = $(this).find('td').eq(1).text(); + + if (port == ''){return;} + + var path = $(this).find('td').eq(2).text(); + var state = $(this).find('select[name="state"]').val(); + var weight = $(this).find('input[name="weight"]').val(); + var max_fails = $(this).find('input[name="max_fails"]').val(); + var fail_timeout = $(this).find('input[name="fail_timeout"]').val(); + + var tmp = { + ip:ip, + port:port, + path:path, + state:state, + weight:weight, + max_fails:max_fails, + fail_timeout:fail_timeout, + } + node_list.push(tmp); + }); + data['node_list'] = node_list; + data['row'] = row; + ooPostCallbak('edit_load_balance', data, function(rdata){ + var rdata = $.parseJSON(rdata.data); + showMsg(rdata.msg, function(){ + layer.close(index); + loadBalanceListRender(); + },{ icon: rdata.status ? 1 : 2 }, 2000); + }); + } + }); +} + +function loadBalanceListRender(){ + ooPost('load_balance_list', {}, function(rdata){ + var rdata = $.parseJSON(rdata.data); + var alist = rdata.data; + + var tbody = ''; + for (var i = 0; i < alist.length; i++) { + tbody += ''; + tbody += ''+alist[i]['domain']+''; + tbody += ''+alist[i]['upstream_name']+''; + tbody += ''+alist[i]['node_list'].length+''; + tbody += '查看'; + tbody += '查看'; + tbody += '修改 | 删除'; + tbody += ''; + } + + $('#nodeTable').html(tbody); + $('.nodeTablePage .Pcount').text('共'+alist.length+'条'); + $('#nodeTable .edit').click(function(){ + var row = $(this).data('row'); + editBalance(alist[row],row); + }); + + $('#nodeTable .log_look').click(function(){ + var row = $(this).data('row'); + var args = {'domain':alist[row]['domain']}; + pluginRollingLogs('op_load_balance','','get_logs',JSON.stringify(args),20); + }); + + $('#nodeTable .health_status').click(function(){ + var row = $(this).data('row'); + ooPost('get_health_status', {row:row}, function(rdata){ + var rdata = $.parseJSON(rdata.data); + + var tval = ''; + for (var i = 0; i < rdata.data.length; i++) { + tval += ''; + tval += ''+rdata.data[i]['name']+''; + + if (typeof(rdata.data[i]['down']) != 'undefined' && rdata.data[i]['down']){ + tval += '不正常'; + } else{ + tval += '正常'; + } + tval += ''; + } + + var tbody = "
\ +
\ +
\ + \ + \ + \ + \ + \ + \ + \ + "+tval+"\ +
地址状态
\ +
\ +
\ +
"; + + layer.open({ + type: 1, + area: ['500px','300px'], + title: '节点状态', + closeBtn: 1, + shift: 5, + shadeClose: true, + btn:['提交','关闭'], + content:tbody, + }); + }); + }); + + $('#nodeTable .delete').click(function(){ + var row = $(this).data('row'); + ooPost('load_balance_delete', {row:row}, function(rdata){ + var rdata = $.parseJSON(rdata.data); + showMsg(rdata.msg, function(){ + loadBalanceListRender(); + },{ icon: rdata.status ? 1 : 2 }, 2000); + }); + }); + + }); +} + +function loadBalanceList() { + var body = '
\ +
\ + \ +
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
网站负载名称节点日志状态操作
\ +
\ +
共0条
\ +
\ +
\ +
\ +
\ + '; + $(".soft-man-con").html(body); + loadBalanceListRender(); +} diff --git a/plugins/op_load_balance/lua/health_check.lua.tpl b/plugins/op_load_balance/lua/health_check.lua.tpl new file mode 100644 index 000000000..94411c942 --- /dev/null +++ b/plugins/op_load_balance/lua/health_check.lua.tpl @@ -0,0 +1,18 @@ +local hc = require "resty.upstream.healthcheck" +local ok, err = hc.spawn_checker { + shm = "healthcheck", + type = "http", + upstream = "{$UPSTREAM_NAME}", + http_req = "GET / HTTP/1.0\r\nHost: {$UPSTREAM_NAME}\r\n\r\n", + interval = 2000, + timeout = 6000, + fall = 3, + rise = 2, + valid_statuses = {200, 302}, + concurrency = 20, +} + +if not ok then + ngx.log(ngx.ERR, "=======> load balance health checker error: ", err) + return +end \ No newline at end of file