背景:我们有一个需求是测试定时任务执行器中要调用的地址的连通性,要求是把数据库中现在所有的执行器的url都测试通不通,并返回结果。连通性测试超时时间为2000ms(2s),所以几百个url的连通性耗时就比较长。需要用多线程处理。
下面直接上代码
未修改前的老代码,执行数据异常缓慢,无法响应结果
public BeeResponseEntity urlConnectTestALL() {
//1.从数据库查询所有执行器
List<SchJobExecute> schJobExecutes = executeDaoService.selectAllNoCondition();
if (schJobExecutes == null || schJobExecutes.isEmpty()){
return BeeResponseEntity.failed(ErrorCodeEnum.EXEC_ADD_UNKNOWN_ERROR);
}
//2.遍历,筛选出url不通的执行器
List<SchJobExecute> disConnectExecuteList = schJobExecutes.stream()
.filter(schJobExecute -> !StringUtils.isEmpty(schJobExecute.getReqUrl()))
.filter(schJobExecute -> !NetConnectUtils.telnetUtil(schJobExecute.getReqUrl()))
.collect(Collectors.toList());
LOGGER.info("分行域名不通清单:{}",disConnectExecuteList);
return BeeResponseEntity.success(disConnectExecuteList);
}
修改后的代码
public class SchJobExecuteTask implements Callable<JSONObject> {
private final SchJobExecute schJobExecute;
@Autowired
private SchJobExecuteDaoService executeDaoService;
public SchJobExecuteTask(SchJobExecute schJobExecute) {
this.schJobExecute = schJobExecute;
}
@Override
public JSONObject call() {
// ip端口连通性测试
boolean telnet = NetConnectUtils.telnetUtil(new ExeDomainReqDTO(schJobExecute.getReqUrl()));
if (!telnet) {
return schJobExecuteToJSON(schJobExecute);
}
return null;
}
private JSONObject schJobExecuteToJSON(SchJobExecute schJobExecute) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("执行器Code", schJobExecute.getExeCode());
jsonObject.put("请求地址", schJobExecute.getReqUrl());
jsonObject.put("渠道号", schJobExecute.getBranchNo());
jsonObject.put("创建人", schJobExecute.getCreNam());
jsonObject.put("创建用户ID", schJobExecute.getCreNbr());
return jsonObject;
}
}
private final ThreadFactory threadFactory = new ThreadFactoryBuilder().setNamePrefix("消息处理统一调度线程").build();
private final ExecutorService executorService = new ThreadPoolExecutor(50, 60, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<>(2048), threadFactory, new ThreadPoolExecutor.AbortPolicy());
@Override
public BeeResponseEntity urlConnectTestALL() {
// 从数据库查询所有执行器
List<SchJobExecute> schJobExecutes = executeDaoService.selectAllNoCondition();
try {
// 提交每个schJobExecute对象的处理任务到线程池,并收集结果
List<Future<JSONObject>> futures = executorService.invokeAll(
schJobExecutes.stream()
.filter(schJobExecute -> !StringUtils.isEmpty(schJobExecute.getReqUrl()))
.map(SchJobExecuteTask::new)
.collect(Collectors.toList())
);
// 处理线程池执行结果
List<JSONObject> disConnectExecuteList = new ArrayList<>();
for (Future<JSONObject> future : futures) {
// 获取任务执行结果
JSONObject result = future.get();
if (result != null) {
disConnectExecuteList.add(result);
}
}
LOGGER.info("连不通的执行器数量:{}", disConnectExecuteList.size());
return BeeResponseEntity.success(disConnectExecuteList);
} catch (Exception e) {
LOGGER.error("连通性测试异常:", e);
return BeeResponseEntity.failed(ErrorCodeEnum.SYSTEM_ERROR, "连通性测试异常");
} finally {
// 关闭线程池
executorService.shutdown();
}
}
连通性测试工具类
public class NetConnectUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(NetConnectUtils.class);
public final static Integer DEFAULT_PORT = 80;
public final static Integer DEFAULT_TIMEOUT = 2000;
public final static String REGEX_RULE = "((([a-zA-Z0-9-]+)(\\.[a-zA-Z0-9-]+)+)|localhost)(:[0-9]+)?";
public final static String REGEX_SPLIT = ":";
public String getExecuteFlag(String str) {
return str;
}
/**
* 判断url地址中的域名网络是否通畅
*
* @param dto
* @return
*/
public static boolean telnetUtil(ExeDomainReqDTO dto) {
ExeDomainDTO dtoNew = createExeDomainDTObyString(dto);
if (null == dtoNew) {
return false;
}
boolean isConnected = false;
Socket socket = new Socket();
try {
socket.connect(new InetSocketAddress(dtoNew.getDomain(), dtoNew.getPort()), dtoNew.getTimeout());
isConnected = socket.isConnected();
} catch (IOException e) {
LOGGER.info("域名:{}:{} 连接异常:{}", dtoNew.getDomain(), dtoNew.getPort(), e.getMessage());
} finally {
try {
socket.close();
} catch (IOException e) {
LOGGER.info("socket关闭失败异常信息:{}", e.getMessage());
}
}
return isConnected;
}
/**
* 根据字符串创建域名对象
*
* @param dto
* @return
*/
private static ExeDomainDTO createExeDomainDTObyString(ExeDomainReqDTO dto) {
ExeDomainDTO domainDTO = new ExeDomainDTO();
Pattern pattern = Pattern.compile(REGEX_RULE);
Matcher matcher = pattern.matcher(dto.getDomain());
if (matcher.find()) {
domainDTO.setTimeout(dto.getTimeout() == null ? DEFAULT_TIMEOUT : dto.getTimeout());
// group有两种格式:域名和端口:域名,需要判断一下
String group = matcher.group();
String[] splitStr = group.split(REGEX_SPLIT);
if (splitStr.length == 1) {
domainDTO.setDomain(splitStr[0]);
domainDTO.setPort(DEFAULT_PORT);
} else {
domainDTO.setDomain(splitStr[0]);
domainDTO.setPort(Integer.valueOf(splitStr[1]));
}
return domainDTO;
}
return null;
}
}
到这里就结束了,记录这篇文章,便于以后参考。