背景
公司新接入一个监控告警需求,在创建监控告警报事时,需要将在线的服务台的人员选取一位加入到新生成的监控告警群中。
解决思路
服务台人员列表使用轮询的方式(基于内存进行实现)平均分配报事
具体解决方案
@Slf4j
public class PollUtil {
/**
* 缓存有效时间
*/
private static long CACHE_VALID_TIME = 0;
private static Lock lock = new ReentrantLock();
/**
* 服务台用户统计量
*/
private static ConcurrentHashMap<String,Integer> userCount = new ConcurrentHashMap<>(16);
/**
* 指定服务用户id(轮询方式)
*
* @param userIdsByassignment 用户id byassignment
* @param cacheFailureHours 缓存失效时间
* @return {@link String}
*/
public static String designateServiceUserId( Set<String> userIdsByassignment,Integer cacheFailureHours) {
try {
lock.lock();
// 清除缓存信息
clearCache(cacheFailureHours);
// 获取轮询信息
return getMinServiceUserId(userIdsByassignment);
} catch (Exception e) {
log.error("初始化MAP出现异常",e);
} finally {
lock.unlock();
}
return null;
}
/**
* 清除缓存,防止数据误差,指定一个时间段,清理一下缓存数据
*
* @param cacheFailureHours 缓存失效时间(单位小时)
*/
private static void clearCache(Integer cacheFailureHours) {
log.info("当前过期时间为:{}",CACHE_VALID_TIME);
log.info("当前时间为:{}",System.currentTimeMillis());
if (System.currentTimeMillis()>CACHE_VALID_TIME){
log.info("初始化内容中的人员数据表");
userCount.clear();
CACHE_VALID_TIME = System.currentTimeMillis() + 1000*60*60*cacheFailureHours;
}
}
/**
* 得到最小服务用户id
*
* @param userIdsByassignment 用户id byassignment
* @return {@link String}
*/
private static String getMinServiceUserId(Set<String> userIdsByassignment) {
List<String> userList = new ArrayList<>(userIdsByassignment);
Collections.shuffle(userList);
// 1. 初始化未初始化到Map的用户列表
initUserCountMap(userList);
// 2. 获取Map中的最小值
String userId = getMinServiceUserId(userList);
log.info("获取到服务台在线人员OA为:{}", userId);
return userId;
}
/**
* 初始化用户统计图
*
* @param userList 用户列表
*/
private static void initUserCountMap(List<String> userList) {
log.info("userCount:{}",JSON.toJSONString(userCount));
for (String user : userList) {
try {
if (StringUtils.isEmpty(user)){
continue;
}
// 增加人员信息
if (userCount.containsKey(user)){
continue;
}
userCount.put(user, Objects.isNull(userCount.get(user)) ? 0 : userCount.get(user) + 1);
}catch (Exception e){
log.error("初始化到Map中出现异常",e);
}
}
log.info("userCount:{}",JSON.toJSONString(userCount));
}
/**
* 得到最小服务用户id
*
* @param userList 用户列表
* @return {@link String}
*/
private static String getMinServiceUserId(List<String> userList) {
String userId = null;
try {
//lock.lock();
List<Map.Entry<String, Integer>> mapList = new ArrayList(userCount.entrySet());
Collections.sort(mapList, (Comparator.comparingInt(Map.Entry::getValue)));
log.info("排序之后的Map集合为:{}", JSON.toJSONString(mapList));
// 查询当前Map中的用户是否在线
for (Map.Entry<String, Integer> map : mapList) {
if (userList.contains(map.getKey())) {
log.info("服务台同事:{}在线,选定该OA", map.getKey());
userCount.put(map.getKey(), userCount.get(map.getKey()) == null ? 0 : userCount.get(map.getKey()) + 1);
userId = map.getKey();
// 跳出循环
break;
}
}
}catch (Exception e){
log.error("获取服务台在线人员OA出现异常",e);
}finally {
//lock.unlock();
}
return userId;
}
}
总结
此次是基于内存的单机版的轮询方式,还可以用很多方式实现更加适合分布式集群的轮询。