feat:2.4.0

1. 修复分布式锁锁定失败问题
2. Pods页面显示负责消费的桶信息
This commit is contained in:
byteblogs168 2023-11-05 08:15:55 +08:00
parent 1f3c6e4762
commit 95e594fabb
65 changed files with 197 additions and 357 deletions

View File

@ -13,4 +13,5 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/
public interface DistributedLockMapper extends BaseMapper<DistributedLock> {
int updateTest(DistributedLock lock);
}

View File

@ -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>

View File

@ -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() {

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

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

View File

@ -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

View File

@ -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}}]);

View File

@ -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

View File

@ -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 # 重试和死信表的分区总数

View File

@ -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();
}
}

View File

@ -29,6 +29,8 @@ public class SceneConfigResponseVO {
private Long deadlineRequest;
private Integer executorTimeout;
private LocalDateTime createDt;
private LocalDateTime updateDt;

View File

@ -31,5 +31,5 @@ public class ServerNodeResponseVO {
private String extAttrs;
private Set<String> consumerGroup;
private Set<Integer> consumerBuckets;
}

View File

@ -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("获取数据异常"));
}
}

View File

@ -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'] }
}
]
},

View File

@ -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

View File

@ -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
}
}
}
}

View File

@ -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