MonitorService.java
16.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
package com.yoho.rfid.service;
import com.yoho.rfid.constant.BackWorkerType;
import com.yoho.rfid.helper.MonitorHelper;
import com.yoho.rfid.model.ClientConfig;
import com.yoho.rfid.model.MonitorFuture;
import com.yoho.rfid.model.SystemConfig;
import com.yoho.rfid.model.TreadNode;
import com.yoho.rfid.model.req.AppReportReq;
import com.yoho.rfid.model.req.InfoScreenHeartBeatPacket;
import com.yoho.rfid.thread.MonitorThreadFactory;
import com.yoho.rfid.util.DateUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.MessageFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
/**
* Created by chenchao on 2017/10/16.
*/
@Service
public class MonitorService extends AbstractMonitorService{
private Logger logger = LoggerFactory.getLogger(getClass());
//
private final static ConcurrentHashMap<String, InfoScreenHeartBeatPacket> HeartBeatMap = new ConcurrentHashMap<>();
//
private final static ConcurrentHashMap<String, AppReportReq> AppReportMap = new ConcurrentHashMap<>();
static final int corePoolSize = 2;
static final int maximumPoolSize = 2;
MonitorThreadFactory monitorThreadFactory = new MonitorThreadFactory();
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
private ExecutorService executorService = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
0L, TimeUnit.MILLISECONDS, workQueue, monitorThreadFactory);
@Autowired
private InfoScreenService infoScreenService;
@Autowired
private MailService mailService;
private HeartBeatTask heartBeatTask;
private AppReportTask appReportTask;
private List<MonitorFuture> backendFutures = new ArrayList<>(2);
@PostConstruct
public void init(){
logger.info("in MonitorService.init");
appReportTask = new AppReportTask();
backendFutures.add(new MonitorFuture(BackWorkerType.appReport, executorService.submit(appReportTask)));
logger.info("appReportTask start");
//
heartBeatTask = new HeartBeatTask();
backendFutures.add(new MonitorFuture(BackWorkerType.heartBeat, executorService.submit(heartBeatTask)));
logger.info("heartBeatTask start");
}
/**
* todo reload
* @param type
*/
public void reload(String type){
//todo cancel runing task by type
for(MonitorFuture future : backendFutures){
if(StringUtils.isBlank(type)){
}else{
}
}
//todo load heartBeatTask or appReportTask by type
}
public List<TreadNode> getThreadNodeList() {
List<TreadNode> threadNodeList = new ArrayList<>(2);
TreadNode heartBeatNode = new TreadNode();
heartBeatNode.setName("heartBeatTask");
long alivetime = heartBeatTask.aliveDatetime;
heartBeatNode.setAliveDatetime(alivetime);
if (alivetime>0L){
heartBeatNode.setAliveDateTime(new SimpleDateFormat(DateUtil.DATE_TIME_FORMAT).format(new Date(alivetime)) );
}
threadNodeList.add(heartBeatNode);
//todo add appReportTask
return threadNodeList;
}
public void acceptHeartBeat(InfoScreenHeartBeatPacket packet){
if(StringUtils.isBlank(packet.getIp())){
return;
}
if (Objects.isNull(packet.getScreenType())){
return;
}
packet.setUpdateDateTime(System.currentTimeMillis());
//update with an new object
if(HeartBeatMap.containsKey(packet.getIp())){
HeartBeatMap.replace(packet.getIp(), packet);
}else {
HeartBeatMap.put(packet.getIp(), packet);
}
}
public void acceptAppReport(AppReportReq req){
if(StringUtils.isBlank(req.getIp())){
return;
}
if (Objects.isNull(req.getScreenType())){
return;
}
if (Objects.isNull(req.getEventName())){
return;
}
req.setUpdateDateTime(System.currentTimeMillis());
if (Objects.equals(CRASH, req.getEventName())){
if (AppReportMap.containsKey(req.getIp())){
AppReportMap.replace(req.getIp(), req);
}else {
AppReportMap.put(req.getIp(), req);
}
}
if (Objects.equals(REBOOTED, req.getEventName())){
logger.info("in acceptAppReport, app has reboot ,{}",req);
AppReportMap.remove(req.getIp());
}
}
public Map<String, AppReportReq> getAppReport(AppReportReq req){
Optional<AppReportReq> reqOptional = Optional.ofNullable(req);
String ip ;
if (StringUtils.isNotBlank(ip = reqOptional.map(AppReportReq::getIp).orElse(""))){
Map<String, AppReportReq> result = new HashMap<>(1);
result.put(ip, AppReportMap.get(ip));
return result;
}
return AppReportMap;
}
public Map<String,InfoScreenHeartBeatPacket> getHeartBeat(InfoScreenHeartBeatPacket packet){
Optional<InfoScreenHeartBeatPacket> reqOptional = Optional.ofNullable(packet);
String ip ;
if (StringUtils.isNotBlank(ip = reqOptional.map(InfoScreenHeartBeatPacket::getIp).orElse(""))){
Map<String, InfoScreenHeartBeatPacket> result = new HashMap<>(1);
result.put(ip, HeartBeatMap.get(ip));
return result;
}
return HeartBeatMap;
}
//60 second
private static final int appRebootTimeout = 60;
//30 second
private static final int heartBeatTimeout = 60;
private StringBuilder buildMailContent(String ip, int screenType){
StringBuilder sb = new StringBuilder();
sb.append("信息屏[").append(ip).append("]连续多次都没获取到心跳").append("</br>");
sb.append("屏幕类型:").append(screenType).append("</br>");
InfoScreenService.ScreenType screenType1 =
InfoScreenService.ScreenTypeMap.get(screenType);
sb.append("包名/类名:").append(screenType1.getPackageName())
.append("\\").append(screenType1.getClassName());
return sb;
}
private StringBuilder buildMailContent4AppCrash(String ip, int screenType){
StringBuilder sb = new StringBuilder();
sb.append("信息屏[").append(ip).append("]崩溃后1分钟还没启动好").append("</br>");
sb.append("屏幕类型:").append(screenType).append("</br>");
InfoScreenService.ScreenType screenType1 =
InfoScreenService.ScreenTypeMap.get(screenType);
sb.append("包名/类名:").append(screenType1.getPackageName())
.append("\\").append(screenType1.getClassName());
return sb;
}
class AppReportTask implements Runnable{
private long aliveDatetime;
@Override
public void run() {
logger.info("1'st time in AppReportTask");
long cnt = 0L;
long timeout = appRebootTimeout*1000L;
while(true){
try {
Thread.currentThread().sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(MapUtils.isEmpty(AppReportMap)){
if (cnt % 60 == 0) {
logger.warn("y AppReportMap empty");
cnt = 0L;
}
cnt++;
}else{
List<String> needRemove = new ArrayList<>();
for(Map.Entry<String, AppReportReq> entry : AppReportMap.entrySet()){
AppReportReq req = entry.getValue();
if (MonitorHelper.isWhiteIp(req.getIp(), SystemConfig.getInstance().getHostWhiteList())){
logger.warn("in AppReportTask, a white ip {}", req.getIp());
needRemove.add(req.getIp());
continue;
}
long lastUpdateDT = req.getUpdateDateTime();
boolean isTimeout = isTimeout(timeout, lastUpdateDT);
if(isTimeout){
logger.warn("AppReportMap find reboot, req {} timeout {}",
req, timeout);
//reboot
//infoScreenService.reboot(req.getIp(), req.getScreenType());
//TODO use queue to split send mail function, use a thread to send mail
boolean cansend = canSend4GlobalScope() && canSend4AppReport();
if(cansend){
mailService.send(buildMailContent4AppCrash(req.getIp(), req.getScreenType()).toString());
needRemove.add(req.getIp());
}
}
}
//remove from map
if(CollectionUtils.isNotEmpty(needRemove)){
needRemove.stream().forEach(ip -> AppReportMap.remove(ip));
}
}
aliveDatetime = System.currentTimeMillis();
}
}
public long getCnt(){
return aliveDatetime;
}
}
class HeartBeatTask implements Runnable{
private boolean isNoticedOpenClient = false;
private long aliveDatetime;
@Override
public void run() {
logger.info("1'st time in HeartBeatTask");
long cnt = 0L;
long timeout = heartBeatTimeout*1000L;
while(true){
try {
Thread.currentThread().sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(MapUtils.isEmpty(HeartBeatMap)){
if (cnt % 60 == 0) {
logger.warn("y HeartBeatMap empty");
cnt = 0L;
}
cnt++;
}else{
List<String> needRemove = new ArrayList<>();
for(Map.Entry<String, InfoScreenHeartBeatPacket> entry : HeartBeatMap.entrySet()){
InfoScreenHeartBeatPacket packet = entry.getValue();
if (MonitorHelper.isWhiteIp(packet.getIp(), SystemConfig.getInstance().getHostWhiteList())){
logger.warn("in HeartBeatTask, a white ip {}", packet.getIp());
needRemove.add(packet.getIp());
continue;
}
long lastUpdateDT = packet.getUpdateDateTime();
boolean isTimeout = isTimeout(timeout, lastUpdateDT);
//todo 合并多个,只发送一封邮件
if(isTimeout){
//reboot
logger.warn("HeartBeatMap find no heartbeat, req {}, timeout {}",
packet, timeout);
//infoScreenService.reboot(req.getIp(), req.getScreenType());
boolean cansend = canSend4GlobalScope() && canSend4HeartBeat();
if(cansend){
mailService.send(buildMailContent(packet.getIp(), packet.getScreenType()).toString());
needRemove.add(packet.getIp());
}
}
}
//remove from map
removeAll(needRemove);
//客户端到23点,早上9点还没有关机就邮件报警
this.sendMailIfExistOpenClient();
}
aliveDatetime = System.currentTimeMillis();
}
}
private void removeAll(List<String> needRemove){
if(CollectionUtils.isNotEmpty(needRemove)){
needRemove.stream().forEach(ip -> HeartBeatMap.remove(ip));
}
}
private void sendMailIfExistOpenClient(){
int currentHour;
SystemConfig systemConfig = SystemConfig.getInstance();
ClientConfig clientConfig = systemConfig.getClientConfig();
//尚未发送通知
if(!isNoticedOpenClient){
currentHour = DateUtil.getPart(new Date(), DateUtil.TimeUnit.hour);
boolean hasData = MapUtils.isNotEmpty(HeartBeatMap);
boolean cansend = clientCanSendMail(clientConfig);
boolean isNotClose = (currentHour >= clientConfig.getCloseHour() || currentHour < clientConfig.getOpenHour());
if (isNotClose && hasData && cansend){
isNoticedOpenClient = true;
List<String> ips = new ArrayList<String>(HeartBeatMap.keySet());
mailService.send(buildMailContentOfExistOpenClient(ips).toString());
//移除的意义不大,心跳会不间断地上报
this.removeAll(ips);
logger.info("in sendMailIfExistOpenClient, ips {}", ips);
}
}
//已经发送通知后,需要恢复标识
if(isNoticedOpenClient){
currentHour = DateUtil.getPart(new Date(), DateUtil.TimeUnit.hour);
//auto wakeup, reset isNoticedOpenClient if it's false
boolean inBussiness = currentHour < clientConfig.getCloseHour()
&& currentHour >= clientConfig.getOpenHour();
if(inBussiness){
isNoticedOpenClient = false;
logger.info("in sendMailIfExistOpenClient, reset isNoticedOpenClient success");
}
}
}
public long getAliveDatetime(){
return aliveDatetime;
}
}
StringBuilder buildMailContentOfExistOpenClient(List<String> ips){
StringBuilder sb = new StringBuilder();
String ipsStr = StringUtils.join(ips, ",") ;
sb.append("信息屏[").append(ipsStr).append("]还没有关闭,请尽早安排其休息,以免次日出现不良情绪");
return sb;
}
private boolean canSend4AppReport(){
SystemConfig systemConfig = SystemConfig.getInstance();
//上午10点到晚上22点发送,其他时间不发
int currentHour = DateUtil.getPart(new Date(), DateUtil.TimeUnit.hour);
if(currentHour<systemConfig.getSendMailBeginHour() || currentHour >=systemConfig.getSendMailEndHour()){
logger.warn("in canSend4AppReport,it's too late, don't need to send mail, thanks developer sailing");
return false;
}
return true;
}
private boolean canSend4GlobalScope(){
SystemConfig systemConfig = SystemConfig.getInstance();
if(!systemConfig.isSendMail()){//不发送一般发生在测试环境
logger.warn("in canSend4GlobalScope,systemConfig.sendMail is set {},u can use manage/sendMail/open to control", systemConfig.isSendMail());
return false;
}
return true;
}
private boolean canSend4HeartBeat(){
SystemConfig systemConfig = SystemConfig.getInstance();
//上午10点到晚上22点发送,其他时间不发
int currentHour = DateUtil.getPart(new Date(), DateUtil.TimeUnit.hour);
if(currentHour<systemConfig.getSendMailBeginHour() || currentHour >=systemConfig.getSendMailEndHour()){
logger.warn("in canSend4HeartBeat,it's too late, don't need to send mail, thanks developer sailing");
return false;
}
return true;
}
public static void main(String[] args) {
long cnt = 1 << 31 -1 ;
Boolean flag = cnt == Integer.MAX_VALUE;
System.out.println(MessageFormatter
.arrayFormat("cnt {},Integer.MAX_VALUE {}, flag {}", new Object[]{cnt, Integer.MAX_VALUE, flag}).getMessage());
System.out.println();
}
}