mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 21:57:56 +08:00
1.3.0
This commit is contained in:
@@ -676,3 +676,5 @@ table th, table td {
|
||||
}
|
||||
|
||||
.x-win {display: none;padding: 20px;}
|
||||
|
||||
.layui-this{font-weight: bold;}
|
@@ -49,7 +49,7 @@
|
||||
<cite>首页</cite>
|
||||
</a>
|
||||
</li>
|
||||
<li class="open">
|
||||
<!--<li class="open">
|
||||
<a>
|
||||
<i class="layui-icon layui-icon-template-1"></i>
|
||||
<cite>配置管理</cite>
|
||||
@@ -62,7 +62,7 @@
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</li>-->
|
||||
<li class="open">
|
||||
<a>
|
||||
<i class="layui-icon layui-icon-app"></i>
|
||||
@@ -81,6 +81,12 @@
|
||||
<cite>路由管理</cite>
|
||||
</a>
|
||||
</li>
|
||||
<li date-refresh="1">
|
||||
<a href="../service/limitManager.html">
|
||||
<i class="layui-icon layui-icon-chart"></i>
|
||||
<cite>限流管理</cite>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="open">
|
||||
|
101
sop-admin/sop-admin-front/pages/service/limitManager.html
Normal file
101
sop-admin/sop-admin-front/pages/service/limitManager.html
Normal file
@@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="x-admin-sm">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>SOP Admin</title>
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport"
|
||||
content="width=device-width,user-scalable=yes, minimum-scale=0.4, initial-scale=0.8"/>
|
||||
<link rel="stylesheet" href="../../assets/css/font.css">
|
||||
<link rel="stylesheet" href="../../assets/css/xadmin.css">
|
||||
<style>
|
||||
.layui-form-label{width: 100px;}
|
||||
.layui-input-block{margin-left: 130px;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="x-nav">
|
||||
<span class="layui-breadcrumb">
|
||||
<a href="../dashboard/welcome.html">首页</a>
|
||||
<a><cite>服务管理</cite></a>
|
||||
<a><cite>限流管理</cite></a>
|
||||
</span>
|
||||
<a class="layui-btn layui-btn-small layui-btn-normal" style="line-height:1.6em;margin-top:4px;float:right"
|
||||
href="javascript:location.replace(location.href);" title="刷新">
|
||||
<i class="layui-icon layui-icon-refresh" style="line-height:30px"></i></a>
|
||||
</div>
|
||||
<div class="x-body" style="padding-top:5px;">
|
||||
<div class="layui-row">
|
||||
<div class="layui-tab" lay-filter="serviceTabFilter" style="margin-top: 0;">
|
||||
<ul id="serviceTab" class="layui-tab-title">
|
||||
</ul>
|
||||
</div>
|
||||
<div class="layui-row">
|
||||
<form class="layui-form layui-col-md12 x-so" action="" lay-filter="searchFrm">
|
||||
路由名称:
|
||||
<input name="id" class="layui-input" style="width: 200px;" placeholder="输入接口名或版本号">
|
||||
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="searchFilter">
|
||||
<i class="layui-icon layui-icon-search"></i>搜索
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<table class="layui-hide" id="limitTable" lay-filter="limitTableFilter"></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="updateWin" class="x-win">
|
||||
<form id="updateForm" class="layui-form" action="" lay-filter="updateWinFilter">
|
||||
<input type="hidden" name="serviceId" />
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">id</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="id" readonly="readonly" class="layui-input"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">限流策略</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="type" value="1" title="漏桶策略" lay-filter="limitTypeFilter">
|
||||
<input type="radio" name="type" value="2" title="令牌桶策略" lay-filter="limitTypeFilter">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item limit-type type1">
|
||||
<label class="layui-form-label">每秒可处理请求数</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="execCountPerSecond" lay-verify="required" class="layui-input"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item limit-type type1">
|
||||
<label class="layui-form-label">错误码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="limitCode" lay-verify="required" class="layui-input"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item limit-type type1">
|
||||
<label class="layui-form-label">错误信息</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="limitMsg" lay-verify="required" class="layui-input"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item limit-type type2">
|
||||
<label class="layui-form-label">令牌桶容量</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="tokenBucketCount" class="layui-input"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="updateWinSubmitFilter">保存</button>
|
||||
<button type="button" class="layui-btn layui-btn-primary" onclick="layer.closeAll()">取消</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="../../assets/js/lib.js"></script>
|
||||
<script type="text/javascript" src="limitManager.js"></script>
|
||||
</body>
|
||||
</html>
|
165
sop-admin/sop-admin-front/pages/service/limitManager.js
Normal file
165
sop-admin/sop-admin-front/pages/service/limitManager.js
Normal file
@@ -0,0 +1,165 @@
|
||||
lib.importJs('../../assets/js/routerole.js')
|
||||
.use(['element', 'table', 'tree', 'form'], function () {
|
||||
var LIMIT_STATUS = {
|
||||
'1': '<span class="x-green">已开启</span>'
|
||||
,'0': '<span class="x-red">已关闭</span>'
|
||||
}
|
||||
// 限流策略,1:漏桶策略,2:令牌桶策略
|
||||
var LIMIT_TYPE = {
|
||||
'1': '漏桶策略'
|
||||
,'2': '令牌桶策略'
|
||||
}
|
||||
var element = layui.element;
|
||||
var form = layui.form;
|
||||
var updateForm = layui.Form('updateForm');
|
||||
var addForm = layui.Form('addForm');
|
||||
var authForm = layui.Form('authForm');
|
||||
var table = layui.table;
|
||||
|
||||
var currentServiceId;
|
||||
var limitTable;
|
||||
var smTitle;
|
||||
|
||||
|
||||
form.on('submit(searchFilter)', function (data) {
|
||||
var param = data.field;
|
||||
param.serviceId = currentServiceId;
|
||||
searchTable(param)
|
||||
return false;
|
||||
});
|
||||
|
||||
// 监听修改提交
|
||||
form.on('submit(updateWinSubmitFilter)', function(data) {
|
||||
ApiUtil.post('route.update', data.field, function (resp) {
|
||||
layer.closeAll();
|
||||
limitTable.reload();
|
||||
})
|
||||
return false;
|
||||
});
|
||||
|
||||
form.on('radio(limitTypeFilter)', function(data){
|
||||
$('.limit-type').hide();
|
||||
$('.type' + data.value).show();
|
||||
});
|
||||
|
||||
element.on('tab(serviceTabFilter)', function(data){
|
||||
loadLimitTable(this.innerHTML);
|
||||
});
|
||||
|
||||
|
||||
function initServiceTab() {
|
||||
ApiUtil.post('service.list', {}, function (resp) {
|
||||
var serviceList = resp.data;
|
||||
var html = [];
|
||||
for (var i = 0; i < serviceList.length; i++) {
|
||||
var serviceInfo = serviceList[i];
|
||||
var clazz = i === 0 ? 'class="layui-this"' : '';
|
||||
html.push('<li ' + clazz + '>' + serviceInfo.serviceId + '</li>');
|
||||
}
|
||||
$('#serviceTab').html(html.join(''));
|
||||
|
||||
if (serviceList.length > 0) {
|
||||
loadLimitTable(serviceList[0].serviceId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function loadLimitTable(serviceId) {
|
||||
currentServiceId = serviceId;
|
||||
searchTable({
|
||||
serviceId: serviceId
|
||||
});
|
||||
smTitle = '[ <strong>serviceId:</strong>' + currentServiceId + ' ]';
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询表格
|
||||
* @param params
|
||||
*/
|
||||
function searchTable(params) {
|
||||
var postData = {
|
||||
data: JSON.stringify(params)
|
||||
};
|
||||
if (!limitTable) {
|
||||
limitTable = renderTable(postData);
|
||||
} else {
|
||||
limitTable.reload({
|
||||
where: postData
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function renderTable(postData) {
|
||||
var limitTable = table.render({
|
||||
elem: '#limitTable'
|
||||
, url: ApiUtil.createUrl('route.limit.list')
|
||||
, where: postData
|
||||
, headers: {access_token: ApiUtil.getAccessToken()}
|
||||
, cellMinWidth: 80 //全局定义常规单元格的最小宽度,layui 2.2.1 新增
|
||||
, cols: [[
|
||||
{field: 'id', title: 'id(接口名+版本号)', width: 200}
|
||||
, {
|
||||
field: 'type', title: '限流策略', width: 80, templet: function (row) {
|
||||
return LIMIT_TYPE[row.type + ''];
|
||||
}
|
||||
}
|
||||
, {
|
||||
field: 'info', title: '限流信息', width: 500, templet: function (row) {
|
||||
if (row.limitStatus == 0) {
|
||||
return '--'
|
||||
}
|
||||
var html = [];
|
||||
if (row.type == 1) {
|
||||
html.push('每秒可处理请求数:' + row.execCountPerSecond);
|
||||
html.push('错误码:' + row.limitCode);
|
||||
html.push('错误信息:' + row.limitMsg);
|
||||
} else if(row.type == 2) {
|
||||
html.push('令牌桶容量:' + row.tokenBucketCount);
|
||||
}
|
||||
return html.join(',');
|
||||
}
|
||||
}
|
||||
, {
|
||||
field: 'limitStatus', title: '状态', width: 80, templet: function (row) {
|
||||
return LIMIT_STATUS[row.limitStatus + ''];
|
||||
}
|
||||
}
|
||||
, {
|
||||
fixed: 'right', title: '操作', width: 100, templet: function (row) {
|
||||
var html = ['<a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="edit">修改</a>'];
|
||||
return html.join('');
|
||||
}
|
||||
}
|
||||
]]
|
||||
});
|
||||
|
||||
//监听单元格事件
|
||||
table.on('tool(limitTableFilter)', function(obj) {
|
||||
var data = obj.data;
|
||||
var event = obj.event;
|
||||
if(event === 'edit'){
|
||||
//表单初始赋值
|
||||
data.serviceId = currentServiceId;
|
||||
|
||||
updateForm.setData(data);
|
||||
$('.limit-type').hide();
|
||||
$('.type' + data.type).show();
|
||||
|
||||
layer.open({
|
||||
type: 1
|
||||
,title: '修改限流' + smTitle
|
||||
,area: ['600px', '380px']
|
||||
,content: $('#updateWin') //这里content是一个DOM,注意:最好该元素要存放在body最外层,否则可能被其它的相对元素所影响
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return limitTable;
|
||||
}
|
||||
|
||||
initServiceTab();
|
||||
|
||||
RouteRole.loadAllRole(form, 'roleArea');
|
||||
|
||||
});
|
@@ -21,37 +21,31 @@
|
||||
href="javascript:location.replace(location.href);" title="刷新">
|
||||
<i class="layui-icon layui-icon-refresh" style="line-height:30px"></i></a>
|
||||
</div>
|
||||
<div class="x-body">
|
||||
<div class="x-body" style="padding-top:5px;">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-md2">
|
||||
<ul id="leftTree"></ul>
|
||||
<div class="layui-tab" lay-filter="serviceTabFilter" style="margin-top: 0;">
|
||||
<ul id="serviceTab" class="layui-tab-title">
|
||||
</ul>
|
||||
</div>
|
||||
<div id="optTip" class="layui-col-md2" style="margin-top: 20px;">
|
||||
<i class="layui-icon layui-icon-return"></i>点击树节点查看
|
||||
<div class="layui-row">
|
||||
<form class="layui-form layui-col-md12 x-so" action="" lay-filter="searchFrm">
|
||||
路由名称:
|
||||
<input name="id" class="layui-input" style="width: 200px;" placeholder="输入接口名或版本号">
|
||||
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="searchFilter">
|
||||
<i class="layui-icon layui-icon-search"></i>搜索
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div id="rightPart" class="layui-col-md10" style="display: none;">
|
||||
<div class="layui-row">
|
||||
<form class="layui-form layui-col-md12 x-so" action="" lay-filter="searchFrm">
|
||||
路由名称:
|
||||
<input name="id" class="layui-input" style="width: 200px;" placeholder="输入接口名或版本号">
|
||||
<button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="searchFilter">
|
||||
<i class="layui-icon layui-icon-search"></i>搜索
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<table class="layui-hide" id="routeTable" lay-filter="routeTableFilter"></table>
|
||||
<script type="text/html" id="toolbar">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-ms layui-btn-normal" lay-event="add">
|
||||
<i class="layui-icon layui-icon-add-1"></i>添加路由
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<table class="layui-hide" id="routeTable" lay-filter="routeTableFilter"></table>
|
||||
<script type="text/html" id="toolbar">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-ms layui-btn-normal" lay-event="add">
|
||||
<i class="layui-icon layui-icon-add-1"></i>添加路由
|
||||
</button>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div id="addWin" class="x-win">
|
||||
@@ -75,20 +69,6 @@
|
||||
<input type="text" name="path" class="layui-input"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">合并结果</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="mergeResult" value="1" title="合并">
|
||||
<input type="radio" name="mergeResult" value="0" title="不合并">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">忽略验证</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="ignoreValidate" value="1" title="是">
|
||||
<input type="radio" name="ignoreValidate" value="0" title="否">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-block">
|
||||
@@ -126,20 +106,6 @@
|
||||
<input type="text" name="path" class="layui-input"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">合并结果</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="mergeResult" value="1" title="合并">
|
||||
<input type="radio" name="mergeResult" value="0" title="不合并">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">忽略验证</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="ignoreValidate" value="1" title="是">
|
||||
<input type="radio" name="ignoreValidate" value="0" title="否">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-block">
|
||||
|
@@ -5,6 +5,7 @@ lib.importJs('../../assets/js/routerole.js')
|
||||
,'1': '<span class="x-green">已启用</span>'
|
||||
,'2': '<span class="x-red">已禁用</span>'
|
||||
}
|
||||
var element = layui.element;
|
||||
var form = layui.form;
|
||||
var updateForm = layui.Form('updateForm');
|
||||
var addForm = layui.Form('addForm');
|
||||
@@ -50,43 +51,30 @@ lib.importJs('../../assets/js/routerole.js')
|
||||
return false;
|
||||
});
|
||||
|
||||
element.on('tab(serviceTabFilter)', function(data){
|
||||
loadRouteTable(this.innerHTML);
|
||||
});
|
||||
|
||||
function initTree() {
|
||||
|
||||
function initServiceTab() {
|
||||
ApiUtil.post('service.list', {}, function (resp) {
|
||||
var serviceList = resp.data;
|
||||
var children = [];
|
||||
var html = [];
|
||||
for (var i = 0; i < serviceList.length; i++) {
|
||||
var serviceInfo = serviceList[i];
|
||||
children.push({
|
||||
id: i + 1,
|
||||
name: serviceInfo.serviceId
|
||||
})
|
||||
var clazz = i === 0 ? 'class="layui-this"' : '';
|
||||
html.push('<li ' + clazz + '>' + serviceInfo.serviceId + '</li>');
|
||||
}
|
||||
$('#serviceTab').html(html.join(''));
|
||||
|
||||
layui.tree({
|
||||
elem: '#leftTree' //传入元素选择器
|
||||
, nodes: [{ //节点
|
||||
name: '服务列表'
|
||||
, spread: true // 展开
|
||||
, children: children
|
||||
}]
|
||||
, click: function (node) {
|
||||
if (node.id) {
|
||||
reloadRightPart(node)
|
||||
}
|
||||
}
|
||||
});
|
||||
if (serviceList.length > 0) {
|
||||
loadRouteTable(serviceList[0].serviceId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新右边部分
|
||||
* @param node 树节点
|
||||
*/
|
||||
function reloadRightPart(node) {
|
||||
$('#optTip').hide();
|
||||
$('#rightPart').show();
|
||||
var serviceId = node.name;
|
||||
|
||||
function loadRouteTable(serviceId) {
|
||||
currentServiceId = serviceId;
|
||||
searchTable({
|
||||
serviceId: serviceId
|
||||
@@ -174,7 +162,7 @@ lib.importJs('../../assets/js/routerole.js')
|
||||
layer.open({
|
||||
type: 1
|
||||
,title: '修改路由' + smTitle
|
||||
,area: ['500px', '460px']
|
||||
,area: ['500px', '350px']
|
||||
,content: $('#updateWin') //这里content是一个DOM,注意:最好该元素要存放在body最外层,否则可能被其它的相对元素所影响
|
||||
});
|
||||
} else if (event === 'auth') {
|
||||
@@ -210,7 +198,7 @@ lib.importJs('../../assets/js/routerole.js')
|
||||
layer.open({
|
||||
type: 1
|
||||
,title: '添加路由' + smTitle
|
||||
,area: ['500px', '460px']
|
||||
,area: ['500px', '350px']
|
||||
,content: $('#addWin')
|
||||
});
|
||||
}
|
||||
@@ -218,7 +206,7 @@ lib.importJs('../../assets/js/routerole.js')
|
||||
return routeTable;
|
||||
}
|
||||
|
||||
initTree();
|
||||
initServiceTab();
|
||||
|
||||
RouteRole.loadAllRole(form, 'roleArea');
|
||||
|
||||
|
@@ -37,7 +37,7 @@
|
||||
<dependency>
|
||||
<groupId>net.oschina.durcframework</groupId>
|
||||
<artifactId>easyopen-spring-boot-starter</artifactId>
|
||||
<version>1.16.0</version>
|
||||
<version>1.16.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@@ -8,10 +8,10 @@ import lombok.Data;
|
||||
*/
|
||||
@Data
|
||||
public class IsvFormVO {
|
||||
@ApiDocField(description = "appKey")
|
||||
@ApiDocField(description = "appKey", example = "aaaa")
|
||||
private String appKey;
|
||||
|
||||
@ApiDocField(description = "secret")
|
||||
@ApiDocField(description = "secret", example = "bbbb")
|
||||
private String secret;
|
||||
|
||||
@ApiDocField(description = "pubKey")
|
||||
|
@@ -12,15 +12,15 @@ import java.util.List;
|
||||
@Data
|
||||
public class IsvVO {
|
||||
/** 数据库字段:id */
|
||||
@ApiDocField(description = "id")
|
||||
@ApiDocField(description = "id", example = "1")
|
||||
private Long id;
|
||||
|
||||
/** appKey, 数据库字段:app_key */
|
||||
@ApiDocField(description = "appKey")
|
||||
@ApiDocField(description = "appKey", example = "aaaa")
|
||||
private String appKey;
|
||||
|
||||
/** secret, 数据库字段:secret */
|
||||
@ApiDocField(description = "appKey")
|
||||
@ApiDocField(description = "secret", example = "bbbb")
|
||||
private String secret;
|
||||
|
||||
/** 公钥, 数据库字段:pub_key */
|
||||
|
@@ -0,0 +1,115 @@
|
||||
package com.gitee.sop.adminserver.api.service;
|
||||
|
||||
import com.gitee.easyopen.annotation.Api;
|
||||
import com.gitee.easyopen.annotation.ApiService;
|
||||
import com.gitee.easyopen.doc.annotation.ApiDoc;
|
||||
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
|
||||
import com.gitee.easyopen.exception.ApiException;
|
||||
import com.gitee.easyopen.util.CopyUtil;
|
||||
import com.gitee.fastmybatis.core.query.Query;
|
||||
import com.gitee.sop.adminserver.api.service.param.LimitParam;
|
||||
import com.gitee.sop.adminserver.api.service.param.RouteSearchParam;
|
||||
import com.gitee.sop.adminserver.api.service.result.LimitVO;
|
||||
import com.gitee.sop.adminserver.bean.GatewayRouteDefinition;
|
||||
import com.gitee.sop.adminserver.bean.RouteConfigDto;
|
||||
import com.gitee.sop.adminserver.common.LimitEnum;
|
||||
import com.gitee.sop.adminserver.entity.ConfigRouteLimit;
|
||||
import com.gitee.sop.adminserver.mapper.ConfigRouteLimitMapper;
|
||||
import com.gitee.sop.adminserver.service.RouteConfigService;
|
||||
import com.gitee.sop.adminserver.service.RouteService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 限流
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@ApiService
|
||||
@ApiDoc("服务管理")
|
||||
@Slf4j
|
||||
public class LimitApi {
|
||||
|
||||
@Autowired
|
||||
RouteService routeService;
|
||||
|
||||
@Autowired
|
||||
RouteConfigService routeConfigService;
|
||||
|
||||
@Autowired
|
||||
ConfigRouteLimitMapper configRouteLimitMapper;
|
||||
|
||||
@Api(name = "route.limit.list")
|
||||
@ApiDocMethod(description = "限流列表", elementClass = LimitVO.class)
|
||||
List<LimitVO> listLimit(RouteSearchParam param) throws Exception {
|
||||
List<GatewayRouteDefinition> routeDefinitionList = routeService.getRouteDefinitionList(param);
|
||||
List<String> routeIdList = getRouteIdList(routeDefinitionList);
|
||||
// key:routeId
|
||||
Map<String, ConfigRouteLimit> routeLimitMap = getStoreConfigRouteLimit(routeIdList);
|
||||
List<LimitVO> gatewayRouteDefinitions = routeDefinitionList
|
||||
.stream()
|
||||
.map(gatewayRouteDefinition -> {
|
||||
LimitVO vo = new LimitVO();
|
||||
CopyUtil.copyProperties(gatewayRouteDefinition, vo);
|
||||
ConfigRouteLimit configRouteLimit = routeLimitMap.getOrDefault(gatewayRouteDefinition.getId(), getDefaultLimit());
|
||||
CopyUtil.copyPropertiesIgnoreNull(configRouteLimit, vo);
|
||||
return vo;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
return gatewayRouteDefinitions;
|
||||
}
|
||||
|
||||
private List<String> getRouteIdList(List<GatewayRouteDefinition> routeDefinitionList) {
|
||||
return routeDefinitionList
|
||||
.stream()
|
||||
.map(GatewayRouteDefinition::getId)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Map<String, ConfigRouteLimit> getStoreConfigRouteLimit(List<String> routeIdList) {
|
||||
Query query = new Query();
|
||||
query.in("route_id", routeIdList);
|
||||
// key:routeId
|
||||
Map<String, ConfigRouteLimit> routeLimitMap = configRouteLimitMapper.list(query)
|
||||
.stream()
|
||||
.collect(Collectors.toMap(ConfigRouteLimit::getRouteId, Function.identity()));
|
||||
return routeLimitMap;
|
||||
}
|
||||
|
||||
private ConfigRouteLimit getDefaultLimit() {
|
||||
ConfigRouteLimit configRouteLimit = new ConfigRouteLimit();
|
||||
configRouteLimit.setType(LimitEnum.TYPE_LEAKY_BUCKET.getVal());
|
||||
configRouteLimit.setLimitStatus(LimitEnum.STATUS_CLOSE.getVal());
|
||||
return configRouteLimit;
|
||||
}
|
||||
|
||||
@Api(name = "route.limit.update")
|
||||
@ApiDocMethod(description = "修改限流")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateLimtit(LimitParam param) {
|
||||
String routeId = param.getRouteId();
|
||||
ConfigRouteLimit configRouteLimit = configRouteLimitMapper.getByColumn("route_id", routeId);
|
||||
if (configRouteLimit == null) {
|
||||
configRouteLimit = new ConfigRouteLimit();
|
||||
CopyUtil.copyPropertiesIgnoreNull(param, configRouteLimit);
|
||||
configRouteLimitMapper.save(configRouteLimit);
|
||||
} else {
|
||||
CopyUtil.copyPropertiesIgnoreNull(param, configRouteLimit);
|
||||
configRouteLimitMapper.updateIgnoreNull(configRouteLimit);
|
||||
}
|
||||
RouteConfigDto routeConfigDto = new RouteConfigDto();
|
||||
CopyUtil.copyPropertiesIgnoreNull(param, routeConfigDto);
|
||||
try {
|
||||
routeConfigService.sendRouteConfigMsg(routeConfigDto);
|
||||
} catch (Exception e) {
|
||||
log.error("推送限流消息错误, param:{}", param, e);
|
||||
throw new ApiException("修改失败,请查看日志");
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,6 +6,7 @@ import com.gitee.easyopen.annotation.ApiService;
|
||||
import com.gitee.easyopen.doc.annotation.ApiDoc;
|
||||
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
|
||||
import com.gitee.easyopen.exception.ApiException;
|
||||
import com.gitee.easyopen.util.CopyUtil;
|
||||
import com.gitee.fastmybatis.core.query.Query;
|
||||
import com.gitee.sop.adminserver.api.isv.result.RoleVO;
|
||||
import com.gitee.sop.adminserver.api.service.param.RouteParam;
|
||||
@@ -14,23 +15,26 @@ import com.gitee.sop.adminserver.api.service.param.RouteSearchParam;
|
||||
import com.gitee.sop.adminserver.api.service.result.RouteVO;
|
||||
import com.gitee.sop.adminserver.api.service.result.ServiceInfo;
|
||||
import com.gitee.sop.adminserver.bean.GatewayRouteDefinition;
|
||||
import com.gitee.sop.adminserver.bean.RouteConfigDto;
|
||||
import com.gitee.sop.adminserver.bean.ZookeeperContext;
|
||||
import com.gitee.sop.adminserver.entity.ConfigRouteBase;
|
||||
import com.gitee.sop.adminserver.entity.PermRole;
|
||||
import com.gitee.sop.adminserver.entity.PermRolePermission;
|
||||
import com.gitee.sop.adminserver.mapper.ConfigRouteBaseMapper;
|
||||
import com.gitee.sop.adminserver.mapper.PermRoleMapper;
|
||||
import com.gitee.sop.adminserver.mapper.PermRolePermissionMapper;
|
||||
import com.gitee.sop.adminserver.service.RouteConfigService;
|
||||
import com.gitee.sop.adminserver.service.RoutePermissionService;
|
||||
import com.gitee.sop.adminserver.service.RouteService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.curator.framework.recipes.cache.ChildData;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -49,36 +53,24 @@ public class RouteApi {
|
||||
@Autowired
|
||||
PermRoleMapper permRoleMapper;
|
||||
|
||||
@Autowired
|
||||
ConfigRouteBaseMapper configRouteBaseMapper;
|
||||
|
||||
@Autowired
|
||||
RoutePermissionService routePermissionService;
|
||||
|
||||
@Autowired
|
||||
RouteConfigService routeConfigService;
|
||||
|
||||
@Autowired
|
||||
RouteService routeService;
|
||||
|
||||
@Api(name = "route.list")
|
||||
@ApiDocMethod(description = "路由列表")
|
||||
List<RouteVO> listRoute(RouteSearchParam param) throws Exception {
|
||||
if (StringUtils.isBlank(param.getServiceId())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
String searchPath = ZookeeperContext.getSopRouteRootPath() + "/" + param.getServiceId();
|
||||
|
||||
List<ChildData> childDataList = ZookeeperContext.getChildrenData(searchPath);
|
||||
|
||||
List<RouteVO> routeDefinitionList = childDataList.stream()
|
||||
.map(childData -> {
|
||||
String serviceNodeData = new String(childData.getData());
|
||||
GatewayRouteDefinition routeDefinition = JSON.parseObject(serviceNodeData, GatewayRouteDefinition.class);
|
||||
return routeDefinition;
|
||||
})
|
||||
.filter(gatewayRouteDefinition -> {
|
||||
boolean isRoute = gatewayRouteDefinition.getOrder() != Integer.MIN_VALUE;
|
||||
String id = param.getId();
|
||||
if (StringUtils.isBlank(id)) {
|
||||
return isRoute;
|
||||
} else {
|
||||
return isRoute && gatewayRouteDefinition.getId().contains(id);
|
||||
}
|
||||
})
|
||||
.map(gatewayRouteDefinition->{
|
||||
List<RouteVO> routeDefinitionList = routeService.getRouteDefinitionList(param)
|
||||
.stream()
|
||||
.map(gatewayRouteDefinition -> {
|
||||
RouteVO vo = new RouteVO();
|
||||
BeanUtils.copyProperties(gatewayRouteDefinition, vo);
|
||||
vo.setRoles(this.getRouteRole(gatewayRouteDefinition.getId()));
|
||||
@@ -96,8 +88,10 @@ public class RouteApi {
|
||||
String zookeeperRoutePath = serviceIdPath + "/" + param.getId();
|
||||
String data = ZookeeperContext.getData(zookeeperRoutePath);
|
||||
GatewayRouteDefinition routeDefinition = JSON.parseObject(data, GatewayRouteDefinition.class);
|
||||
BeanUtils.copyProperties(param, routeDefinition);
|
||||
CopyUtil.copyPropertiesIgnoreNull(param, routeDefinition);
|
||||
ZookeeperContext.updatePathData(zookeeperRoutePath, JSON.toJSONString(routeDefinition));
|
||||
|
||||
this.updateRouteConfig(param);
|
||||
}
|
||||
|
||||
@Api(name = "route.add")
|
||||
@@ -109,7 +103,7 @@ public class RouteApi {
|
||||
throw new ApiException("id已存在");
|
||||
}
|
||||
GatewayRouteDefinition routeDefinition = new GatewayRouteDefinition();
|
||||
BeanUtils.copyProperties(param, routeDefinition);
|
||||
CopyUtil.copyPropertiesIgnoreNull(param, routeDefinition);
|
||||
ZookeeperContext.createNewData(zookeeperRoutePath, JSON.toJSONString(routeDefinition));
|
||||
ServiceInfo serviceInfo = new ServiceInfo();
|
||||
serviceInfo.setServiceId(param.getServiceId());
|
||||
@@ -118,6 +112,37 @@ public class RouteApi {
|
||||
serviceInfo.setCreateTime(now);
|
||||
serviceInfo.setUpdateTime(now);
|
||||
ZookeeperContext.updatePathData(serviceIdPath, JSON.toJSONString(serviceInfo));
|
||||
|
||||
this.updateRouteConfig(param);
|
||||
}
|
||||
|
||||
private void updateRouteConfig(RouteParam param) {
|
||||
try {
|
||||
String routeId = param.getId();
|
||||
ConfigRouteBase configRouteBase = configRouteBaseMapper.getByColumn("route_id", routeId);
|
||||
boolean doSave = configRouteBase == null;
|
||||
if (doSave) {
|
||||
configRouteBase = new ConfigRouteBase();
|
||||
configRouteBase.setRouteId(param.getId());
|
||||
}
|
||||
configRouteBase.setStatus(param.getStatus().byteValue());
|
||||
|
||||
int i = doSave ? configRouteBaseMapper.save(configRouteBase)
|
||||
: configRouteBaseMapper.update(configRouteBase);
|
||||
|
||||
if (i > 0) {
|
||||
this.sendMsg(param);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发送msg失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMsg(RouteParam param) throws Exception {
|
||||
RouteConfigDto routeConfigDto = new RouteConfigDto();
|
||||
routeConfigDto.setRouteId(param.getId());
|
||||
routeConfigDto.setStatus(param.getStatus());
|
||||
routeConfigService.sendRouteConfigMsg(routeConfigDto);
|
||||
}
|
||||
|
||||
@Api(name = "route.role.get")
|
||||
@@ -166,9 +191,10 @@ public class RouteApi {
|
||||
}
|
||||
|
||||
try {
|
||||
routePermissionService.sendRoutePermissionReloadToZookeeper();
|
||||
routePermissionService.sendRoutePermissionReloadMsg();
|
||||
} catch (Exception e) {
|
||||
log.info("消息推送--路由权限(reload)失败",e);
|
||||
log.info("消息推送--路由权限(reload)失败", e);
|
||||
throw new ApiException("修改失败,请查看日志");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,45 @@
|
||||
package com.gitee.sop.adminserver.api.service.param;
|
||||
|
||||
import com.gitee.easyopen.doc.annotation.ApiDocField;
|
||||
import com.gitee.sop.adminserver.bean.GatewayRouteDefinition;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 限流
|
||||
* @author tanghc
|
||||
*/
|
||||
@Data
|
||||
public class LimitParam {
|
||||
@ApiDocField(description = "routeId")
|
||||
@NotBlank
|
||||
private String routeId;
|
||||
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:type */
|
||||
@ApiDocField(description = "限流策略,1:漏桶策略,2:令牌桶策略")
|
||||
@NotNull
|
||||
private Byte type;
|
||||
|
||||
/** 每秒可处理请求数, 数据库字段:exec_count_per_second */
|
||||
@ApiDocField(description = "每秒可处理请求数")
|
||||
private Integer execCountPerSecond;
|
||||
|
||||
/** 返回的错误码, 数据库字段:limit_code */
|
||||
@ApiDocField(description = "返回的错误码")
|
||||
private String limitCode;
|
||||
|
||||
/** 返回的错误信息, 数据库字段:limit_msg */
|
||||
@ApiDocField(description = "返回的错误信息")
|
||||
private String limitMsg;
|
||||
|
||||
/** 令牌桶容量, 数据库字段:token_bucket_count */
|
||||
@ApiDocField(description = "令牌桶容量")
|
||||
private Integer tokenBucketCount;
|
||||
|
||||
/** 1:开启,0关闭, 数据库字段:limit_status */
|
||||
@ApiDocField(description = "1:开启,0关闭")
|
||||
@NotNull
|
||||
private Byte limitStatus;
|
||||
}
|
@@ -38,14 +38,6 @@ public class RouteParam {
|
||||
@ApiDocField(description = "路由path")
|
||||
private String path;
|
||||
|
||||
|
||||
/**
|
||||
* 是否忽略验证,业务参数验证除外
|
||||
*/
|
||||
@NotNull
|
||||
@ApiDocField(description = "是否忽略验证,1:是,0:否")
|
||||
private Integer ignoreValidate;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
@@ -53,10 +45,4 @@ public class RouteParam {
|
||||
@ApiDocField(description = "状态,0:审核,1:启用,2:禁用")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
@NotNull
|
||||
@ApiDocField(description = "是否合并结果,1:是,0:否")
|
||||
private Integer mergeResult;
|
||||
}
|
||||
|
@@ -4,12 +4,18 @@ import com.gitee.easyopen.doc.annotation.ApiDocField;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class RouteSearchParam extends ServiceSearchParam {
|
||||
@ApiDocField(description = "id")
|
||||
public class RouteSearchParam {
|
||||
@ApiDocField(description = "服务名serviceId")
|
||||
@NotBlank(message = "serviceId不能为空")
|
||||
private String serviceId;
|
||||
|
||||
@ApiDocField(description = "路由id")
|
||||
private String id;
|
||||
}
|
||||
|
@@ -4,6 +4,8 @@ import com.gitee.easyopen.doc.annotation.ApiDocField;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@@ -11,6 +13,6 @@ import lombok.Setter;
|
||||
@Setter
|
||||
public class ServiceSearchParam {
|
||||
|
||||
@ApiDocField(description = "serviceId")
|
||||
@ApiDocField(description = "服务名serviceId")
|
||||
private String serviceId;
|
||||
}
|
||||
|
@@ -0,0 +1,54 @@
|
||||
package com.gitee.sop.adminserver.api.service.result;
|
||||
|
||||
import com.gitee.easyopen.doc.annotation.ApiDocField;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 限流
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Data
|
||||
public class LimitVO {
|
||||
@ApiDocField(description = "路由id")
|
||||
private String id;
|
||||
|
||||
@ApiDocField(description = "serviceId")
|
||||
private String serviceId;
|
||||
|
||||
/**
|
||||
* 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:type
|
||||
*/
|
||||
@ApiDocField(description = "限流策略,1:漏桶策略,2:令牌桶策略")
|
||||
private Byte type;
|
||||
|
||||
/**
|
||||
* 每秒可处理请求数, 数据库字段:exec_count_per_second
|
||||
*/
|
||||
@ApiDocField(description = "每秒可处理请求数")
|
||||
private Integer execCountPerSecond;
|
||||
|
||||
/**
|
||||
* 返回的错误码, 数据库字段:limit_code
|
||||
*/
|
||||
@ApiDocField(description = "返回的错误码")
|
||||
private String limitCode;
|
||||
|
||||
/**
|
||||
* 返回的错误信息, 数据库字段:limit_msg
|
||||
*/
|
||||
@ApiDocField(description = "返回的错误信息")
|
||||
private String limitMsg;
|
||||
|
||||
/**
|
||||
* 令牌桶容量, 数据库字段:token_bucket_count
|
||||
*/
|
||||
@ApiDocField(description = "令牌桶容量")
|
||||
private Integer tokenBucketCount;
|
||||
|
||||
/**
|
||||
* 1:开启,0关闭, 数据库字段:limit_status
|
||||
*/
|
||||
@ApiDocField(description = "1:开启,0关闭")
|
||||
private Byte limitStatus;
|
||||
}
|
@@ -1,9 +1,17 @@
|
||||
package com.gitee.sop.adminserver.api.system.param;
|
||||
|
||||
import com.gitee.easyopen.doc.annotation.ApiDocField;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class LoginForm {
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@ApiDocField(description = "用户名", required = true)
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@ApiDocField(description = "密码", required = true)
|
||||
private String password;
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
package com.gitee.sop.adminserver.bean;
|
||||
|
||||
import com.gitee.easyopen.doc.annotation.ApiDocField;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -13,6 +14,7 @@ public class GatewayRouteDefinition {
|
||||
/**
|
||||
* 路由的Id
|
||||
*/
|
||||
@ApiDocField(description = "路由id")
|
||||
private String id = "";
|
||||
|
||||
/**
|
||||
@@ -28,11 +30,13 @@ public class GatewayRouteDefinition {
|
||||
/**
|
||||
* 路由规则转发的目标uri
|
||||
*/
|
||||
@ApiDocField(description = "uri")
|
||||
private String uri;
|
||||
|
||||
/**
|
||||
* uri后面跟的path
|
||||
*/
|
||||
@ApiDocField(description = "path")
|
||||
private String path;
|
||||
|
||||
/**
|
||||
@@ -43,20 +47,24 @@ public class GatewayRouteDefinition {
|
||||
/**
|
||||
* 是否忽略验证,业务参数验证除外
|
||||
*/
|
||||
@ApiDocField(description = "是否忽略验证,业务参数验证除外,1:忽略,0:不忽略")
|
||||
private int ignoreValidate;
|
||||
|
||||
/**
|
||||
* 状态,0:待审核,1:启用,2:禁用
|
||||
*/
|
||||
@ApiDocField(description = "状态,0:待审核,1:启用,2:禁用")
|
||||
private int status = 1;
|
||||
|
||||
/**
|
||||
* 合并结果
|
||||
*/
|
||||
@ApiDocField(description = "合并结果,1:合并,2:不合并")
|
||||
private int mergeResult = 1;
|
||||
|
||||
/**
|
||||
* 是否需要授权才能访问
|
||||
*/
|
||||
@ApiDocField(description = "是否需要授权才能访问,1:是,2:否")
|
||||
private int permission;
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package com.gitee.sop.adminserver.bean;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Data
|
||||
public class RouteConfigDto {
|
||||
|
||||
private String routeId;
|
||||
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:type */
|
||||
private Byte type;
|
||||
|
||||
/** 每秒可处理请求数, 数据库字段:exec_count_per_second */
|
||||
private Integer execCountPerSecond;
|
||||
|
||||
/** 返回的错误码, 数据库字段:limit_code */
|
||||
private String limitCode;
|
||||
|
||||
/** 返回的错误信息, 数据库字段:limit_msg */
|
||||
private String limitMsg;
|
||||
|
||||
/** 令牌桶容量, 数据库字段:token_bucket_count */
|
||||
private Integer tokenBucketCount;
|
||||
|
||||
/** 限流开启状态,1:开启,0关闭, 数据库字段:limit_status */
|
||||
private Byte limitStatus;
|
||||
|
||||
/**
|
||||
* 状态,0:待审核,1:启用,2:禁用
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
|
||||
}
|
@@ -67,6 +67,10 @@ public class ZookeeperContext {
|
||||
return SOP_MSG_CHANNEL_PATH + "/isv-route-permission";
|
||||
}
|
||||
|
||||
public static String getRouteConfigChannelPath() {
|
||||
return SOP_MSG_CHANNEL_PATH + "/route-conf";
|
||||
}
|
||||
|
||||
public static CuratorFramework getClient() {
|
||||
return client;
|
||||
}
|
||||
|
@@ -0,0 +1,36 @@
|
||||
package com.gitee.sop.adminserver.common;
|
||||
|
||||
/**
|
||||
* 限流
|
||||
* @author tanghc
|
||||
*/
|
||||
public enum LimitEnum {
|
||||
/**
|
||||
* 限流策略,1:漏桶策略
|
||||
*/
|
||||
TYPE_LEAKY_BUCKET((byte) 1),
|
||||
/**
|
||||
* 限流策略,2:令牌桶策略
|
||||
*/
|
||||
TYPE_TOKEN_BUCKET((byte) 2),
|
||||
/**
|
||||
* 1:开启
|
||||
*/
|
||||
STATUS_OPEN((byte) 1),
|
||||
/**
|
||||
* 0关闭
|
||||
*/
|
||||
STATUS_CLOSE((byte) 0),
|
||||
|
||||
;
|
||||
|
||||
LimitEnum(byte val) {
|
||||
this.val = val;
|
||||
}
|
||||
|
||||
private byte val;
|
||||
|
||||
public byte getVal() {
|
||||
return val;
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
package com.gitee.sop.adminserver.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
|
||||
/**
|
||||
* 表名:config_route_base
|
||||
* 备注:路由配置表
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Table(name = "config_route_base")
|
||||
@Data
|
||||
public class ConfigRouteBase {
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
/** 数据库字段:id */
|
||||
private Long id;
|
||||
|
||||
/** 路由id, 数据库字段:route_id */
|
||||
private String routeId;
|
||||
|
||||
/** 状态,1:启用,2:禁用, 数据库字段:status */
|
||||
private Byte status;
|
||||
|
||||
/** 数据库字段:gmt_create */
|
||||
private Date gmtCreate;
|
||||
|
||||
/** 数据库字段:gmt_modified */
|
||||
private Date gmtModified;
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
package com.gitee.sop.adminserver.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
|
||||
/**
|
||||
* 表名:config_route_limit
|
||||
* 备注:路由限流配置
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Table(name = "config_route_limit")
|
||||
@Data
|
||||
public class ConfigRouteLimit {
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
/** 数据库字段:id */
|
||||
private Integer id;
|
||||
|
||||
/** 路由id, 数据库字段:route_id */
|
||||
private String routeId;
|
||||
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:type */
|
||||
private Byte type;
|
||||
|
||||
/** 每秒可处理请求数, 数据库字段:exec_count_per_second */
|
||||
private Integer execCountPerSecond;
|
||||
|
||||
/** 返回的错误码, 数据库字段:limit_code */
|
||||
private String limitCode;
|
||||
|
||||
/** 返回的错误信息, 数据库字段:limit_msg */
|
||||
private String limitMsg;
|
||||
|
||||
/** 令牌桶容量, 数据库字段:token_bucket_count */
|
||||
private Integer tokenBucketCount;
|
||||
|
||||
/** 1:开启,0关闭, 数据库字段:limit_status */
|
||||
private Byte limitStatus;
|
||||
|
||||
/** 数据库字段:gmt_create */
|
||||
private Date gmtCreate;
|
||||
|
||||
/** 数据库字段:gmt_modified */
|
||||
private Date gmtModified;
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.gitee.sop.adminserver.mapper;
|
||||
|
||||
import com.gitee.fastmybatis.core.mapper.CrudMapper;
|
||||
|
||||
import com.gitee.sop.adminserver.entity.ConfigRouteBase;
|
||||
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface ConfigRouteBaseMapper extends CrudMapper<ConfigRouteBase, Long> {
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.gitee.sop.adminserver.mapper;
|
||||
|
||||
import com.gitee.fastmybatis.core.mapper.CrudMapper;
|
||||
|
||||
import com.gitee.sop.adminserver.entity.ConfigRouteLimit;
|
||||
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface ConfigRouteLimitMapper extends CrudMapper<ConfigRouteLimit, Integer> {
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package com.gitee.sop.adminserver.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Service
|
||||
public class ConfigRouteBaseService {
|
||||
|
||||
public void updateConfigRouteBase() {
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package com.gitee.sop.adminserver.service;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.gitee.sop.adminserver.bean.ChannelMsg;
|
||||
import com.gitee.sop.adminserver.bean.RouteConfigDto;
|
||||
import com.gitee.sop.adminserver.bean.ZookeeperContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class RouteConfigService {
|
||||
|
||||
/**
|
||||
* 发送路由配置消息
|
||||
* @param routeConfigDto
|
||||
* @throws Exception
|
||||
*/
|
||||
public void sendRouteConfigMsg(RouteConfigDto routeConfigDto) throws Exception {
|
||||
String configData = JSON.toJSONString(routeConfigDto);
|
||||
ChannelMsg channelMsg = new ChannelMsg("update", configData);
|
||||
String jsonData = JSON.toJSONString(channelMsg);
|
||||
String path = ZookeeperContext.getRouteConfigChannelPath();
|
||||
log.info("消息推送--路由配置(update), path:{}, data:{}", path, jsonData);
|
||||
ZookeeperContext.createOrUpdateData(path, jsonData);
|
||||
}
|
||||
}
|
@@ -93,7 +93,7 @@ public class RoutePermissionService {
|
||||
/**
|
||||
* 推送所有路由权限到zookeeper
|
||||
*/
|
||||
public void sendRoutePermissionReloadToZookeeper() throws Exception {
|
||||
public void sendRoutePermissionReloadMsg() throws Exception {
|
||||
ChannelMsg channelMsg = new ChannelMsg("reload", null);
|
||||
String jsonData = JSON.toJSONString(channelMsg);
|
||||
String path = ZookeeperContext.getIsvRoutePermissionChannelPath();
|
||||
|
@@ -0,0 +1,53 @@
|
||||
package com.gitee.sop.adminserver.service;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.gitee.sop.adminserver.api.service.param.RouteSearchParam;
|
||||
import com.gitee.sop.adminserver.bean.GatewayRouteDefinition;
|
||||
import com.gitee.sop.adminserver.bean.ZookeeperContext;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.curator.framework.recipes.cache.ChildData;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Service
|
||||
public class RouteService {
|
||||
|
||||
public List<GatewayRouteDefinition> getRouteDefinitionList(RouteSearchParam param) throws Exception {
|
||||
if (StringUtils.isBlank(param.getServiceId())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
String searchPath = ZookeeperContext.getSopRouteRootPath() + "/" + param.getServiceId();
|
||||
|
||||
List<ChildData> childDataList = ZookeeperContext.getChildrenData(searchPath);
|
||||
|
||||
List<GatewayRouteDefinition> routeDefinitionStream = childDataList.stream()
|
||||
.map(childData -> {
|
||||
String serviceNodeData = new String(childData.getData());
|
||||
GatewayRouteDefinition routeDefinition = JSON.parseObject(serviceNodeData, GatewayRouteDefinition.class);
|
||||
return routeDefinition;
|
||||
})
|
||||
.filter(gatewayRouteDefinition -> {
|
||||
boolean isRoute = gatewayRouteDefinition.getOrder() != Integer.MIN_VALUE;
|
||||
String id = param.getId();
|
||||
if (StringUtils.isBlank(id)) {
|
||||
return isRoute;
|
||||
} else {
|
||||
return isRoute && gatewayRouteDefinition.getId().contains(id);
|
||||
}
|
||||
})
|
||||
.collect(toList())
|
||||
;
|
||||
|
||||
return routeDefinitionStream;
|
||||
}
|
||||
|
||||
}
|
@@ -4,7 +4,9 @@ import com.gitee.sop.gatewaycommon.gateway.param.GatewayParamBuilder;
|
||||
import com.gitee.sop.gatewaycommon.gateway.result.GatewayResult;
|
||||
import com.gitee.sop.gatewaycommon.gateway.result.GatewayResultExecutor;
|
||||
import com.gitee.sop.gatewaycommon.manager.DefaultIsvRoutePermissionManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.DefaultRouteConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.IsvRoutePermissionManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.param.ParamBuilder;
|
||||
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
|
||||
import com.gitee.sop.gatewaycommon.secret.CacheIsvManager;
|
||||
@@ -94,6 +96,11 @@ public class ApiConfig {
|
||||
*/
|
||||
private IsvRoutePermissionManager isvRoutePermissionManager = new DefaultIsvRoutePermissionManager();
|
||||
|
||||
/**
|
||||
* 路由配置管理
|
||||
*/
|
||||
private RouteConfigManager routeConfigManager = new DefaultRouteConfigManager();
|
||||
|
||||
// -------- fields ---------
|
||||
|
||||
/**
|
||||
|
@@ -34,11 +34,6 @@ public class BaseRouteDefinition {
|
||||
*/
|
||||
private int ignoreValidate;
|
||||
|
||||
/**
|
||||
* 状态,0:待审核,1:启用,2:禁用
|
||||
*/
|
||||
private int status = 1;
|
||||
|
||||
/**
|
||||
* 是否合并结果
|
||||
*/
|
||||
@@ -48,8 +43,4 @@ public class BaseRouteDefinition {
|
||||
* 接口是否需要授权才能访问
|
||||
*/
|
||||
private int permission;
|
||||
|
||||
public boolean enable() {
|
||||
return status == RouteStatus.ENABLE.getStatus();
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,46 @@
|
||||
package com.gitee.sop.gatewaycommon.bean;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Data
|
||||
public class RouteConfig {
|
||||
|
||||
public static final byte STATUS_ENABLE = 1;
|
||||
public static final byte LIMIT_STATUS_CLOSE = 0;
|
||||
|
||||
private String routeId;
|
||||
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:type */
|
||||
private Byte type;
|
||||
|
||||
/** 每秒可处理请求数, 数据库字段:exec_count_per_second */
|
||||
private Integer execCountPerSecond;
|
||||
|
||||
/** 返回的错误码, 数据库字段:limit_code */
|
||||
private String limitCode;
|
||||
|
||||
/** 返回的错误信息, 数据库字段:limit_msg */
|
||||
private String limitMsg;
|
||||
|
||||
/** 令牌桶容量, 数据库字段:token_bucket_count */
|
||||
private Integer tokenBucketCount;
|
||||
|
||||
/** 限流开启状态,1:开启,0关闭, 数据库字段:limit_status */
|
||||
private Byte limitStatus = LIMIT_STATUS_CLOSE;
|
||||
|
||||
/**
|
||||
* 状态,0:待审核,1:启用,2:禁用
|
||||
*/
|
||||
private Byte status = STATUS_ENABLE;
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
* @return
|
||||
*/
|
||||
public boolean enable() {
|
||||
return status == STATUS_ENABLE;
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package com.gitee.sop.gatewaycommon.bean;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Data
|
||||
public class RouteConfigDto {
|
||||
|
||||
private String routeId;
|
||||
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:type */
|
||||
private Byte type;
|
||||
|
||||
/** 每秒可处理请求数, 数据库字段:exec_count_per_second */
|
||||
private Integer execCountPerSecond;
|
||||
|
||||
/** 返回的错误码, 数据库字段:limit_code */
|
||||
private String limitCode;
|
||||
|
||||
/** 返回的错误信息, 数据库字段:limit_msg */
|
||||
private String limitMsg;
|
||||
|
||||
/** 令牌桶容量, 数据库字段:token_bucket_count */
|
||||
private Integer tokenBucketCount;
|
||||
|
||||
/** 限流开启状态,1:开启,0关闭, 数据库字段:limit_status */
|
||||
private Byte limitStatus;
|
||||
|
||||
/**
|
||||
* 状态,0:待审核,1:启用,2:禁用
|
||||
*/
|
||||
private Byte status;
|
||||
|
||||
|
||||
}
|
@@ -1,7 +1,10 @@
|
||||
package com.gitee.sop.gatewaycommon.gateway.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
|
||||
import com.gitee.sop.gatewaycommon.param.ParamNames;
|
||||
import com.gitee.sop.gatewaycommon.util.RequestUtil;
|
||||
@@ -64,7 +67,9 @@ public class NameVersionRoutePredicateFactory extends AbstractRoutePredicateFact
|
||||
boolean match = (name + version).equals(nameVersion);
|
||||
if (match) {
|
||||
TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(nameVersion);
|
||||
if (targetRoute != null && !targetRoute.getRouteDefinition().enable()) {
|
||||
RouteConfigManager routeConfigManager = ApiConfig.getInstance().getRouteConfigManager();
|
||||
RouteConfig routeConfig = routeConfigManager.get(nameVersion);
|
||||
if (targetRoute != null && !routeConfig.enable()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,49 @@
|
||||
package com.gitee.sop.gatewaycommon.manager;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfigDto;
|
||||
import com.gitee.sop.gatewaycommon.util.MyBeanUtil;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class DefaultRouteConfigManager implements RouteConfigManager {
|
||||
/**
|
||||
* key: routeId
|
||||
*/
|
||||
protected static Map<String, RouteConfig> routeConfigMap = new ConcurrentHashMap<>(64);
|
||||
|
||||
private static RouteConfig DEFAULT_CONFIG;
|
||||
|
||||
public DefaultRouteConfigManager() {
|
||||
DEFAULT_CONFIG = this.newRouteConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(RouteConfigDto routeConfigDto) {
|
||||
String key = routeConfigDto.getRouteId();
|
||||
RouteConfig routeConfig = routeConfigMap.get(key);
|
||||
if (routeConfig == null) {
|
||||
routeConfig = newRouteConfig();
|
||||
routeConfigMap.put(key, routeConfig);
|
||||
}
|
||||
MyBeanUtil.copyPropertiesIgnoreNull(routeConfigDto, routeConfig);
|
||||
}
|
||||
|
||||
protected RouteConfig newRouteConfig() {
|
||||
return new RouteConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RouteConfig get(String routeId) {
|
||||
return routeConfigMap.getOrDefault(routeId, DEFAULT_CONFIG);
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package com.gitee.sop.gatewaycommon.manager;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfigDto;
|
||||
|
||||
/**
|
||||
* 路由配置管理
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface RouteConfigManager {
|
||||
void load();
|
||||
|
||||
/**
|
||||
* 更新路由配置
|
||||
* @param routeConfigDto
|
||||
*/
|
||||
void update(RouteConfigDto routeConfigDto);
|
||||
|
||||
/**
|
||||
* 获取路由配置
|
||||
* @param routeId
|
||||
* @return
|
||||
*/
|
||||
RouteConfig get(String routeId);
|
||||
}
|
@@ -74,6 +74,10 @@ public class ZookeeperContext {
|
||||
return SOP_MSG_CHANNEL_PATH + "/isv-route-permission";
|
||||
}
|
||||
|
||||
public static String getRouteConfigChannelPath() {
|
||||
return SOP_MSG_CHANNEL_PATH + "/route-conf";
|
||||
}
|
||||
|
||||
public static CuratorFramework getClient() {
|
||||
return client;
|
||||
}
|
||||
|
@@ -10,8 +10,11 @@ public enum ErrorEnum {
|
||||
|
||||
/** 服务暂不可用 */
|
||||
ISP_UNKNOW_ERROR(Codes.CODE_UNKNOW, "isp.unknow-error"),
|
||||
/** */
|
||||
/** 服务暂不可用 */
|
||||
AOP_UNKNOW_ERROR(Codes.CODE_UNKNOW, "aop.unknow-error"),
|
||||
/** 服务不可用,路由被禁用 */
|
||||
ISP_API_DISABLED(Codes.CODE_UNKNOW, "isp.service-not-available"),
|
||||
|
||||
|
||||
/** 无效的访问令牌 */
|
||||
AOP_INVALID_AUTH_TOKEN(Codes.CODE_AUTH, "aop.invalid-auth-token"),
|
||||
|
@@ -0,0 +1,130 @@
|
||||
package com.gitee.sop.gatewaycommon.util;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 对象拷贝
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
public class MyBeanUtil extends org.springframework.beans.BeanUtils {
|
||||
|
||||
/**
|
||||
* 属性拷贝,第一个参数中的属性值拷贝到第二个参数中<br>
|
||||
* 注意:当第一个参数中的属性有null值时,不会拷贝进去
|
||||
*
|
||||
* @param source 源对象
|
||||
* @param target 目标对象
|
||||
* @throws BeansException
|
||||
*/
|
||||
public static void copyPropertiesIgnoreNull(Object source, Object target)
|
||||
throws BeansException {
|
||||
Assert.notNull(source, "Source must not be null");
|
||||
Assert.notNull(target, "Target must not be null");
|
||||
Class<?> actualEditable = target.getClass();
|
||||
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
|
||||
for (PropertyDescriptor targetPd : targetPds) {
|
||||
Method writeMethod = targetPd.getWriteMethod();
|
||||
if (writeMethod != null) {
|
||||
PropertyDescriptor sourcePd = getPropertyDescriptor(
|
||||
source.getClass(), targetPd.getName());
|
||||
if (sourcePd != null && sourcePd.getReadMethod() != null) {
|
||||
try {
|
||||
Method readMethod = sourcePd.getReadMethod();
|
||||
if (!Modifier.isPublic(readMethod.getDeclaringClass()
|
||||
.getModifiers())) {
|
||||
readMethod.setAccessible(true);
|
||||
}
|
||||
Object value = readMethod.invoke(source);
|
||||
// 这里判断value是否为空 当然这里也能进行一些特殊要求的处理
|
||||
// 例如绑定时格式转换等等
|
||||
if (value != null) {
|
||||
if (!Modifier.isPublic(writeMethod
|
||||
.getDeclaringClass().getModifiers())) {
|
||||
writeMethod.setAccessible(true);
|
||||
}
|
||||
writeMethod.invoke(target, value);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
throw new FatalBeanException(
|
||||
"Could not copy properties from source to target field name mismatch:" + targetPd.getName(),
|
||||
ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 属性拷贝,把map中的值拷贝到target中去
|
||||
*
|
||||
* @param map map对象
|
||||
* @param target 目标对象
|
||||
*/
|
||||
public static void copyPropertiesForMap(Map<String, Object> map, Object target) {
|
||||
Assert.notNull(map, "map must not be null");
|
||||
Assert.notNull(target, "Target must not be null");
|
||||
Object pojo = mapToPojo(map, target.getClass());
|
||||
copyProperties(pojo, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将实体对象转换成Map
|
||||
*
|
||||
* @param pojo 实体类
|
||||
* @return 返回map
|
||||
*/
|
||||
public static Map<String, Object> pojoToMap(Object pojo) {
|
||||
if (pojo == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
String json = JSON.toJSONString(pojo);
|
||||
return JSON.parseObject(json);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将map对象转换成普通类
|
||||
*
|
||||
* @param <T> 普通类类型
|
||||
* @param map map对象
|
||||
* @param pojoClass 普通类
|
||||
* @return 返回普通类
|
||||
*/
|
||||
public static <T> T mapToPojo(Map<String, Object> map, Class<T> pojoClass) {
|
||||
return new JSONObject(map).toJavaObject(pojoClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* map集合转换成对象集合
|
||||
*
|
||||
* @param <T> 普通类类型
|
||||
* @param list map集合
|
||||
* @param pojoClass 待转换的对象类型
|
||||
* @return 返回对象集合
|
||||
*/
|
||||
public static <T> List<T> mapListToObjList(List<Map<String, Object>> list, Class<T> pojoClass) {
|
||||
if (list == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<T> retList = new ArrayList<>(list.size());
|
||||
for (Map<String, Object> map : list) {
|
||||
retList.add(mapToPojo(map, pojoClass));
|
||||
}
|
||||
return retList;
|
||||
}
|
||||
|
||||
}
|
@@ -1,5 +1,8 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
|
||||
@@ -51,8 +54,10 @@ public class SopRouteLocator implements RouteLocator, Ordered {
|
||||
return null;
|
||||
}
|
||||
// 路由是否启用
|
||||
if (!zuulTargetRoute.getRouteDefinition().enable()) {
|
||||
throw ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getException();
|
||||
RouteConfigManager routeConfigManager = ApiConfig.getInstance().getRouteConfigManager();
|
||||
RouteConfig routeConfig = routeConfigManager.get(zuulTargetRoute.getRouteDefinition().getId());
|
||||
if (!routeConfig.enable()) {
|
||||
throw ErrorEnum.ISP_API_DISABLED.getErrorMeta().getException();
|
||||
}
|
||||
return zuulTargetRoute.getTargetRouteDefinition();
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ open.error_10000=Success
|
||||
open.error_20000=Service is temporarily unavailable
|
||||
open.error_20000_isp.unknow-error=Service is temporarily unavailable
|
||||
open.error_20000_aop.unknow-error=Service is temporarily unavailable
|
||||
open.error_20000_isp.service-not-available=Service is temporarily unavailable
|
||||
|
||||
open.error_20001=Insufficient authorization authority
|
||||
open.error_20001_aop.invalid-auth-token=Invalid access token
|
||||
|
@@ -3,6 +3,7 @@
|
||||
#open.error_20000=服务不可用
|
||||
#open.error_20000_isp.unknow-error=服务暂不可用
|
||||
#open.error_20000_aop.unknow-error=服务暂不可用
|
||||
#open.error_20000_isp.service-not-available=服务暂不可用
|
||||
#
|
||||
#open.error_20001=授权权限不足
|
||||
#open.error_20001_aop.invalid-auth-token=无效的访问令牌
|
||||
@@ -59,6 +60,7 @@ open.error_10000=Success
|
||||
open.error_20000=\u670d\u52a1\u4e0d\u53ef\u7528
|
||||
open.error_20000_isp.unknow-error=\u670d\u52a1\u6682\u4e0d\u53ef\u7528
|
||||
open.error_20000_aop.unknow-error=\u670d\u52a1\u6682\u4e0d\u53ef\u7528
|
||||
open.error_20000_isp.service-not-available=\u670d\u52a1\u6682\u4e0d\u53ef\u7528
|
||||
|
||||
open.error_20001=\u6388\u6743\u6743\u9650\u4e0d\u8db3
|
||||
open.error_20001_aop.invalid-auth-token=\u65e0\u6548\u7684\u8bbf\u95ee\u4ee4\u724c
|
||||
|
@@ -23,7 +23,7 @@
|
||||
<dependency>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-service-common</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<version>1.2.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
|
@@ -29,7 +29,7 @@
|
||||
<dependency>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-service-common</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<version>1.2.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
|
@@ -0,0 +1,39 @@
|
||||
package com.gitee.sop.gateway.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
* 表名:config_route_base
|
||||
* 备注:路由配置表
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Table(name = "config_route_base")
|
||||
@Data
|
||||
public class ConfigRouteBase {
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
/** 数据库字段:id */
|
||||
private Long id;
|
||||
|
||||
/** 路由id, 数据库字段:route_id */
|
||||
private String routeId;
|
||||
|
||||
/** 状态,1:启用,2:禁用, 数据库字段:status */
|
||||
private Byte status;
|
||||
|
||||
/** 数据库字段:gmt_create */
|
||||
private Date gmtCreate;
|
||||
|
||||
/** 数据库字段:gmt_modified */
|
||||
private Date gmtModified;
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
package com.gitee.sop.gateway.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
* 表名:config_route_limit
|
||||
* 备注:路由限流配置
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Table(name = "config_route_limit")
|
||||
@Data
|
||||
public class ConfigRouteLimit {
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
/** 数据库字段:id */
|
||||
private Integer id;
|
||||
|
||||
/** 路由id, 数据库字段:route_id */
|
||||
private String routeId;
|
||||
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:type */
|
||||
private Byte type;
|
||||
|
||||
/** 每秒可处理请求数, 数据库字段:exec_count_per_second */
|
||||
private Integer execCountPerSecond;
|
||||
|
||||
/** 返回的错误码, 数据库字段:limit_code */
|
||||
private String limitCode;
|
||||
|
||||
/** 返回的错误信息, 数据库字段:limit_msg */
|
||||
private String limitMsg;
|
||||
|
||||
/** 令牌桶容量, 数据库字段:token_bucket_count */
|
||||
private Integer tokenBucketCount;
|
||||
|
||||
/** 1:开启,0关闭, 数据库字段:limit_status */
|
||||
private Byte limitStatus;
|
||||
|
||||
/** 数据库字段:gmt_create */
|
||||
private Date gmtCreate;
|
||||
|
||||
/** 数据库字段:gmt_modified */
|
||||
private Date gmtModified;
|
||||
}
|
@@ -33,7 +33,6 @@ public class DbIsvManager extends CacheIsvManager {
|
||||
|
||||
@Override
|
||||
public void load(Function<Object, String> secretGetter) {
|
||||
log.info("从数据库读取ISV信息,保存到本地");
|
||||
List<IsvInfo> isvInfoList = isvInfoMapper.list(new Query());
|
||||
isvInfoList.stream()
|
||||
.forEach(isvInfo -> {
|
||||
|
@@ -0,0 +1,83 @@
|
||||
package com.gitee.sop.gateway.manager;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.gitee.fastmybatis.core.query.Query;
|
||||
import com.gitee.sop.gateway.mapper.ConfigRouteBaseMapper;
|
||||
import com.gitee.sop.gateway.mapper.ConfigRouteLimitMapper;
|
||||
import com.gitee.sop.gatewaycommon.bean.ChannelMsg;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfigDto;
|
||||
import com.gitee.sop.gatewaycommon.manager.DefaultRouteConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.ZookeeperContext;
|
||||
import com.gitee.sop.gatewaycommon.util.MyBeanUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class DbRouteConfigManager extends DefaultRouteConfigManager {
|
||||
|
||||
@Autowired
|
||||
ConfigRouteBaseMapper configRouteBaseMapper;
|
||||
|
||||
@Autowired
|
||||
ConfigRouteLimitMapper configRouteLimitMapper;
|
||||
|
||||
@Autowired
|
||||
Environment environment;
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
Query query = new Query();
|
||||
|
||||
configRouteBaseMapper.list(query)
|
||||
.stream()
|
||||
.forEach(configRouteBase -> {
|
||||
String key = configRouteBase.getRouteId();
|
||||
putVal(key, configRouteBase);
|
||||
});
|
||||
|
||||
configRouteLimitMapper.list(query)
|
||||
.stream()
|
||||
.forEach(configRouteLimit -> {
|
||||
String key = configRouteLimit.getRouteId();
|
||||
putVal(key, configRouteLimit);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
protected void putVal(String key, Object object) {
|
||||
RouteConfig routeConfig = routeConfigMap.getOrDefault(key, newRouteConfig());
|
||||
MyBeanUtil.copyPropertiesIgnoreNull(object, routeConfig);
|
||||
routeConfigMap.put(key, routeConfig);
|
||||
}
|
||||
|
||||
|
||||
@PostConstruct
|
||||
protected void after() throws Exception {
|
||||
ZookeeperContext.setEnvironment(environment);
|
||||
String path = ZookeeperContext.getRouteConfigChannelPath();
|
||||
ZookeeperContext.listenPath(path, nodeCache -> {
|
||||
String nodeData = new String(nodeCache.getCurrentData().getData());
|
||||
ChannelMsg channelMsg = JSON.parseObject(nodeData, ChannelMsg.class);
|
||||
final RouteConfigDto routeConfigDto = JSON.parseObject(channelMsg.getData(), RouteConfigDto.class);
|
||||
switch (channelMsg.getOperation()) {
|
||||
case "reload":
|
||||
log.info("重新加载路由配置信息,routeConfigDto:{}", routeConfigDto);
|
||||
load();
|
||||
break;
|
||||
case "update":
|
||||
log.info("更新路由配置信息,routeConfigDto:{}", routeConfigDto);
|
||||
update(routeConfigDto);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@@ -13,15 +13,19 @@ import org.springframework.stereotype.Component;
|
||||
@Slf4j
|
||||
public class ManagerInitializer {
|
||||
@Autowired
|
||||
private DbIsvManager dbIsvManager;
|
||||
DbIsvManager dbIsvManager;
|
||||
|
||||
@Autowired
|
||||
private DbIsvRoutePermissionManager dbIsvRoutePermissionManager;
|
||||
DbIsvRoutePermissionManager dbIsvRoutePermissionManager;
|
||||
|
||||
@Autowired
|
||||
DbRouteConfigManager dbRouteConfigManager;
|
||||
|
||||
public void init() {
|
||||
ApiConfig apiConfig = ApiConfig.getInstance();
|
||||
apiConfig.setIsvManager(dbIsvManager);
|
||||
apiConfig.setIsvRoutePermissionManager(dbIsvRoutePermissionManager);
|
||||
apiConfig.setRouteConfigManager(dbRouteConfigManager);
|
||||
|
||||
// 从数据库加载isv信息
|
||||
log.debug("从数据库加载isv信息");
|
||||
@@ -32,5 +36,8 @@ public class ManagerInitializer {
|
||||
|
||||
log.debug("从数据库加载路由权限信息");
|
||||
dbIsvRoutePermissionManager.load();
|
||||
|
||||
log.debug("从数据库加载路由配置信息");
|
||||
dbRouteConfigManager.load();
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,11 @@
|
||||
package com.gitee.sop.gateway.mapper;
|
||||
|
||||
import com.gitee.fastmybatis.core.mapper.CrudMapper;
|
||||
import com.gitee.sop.gateway.entity.ConfigRouteBase;
|
||||
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface ConfigRouteBaseMapper extends CrudMapper<ConfigRouteBase, Long> {
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package com.gitee.sop.gateway.mapper;
|
||||
|
||||
import com.gitee.fastmybatis.core.mapper.CrudMapper;
|
||||
import com.gitee.sop.gateway.entity.ConfigRouteLimit;
|
||||
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface ConfigRouteLimitMapper extends CrudMapper<ConfigRouteLimit, Integer> {
|
||||
}
|
Reference in New Issue
Block a user