feat: 2.5.0

1. 优化客户端注册
2. 优化告警通知
3. 客户端添加命名空间的配置
This commit is contained in:
byteblogs168 2023-11-24 14:21:48 +08:00
parent 2ff534a094
commit 3427401cc9
19 changed files with 492 additions and 173 deletions

View File

@ -23,6 +23,11 @@ import java.util.Objects;
@Setter @Setter
public class EasyRetryProperties { public class EasyRetryProperties {
/**
* 服务端对应的group
*/
private String namespace;
/** /**
* 服务端对应的group * 服务端对应的group
*/ */

View File

@ -4,6 +4,7 @@ import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.aizuda.easy.retry.client.common.cache.GroupVersionCache; import com.aizuda.easy.retry.client.common.cache.GroupVersionCache;
import com.aizuda.easy.retry.client.common.config.EasyRetryProperties; import com.aizuda.easy.retry.client.common.config.EasyRetryProperties;
import com.aizuda.easy.retry.common.core.constant.SystemConstants;
import com.aizuda.easy.retry.common.core.context.SpringContext; import com.aizuda.easy.retry.common.core.context.SpringContext;
import com.aizuda.easy.retry.common.core.enums.HeadersEnum; import com.aizuda.easy.retry.common.core.enums.HeadersEnum;
import com.aizuda.easy.retry.common.core.log.LogUtils; import com.aizuda.easy.retry.common.core.log.LogUtils;
@ -103,6 +104,8 @@ public class NettyChannel {
.set(HeadersEnum.HOST_PORT.getKey(), port) .set(HeadersEnum.HOST_PORT.getKey(), port)
.set(HeadersEnum.VERSION.getKey(), GroupVersionCache.getVersion()) .set(HeadersEnum.VERSION.getKey(), GroupVersionCache.getVersion())
.set(HeadersEnum.HOST.getKey(), serverConfig.getHost()) .set(HeadersEnum.HOST.getKey(), serverConfig.getHost())
.set(HeadersEnum.NAMESPACE.getKey(), Optional.ofNullable(easyRetryProperties.getNamespace()).orElse(
SystemConstants.DEFAULT_NAMESPACE))
; ;
//发送数据 //发送数据

View File

@ -1,7 +1,5 @@
package com.aizuda.easy.retry.common.core.constant; package com.aizuda.easy.retry.common.core.constant;
import java.time.format.DateTimeFormatter;
/** /**
* 系统通用常量 * 系统通用常量
* *
@ -88,4 +86,9 @@ public interface SystemConstants {
* 延迟30s为了尽可能保障集群节点都启动完成在进行rebalance * 延迟30s为了尽可能保障集群节点都启动完成在进行rebalance
*/ */
Long SCHEDULE_INITIAL_DELAY = 30L; Long SCHEDULE_INITIAL_DELAY = 30L;
/**
* 默认名称空间
*/
String DEFAULT_NAMESPACE = "764d604ec6fc45f68cd92514c40e9e1a";
} }

View File

@ -17,11 +17,6 @@ public class RetryTaskDTO implements Serializable {
*/ */
private String groupName; private String groupName;
/**
* namespaceId
*/
private String namespaceId;
/** /**
* sceneName * sceneName
*/ */

View File

@ -62,6 +62,7 @@ public interface ConfigAccess<T> extends Access<T> {
* @param notifyScene {@link NotifySceneEnum} 场景类型 * @param notifyScene {@link NotifySceneEnum} 场景类型
* @return {@link NotifyConfig} 场景配置 * @return {@link NotifyConfig} 场景配置
*/ */
@Deprecated
List<NotifyConfig> getNotifyConfigByGroupNameAndSceneName(String groupName, String sceneName, Integer notifyScene); List<NotifyConfig> getNotifyConfigByGroupNameAndSceneName(String groupName, String sceneName, Integer notifyScene);
/** /**

View File

@ -7,12 +7,15 @@ import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List;
@Mapper @Mapper
public interface ServerNodeMapper extends BaseMapper<ServerNode> { public interface ServerNodeMapper extends BaseMapper<ServerNode> {
int insertOrUpdate(ServerNode record); int insertOrUpdate(ServerNode record);
int insertOrUpdate(List<ServerNode> records);
int deleteByExpireAt(@Param("endTime") LocalDateTime endTime); int deleteByExpireAt(@Param("endTime") LocalDateTime endTime);
} }

View File

@ -20,10 +20,13 @@
<insert id="insertOrUpdate" parameterType="com.aizuda.easy.retry.template.datasource.persistence.po.ServerNode" useGeneratedKeys="true" keyProperty="id"> <insert id="insertOrUpdate" parameterType="com.aizuda.easy.retry.template.datasource.persistence.po.ServerNode" useGeneratedKeys="true" keyProperty="id">
insert into server_node (id, group_name, host_id, host_ip, host_port, insert into server_node (id, group_name, host_id, host_ip, host_port,
expire_at, node_type, ext_attrs, context_path, create_dt) expire_at, node_type, ext_attrs, context_path, create_dt)
values (#{id,jdbcType=BIGINT}, #{groupName,jdbcType=VARCHAR}, #{hostId,jdbcType=VARCHAR}, #{hostIp,jdbcType=VARCHAR}, values
#{hostPort,jdbcType=INTEGER}, <foreach collection="records" item="item" index="index" separator="," open="(" close=")">
#{expireAt,jdbcType=TIMESTAMP}, #{nodeType,jdbcType=TINYINT}, #{extAttrs,jdbcType=VARCHAR}, #{contextPath,jdbcType=VARCHAR}, #{createDt,jdbcType=TIMESTAMP} #{id,jdbcType=BIGINT}, #{groupName,jdbcType=VARCHAR}, #{hostId,jdbcType=VARCHAR}, #{hostIp,jdbcType=VARCHAR},
) ON DUPLICATE KEY UPDATE #{hostPort,jdbcType=INTEGER},
#{expireAt,jdbcType=TIMESTAMP}, #{nodeType,jdbcType=TINYINT}, #{extAttrs,jdbcType=VARCHAR}, #{contextPath,jdbcType=VARCHAR}, #{createDt,jdbcType=TIMESTAMP}
</foreach>
ON DUPLICATE KEY UPDATE
expire_at = values(`expire_at`) expire_at = values(`expire_at`)
</insert> </insert>
<delete id="deleteByExpireAt"> <delete id="deleteByExpireAt">

View File

@ -19,12 +19,15 @@
</sql> </sql>
<insert id="insertOrUpdate" parameterType="com.aizuda.easy.retry.template.datasource.persistence.po.ServerNode" useGeneratedKeys="true" keyProperty="id"> <insert id="insertOrUpdate" parameterType="com.aizuda.easy.retry.template.datasource.persistence.po.ServerNode" useGeneratedKeys="true" keyProperty="id">
insert into server_node (id, group_name, host_id, host_ip, host_port, insert into server_node (id, group_name, host_id, host_ip, host_port,
expire_at, node_type, ext_attrs, context_path, create_dt) expire_at, node_type, ext_attrs, context_path, create_dt)
values (#{id,jdbcType=BIGINT}, #{groupName,jdbcType=VARCHAR}, #{hostId,jdbcType=VARCHAR}, #{hostIp,jdbcType=VARCHAR}, values
#{hostPort,jdbcType=INTEGER}, <foreach collection="records" item="item" index="index" separator="," open="(" close=")">
#{expireAt,jdbcType=TIMESTAMP}, #{nodeType,jdbcType=TINYINT}, #{extAttrs,jdbcType=VARCHAR}, #{contextPath,jdbcType=VARCHAR}, #{createDt,jdbcType=TIMESTAMP} #{id,jdbcType=BIGINT}, #{groupName,jdbcType=VARCHAR}, #{hostId,jdbcType=VARCHAR}, #{hostIp,jdbcType=VARCHAR},
) ON DUPLICATE KEY UPDATE #{hostPort,jdbcType=INTEGER},
expire_at = values(`expire_at`) #{expireAt,jdbcType=TIMESTAMP}, #{nodeType,jdbcType=TINYINT}, #{extAttrs,jdbcType=VARCHAR}, #{contextPath,jdbcType=VARCHAR}, #{createDt,jdbcType=TIMESTAMP}
</foreach>
ON DUPLICATE KEY UPDATE
expire_at = values(`expire_at`)
</insert> </insert>
<delete id="deleteByExpireAt"> <delete id="deleteByExpireAt">
delete from server_node delete from server_node

View File

@ -1,16 +0,0 @@
package com.aizuda.easy.retry.server.common;
import com.google.common.cache.Cache;
import com.google.common.util.concurrent.RateLimiter;
/**
* 流量控制
* @author: zuoJunLin
* @date : 2023-11-21 13:04
* @since 2.5.0
*/
public interface FlowControl {
RateLimiter getRateLimiter(Cache<String, RateLimiter> rateLimiterCache, String key, double rateLimiterThreshold);
}

View File

@ -1,4 +1,4 @@
package com.aizuda.easy.retry.server.retry.task.support.cache; package com.aizuda.easy.retry.server.common.cache;
import com.aizuda.easy.retry.common.core.log.LogUtils; import com.aizuda.easy.retry.common.core.log.LogUtils;
import com.aizuda.easy.retry.server.common.Lifecycle; import com.aizuda.easy.retry.server.common.Lifecycle;
@ -43,6 +43,15 @@ public class CacheNotifyRateLimiter implements Lifecycle {
return CACHE.getIfPresent(key); return CACHE.getIfPresent(key);
} }
/**
* 获取所有缓存
*
* @return 缓存对象
*/
public static void put(String key, RateLimiter rateLimiter) {
CACHE.put(key, rateLimiter);
}
@Override @Override
public void start() { public void start() {
LogUtils.info(log, "CacheNotifyRateLimiter start"); LogUtils.info(log, "CacheNotifyRateLimiter start");

View File

@ -1,8 +1,36 @@
package com.aizuda.easy.retry.server.common.flow.control; package com.aizuda.easy.retry.server.common.flow.control;
import com.aizuda.easy.retry.server.common.FlowControl;
import com.google.common.cache.Cache; import com.aizuda.easy.retry.common.core.alarm.Alarm;
import com.aizuda.easy.retry.common.core.alarm.AlarmContext;
import com.aizuda.easy.retry.common.core.alarm.EasyRetryAlarmFactory;
import com.aizuda.easy.retry.common.core.enums.NotifySceneEnum;
import com.aizuda.easy.retry.common.core.enums.StatusEnum;
import com.aizuda.easy.retry.common.core.log.LogUtils;
import com.aizuda.easy.retry.server.common.cache.CacheNotifyRateLimiter;
import com.aizuda.easy.retry.server.common.triple.ImmutableTriple;
import com.aizuda.easy.retry.server.common.triple.Triple;
import com.aizuda.easy.retry.template.datasource.access.AccessTemplate;
import com.aizuda.easy.retry.template.datasource.persistence.po.NotifyConfig;
import com.aizuda.easy.retry.template.datasource.persistence.po.RetryDeadLetter;
import com.aizuda.easy.retry.template.datasource.persistence.po.RetryTask;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.util.concurrent.RateLimiter; import com.google.common.util.concurrent.RateLimiter;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.util.CollectionUtils;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/** /**
@ -10,13 +38,110 @@ import java.util.Objects;
* @date : 2023-11-21 13:04 * @date : 2023-11-21 13:04
* @since 2.5.0 * @since 2.5.0
*/ */
public abstract class AbstractFlowControl implements FlowControl { @Slf4j
public abstract class AbstractFlowControl<E extends ApplicationEvent> implements ApplicationListener<E> {
public RateLimiter getRateLimiter(Cache<String, RateLimiter> rateLimiterCache, String key, double rateLimiterThreshold) {
RateLimiter rateLimiter = rateLimiterCache.getIfPresent(key); @Autowired
if (Objects.isNull(rateLimiter)||rateLimiter.getRate()!=rateLimiterThreshold) { private EasyRetryAlarmFactory easyRetryAlarmFactory;
rateLimiterCache.put(key, RateLimiter.create(rateLimiterThreshold));
@Autowired
protected AccessTemplate accessTemplate;
protected RateLimiter getRateLimiter(String key, double rateLimiterThreshold) {
RateLimiter rateLimiter = CacheNotifyRateLimiter.getRateLimiterByKey(key);
if (Objects.isNull(rateLimiter) || rateLimiter.getRate() != rateLimiterThreshold) {
CacheNotifyRateLimiter.put(key, RateLimiter.create(rateLimiterThreshold));
} }
return rateLimiterCache.getIfPresent(key);
return rateLimiter;
}
protected Map<Triple<String, String, String>, List<NotifyConfig>> getNotifyConfigMap(final Set<String> namespaceIds,
final Set<String> groupNames, final Set<String> sceneNames) {
// 批量获取所需的通知配置
List<NotifyConfig> notifyConfigs = accessTemplate.getNotifyConfigAccess().list(
new LambdaQueryWrapper<NotifyConfig>()
.eq(NotifyConfig::getNotifyStatus, StatusEnum.YES)
.eq(NotifyConfig::getNotifyScene, NotifySceneEnum.RETRY_TASK_ENTER_DEAD_LETTER.getNotifyScene())
.in(NotifyConfig::getNamespaceId, namespaceIds)
.in(NotifyConfig::getGroupName, groupNames)
.in(NotifyConfig::getSceneName, sceneNames)
);
if (CollectionUtils.isEmpty(notifyConfigs)) {
return null;
}
return notifyConfigs.stream()
.collect(Collectors.groupingBy(i -> {
String namespaceId = i.getNamespaceId();
String groupName = i.getGroupName();
String sceneName = i.getSceneName();
return ImmutableTriple.of(namespaceId, groupName, sceneName);
}));
}
protected void doSendAlarm(Triple<String, String, String> key,
List<NotifyConfig> notifyConfigsList,
AlarmDTO alarmDTO
) {
for (final NotifyConfig notifyConfig : notifyConfigsList) {
if (Objects.equals(notifyConfig.getRateLimiterStatus(), StatusEnum.YES.getStatus())) {
// 限流
RateLimiter rateLimiter = getRateLimiter(String.valueOf(notifyConfig.getId()),
notifyConfig.getRateLimiterThreshold());
// 每秒发送rateLimiterThreshold个告警
if (Objects.nonNull(rateLimiter) && !rateLimiter.tryAcquire(1, TimeUnit.SECONDS)) {
LogUtils.warn(log,
"namespaceId:[{}] groupName:[{}] senceName:[{}] idempotentId:[{}] 任务重试失败进入死信队列已到达最大限流阈值,本次通知不执行",
key.getLeft(), key.getMiddle(), key.getRight(),
alarmDTO.getIdempotentId());
continue;
}
}
AlarmContext context = buildAlarmContext(alarmDTO, notifyConfig);
Alarm<AlarmContext> alarmType = easyRetryAlarmFactory.getAlarmType(
notifyConfig.getNotifyType());
alarmType.asyncSendMessage(context);
}
}
protected abstract AlarmContext buildAlarmContext(final AlarmDTO alarmDTO, NotifyConfig notifyConfig);
@Data
public static class AlarmDTO {
private String uniqueId;
private String groupName;
private String sceneName;
private String idempotentId;
private String bizNo;
private String executorName;
private String argsStr;
private Integer retryCount;
private LocalDateTime createDt;
}
@Mapper
public interface AlarmDTOConverter {
AlarmDTOConverter INSTANCE = Mappers.getMapper(AlarmDTOConverter.class);
AlarmDTO toAlarmDTO(RetryDeadLetter retryDeadLetter);
AlarmDTO toAlarmDTO(RetryTask retryTask);
} }
} }

View File

@ -10,6 +10,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List;
/** /**
* @author www.byteblogs.com * @author www.byteblogs.com
@ -32,16 +33,23 @@ public abstract class AbstractRegister implements Register, Lifecycle {
return doRegister(context, serverNode); return doRegister(context, serverNode);
} }
protected void refreshExpireAt(ServerNode serverNode) { protected void refreshExpireAt(List<ServerNode> serverNodes) {
try { try {
serverNode.setExpireAt(getExpireAt());
serverNodeMapper.insertOrUpdate(serverNode); for (final ServerNode serverNode : serverNodes) {
// 刷新本地缓存过期时间 serverNode.setExpireAt(getExpireAt());
CacheRegisterTable.refreshExpireAt(serverNode); }
serverNodeMapper.insertOrUpdate(serverNodes);
for (final ServerNode serverNode : serverNodes) {
// 刷新本地缓存过期时间
CacheRegisterTable.refreshExpireAt(serverNode);
}
}catch (Exception e) { }catch (Exception e) {
LogUtils.error(log,"注册节点失败 groupName:[{}] hostIp:[{}]", LogUtils.error(log,"注册节点失败", e);
serverNode.getGroupName(), serverNode.getHostIp(), e);
} }
} }

View File

@ -5,13 +5,17 @@ import com.aizuda.easy.retry.common.core.enums.NodeTypeEnum;
import com.aizuda.easy.retry.common.core.log.LogUtils; import com.aizuda.easy.retry.common.core.log.LogUtils;
import com.aizuda.easy.retry.server.common.cache.CacheConsumerGroup; import com.aizuda.easy.retry.server.common.cache.CacheConsumerGroup;
import com.aizuda.easy.retry.server.common.cache.CacheRegisterTable; import com.aizuda.easy.retry.server.common.cache.CacheRegisterTable;
import com.aizuda.easy.retry.template.datasource.persistence.po.RetryTask;
import com.aizuda.easy.retry.template.datasource.persistence.po.ServerNode; import com.aizuda.easy.retry.template.datasource.persistence.po.ServerNode;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -19,6 +23,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/** /**
* 客户端注册 * 客户端注册
@ -35,7 +40,7 @@ public class ClientRegister extends AbstractRegister implements Runnable {
public static final int DELAY_TIME = 30; public static final int DELAY_TIME = 30;
private Thread THREAD = null; private Thread THREAD = null;
protected static final LinkedBlockingDeque<ServerNode> QUEUE = new LinkedBlockingDeque<>(256); protected static final LinkedBlockingDeque<ServerNode> QUEUE = new LinkedBlockingDeque<>(1000);
@Override @Override
public boolean supports(int type) { public boolean supports(int type) {
@ -84,7 +89,16 @@ public class ClientRegister extends AbstractRegister implements Runnable {
try { try {
ServerNode serverNode = QUEUE.poll(5L, TimeUnit.SECONDS); ServerNode serverNode = QUEUE.poll(5L, TimeUnit.SECONDS);
if (Objects.nonNull(serverNode)) { if (Objects.nonNull(serverNode)) {
refreshExpireAt(serverNode); List<ServerNode> lists = Lists.newArrayList(serverNode);
QUEUE.drainTo(lists, 100);
// 去重
lists = new ArrayList<>(lists.stream()
.collect(
Collectors.toMap(ServerNode::getHostId, node -> node, (existing, replacement) -> existing))
.values());
refreshExpireAt(lists);
} }
// 同步当前POD消费的组的节点信息 // 同步当前POD消费的组的节点信息
@ -92,16 +106,18 @@ public class ClientRegister extends AbstractRegister implements Runnable {
ConcurrentMap<String, String> allConsumerGroupName = CacheConsumerGroup.getAllConsumerGroupName(); ConcurrentMap<String, String> allConsumerGroupName = CacheConsumerGroup.getAllConsumerGroupName();
if (!CollectionUtils.isEmpty(allConsumerGroupName)) { if (!CollectionUtils.isEmpty(allConsumerGroupName)) {
List<ServerNode> serverNodes = serverNodeMapper.selectList( List<ServerNode> serverNodes = serverNodeMapper.selectList(
new LambdaQueryWrapper<ServerNode>() new LambdaQueryWrapper<ServerNode>()
.eq(ServerNode::getNodeType, NodeTypeEnum.CLIENT.getType()) .eq(ServerNode::getNodeType, NodeTypeEnum.CLIENT.getType())
.in(ServerNode::getNamespaceId, new HashSet<>(allConsumerGroupName.values())) .in(ServerNode::getNamespaceId, new HashSet<>(allConsumerGroupName.values()))
.in(ServerNode::getGroupName, allConsumerGroupName.keySet())); .in(ServerNode::getGroupName, allConsumerGroupName.keySet()));
for (final ServerNode node : serverNodes) { for (final ServerNode node : serverNodes) {
// 刷新全量本地缓存 // 刷新全量本地缓存
CacheRegisterTable.addOrUpdate(node); CacheRegisterTable.addOrUpdate(node);
} }
} }
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
} catch (Exception e) { } catch (Exception e) {
LogUtils.error(log, "client refresh expireAt error."); LogUtils.error(log, "client refresh expireAt error.");
} finally { } finally {
@ -109,6 +125,7 @@ public class ClientRegister extends AbstractRegister implements Runnable {
try { try {
TimeUnit.MILLISECONDS.sleep(5000); TimeUnit.MILLISECONDS.sleep(5000);
} catch (InterruptedException ignored) { } catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
} }
} }
} }

View File

@ -12,6 +12,7 @@ import com.aizuda.easy.retry.server.common.config.SystemProperties;
import com.aizuda.easy.retry.server.common.dto.ServerNodeExtAttrs; import com.aizuda.easy.retry.server.common.dto.ServerNodeExtAttrs;
import com.aizuda.easy.retry.server.common.handler.ServerNodeBalance; import com.aizuda.easy.retry.server.common.handler.ServerNodeBalance;
import com.aizuda.easy.retry.template.datasource.persistence.po.ServerNode; import com.aizuda.easy.retry.template.datasource.persistence.po.ServerNode;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.ServerProperties;
@ -76,7 +77,7 @@ public class ServerRegister extends AbstractRegister {
@Override @Override
protected boolean doRegister(RegisterContext context, ServerNode serverNode) { protected boolean doRegister(RegisterContext context, ServerNode serverNode) {
refreshExpireAt(serverNode); refreshExpireAt(Lists.newArrayList(serverNode));
return Boolean.TRUE; return Boolean.TRUE;
} }

View File

@ -0,0 +1,41 @@
package com.aizuda.easy.retry.server.common.triple;
/**
* @author: xiaowoniu
* @date : 2023-11-24 08:56
* @since : 2.5.0
*/
public final class ImmutableTriple<L, M, R> extends Triple<L, M, R> {
private static final long serialVersionUID = 1L;
public final L left;
public final M middle;
public final R right;
public static <L, M, R> ImmutableTriple<L, M, R> of(final L left, final M middle, final R right) {
return new ImmutableTriple<>(left, middle, right);
}
public ImmutableTriple(final L left, final M middle, final R right) {
super();
this.left = left;
this.middle = middle;
this.right = right;
}
@Override
public L getLeft() {
return left;
}
@Override
public M getMiddle() {
return middle;
}
@Override
public R getRight() {
return right;
}
}

View File

@ -0,0 +1,66 @@
package com.aizuda.easy.retry.server.common.triple;
import cn.hutool.core.builder.CompareToBuilder;
import java.io.Serializable;
import java.util.Objects;
/**
* @author: xiaowoniu
* @date : 2023-11-24 08:54
* @since : 2.5.0
*/
public abstract class Triple<L, M, R> implements Comparable<Triple<L, M, R>>, Serializable {
private static final long serialVersionUID = 1L;
public static <L, M, R> Triple<L, M, R> of(final L left, final M middle, final R right) {
return new ImmutableTriple<>(left, middle, right);
}
public abstract L getLeft();
public abstract M getMiddle();
public abstract R getRight();
@Override
public int compareTo(final Triple<L, M, R> other) {
return new CompareToBuilder().append(getLeft(), other.getLeft())
.append(getMiddle(), other.getMiddle())
.append(getRight(), other.getRight()).toComparison();
}
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Triple<?, ?, ?>) {
final Triple<?, ?, ?> other = (Triple<?, ?, ?>) obj;
return Objects.equals(getLeft(), other.getLeft())
&& Objects.equals(getMiddle(), other.getMiddle())
&& Objects.equals(getRight(), other.getRight());
}
return false;
}
@Override
public int hashCode() {
return (getLeft() == null ? 0 : getLeft().hashCode()) ^
(getMiddle() == null ? 0 : getMiddle().hashCode()) ^
(getRight() == null ? 0 : getRight().hashCode());
}
@Override
public String toString() {
return "(" + getLeft() + "," + getMiddle() + "," + getRight() + ")";
}
public String toString(final String format) {
return String.format(format, getLeft(), getMiddle(), getRight());
}
}

View File

@ -34,7 +34,7 @@ public class ConfigVersionSyncHandler implements Lifecycle, Runnable {
private static final LinkedBlockingQueue<ConfigSyncTask> QUEUE = new LinkedBlockingQueue<>(256); private static final LinkedBlockingQueue<ConfigSyncTask> QUEUE = new LinkedBlockingQueue<>(256);
public static final String URL = "http://{0}:{1}/{2}/retry/sync/version/v1"; public static final String URL = "http://{0}:{1}/{2}/retry/sync/version/v1";
public static Thread THREAD = null; public Thread THREAD = null;
@Autowired @Autowired
private RestTemplate restTemplate; private RestTemplate restTemplate;
@Autowired @Autowired

View File

@ -1,44 +1,44 @@
package com.aizuda.easy.retry.server.retry.task.support.listener; package com.aizuda.easy.retry.server.retry.task.support.listener;
import com.aizuda.easy.retry.common.core.alarm.Alarm;
import com.aizuda.easy.retry.common.core.alarm.AlarmContext; import com.aizuda.easy.retry.common.core.alarm.AlarmContext;
import com.aizuda.easy.retry.common.core.alarm.EasyRetryAlarmFactory;
import com.aizuda.easy.retry.common.core.enums.NotifySceneEnum;
import com.aizuda.easy.retry.common.core.enums.StatusEnum;
import com.aizuda.easy.retry.common.core.log.LogUtils; import com.aizuda.easy.retry.common.core.log.LogUtils;
import com.aizuda.easy.retry.common.core.util.EnvironmentUtils; import com.aizuda.easy.retry.common.core.util.EnvironmentUtils;
import com.aizuda.easy.retry.common.core.util.HostUtils; import com.aizuda.easy.retry.common.core.util.HostUtils;
import com.aizuda.easy.retry.server.common.Lifecycle; import com.aizuda.easy.retry.server.common.Lifecycle;
import com.aizuda.easy.retry.server.common.flow.control.AbstractFlowControl; import com.aizuda.easy.retry.server.common.flow.control.AbstractFlowControl;
import com.aizuda.easy.retry.server.common.util.DateUtils; import com.aizuda.easy.retry.server.common.util.DateUtils;
import com.aizuda.easy.retry.server.retry.task.support.cache.CacheNotifyRateLimiter; import com.aizuda.easy.retry.server.common.triple.ImmutableTriple;
import com.aizuda.easy.retry.server.common.triple.Triple;
import com.aizuda.easy.retry.server.retry.task.support.event.RetryTaskFailDeadLetterAlarmEvent; import com.aizuda.easy.retry.server.retry.task.support.event.RetryTaskFailDeadLetterAlarmEvent;
import com.aizuda.easy.retry.template.datasource.access.AccessTemplate; import com.aizuda.easy.retry.template.datasource.access.AccessTemplate;
import com.aizuda.easy.retry.template.datasource.persistence.po.NotifyConfig; import com.aizuda.easy.retry.template.datasource.persistence.po.NotifyConfig;
import com.aizuda.easy.retry.template.datasource.persistence.po.RetryDeadLetter; import com.aizuda.easy.retry.template.datasource.persistence.po.RetryDeadLetter;
import com.google.common.cache.Cache;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* 重试任务失败进入死信队列监听器 * 重试任务失败进入死信队列监听器
*
* @author: zuoJunLin * @author: zuoJunLin
* @date : 2023-11-20 21:40 * @date : 2023-11-20 21:40
* @since 2.5.0 * @since 2.5.0
*/ */
@Component @Component
@Slf4j @Slf4j
public class RetryTaskFailDeadLetterAlarmListener extends AbstractFlowControl implements Runnable, Lifecycle, ApplicationListener<RetryTaskFailDeadLetterAlarmEvent> { public class RetryTaskFailDeadLetterAlarmListener extends AbstractFlowControl<RetryTaskFailDeadLetterAlarmEvent> implements Runnable, Lifecycle {
/** /**
* 死信告警数据 * 死信告警数据
@ -46,89 +46,109 @@ public class RetryTaskFailDeadLetterAlarmListener extends AbstractFlowControl im
private LinkedBlockingQueue<List<RetryDeadLetter>> queue = new LinkedBlockingQueue<>(1000); private LinkedBlockingQueue<List<RetryDeadLetter>> queue = new LinkedBlockingQueue<>(1000);
private static String retryTaskDeadTextMessagesFormatter = private static String retryTaskDeadTextMessagesFormatter =
"<font face=\"微软雅黑\" color=#ff0000 size=4>{}环境 重试任务失败进入死信队列</font> \n" + "<font face=\"微软雅黑\" color=#ff0000 size=4>{}环境 重试任务失败进入死信队列</font> \n" +
"> 组名称:{} \n" + "> 组名称:{} \n" +
"> 执行器名称:{} \n" + "> 执行器名称:{} \n" +
"> 场景名称:{} \n" + "> 场景名称:{} \n" +
"> 业务数据:{} \n" + "> 业务数据:{} \n" +
"> 时间:{} \n"; "> 时间:{} \n";
@Autowired
private EasyRetryAlarmFactory easyRetryAlarmFactory;
@Autowired @Autowired
protected AccessTemplate accessTemplate; protected AccessTemplate accessTemplate;
private Thread thread;
@Override @Override
public void start() { public void start() {
new Thread(this).start(); thread = new Thread(this);
thread.start();
} }
@Override @Override
public void close() { public void close() {
if (Objects.nonNull(thread)) {
thread.interrupt();
}
} }
@Override @Override
public void run() { public void run() {
LogUtils.info(log, "RetryTaskFailDeadLetterAlarmListener time[{}] ip:[{}]", LocalDateTime.now(), HostUtils.getIp()); LogUtils.info(log, "RetryTaskFailDeadLetterAlarmListener time[{}] ip:[{}]", LocalDateTime.now(),
for (; ; ) { HostUtils.getIp());
while (!Thread.currentThread().isInterrupted()) {
try { try {
List<RetryDeadLetter> allRetryDeadLetterList = queue.poll(5, TimeUnit.SECONDS); List<RetryDeadLetter> allRetryDeadLetterList = queue.poll(5, TimeUnit.SECONDS);
if (CollectionUtils.isEmpty(allRetryDeadLetterList)) { if (CollectionUtils.isEmpty(allRetryDeadLetterList)) {
continue; continue;
} }
//组分组
Map<String, List<RetryDeadLetter>> groupNameMap = allRetryDeadLetterList.stream() Set<String> namespaceIds = new HashSet<>();
.collect(Collectors.groupingBy(RetryDeadLetter::getGroupName)); Set<String> groupNames = new HashSet<>();
groupNameMap.forEach(((groupName, groupRetryDeadLetterList) -> { Set<String> sceneNames = new HashSet<>();
//场景分组 Map<Triple<String, String, String>, List<RetryDeadLetter>> retryDeadLetterMap = getRetryDeadLetterMap(
Map<String, List<RetryDeadLetter>> sceneNameMap = groupRetryDeadLetterList.stream() allRetryDeadLetterList, namespaceIds, groupNames, sceneNames);
.collect(Collectors.groupingBy(RetryDeadLetter::getSceneName));
sceneNameMap.forEach(((sceneName, sceneRetryDeadLetterList) -> { Map<Triple<String, String, String>, List<NotifyConfig>> notifyConfigMap = getNotifyConfigMap(
//获取通知配置 namespaceIds, groupNames, sceneNames);
List<NotifyConfig> notifyConfigs = accessTemplate.getNotifyConfigAccess().getNotifyConfigByGroupNameAndSceneName(groupName, sceneName, NotifySceneEnum.RETRY_TASK_ENTER_DEAD_LETTER.getNotifyScene()); if (notifyConfigMap == null) {
for (RetryDeadLetter retryDeadLetter : sceneRetryDeadLetterList) { continue;
for (NotifyConfig notifyConfig : notifyConfigs) { }
if (Objects.equals(notifyConfig.getRateLimiterStatus(),StatusEnum.YES.getStatus())) {
//限流 // 循环发送消息
RateLimiter rateLimiter = getRateLimiter(CacheNotifyRateLimiter.getAll(), String.valueOf(notifyConfig.getId()), notifyConfig.getRateLimiterThreshold()); retryDeadLetterMap.forEach((key, list) -> {
if (Objects.nonNull(rateLimiter) && !rateLimiter.tryAcquire(100, TimeUnit.MILLISECONDS)) { List<NotifyConfig> notifyConfigsList = notifyConfigMap.get(key);
LogUtils.warn(log, "组:[{}] 场景:[{}] 幂等id:[{}] 任务重试失败进入死信队列已到达最大限流阈值,本次通知不执行", groupName, sceneName, retryDeadLetter.getIdempotentId()); for (RetryDeadLetter retryDeadLetter : list) {
continue; doSendAlarm(key, notifyConfigsList, AlarmDTOConverter.INSTANCE.toAlarmDTO(retryDeadLetter));
} }
} });
// 预警 } catch (InterruptedException e) {
AlarmContext context = AlarmContext.build().text(retryTaskDeadTextMessagesFormatter, LogUtils.info(log, "retry task fail dead letter alarm stop");
EnvironmentUtils.getActiveProfile(), Thread.currentThread().interrupt();
retryDeadLetter.getGroupName(),
retryDeadLetter.getExecutorName(),
retryDeadLetter.getSceneName(),
retryDeadLetter.getArgsStr(),
DateUtils.format(retryDeadLetter.getCreateDt(), DateUtils.NORM_DATETIME_PATTERN))
.title("{}环境 重试任务失败进入死信队列", EnvironmentUtils.getActiveProfile())
.notifyAttribute(notifyConfig.getNotifyAttribute());
Alarm<AlarmContext> alarmType = easyRetryAlarmFactory.getAlarmType(notifyConfig.getNotifyType());
alarmType.asyncSendMessage(context);
}
}
}));
}));
} catch (Exception e) { } catch (Exception e) {
LogUtils.error(log, "RetryTaskFailDeadLetterAlarmListener queue poll Exception", e); LogUtils.error(log, "RetryTaskFailDeadLetterAlarmListener queue poll Exception", e);
} }
} }
} }
@NotNull
private static Map<Triple<String, String, String>, List<RetryDeadLetter>> getRetryDeadLetterMap(
final List<RetryDeadLetter> allRetryDeadLetterList, final Set<String> namespaceIds,
final Set<String> groupNames, final Set<String> sceneNames) {
return allRetryDeadLetterList.stream()
.collect(Collectors.groupingBy(i -> {
String namespaceId = i.getNamespaceId();
String groupName = i.getGroupName();
String sceneName = i.getSceneName();
namespaceIds.add(namespaceId);
groupNames.add(groupName);
sceneNames.add(sceneName);
return ImmutableTriple.of(namespaceId, groupName, sceneName);
}));
}
@Override @Override
public void onApplicationEvent(RetryTaskFailDeadLetterAlarmEvent event) { public void onApplicationEvent(RetryTaskFailDeadLetterAlarmEvent event) {
try { if (queue.offer(event.getRetryDeadLetters())) {
queue.put(event.getRetryDeadLetters()); LogUtils.warn(log, "任务重试失败进入死信队列告警队列已满");
} catch (InterruptedException e) {
LogUtils.error(log, "RetryTaskFailDeadLetterAlarmListener queue push Exception", e);
} }
} }
@Override
protected AlarmContext buildAlarmContext(final AlarmDTO alarmDTO, final NotifyConfig notifyConfig) {
// 预警
return AlarmContext.build().text(retryTaskDeadTextMessagesFormatter,
EnvironmentUtils.getActiveProfile(),
alarmDTO.getGroupName(),
alarmDTO.getExecutorName(),
alarmDTO.getSceneName(),
alarmDTO.getArgsStr(),
DateUtils.format(alarmDTO.getCreateDt(), DateUtils.NORM_DATETIME_PATTERN))
.title("组:[{}] 场景:[{}] 环境重试任务失败进入死信队列",
alarmDTO.getGroupName(), alarmDTO.getSceneName())
.notifyAttribute(notifyConfig.getNotifyAttribute());
}
} }

View File

@ -1,48 +1,50 @@
package com.aizuda.easy.retry.server.retry.task.support.listener; package com.aizuda.easy.retry.server.retry.task.support.listener;
import com.aizuda.easy.retry.common.core.alarm.Alarm;
import com.aizuda.easy.retry.common.core.alarm.AlarmContext; import com.aizuda.easy.retry.common.core.alarm.AlarmContext;
import com.aizuda.easy.retry.common.core.alarm.EasyRetryAlarmFactory;
import com.aizuda.easy.retry.common.core.enums.NotifySceneEnum;
import com.aizuda.easy.retry.common.core.enums.StatusEnum;
import com.aizuda.easy.retry.common.core.log.LogUtils; import com.aizuda.easy.retry.common.core.log.LogUtils;
import com.aizuda.easy.retry.common.core.util.EnvironmentUtils; import com.aizuda.easy.retry.common.core.util.EnvironmentUtils;
import com.aizuda.easy.retry.common.core.util.HostUtils; import com.aizuda.easy.retry.common.core.util.HostUtils;
import com.aizuda.easy.retry.server.common.Lifecycle; import com.aizuda.easy.retry.server.common.Lifecycle;
import com.aizuda.easy.retry.server.common.flow.control.AbstractFlowControl; import com.aizuda.easy.retry.server.common.flow.control.AbstractFlowControl;
import com.aizuda.easy.retry.server.common.util.DateUtils; import com.aizuda.easy.retry.server.common.util.DateUtils;
import com.aizuda.easy.retry.server.retry.task.support.cache.CacheNotifyRateLimiter; import com.aizuda.easy.retry.server.common.triple.ImmutableTriple;
import com.aizuda.easy.retry.server.common.triple.Triple;
import com.aizuda.easy.retry.server.retry.task.support.event.RetryTaskFailMoreThresholdAlarmEvent; import com.aizuda.easy.retry.server.retry.task.support.event.RetryTaskFailMoreThresholdAlarmEvent;
import com.aizuda.easy.retry.template.datasource.access.AccessTemplate;
import com.aizuda.easy.retry.template.datasource.persistence.po.NotifyConfig; import com.aizuda.easy.retry.template.datasource.persistence.po.NotifyConfig;
import com.aizuda.easy.retry.template.datasource.persistence.po.RetryTask; import com.aizuda.easy.retry.template.datasource.persistence.po.RetryTask;
import com.google.common.util.concurrent.RateLimiter; import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.jetbrains.annotations.NotNull;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit; import java.util.stream.Collectors;
/** /**
* 重试任务失败数量超过阈值监听器 * 重试任务失败数量超过阈值监听器
*
* @author: zuoJunLin * @author: zuoJunLin
* @date : 2023-11-20 21:40 * @date : 2023-11-20 21:40
* @since 2.5.0 * @since 2.5.0
*/ */
@Component @Component
@Slf4j @Slf4j
public class RetryTaskFailMoreThresholdAlarmListener extends AbstractFlowControl implements Runnable, Lifecycle, ApplicationListener<RetryTaskFailMoreThresholdAlarmEvent> { public class RetryTaskFailMoreThresholdAlarmListener extends
AbstractFlowControl<RetryTaskFailMoreThresholdAlarmEvent> implements Runnable, Lifecycle {
/** /**
* 重试任务失败数量超过阈值告警数据 * 重试任务失败数量超过阈值告警数据
*/ */
private LinkedBlockingQueue<RetryTask> queue = new LinkedBlockingQueue<>(1000); private LinkedBlockingQueue<RetryTask> queue = new LinkedBlockingQueue<>(1000);
private Thread thread;
private static String retryTaskDeadTextMessagesFormatter = private static String retryTaskFailMoreThresholdMessagesFormatter =
"<font face=\"微软雅黑\" color=#ff0000 size=4>{}环境 重试任务失败数量超过阈值</font> \n" + "<font face=\"微软雅黑\" color=#ff0000 size=4>{}环境 重试任务失败数量超过阈值</font> \n" +
"> 组名称:{} \n" + "> 组名称:{} \n" +
"> 执行器名称:{} \n" + "> 执行器名称:{} \n" +
"> 场景名称:{} \n" + "> 场景名称:{} \n" +
@ -50,60 +52,55 @@ public class RetryTaskFailMoreThresholdAlarmListener extends AbstractFlowControl
"> 业务数据:{} \n" + "> 业务数据:{} \n" +
"> 时间:{} \n"; "> 时间:{} \n";
@Autowired
private EasyRetryAlarmFactory easyRetryAlarmFactory;
@Autowired
protected AccessTemplate accessTemplate;
@Override @Override
public void start() { public void start() {
new Thread(this).start(); thread = new Thread(this);
thread.start();
} }
@Override @Override
public void close() { public void close() {
if (Objects.nonNull(thread)) {
thread.interrupt();
}
} }
@Override @Override
public void run() { public void run() {
LogUtils.info(log, "RetryTaskFailMoreThresholdAlarmListener time[{}] ip:[{}]", LocalDateTime.now(), HostUtils.getIp()); LogUtils.info(log, "RetryTaskFailMoreThresholdAlarmListener time[{}] ip:[{}]", LocalDateTime.now(),
for (; ; ) { HostUtils.getIp());
while (!Thread.currentThread().isInterrupted()) {
try { try {
RetryTask retryTask = queue.poll(5, TimeUnit.SECONDS);
if (Objects.isNull(retryTask)) { // 无数据时阻塞线程
RetryTask retryTask = queue.take();
// 拉取100条
List<RetryTask> lists = Lists.newArrayList(retryTask);
queue.drainTo(lists, 200);
Set<String> namespaceIds = new HashSet<>();
Set<String> groupNames = new HashSet<>();
Set<String> sceneNames = new HashSet<>();
Map<Triple<String, String, String>, List<RetryTask>> retryTaskMap = getRetryTaskMap(
lists, namespaceIds, groupNames, sceneNames);
Map<Triple<String, String, String>, List<NotifyConfig>> notifyConfigMap = getNotifyConfigMap(
namespaceIds, groupNames, sceneNames);
if (notifyConfigMap == null) {
continue; continue;
} }
//获取通知配置
List<NotifyConfig> notifyConfigs = accessTemplate.getNotifyConfigAccess().getNotifyConfigByGroupNameAndSceneName(retryTask.getGroupName(), retryTask.getSceneName(), NotifySceneEnum.RETRY_TASK_REACH_THRESHOLD.getNotifyScene()); // 循环发送消息
for (NotifyConfig notifyConfig : notifyConfigs) { retryTaskMap.forEach((key, list) -> {
//阈值判断 List<NotifyConfig> notifyConfigsList = notifyConfigMap.get(key);
if (retryTask.getRetryCount() >= notifyConfig.getNotifyThreshold()) { for (RetryTask retryTask1 : list) {
//限流判断 doSendAlarm(key, notifyConfigsList, AlarmDTOConverter.INSTANCE.toAlarmDTO(retryTask1));
if (Objects.equals(notifyConfig.getRateLimiterStatus(), StatusEnum.YES.getStatus())) {
RateLimiter rateLimiter = getRateLimiter(CacheNotifyRateLimiter.getAll(), String.valueOf(notifyConfig.getId()), notifyConfig.getRateLimiterThreshold());
if (Objects.nonNull(rateLimiter) && !rateLimiter.tryAcquire(100, TimeUnit.MILLISECONDS)) {
LogUtils.warn(log, "组:[{}] 场景:[{}] 通知阈值:[{}] 幂等id:[{}] 重试任务失败数量超过阈值已到达最大限流阈值,本次通知不执行", notifyConfig.getGroupName(), notifyConfig.getSceneName(), notifyConfig.getNotifyThreshold(), retryTask.getIdempotentId());
continue;
}
}
// 预警
AlarmContext context = AlarmContext.build().text(retryTaskDeadTextMessagesFormatter,
EnvironmentUtils.getActiveProfile(),
retryTask.getGroupName(),
retryTask.getExecutorName(),
retryTask.getSceneName(),
retryTask.getRetryCount(),
retryTask.getArgsStr(),
DateUtils.format(retryTask.getCreateDt(), DateUtils.NORM_DATETIME_PATTERN))
.title("{}环境 环境重试任务失败数量超过阈值", EnvironmentUtils.getActiveProfile())
.notifyAttribute(notifyConfig.getNotifyAttribute());
Alarm<AlarmContext> alarmType = easyRetryAlarmFactory.getAlarmType(notifyConfig.getNotifyType());
alarmType.asyncSendMessage(context);
} }
} });
} catch (InterruptedException e) {
LogUtils.info(log, "retry task fail more alarm stop");
Thread.currentThread().interrupt();
} catch (Exception e) { } catch (Exception e) {
LogUtils.error(log, "RetryTaskFailMoreThresholdAlarmListener queue poll Exception", e); LogUtils.error(log, "RetryTaskFailMoreThresholdAlarmListener queue poll Exception", e);
} }
@ -112,10 +109,45 @@ public class RetryTaskFailMoreThresholdAlarmListener extends AbstractFlowControl
@Override @Override
public void onApplicationEvent(RetryTaskFailMoreThresholdAlarmEvent event) { public void onApplicationEvent(RetryTaskFailMoreThresholdAlarmEvent event) {
try { if (queue.offer(event.getRetryTask())) {
queue.put(event.getRetryTask()); LogUtils.warn(log, "任务失败数量超过阈值告警队列已满");
} catch (InterruptedException e) {
LogUtils.error(log, "RetryTaskFailMoreThresholdAlarmListener queue push Exception", e);
} }
} }
@Override
protected AlarmContext buildAlarmContext(AlarmDTO alarmDTO, NotifyConfig notifyConfig) {
// 预警
return AlarmContext.build().text(retryTaskFailMoreThresholdMessagesFormatter,
EnvironmentUtils.getActiveProfile(),
alarmDTO.getGroupName(),
alarmDTO.getExecutorName(),
alarmDTO.getSceneName(),
alarmDTO.getRetryCount(),
alarmDTO.getArgsStr(),
DateUtils.format(alarmDTO.getCreateDt(), DateUtils.NORM_DATETIME_PATTERN))
.title("组:[{}] 场景:[{}] 环境重试任务失败数量超过阈值", alarmDTO.getGroupName(), alarmDTO.getSceneName())
.notifyAttribute(notifyConfig.getNotifyAttribute());
}
@NotNull
private static Map<Triple<String, String, String>, List<RetryTask>> getRetryTaskMap(
final List<RetryTask> list, final Set<String> namespaceIds,
final Set<String> groupNames, final Set<String> sceneNames) {
return list.stream()
.collect(Collectors.groupingBy(retryTask -> {
String namespaceId = retryTask.getNamespaceId();
String groupName = retryTask.getGroupName();
String sceneName = retryTask.getSceneName();
namespaceIds.add(namespaceId);
groupNames.add(groupName);
sceneNames.add(sceneName);
return ImmutableTriple.of(namespaceId, groupName, sceneName);
}));
}
} }