安装apisix
安装依赖
# install epel, `luarocks` need it.
wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
sudo rpm -ivh epel-release-latest-7.noarch.rpm
# install etcd
wget https://github.com/etcd-io/etcd/releases/download/v3.4.13/etcd-v3.4.13-linux-amd64.tar.gz
tar -xvf etcd-v3.4.13-linux-amd64.tar.gz && \
cd etcd-v3.4.13-linux-amd64 && \
sudo cp -a etcd etcdctl /usr/bin/
# add OpenResty source
sudo yum install yum-utils
sudo yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
# install OpenResty and some compilation tools
sudo yum install -y openresty curl git gcc luarocks lua-devel
# start etcd server
nohup etcd &
安装apisix
sudo yum install -y https://github.com/apache/apisix/releases/download/2.2/apisix-2.2-0.x86_64.rpm
安装好户输入 apisix 即可得到版本号
修改配置文件
安装好的apisix在user/local/apisix目录下
安装好后测试一下是否有效
curl "http://127.0.0.1:9080/apisix/admin/services/" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1'
我们修改一下配置文件,让apisix可以公网访问
修改文件格式如下
apisix:
allow_admin: # http://nginx.org/en/docs/http/ngx_http_access_module.html#allow
- 0.0.0.0/0
admin_key:
-
name: "admin"
key: edd1c9f034335f136f87ad84b625c8f2 # using fixed API token has security risk, please
# update it when you deploy to production environment
role: admin
plugins:
- echo
- xiaoyou
然后重新apisix apisix restart
我们使用apifox来访问测试,可以看到,外网可以访问了
配置插件
自己新建一个插件文件,可以建在plugins文件夹下面 我这里取名为 xiaoyou.lua
插件内容如下
local core = require("apisix.core")
local roundrobin = require("resty.roundrobin")
local limit_conn = require("resty.limit.conn")
local sleep = core.sleep -- 延迟的
local ngx = ngx
-- 全局状态
local status = {}
-- 设置权重的函数
-- 用于统计等待队列里面的请求数量
local lim, err = limit_conn.new("my_limit_conn",5, 1000, 2)
if not lim then
core.log.error(core.json.encode(err))
end
local setWeight
--设置定时任务,定时设置权重,这里设置的是定时10s
setWeight = function()
ngx.timer.at(10, setWeight)
-- 先计算每个节点的权重
CalculationWeight()
end
-- 我们创建一个回调函数
local ok, err1 = ngx.timer.at(10, setWeight)
-- 下面是计算需要提前定义的变量-------------------------------------------------------------
-- γi 是静态的所有服务器都一样
local g1 = 0.3
local g2 = 0.13
local g3 = 0.15
local g4 = 0.22
local g5 = 0.2
-- 设置节点的数量
local n_num = 4
-- r1 = 0.7 -- 使用的是单位时间内完成成功的请求平均数
-- r2 = 0.015865084322299 -- cpu使用率
-- r3 = 0.51381299570136 -- 内存使用率
-- r4 = 0.000123 -- 网络带宽使用率
-- r5 = 0.99 -- 该节点请求成功的除以所有请求
-- r6 = 10 -- 代表节点接收的所有请求
local Qmax = 100000 -- 提前定义的最大排队数量
-- 排队等待队列长度
local Q = 0
-- 是β
local B = 0
-- α是指集群节点设备每秒处理的客户请求数量,每个节点的,是静态的需要提前定义
local a = {100, 121, 141, 161}
-- Rall 每个服务器节点的资源可用性
local Rall = 0
-- ωi为每个集群节点上客户请求的到达概率
local w = {}
-- ω_t是上面的wi全部相加的总和
local w_t = 0
-- Qdi 存储计算出来的权重
local Qdi = {}
-- Qall 所有r6相加,是集群接收的请求数量
local Qall = 0
--------------------------------------------上面定义一些变量---------------------------------------------------------------------
-- 插件源数据配置
local schema = {
-- 类型
type = "object",
-- 插件接收的配置参数
properties = {},
minProperties = 1,
-- 附加属性
additionalProperties = false,
}
-- 插件名
local plugin_name = "xiaoyou"
-- 插件的配置信息
local _M = {
-- 版本
version = 0.1,
-- 优先级
priority = 999999,
-- 插件的类型
--type = 'auth',
-- 插件名字
name = plugin_name,
-- 插件的参数
schema = schema,
}
-------------------------------------上面配置插件信息----------------------------------------------------------------------
-- 服务器上报数据
local upload =function(ctx)
-- 获取系统状态
-- local ip = ngx.ctx.api_ctx.var.host .. ":" .. tostring(ngx.ctx.api_ctx.var.server_port)
-- r1 服务率,节点单位时间内处理成功的数量
-- r2 cpu使用率
-- r3 内存使用率
-- r4 网络使用率,网络带宽占用率
-- r5 节点响应率,响应成功的请求
-- r6 改节点接收的所有请求
local info ={}
local n = tonumber(ngx.var.arg_n) -- 获取服务器的标志位,标志这个服务器的类别
core.log.error("标志位 ", n)
-- 全部都转数字
info["r1"] = tonumber(ngx.var.arg_r1)
info["r2"] = tonumber(ngx.var.arg_r2)
info["r3"] = tonumber(ngx.var.arg_r3)
info["r4"] = tonumber(ngx.var.arg_r4)
info["r5"] = tonumber(ngx.var.arg_r5)
info["r6"] = tonumber(ngx.var.arg_r6)
-- 记录当前时间戳
info["flag"] = 6
status[n] = info
ngx.say(core.json.encode(status))
end
-- 计算所有节点的权重,并且存储起来
function CalculationWeight()
-- 求每个w
for i = 1, n_num, 1 do
core.log.error("status状态--- ", i, ": " , core.json.encode(status))
Rall = math.pow(status[i]["r1"], g1) * math.pow(status[i]["r2"], g2) * math.pow(status[i]["r3"], g3) * math.pow(status[i]["r4"], g4) * math.pow(status[i]["r5"], g5)
core.log.error("Rall: ", i, "--", Rall, "--")
B = a[i] * Rall
core.log.error("B: ", i, "--", B, "--")
w[i] = ((Qmax - Q)/(Qmax - Q + 1)) * a[i] * Rall
core.log.error("WI: ", i, "--", w[i], "--")
-- 求w的总和
w_t = w_t + w[i]
Qall = Qall + status[i]["r6"]
end
core.log.error("w_t: ", "--", w_t, "--")
core.log.error("Qall: ", "--", Qall, "--")
-- 求每个Qdi
for i = 1, n_num, 1 do
-- 判断flag是否大于0
if status[i]["flag"] > 0 then
Qdi[i] = Qall * (w[i]/w_t)
else
Qdi[i] = 0
end
core.log.error("weight ", i, ": " , Qdi[i])
end
-- 重新设置权重
reloadWeight()
end
-- 返回全局变量中的数据
local get = function()
ngx.say(core.json.encode(status))
ngx.say(Q)
end
-- 重新设置权重
function reloadWeight()
-- 每个节点的flag减一
subFlag()
-- 引入resty的http请求模块
local http = require("resty.http")
-- 新建一个http请求
local httpc = http.new()
-- 负载均衡的节点列表
local hosts = {}
hosts[1] = Qdi[1]
hosts[2] = Qdi[2]
hosts[3] = Qdi[3]
hosts[4] = Qdi[4]
-- 发送一个http请求
local resp, err = httpc:request_uri("http://192.168.123.119:82/send", {
method = "POST",
headers = {
-- 设置发送的数据类型
["Content-Type"] = "application/json"
},
body = core.json.encode(hosts)
})
--如果请求为空,那么就返回错误信息
if not resp then
ngx.say("request error :", err)
return
end
--返回请求结果
ngx.say(resp.body)
--关闭http请求
httpc:close()
end
-- 每个节点的flag减一
function subFlag()
for i = 1, n_num, 1 do
status[i]["flag"] = status[i]["flag"] - 1
end
end
---- 自己注册路由一个上报数据,一个获取数据
function _M.api()
return {
{
methods = {"GET"},
uri = "/apisix/xiaoyou/upload",
handler = upload
},
{
methods = {"GET"},
uri = "/apisix/xiaoyou/get",
handler = get
}
}
end
-- 在balancer阶段来处理请求
-- 常见的阶段:init, rewrite,access,balancer,header filer,body filter 和 log 阶段
function _M.access(conf, ctx)
----当一个请求到达时会触发一个传入事件,并会根据指定的key计算当前请求应该延迟处理或者应该立即拒绝。语法如下:
local delay, err = lim:incoming("remote_addr", true)
-- 获取当前请求队列数
core.log.error("now queue: ", err, "---")
--sleep(1)
local conn= lim:leaving("remote_addr")
-- 新的并发请求数(或活跃的连接数)。与incoming不同是,该方法总是会提交到共享字典空间。
-- 记录当前需要处理的请求数量
Q = conn
core.log.error("leave queue: ", conn)
end
-- 返回插件对象
return _M
配置路由
我们需要让我们的apisix进行负载均衡,我们这里为了方便起见直接用的内网地址
配置Python服务器转发
我们需要使用Python来进行转发,同样给服务器安装Pythonyum install python3
,脚本内容如下
# coding=UTF-8
import json
import requests
from flask import Flask,request
import math
app = Flask(__name__)
@app.route('/log',methods=["POST"])
def log():
print(request.get_json())
return '45'
def get_num(num):
return math.ceil(num*1000000)
@app.route('/send',methods=["POST"])
def server1():
# 设置头部信息
heard = {
"X-API-KEY": "edd1c9f034335f136f87ad84b625c8f2",
"Content-Type": "application/json"
}
print('获取数据')
weight = request.get_json()
total = weight[0] + weight[1] + weight[2]
node1 = int(weight[0] / total * 100)
node2 = int(weight[1] / total * 100)
node3 = int(weight[2] / total * 100)
# node1 = 1
# node2 = 1
# node3 = 1
print(weight,node1,node2,node3)
# 获取request的参数
# 设置body数据
body = {
"plugins": {
"xiaoyou": {}
},
"upstream": {
"nodes": {
'172.25.12.14:8085': node1,
'172.25.12.16:8085': node2,
'172.25.12.18:8085': node3,
},
"type": "roundrobin"
},
"uri": "/*"
}
response = requests.put('http://127.0.0.1:9080/apisix/admin/routes/1', headers=heard, data=json.dumps(body))
return response.text
if __name__ == "__main__":
# 先启动4个线程
app.run(port=82, host='0.0.0.0')
使用pip安装依赖
pip3 install flask
pip3 install requests
运行项目 python3 nginx.py
这里会显示各个项目的权重
注意修改一下插件的Python项目的路径
配置虚拟内存
首先我们修改这个配置文件
这里我们给自己的插件分配内容
这里我们的limit设置
节点设置
每个节点都安装一个宝塔,使用宝塔我们导入我们的数据库
然后安装java,centos安装命令
yum install java-1.8.0-openjdk-devel.x86_64
ubuntu安装命令
apt install openjdk-8-jdk
windows安装命令: 直接到官网下载 Python Releases for Windows | Python.org
同时我们还需要安装Python环境
centos安装命令 yum install python3
ubuntu默认安装了,我们不管
然后就是打包项目,这个实验idea打包即可(linux系统需要修改一下路径)
同时还需要把net.py文件放入root目录
net.py文件内容如下
#!/usr/bin/env python
# -*- coding: utf-8 -*-
try:
import psutil
except ImportError:
print('Error: psutil module not found!')
exit()
def get_key():
key_info = psutil.net_io_counters(pernic=True).keys()
recv = {}
sent = {}
for key in key_info:
recv.setdefault(key, psutil.net_io_counters(pernic=True).get(key).bytes_recv)
sent.setdefault(key, psutil.net_io_counters(pernic=True).get(key).bytes_sent)
return key_info, recv, sent
def get_rate(func):
import time
key_info, old_recv, old_sent = func()
time.sleep(1)
key_info, now_recv, now_sent = func()
net_in = {}
net_out = {}
for key in key_info:
# float('%.2f' % a)
net_in.setdefault(key, float('%.2f' % ((now_recv.get(key) - old_recv.get(key)) / 1024)))
net_out.setdefault(key, float('%.2f' % ((now_sent.get(key) - old_sent.get(key)) / 1024)))
return key_info, net_in, net_out
if __name__ == "__main__":
try:
key_info, net_in, net_out = get_rate(get_key)
for key in key_info:
if key == 'eth0':
# 获取网络带宽使用率
print('%f,%f' % (net_out.get(key), net_out.get(key) / (100 / 8 * 1024)))
except KeyboardInterrupt:
exit()
运行Python文件需要下面这几个依赖
centos:
sudo yum install python3-devel
pip3 install psutil
ubuntu:
apt install python3-pip
sudo apt-get install python3-dev
pip3 install psutil
如果运行成功,会有下面的提示
最后我们运行一下java项目 java -jar jenkins-test.jar
注意里面的内容需要正常显示
访问网址,如果出现下面的内容说明安装成功
我们也可以测试一下数据库操作
windows 解除端口占用:
netstat -aon|findstr "8081"
taskkill /T /F /PID 9088 PID号
压力测试
这里说一下这个函数如何生成