parent
45ab847197
commit
a3b87a5f5f
@ -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> |
@ -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'); |
||||||
|
|
||||||
|
}); |
@ -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("修改失败,请查看日志"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
@ -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; |
package com.gitee.sop.adminserver.api.system.param; |
||||||
|
|
||||||
|
import com.gitee.easyopen.doc.annotation.ApiDocField; |
||||||
import lombok.Data; |
import lombok.Data; |
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank; |
||||||
|
|
||||||
@Data |
@Data |
||||||
public class LoginForm { |
public class LoginForm { |
||||||
|
@NotBlank(message = "用户名不能为空") |
||||||
|
@ApiDocField(description = "用户名", required = true) |
||||||
private String username; |
private String username; |
||||||
|
|
||||||
|
@NotBlank(message = "密码不能为空") |
||||||
|
@ApiDocField(description = "密码", required = true) |
||||||
private String password; |
private String password; |
||||||
} |
} |
@ -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; |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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; |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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; |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
@ -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> { |
||||||
|
} |
Loading…
Reference in new issue