feat:2.4.0
1. 修复分布式锁锁定失败问题 2. Pods页面显示负责消费的桶信息
This commit is contained in:
parent
1f3c6e4762
commit
95e594fabb
@ -13,4 +13,5 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
*/
|
||||
public interface DistributedLockMapper extends BaseMapper<DistributedLock> {
|
||||
|
||||
int updateTest(DistributedLock lock);
|
||||
}
|
||||
|
@ -18,4 +18,10 @@
|
||||
id, name, lock_until, locked_at, locked_by, create_dt, update_dt
|
||||
</sql>
|
||||
|
||||
<update id="updateTest">
|
||||
update distributed_lock set locked_by = #{lockedBy},
|
||||
lock_until = #{lockUntil},
|
||||
locked_at = #{lockedAt}
|
||||
where name = #{name} and lock_until <![CDATA[ <= ]]> #{lockedAt}
|
||||
</update>
|
||||
</mapper>
|
||||
|
@ -5,10 +5,10 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* www.byteblogs.com
|
||||
*
|
||||
* @author: shuguang.zhang
|
||||
* @author: www.byteblogs.com
|
||||
* @date : 2023-09-21 09:26
|
||||
* since: 2.4.0
|
||||
*/
|
||||
public class DistributeInstance {
|
||||
private DistributeInstance() {
|
||||
|
@ -21,8 +21,8 @@ public abstract class AbstractLockProvider implements LockProvider {
|
||||
|
||||
String lockName = lockConfig.getLockName();
|
||||
|
||||
boolean tryToCreateLockRecord = CacheLockRecord.lockRecordRecentlyCreated(lockName);
|
||||
if (!tryToCreateLockRecord) {
|
||||
boolean tryToCreateLockRecord = !CacheLockRecord.lockRecordRecentlyCreated(lockName);
|
||||
if (tryToCreateLockRecord) {
|
||||
if (doLock(lockConfig)) {
|
||||
CacheLockRecord.addLockRecord(lockName);
|
||||
return true;
|
||||
|
@ -1,11 +1,13 @@
|
||||
package com.aizuda.easy.retry.server.common.lock;
|
||||
|
||||
import com.aizuda.easy.retry.common.core.log.LogUtils;
|
||||
import com.aizuda.easy.retry.common.core.util.JsonUtil;
|
||||
import com.aizuda.easy.retry.server.common.config.SystemProperties;
|
||||
import com.aizuda.easy.retry.server.common.dto.LockConfig;
|
||||
import com.aizuda.easy.retry.server.common.register.ServerRegister;
|
||||
import com.aizuda.easy.retry.template.datasource.persistence.mapper.DistributedLockMapper;
|
||||
import com.aizuda.easy.retry.template.datasource.persistence.po.DistributedLock;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -48,14 +50,13 @@ public class JdbcLockProvider extends AbstractLockProvider {
|
||||
LocalDateTime now = lockConfig.getCreateDt();
|
||||
DistributedLock distributedLock = new DistributedLock();
|
||||
distributedLock.setLockedBy(ServerRegister.CURRENT_CID);
|
||||
distributedLock.setLockedAt(now);
|
||||
LocalDateTime lockAtLeast = lockConfig.getLockAtLeast();
|
||||
distributedLock.setLockUntil(now.isBefore(lockAtLeast) ? lockAtLeast : now);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
try {
|
||||
return distributedLockMapper.update(distributedLock, new LambdaUpdateWrapper<DistributedLock>()
|
||||
.eq(DistributedLock::getName, lockConfig.getLockName())) > 0;
|
||||
return distributedLockMapper.update(distributedLock, new LambdaUpdateWrapper<DistributedLock>()
|
||||
.eq(DistributedLock::getName, lockConfig.getLockName())) > 0;
|
||||
} catch (Exception e) {
|
||||
LogUtils.error(log, "unlock error. retrying attempt [{}] ", i, e);
|
||||
}
|
||||
@ -78,10 +79,9 @@ public class JdbcLockProvider extends AbstractLockProvider {
|
||||
distributedLock.setUpdateDt(now);
|
||||
return distributedLockMapper.insert(distributedLock) > 0;
|
||||
} catch (DuplicateKeyException | ConcurrencyFailureException | TransactionSystemException e) {
|
||||
// LogUtils.warn(log,"Duplicate key. lockName:[{}]", lockConfig.getLockName());
|
||||
return false;
|
||||
} catch (DataIntegrityViolationException | BadSqlGrammarException | UncategorizedSQLException e) {
|
||||
LogUtils.error(log,"Unexpected exception. lockName:[{}]", lockConfig.getLockName(), e);
|
||||
LogUtils.error(log, "Unexpected exception. lockName:[{}]", lockConfig.getLockName(), e);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -95,11 +95,16 @@ public class JdbcLockProvider extends AbstractLockProvider {
|
||||
distributedLock.setLockedBy(ServerRegister.CURRENT_CID);
|
||||
distributedLock.setLockedAt(now);
|
||||
distributedLock.setLockUntil(lockConfig.getLockAtMost());
|
||||
return distributedLockMapper.update(distributedLock, new LambdaUpdateWrapper<DistributedLock>()
|
||||
.eq(DistributedLock::getName, lockConfig.getLockName())
|
||||
.le(DistributedLock::getLockUntil, now)) > 0;
|
||||
distributedLock.setName(lockConfig.getLockName());
|
||||
try {
|
||||
return distributedLockMapper.update(distributedLock, new LambdaUpdateWrapper<DistributedLock>()
|
||||
.eq(DistributedLock::getName, lockConfig.getLockName())
|
||||
.le(DistributedLock::getLockUntil, now)) > 0;
|
||||
} catch (ConcurrencyFailureException | DataIntegrityViolationException | TransactionSystemException |
|
||||
UncategorizedSQLException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -45,14 +45,18 @@ public abstract class AbstractSchedule implements Schedule {
|
||||
LockConfig lockConfig = new LockConfig(LocalDateTime.now(), lockName, Duration.parse(lockAtMost), Duration.parse(lockAtLeast));
|
||||
|
||||
LockProvider lockProvider = getLockAccess();
|
||||
boolean lock = false;
|
||||
try {
|
||||
if (lockProvider.lock(lockConfig)) {
|
||||
lock = lockProvider.lock(lockConfig);
|
||||
if (lock) {
|
||||
doExecute();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.error(log, this.getClass().getName() + " execute error. lockName:[{}]", lockName, e);
|
||||
} finally {
|
||||
lockProvider.unlock(lockConfig);
|
||||
if (lock) {
|
||||
lockProvider.unlock(lockConfig);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0aa660"],{"119c":function(t,a,e){"use strict";e.r(a);e("b0c0");var o=function(){var t=this,a=t._self._c;return a("div",[a("page-header-wrapper",{staticStyle:{margin:"-24px -1px 0"},on:{back:function(){return t.$router.go(-1)}}},[a("div")]),null!==t.jobBatchInfo?a("a-card",{attrs:{bordered:!1}},[a("a-descriptions",{attrs:{title:"",column:3,bordered:""}},[a("a-descriptions-item",{attrs:{label:"组名称"}},[t._v(" "+t._s(t.jobBatchInfo.groupName)+" ")]),a("a-descriptions-item",{attrs:{label:"任务名称"}},[t._v(" "+t._s(t.jobBatchInfo.jobName)+" ")]),a("a-descriptions-item",{attrs:{label:"状态"}},[a("a-tag",{attrs:{color:t.taskBatchStatus[t.jobBatchInfo.taskBatchStatus].color}},[t._v(" "+t._s(t.taskBatchStatus[t.jobBatchInfo.taskBatchStatus].name)+" ")])],1),a("a-descriptions-item",{attrs:{label:"执行器类型"}},[a("a-tag",{attrs:{color:t.executorType[t.jobBatchInfo.executorType].color}},[t._v(" "+t._s(t.executorType[t.jobBatchInfo.executorType].name)+" ")])],1),a("a-descriptions-item",{attrs:{label:"操作原因"}},[a("a-tag",{attrs:{color:t.operationReason[t.jobBatchInfo.operationReason].color}},[t._v(" "+t._s(t.operationReason[t.jobBatchInfo.operationReason].name)+" ")])],1),a("a-descriptions-item",{attrs:{label:"开始执行时间"}},[t._v(" "+t._s(t.jobBatchInfo.executionAt)+" ")]),a("a-descriptions-item",{attrs:{label:"执行器名称",span:"4"}},[t._v(" "+t._s(t.jobBatchInfo.executorInfo)+" ")]),a("a-descriptions-item",{attrs:{label:"创建时间"}},[t._v(" "+t._s(t.jobBatchInfo.createDt)+" ")])],1)],1):t._e(),a("div",{staticStyle:{margin:"20px 0","border-left":"#f5222d 5px solid","font-size":"medium","font-weight":"bold"}},[t._v(" 任务项列表 ")]),a("JobTaskList",{ref:"JobTaskListRef"})],1)},r=[],s=e("3b7a"),n=e("c1df"),c=e.n(n),i=e("38b7"),u=e.n(i),p=e("36e8"),b={name:"JobInfo",components:{JobTaskList:p["default"]},data:function(){return{jobBatchInfo:null,taskBatchStatus:u.a.taskBatchStatus,operationReason:u.a.operationReason,taskType:u.a.taskType,triggerType:u.a.triggerType,blockStrategy:u.a.blockStrategy,executorType:u.a.executorType}},created:function(){var t=this,a=this.$route.query.id,e=this.$route.query.groupName;a&&e?Object(s["d"])(a).then((function(e){t.jobBatchInfo=e.data,t.queryParam={groupName:t.jobBatchInfo.groupName,taskBatchId:a},t.$refs.JobTaskListRef.refreshTable(t.queryParam)})):this.$router.push({path:"/404"})},methods:{jobTaskList:s["h"],parseDate:function(t){return c()(t).format("YYYY-MM-DD HH:mm:ss")}}},l=b,f=e("2877"),h=Object(f["a"])(l,o,r,!1,null,"1a941578",null);a["default"]=h.exports}}]);
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-74bac939"],{"9141d":function(t,e,a){"use strict";a.r(e);var o=function(){var t=this,e=t._self._c;return e("a-card",{attrs:{bordered:!1}},[e("div",{staticClass:"table-page-search-wrapper"},[e("a-form",{attrs:{layout:"inline"}},[e("a-row",{attrs:{gutter:48}},[[e("a-col",{attrs:{md:8,sm:24}},[e("a-form-item",{attrs:{label:"组名称"}},[e("a-input",{attrs:{placeholder:"请输入组名称",allowClear:""},model:{value:t.queryParam.groupName,callback:function(e){t.$set(t.queryParam,"groupName",e)},expression:"queryParam.groupName"}})],1)],1)],e("a-col",{attrs:{md:t.advanced?24:8,sm:24}},[e("span",{staticClass:"table-page-search-submitButtons",style:t.advanced&&{float:"right",overflow:"hidden"}||{}},[e("a-button",{attrs:{type:"primary"},on:{click:function(e){return t.$refs.table.refresh(!0)}}},[t._v("查询")]),e("a-button",{staticStyle:{"margin-left":"8px"},on:{click:function(){return t.queryParam={}}}},[t._v("重置")])],1)])],2)],1)],1),e("s-table",{ref:"table",attrs:{size:"default",rowKey:"key",columns:t.columns,data:t.loadData,alert:t.options.alert,rowSelection:t.options.rowSelection,scroll:{x:1600}},scopedSlots:t._u([{key:"serial",fn:function(a,o,n){return e("span",{},[t._v(" "+t._s(n+1)+" ")])}},{key:"contextPath",fn:function(a,o){return e("span",{},[1===o.nodeType?e("div",[t._v(" Path: "),e("a-tag",{attrs:{color:"#108ee9"}},[t._v(" "+t._s(a)+" ")])],1):e("div",[t._v(" Bucket: "),e("a-popover",{attrs:{placement:"topLeft"}},[e("template",{slot:"content"},t._l(o.consumerBuckets,(function(a){return e("a-tag",{key:a,staticStyle:{"margin-bottom":"16px"},attrs:{color:"pink"}},[t._v(" "+t._s(a)+" ")])})),1),e("template",{slot:"title"},[e("span",[t._v("Bucket列表")])]),t._l(5,(function(a){return e("a-tag",{key:a,staticStyle:{"margin-bottom":"16px"},attrs:{color:"pink"}},[t._v(" "+t._s(o.consumerBuckets[a-1])+" ")])})),o.consumerBuckets.length>5?e("a-tag",{staticStyle:{"margin-bottom":"16px"},attrs:{color:"pink"}},[t._v(" ... ")]):t._e()],2)],1)])}}])})],1)},n=[],r=a("c1df"),s=a.n(r),l=a("0fea"),c=a("2af9"),i={name:"PodList",components:{STable:c["j"]},data:function(){var t=this;return{advanced:!1,queryParam:{},columns:[{title:"#",scopedSlots:{customRender:"serial"},width:"6%"},{title:"类型",dataIndex:"nodeType",customRender:function(e){return t.nodeType[e]},width:"8%"},{title:"组名称",dataIndex:"groupName",width:"10%"},{title:"PodId",dataIndex:"hostId",width:"18%"},{title:"IP",dataIndex:"hostIp",width:"12%"},{title:"Port",dataIndex:"hostPort",width:"8%"},{title:"路径/组",dataIndex:"contextPath",scopedSlots:{customRender:"contextPath"},ellipsis:!0,width:"22%"},{title:"更新时间",dataIndex:"updateDt",sorter:!0,customRender:function(t){return s()(t).format("YYYY-MM-DD HH:mm:ss")}}],loadData:function(e){return Object(l["D"])(Object.assign(e,t.queryParam)).then((function(t){return t}))},selectedRowKeys:[],selectedRows:[],options:{alert:{show:!0,clear:function(){t.selectedRowKeys=[]}},rowSelection:{selectedRowKeys:this.selectedRowKeys,onChange:this.onSelectChange}},nodeType:{1:"客户端",2:"服务端"}}},methods:{}},d=i,u=a("2877"),p=Object(u["a"])(d,o,n,!1,null,"38a41799",null);e["default"]=p.exports}}]);
|
@ -1 +0,0 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-74bac939"],{"9141d":function(t,e,a){"use strict";a.r(e);var o=function(){var t=this,e=t._self._c;return e("a-card",{attrs:{bordered:!1}},[e("div",{staticClass:"table-page-search-wrapper"},[e("a-form",{attrs:{layout:"inline"}},[e("a-row",{attrs:{gutter:48}},[[e("a-col",{attrs:{md:8,sm:24}},[e("a-form-item",{attrs:{label:"组名称"}},[e("a-input",{attrs:{placeholder:"请输入组名称",allowClear:""},model:{value:t.queryParam.groupName,callback:function(e){t.$set(t.queryParam,"groupName",e)},expression:"queryParam.groupName"}})],1)],1)],e("a-col",{attrs:{md:t.advanced?24:8,sm:24}},[e("span",{staticClass:"table-page-search-submitButtons",style:t.advanced&&{float:"right",overflow:"hidden"}||{}},[e("a-button",{attrs:{type:"primary"},on:{click:function(e){return t.$refs.table.refresh(!0)}}},[t._v("查询")]),e("a-button",{staticStyle:{"margin-left":"8px"},on:{click:function(){return t.queryParam={}}}},[t._v("重置")])],1)])],2)],1)],1),e("s-table",{ref:"table",attrs:{size:"default",rowKey:"key",columns:t.columns,data:t.loadData,alert:t.options.alert,rowSelection:t.options.rowSelection,scroll:{x:1600}},scopedSlots:t._u([{key:"serial",fn:function(a,o,n){return e("span",{},[t._v(" "+t._s(n+1)+" ")])}},{key:"contextPath",fn:function(a,o){return e("span",{},[1===o.nodeType?e("div",[t._v(" 路径: "),e("a-tag",{attrs:{color:"#108ee9"}},[t._v(" "+t._s(a)+" ")])],1):e("div",[t._v(" 组: "),t._l(o.consumerGroup,(function(a){return e("a-tag",{key:a,staticStyle:{"margin-bottom":"16px"},attrs:{color:"pink"}},[t._v(" "+t._s(a)+" ")])}))],2)])}}])})],1)},n=[],r=a("c1df"),s=a.n(r),l=a("0fea"),c=a("2af9"),d={name:"PodList",components:{STable:c["j"]},data:function(){var t=this;return{advanced:!1,queryParam:{},columns:[{title:"#",scopedSlots:{customRender:"serial"},width:"6%"},{title:"类型",dataIndex:"nodeType",customRender:function(e){return t.nodeType[e]},width:"8%"},{title:"组名称",dataIndex:"groupName",width:"10%"},{title:"PodId",dataIndex:"hostId",width:"18%"},{title:"IP",dataIndex:"hostIp",width:"12%"},{title:"Port",dataIndex:"hostPort",width:"8%"},{title:"路径/组",dataIndex:"contextPath",scopedSlots:{customRender:"contextPath"},width:"22%"},{title:"更新时间",dataIndex:"updateDt",sorter:!0,customRender:function(t){return s()(t).format("YYYY-MM-DD HH:mm:ss")}}],loadData:function(e){return Object(l["D"])(Object.assign(e,t.queryParam)).then((function(t){return t}))},selectedRowKeys:[],selectedRows:[],options:{alert:{show:!0,clear:function(){t.selectedRowKeys=[]}},rowSelection:{selectedRowKeys:this.selectedRowKeys,onChange:this.onSelectChange}},nodeType:{1:"客户端",2:"服务端"}}},methods:{}},i=d,u=a("2877"),p=Object(u["a"])(i,o,n,!1,null,"1902f2a8",null);e["default"]=p.exports}}]);
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -37,7 +37,6 @@ logging:
|
||||
config: classpath:logback-boot.xml
|
||||
|
||||
easy-retry:
|
||||
last-days: 30 # 拉取重试数据的天数
|
||||
retry-pull-page-size: 100 # 拉取重试数据的每批次的大小
|
||||
netty-port: 1788 # 服务端netty端口
|
||||
total-partition: 2 # 重试和死信表的分区总数
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.aizuda.easy.retry.server.web.controller;
|
||||
|
||||
import com.aizuda.easy.retry.server.common.cache.CacheConsumerGroup;
|
||||
import com.aizuda.easy.retry.server.common.dto.DistributeInstance;
|
||||
import com.aizuda.easy.retry.server.web.model.base.PageResult;
|
||||
import com.aizuda.easy.retry.server.web.model.request.ServerNodeQueryVO;
|
||||
import com.aizuda.easy.retry.server.web.model.response.ActivePodQuantityResponseVO;
|
||||
@ -77,9 +78,9 @@ public class DashBoardController {
|
||||
return dashBoardService.pods(serverNodeQueryVO);
|
||||
}
|
||||
|
||||
@GetMapping("/consumer/group")
|
||||
public Set<String> allConsumerGroupName() {
|
||||
return CacheConsumerGroup.getAllConsumerGroupName();
|
||||
@GetMapping("/consumer/bucket")
|
||||
public Set<Integer> allConsumerGroupName() {
|
||||
return DistributeInstance.INSTANCE.getConsumerBucket();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ public class SceneConfigResponseVO {
|
||||
|
||||
private Long deadlineRequest;
|
||||
|
||||
private Integer executorTimeout;
|
||||
|
||||
private LocalDateTime createDt;
|
||||
|
||||
private LocalDateTime updateDt;
|
||||
|
@ -31,5 +31,5 @@ public class ServerNodeResponseVO {
|
||||
|
||||
private String extAttrs;
|
||||
|
||||
private Set<String> consumerGroup;
|
||||
private Set<Integer> consumerBuckets;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import com.aizuda.easy.retry.common.core.log.LogUtils;
|
||||
import com.aizuda.easy.retry.common.core.model.Result;
|
||||
import com.aizuda.easy.retry.common.core.util.JsonUtil;
|
||||
import com.aizuda.easy.retry.server.common.cache.CacheConsumerGroup;
|
||||
import com.aizuda.easy.retry.server.common.dto.DistributeInstance;
|
||||
import com.aizuda.easy.retry.server.common.dto.ServerNodeExtAttrs;
|
||||
import com.aizuda.easy.retry.server.common.register.ServerRegister;
|
||||
import com.aizuda.easy.retry.server.web.service.convert.DispatchQuantityResponseVOConverter;
|
||||
@ -60,7 +61,7 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
@Slf4j
|
||||
public class DashBoardServiceImpl implements DashBoardService {
|
||||
public static final String URL = "http://{0}:{1}/dashboard/consumer/group";
|
||||
public static final String URL = "http://{0}:{1}/dashboard/consumer/bucket";
|
||||
|
||||
@Autowired
|
||||
private RetryTaskLogMapper retryTaskLogMapper;
|
||||
@ -200,7 +201,7 @@ public class DashBoardServiceImpl implements DashBoardService {
|
||||
|
||||
// 若是本地节点则直接从缓存中取
|
||||
if (ServerRegister.CURRENT_CID.equals(serverNodeResponseVO.getHostId())) {
|
||||
serverNodeResponseVO.setConsumerGroup(CacheConsumerGroup.getAllConsumerGroupName());
|
||||
serverNodeResponseVO.setConsumerBuckets(DistributeInstance.INSTANCE.getConsumerBucket());
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -216,15 +217,14 @@ public class DashBoardServiceImpl implements DashBoardService {
|
||||
// 从远程节点取
|
||||
String format = MessageFormat
|
||||
.format(URL, serverNodeResponseVO.getHostIp(), serverNodeExtAttrs.getWebPort().toString());
|
||||
Result<List<String>> result = restTemplate.getForObject(format, Result.class);
|
||||
List<String> data = result.getData();
|
||||
Result<List<Integer>> result = restTemplate.getForObject(format, Result.class);
|
||||
List<Integer> data = result.getData();
|
||||
if (!CollectionUtils.isEmpty(data)) {
|
||||
serverNodeResponseVO.setConsumerGroup(new HashSet<>(data));
|
||||
serverNodeResponseVO.setConsumerBuckets(new HashSet<>(data));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LogUtils.error(log, "Failed to retrieve consumer group for node [{}:{}].", serverNodeResponseVO.getHostIp(), serverNodeExtAttrs.getWebPort());
|
||||
serverNodeResponseVO.setConsumerGroup(Sets.newHashSet("获取数据异常"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -58,32 +58,6 @@ export const asyncRouterMap = [
|
||||
redirect: '/retry/list',
|
||||
meta: { title: '重试任务管理', icon: 'schedule', permission: ['retryTask'] },
|
||||
children: [
|
||||
{
|
||||
path: '/retry/scene/list',
|
||||
name: 'SceneList',
|
||||
component: () => import('@/views/task/SceneList'),
|
||||
meta: { title: '场景列表', icon: 'profile', keepAlive: true, permission: ['retryTask'] }
|
||||
},
|
||||
{
|
||||
path: '/retry/scene/config',
|
||||
name: 'SceneFrom',
|
||||
hidden: true,
|
||||
component: () => import('@/views/task/form/SceneFrom'),
|
||||
meta: { title: '场景配置', icon: 'profile', keepAlive: true, permission: ['retryTask'] }
|
||||
},
|
||||
{
|
||||
path: '/retry/notify/list',
|
||||
name: 'NotifyList',
|
||||
component: () => import('@/views/task/NotifyList'),
|
||||
meta: { title: '通知配置', icon: 'profile', keepAlive: true, permission: ['retryTask'] }
|
||||
},
|
||||
{
|
||||
path: '/retry/notify/config',
|
||||
name: 'NotifyFrom',
|
||||
hidden: true,
|
||||
component: () => import('@/views/task/form/NotifyFrom'),
|
||||
meta: { title: '通知配置', icon: 'profile', keepAlive: true, permission: ['retryTask'] }
|
||||
},
|
||||
{
|
||||
path: '/retry/list',
|
||||
name: 'RetryTaskList',
|
||||
@ -122,6 +96,32 @@ export const asyncRouterMap = [
|
||||
hidden: true,
|
||||
component: () => import('@/views/task/RetryLogInfo'),
|
||||
meta: { title: '重试日志详情', icon: 'profile', permission: ['retryLog'] }
|
||||
},
|
||||
{
|
||||
path: '/retry/scene/list',
|
||||
name: 'SceneList',
|
||||
component: () => import('@/views/task/SceneList'),
|
||||
meta: { title: '场景列表', icon: 'profile', keepAlive: true, permission: ['retryTask'] }
|
||||
},
|
||||
{
|
||||
path: '/retry/scene/config',
|
||||
name: 'SceneFrom',
|
||||
hidden: true,
|
||||
component: () => import('@/views/task/form/SceneFrom'),
|
||||
meta: { title: '场景配置', icon: 'profile', keepAlive: true, permission: ['retryTask'] }
|
||||
},
|
||||
{
|
||||
path: '/retry/notify/list',
|
||||
name: 'NotifyList',
|
||||
component: () => import('@/views/task/NotifyList'),
|
||||
meta: { title: '通知配置', icon: 'profile', keepAlive: true, permission: ['retryTask'] }
|
||||
},
|
||||
{
|
||||
path: '/retry/notify/config',
|
||||
name: 'NotifyFrom',
|
||||
hidden: true,
|
||||
component: () => import('@/views/task/form/NotifyFrom'),
|
||||
meta: { title: '通知配置', icon: 'profile', keepAlive: true, permission: ['retryTask'] }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -36,17 +36,30 @@
|
||||
</span>
|
||||
<span slot="contextPath" slot-scope="text, record">
|
||||
<div v-if="record.nodeType === 1">
|
||||
路径:
|
||||
Path:
|
||||
<a-tag color="#108ee9" >
|
||||
{{ text }}
|
||||
</a-tag>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
组:
|
||||
<a-tag color="pink" v-for="item in record.consumerGroup" :key="item" style="margin-bottom: 16px">
|
||||
{{ item }}
|
||||
</a-tag>
|
||||
Bucket:
|
||||
<a-popover placement="topLeft">
|
||||
<template slot="content">
|
||||
<a-tag color="pink" v-for="item in record.consumerBuckets" :key="item" style="margin-bottom: 16px">
|
||||
{{ item }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template slot="title">
|
||||
<span>Bucket列表</span>
|
||||
</template>
|
||||
<a-tag color="pink" v-for="item in 5" :key="item" style="margin-bottom: 16px">
|
||||
{{ record.consumerBuckets[item-1] }}
|
||||
</a-tag>
|
||||
<a-tag color="pink" style="margin-bottom: 16px" v-if="record.consumerBuckets.length > 5">
|
||||
...
|
||||
</a-tag>
|
||||
</a-popover>
|
||||
</div>
|
||||
|
||||
</span>
|
||||
@ -107,6 +120,7 @@ export default {
|
||||
title: '路径/组',
|
||||
dataIndex: 'contextPath',
|
||||
scopedSlots: { customRender: 'contextPath' },
|
||||
ellipsis: true,
|
||||
width: '22%'
|
||||
},
|
||||
{
|
||||
@ -118,7 +132,6 @@ export default {
|
||||
],
|
||||
// 加载数据方法 必须为 Promise 对象
|
||||
loadData: parameter => {
|
||||
console.log('loadData.parameter', parameter)
|
||||
return pods(Object.assign(parameter, this.queryParam))
|
||||
.then(res => {
|
||||
return res
|
||||
|
@ -48,7 +48,7 @@
|
||||
:data="loadData"
|
||||
:alert="options.alert"
|
||||
:rowSelection="options.rowSelection"
|
||||
:scroll="{ x: 1500 }"
|
||||
:scroll="{ x: 2000 }"
|
||||
>
|
||||
<span slot="serial" slot-scope="text, record">
|
||||
{{ record.id }}
|
||||
@ -64,7 +64,18 @@
|
||||
</a-tag>
|
||||
</span>
|
||||
<span slot="triggerInterval" slot-scope="text">
|
||||
{{ text ? text : '10s,15s,30s,35s,40s,50s,1m,2m,4m,6m,8m,10m,20m,40m,1h,2h,3h,4h,5h,6h,7h,8h,9h,10h,11h,12h' }}
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
{{ text ? text : '10s,15s,30s,35s,40s,50s,1m,2m,4m,6m,8m,10m,20m,40m,1h,2h,3h,4h,5h,6h,7h,8h,9h,10h,11h,12h' }}
|
||||
</template>
|
||||
{{ text ? text : '10s,15s,30s,35s,40s,50s,1m,2m,4m,6m,8m,10m,20m,40m,1h,2h,3h,4h,5h,6h,7h,8h,9h,10h,11h,12h' }}
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<span slot="deadlineRequest" slot-scope="text">
|
||||
{{ text }}(ms)
|
||||
</span>
|
||||
<span slot="executorTimeout" slot-scope="text">
|
||||
{{ text }}(s)
|
||||
</span>
|
||||
<span slot="action" slot-scope="record">
|
||||
<template>
|
||||
@ -92,19 +103,19 @@ export default {
|
||||
{
|
||||
title: '场景名称',
|
||||
dataIndex: 'sceneName',
|
||||
width: '15%'
|
||||
width: '10%'
|
||||
},
|
||||
{
|
||||
title: '场景状态',
|
||||
dataIndex: 'sceneStatus',
|
||||
width: '8%',
|
||||
width: '10%',
|
||||
scopedSlots: { customRender: 'sceneStatus' }
|
||||
},
|
||||
{
|
||||
title: '退避策略',
|
||||
dataIndex: 'backOff',
|
||||
key: 'backOff',
|
||||
width: '12%',
|
||||
width: '10%',
|
||||
scopedSlots: { customRender: 'backOff' }
|
||||
},
|
||||
{
|
||||
@ -114,6 +125,14 @@ export default {
|
||||
width: '10%',
|
||||
scopedSlots: { customRender: 'maxRetryCount' }
|
||||
},
|
||||
{
|
||||
title: '间隔时间',
|
||||
dataIndex: 'triggerInterval',
|
||||
key: 'triggerInterval',
|
||||
ellipsis: true,
|
||||
width: '10%',
|
||||
scopedSlots: { customRender: 'triggerInterval' }
|
||||
},
|
||||
{
|
||||
title: '调用链超时时间',
|
||||
dataIndex: 'deadlineRequest',
|
||||
@ -122,18 +141,29 @@ export default {
|
||||
scopedSlots: { customRender: 'deadlineRequest' }
|
||||
},
|
||||
{
|
||||
title: '间隔时间',
|
||||
dataIndex: 'triggerInterval',
|
||||
key: 'triggerInterval',
|
||||
ellipsis: true,
|
||||
width: '15%',
|
||||
scopedSlots: { customRender: 'triggerInterval' }
|
||||
title: '执行超时时间',
|
||||
dataIndex: 'executorTimeout',
|
||||
key: 'executorTimeout',
|
||||
width: '10%',
|
||||
scopedSlots: { customRender: 'executorTimeout' }
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createDt',
|
||||
key: 'createDt',
|
||||
width: '10%'
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
dataIndex: 'updateDt',
|
||||
key: 'updateDt',
|
||||
width: '10%'
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
width: '18%',
|
||||
width: '10%',
|
||||
scopedSlots: { customRender: 'description' }
|
||||
},
|
||||
{
|
||||
@ -148,44 +178,8 @@ export default {
|
||||
pagination: {},
|
||||
backOffLabels: enums.backOffLabels,
|
||||
sceneStatus: enums.sceneStatus,
|
||||
triggerInterval: {
|
||||
'1': {
|
||||
placeholder: '',
|
||||
tooltip: ''
|
||||
},
|
||||
'2': {
|
||||
placeholder: '请输入固定间隔时间',
|
||||
tooltip: '请输入固定间隔时间'
|
||||
},
|
||||
'3': {
|
||||
placeholder: '请输入CRON表达式',
|
||||
tooltip: '通过CRON表达式计算执行时间'
|
||||
},
|
||||
'4': {
|
||||
placeholder: '请输入最大间隔时间',
|
||||
tooltip: '随机生成范围在[0, x]内的延迟时间; 其中x代表最大间隔时间'
|
||||
}
|
||||
},
|
||||
// 高级搜索 展开/关闭
|
||||
advanced: false,
|
||||
maxRetryCount: {
|
||||
'1': {
|
||||
placeholder: '请输入延迟等级(max:26)',
|
||||
tooltip: '请输入延迟等级(max:26)'
|
||||
},
|
||||
'2': {
|
||||
placeholder: '请输入最大重试次数',
|
||||
tooltip: '请输入最大重试次数'
|
||||
},
|
||||
'3': {
|
||||
placeholder: '请输入最大重试次数',
|
||||
tooltip: '请输入最大重试次数'
|
||||
},
|
||||
'4': {
|
||||
placeholder: '请输入最大重试次数',
|
||||
tooltip: '请输入最大重试次数'
|
||||
}
|
||||
},
|
||||
queryParam: {},
|
||||
loadData: (parameter) => {
|
||||
return getScenePage(Object.assign(parameter, this.queryParam)).then((res) => {
|
||||
@ -216,16 +210,6 @@ export default {
|
||||
getAllGroupNameList().then((res) => {
|
||||
this.groupNameList = res.data
|
||||
})
|
||||
|
||||
const groupName = this.$route.query.groupName
|
||||
if (groupName) {
|
||||
this.fetch({
|
||||
groupName: groupName,
|
||||
size: 6,
|
||||
page: 1
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleNew () {
|
||||
@ -233,179 +217,6 @@ export default {
|
||||
},
|
||||
handleEdit (record) {
|
||||
this.$router.push({ path: '/retry/scene/config', query: { id: record.id } })
|
||||
},
|
||||
reset () {
|
||||
this.formData = []
|
||||
this.data = []
|
||||
const groupName = this.$route.query.groupName
|
||||
if (groupName) {
|
||||
this.fetch({
|
||||
groupName: groupName,
|
||||
size: 6,
|
||||
page: 1
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
handleTableChange (pagination, filters, sorter) {
|
||||
console.log(pagination)
|
||||
const pager = { ...this.pagination }
|
||||
pager.current = pagination.current
|
||||
this.pagination = pager
|
||||
this.fetch({
|
||||
groupName: this.$route.query.groupName,
|
||||
size: pagination.pageSize,
|
||||
page: pagination.current,
|
||||
sortField: sorter.field,
|
||||
sortOrder: sorter.order,
|
||||
...filters
|
||||
})
|
||||
},
|
||||
queryChange () {
|
||||
this.fetch({
|
||||
groupName: this.$route.query.groupName,
|
||||
size: 6,
|
||||
page: 1,
|
||||
sceneName: this.queryParam.sceneName
|
||||
}
|
||||
)
|
||||
},
|
||||
fetch (params = {}) {
|
||||
this.loading = true
|
||||
getScenePage(params).then(res => {
|
||||
this.data = []
|
||||
res.data.map(record => {
|
||||
this.loading = false
|
||||
const { id, sceneName, sceneStatus, maxRetryCount, backOff, triggerInterval, description, deadlineRequest } = record
|
||||
this.data.push({
|
||||
key: id,
|
||||
sceneName: sceneName,
|
||||
sceneStatus: sceneStatus.toString(),
|
||||
maxRetryCount: maxRetryCount,
|
||||
backOff: backOff.toString(),
|
||||
triggerInterval: triggerInterval,
|
||||
description: description,
|
||||
deadlineRequest: deadlineRequest,
|
||||
editable: false,
|
||||
isNew: false
|
||||
})
|
||||
})
|
||||
|
||||
const pagination = { ...this.pagination }
|
||||
|
||||
pagination.pageSize = res.size
|
||||
pagination.current = res.page
|
||||
pagination.total = res.total
|
||||
|
||||
this.pagination = pagination
|
||||
})
|
||||
},
|
||||
remove (delKey) {
|
||||
const delData = this.data.find(item => item.key === delKey)
|
||||
const { key, sceneName, sceneStatus, maxRetryCount, backOff, triggerInterval, description, deadlineRequest } = delData
|
||||
this.formData.push({
|
||||
key: key,
|
||||
sceneName: sceneName,
|
||||
sceneStatus: sceneStatus,
|
||||
maxRetryCount: maxRetryCount,
|
||||
backOff: backOff,
|
||||
triggerInterval: triggerInterval,
|
||||
deadlineRequest: deadlineRequest,
|
||||
description: description,
|
||||
isDeleted: 1
|
||||
})
|
||||
|
||||
const newData = this.data.filter(item => item.key !== delKey)
|
||||
this.data = newData
|
||||
},
|
||||
saveRow (record) {
|
||||
this.memberLoading = true
|
||||
const { key, sceneName, sceneStatus, maxRetryCount, backOff, triggerInterval, description, deadlineRequest } = record
|
||||
if (!sceneName || !sceneStatus || !maxRetryCount || !backOff || (backOff === '1' ? false : !triggerInterval)) {
|
||||
this.memberLoading = false
|
||||
this.$message.error('请填写完整成员信息。')
|
||||
return
|
||||
}
|
||||
|
||||
const regex = /^[A-Za-z0-9_]{1,64}$/
|
||||
if (!regex.test(sceneName)) {
|
||||
this.memberLoading = false
|
||||
this.$message.error('场景名称: 仅支持长度为:1~64位字符.格式为:数字、字母、下划线。')
|
||||
return
|
||||
}
|
||||
|
||||
if (description.length > 256) {
|
||||
this.memberLoading = false
|
||||
this.$message.error('描述: 仅支持长度为:1~256位字符')
|
||||
return
|
||||
}
|
||||
|
||||
if ((backOff === '2' || backOff === '4') && triggerInterval < 10) {
|
||||
this.memberLoading = false
|
||||
this.$message.error('描述: 间隔时间最小为10秒')
|
||||
return
|
||||
}
|
||||
|
||||
const target = this.formData.find(item => key === item.key)
|
||||
if (!target) {
|
||||
this.formData.push({
|
||||
key: key,
|
||||
sceneName: sceneName,
|
||||
sceneStatus: sceneStatus,
|
||||
maxRetryCount: maxRetryCount,
|
||||
backOff: backOff,
|
||||
triggerInterval: triggerInterval,
|
||||
description: description,
|
||||
deadlineRequest: deadlineRequest,
|
||||
isDeleted: 0
|
||||
})
|
||||
}
|
||||
|
||||
new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve({ loop: false })
|
||||
}, 200)
|
||||
}).then(() => {
|
||||
const target = this.data.find(item => item.key === key)
|
||||
target.editable = false
|
||||
target.isNew = false
|
||||
this.memberLoading = false
|
||||
this.$message.warning('请点击右下角提交按钮以保存所有页面数据')
|
||||
})
|
||||
},
|
||||
toggle (key) {
|
||||
const target = this.data.find(item => item.key === key)
|
||||
target._originalData = { ...target }
|
||||
target.editable = !target.editable
|
||||
},
|
||||
getRowByKey (key, newData) {
|
||||
const data = this.data
|
||||
return (newData || data).find(item => item.key === key)
|
||||
},
|
||||
cancel (key) {
|
||||
const target = this.data.find(item => item.key === key)
|
||||
Object.keys(target).forEach(key => { target[key] = target._originalData[key] })
|
||||
target._originalData = undefined
|
||||
},
|
||||
handleChange (value, key, column) {
|
||||
if (column === 'backOff') {
|
||||
switch (value) {
|
||||
case '1':
|
||||
this.triggerIntervalDisabled = true
|
||||
this.max = 26
|
||||
break
|
||||
default:
|
||||
this.triggerIntervalDisabled = false
|
||||
this.max = 99999
|
||||
}
|
||||
}
|
||||
|
||||
const newData = [...this.data]
|
||||
const target = newData.find(item => key === item.key)
|
||||
if (target) {
|
||||
target[column] = value
|
||||
this.data = newData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -118,8 +118,6 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row class="form-row" :gutter="16">
|
||||
<a-col :lg="8" :md="24" :sm="24">
|
||||
<a-form-item label="超时时间(秒)">
|
||||
<a-input-number
|
||||
@ -145,12 +143,14 @@
|
||||
v-decorator="[
|
||||
'maxRetryCount',
|
||||
{
|
||||
initialValue: '3',
|
||||
initialValue: '16',
|
||||
rules: [{ required: true, message: '请输入最大重试次数'}]
|
||||
}
|
||||
]" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row class="form-row" :gutter="16">
|
||||
<a-col :lg="8" :md="24" :sm="24">
|
||||
<a-form-item label="调用链超时时间(毫秒)">
|
||||
<a-input-number
|
||||
@ -296,7 +296,7 @@ export default {
|
||||
}).then(() => {
|
||||
const formData = pick(data, [
|
||||
'id', 'sceneName', 'groupName', 'sceneStatus', 'deadlineRequest', 'maxRetryCount', 'description',
|
||||
'backOff', 'triggerInterval'])
|
||||
'backOff', 'triggerInterval', 'executorTimeout'])
|
||||
formData.sceneStatus = formData.sceneStatus.toString()
|
||||
formData.backOff = formData.backOff.toString()
|
||||
this.backOff = formData.backOff
|
||||
|
Loading…
Reference in New Issue
Block a user