业务场景:定时器中每天凌晨执行一段逻辑,因为调用外部接口可能失败从而导致数据过不来,所以在定时器中如果调用外部接口失败,那么就启用一个新的定时器,高频率的调用接口,直到成功
下面是实例:
@Component
@Slf4j
public class TimerPadIpController {
@Autowired
private RFIDPadIpService rfidPadIpService;
@Scheduled(cron = "0 0 2 * * ?")
public void getPadDataAndSave() {
String url = NewStaticCommon.ipAndPadUrl;
//请求数据
String httpMap = sendHttpRequest(url);
int day = LocalDateTime.now().getDayOfMonth();
if (httpMap==null){
log.info("调用更新pad数据的接口返回信息为null");
//启用线程来循环调用
new Timer().schedule(new TimerTask() {
public void run() {
String result = sendHttpRequest(url);
if(result!=null){
saveData(result);
super.cancel();
}
if( LocalDateTime.now().getDayOfMonth()!=day ){
super.cancel();
}
}
},0,1000*60*60);
return;
}
saveData(httpMap);
}
private void saveData(String httpMap){
JSONObject objectResult = JSONObject.parseObject(httpMap);
objectResult = objectResult.getJSONObject("data");
JSONArray jsonRows = objectResult.getJSONArray("rows");
if (jsonRows!= null && jsonRows.size()>0){
List<Map> maps = JSON.parseArray(objectResult.getString("rows"), Map.class);
if(!CollectionUtils.isEmpty(maps)){
savePadDataMethod(maps);
}
}
log.info("调用更新pad数据的接口成功");
}
private void savePadDataMethod(List<Map> maps) {
if (maps!=null&& maps.size()>0){
log.info("更新RIFDPadip数据成功》"+maps.size());
rfidPadIpService.delectAll();
rfidPadIpService.insertSelective(maps);
}
}
private String sendHttpRequest(String utl) {
try {
disableSslVerification();
URL reqURL = new URL(utl );
HttpsURLConnection httpsConn = (HttpsURLConnection)reqURL.openConnection();
//取得该连接的输入流,以读取响应内容
InputStreamReader insr = new InputStreamReader(httpsConn.getInputStream());
StringBuilder stringBuilder = new StringBuilder();
//读取服务器的响应内容并显示
int respInt = insr.read();
while( respInt != -1){
stringBuilder.append((char)respInt);
// System.out.print((char)respInt);
respInt = insr.read();
}
insr.close();
return stringBuilder.toString();
} catch (Exception e) {
log.error("sendHttpRequest---error", e);
}
return null;
}
private static void disableSslVerification() {
try {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
}
}
其中最核心的程序是Timer:
if (httpMap==null){
log.info("调用更新pad数据的接口返回信息为null");
//启用线程来循环调用
new Timer().schedule(new TimerTask() {
public void run() {
String result = sendHttpRequest(url);
if(result!=null){
saveData(result);
super.cancel();
}
if( LocalDateTime.now().getDayOfMonth()!=day ){
super.cancel();
}
}
},0,1000*60*60);
return;
}
如果接口调用失败或者返回数据有问题,那么我就认为是有问题的,然后启用timer循环调用直到成功,需要注意的两点:
- 1.Timer是一个新的线程,与主线程独立分开,所以才有了if里面的return,以防止主线程进入下一个代码
- 2.因为是在定时器里面启动Timer,而定时器每天都会执行一次,有一种异常情况就是加入接口一直调用不成功,那么到了第二天定时器就又会启用,这个时候又会启动一个Timer,如果持续很多天,这样就启动了很多重复的Timer,所以这里加入一个逻辑,如果定时器启动的天与Timer执行的不一致,那么Timer给停止掉,防止不断的生成新的Timer。
网友评论