启动日志记录线程,项目一启动就开始运行
public class GatewayServerBootstrap {
public static void main(String[] args) {
DBLog.getInstance().start();//启动日志线程
SpringApplication.run(GatewayServerBootstrap.class, args);
}
}
//注入logService 的bean
@Autowired
private LogService logService;
//
DBLog.getInstance().setLogService(logService).offerQueue(logInfo); //logInfo为日志信息类,此处省略
@Slf4j
public class DBLog extends Thread {
private static DBLog dblog = null;//日志类
private static BlockingQueue<LogInfo> logInfoQueue = new LinkedBlockingQueue<LogInfo>(1024);//阻塞队列
private LogService logService;
public LogService getLogService() {
return logService;
}
public DBLog setLogService(LogService logService) {
if(this.logService==null) {
this.logService = logService;
}
return this;
}
//单例模式
public static synchronized DBLog getInstance() {
if (dblog == null) {
dblog = new DBLog();
}
return dblog;
}
private DBLog() {
super("CLogOracleWriterThread");
}
public void offerQueue(LogInfo logInfo) {
try {
//待保存的日志信息类放入阻塞队列
logInfoQueue.offer(logInfo);
} catch (Exception e) {
log.error("日志写入失败", e);
}
}
@Override
public void run() {
List<LogInfo> bufferedLogList = new ArrayList<LogInfo>(); // 缓冲队列
while (true) {
try {
//take()会获取队列头数据,并移除,如果队列为空,会一直阻塞等待,直到可获取
bufferedLogList.add(logInfoQueue.take());
logInfoQueue.drainTo(bufferedLogList);//把阻塞队列所有数据放入缓冲队列
if (bufferedLogList != null && bufferedLogList.size() > 0) {
//挨个保存日志
for(LogInfo log:bufferedLogList){
logService.saveLog(log);
}
}
} catch (Exception e) {
e.printStackTrace();
// 防止缓冲队列填充数据出现异常时不断刷屏
try {
Thread.sleep(1000);
} catch (Exception eee) {
}
} finally {
if (bufferedLogList != null && bufferedLogList.size() > 0) {
try {
bufferedLogList.clear();
} catch (Exception e) {
}
}
}
}
}
}
LinkedBlockingQueue使用优点:
- 线程安全:LinkedBlockingQueue实现了同步和锁定机制,保证了多个线程同时访问时的线程安全性。
- 可伸缩性:LinkedBlockingQueue是基于链表实现的,可以动态地增加或减少节点,从而调整队列的大小。
- 阻塞性:当队列为空时,从队列中获取元素的操作会阻塞,直到队列中有新的元素插入;当队列已满时,向队列中插入元素的操作会阻塞,直到队列中有元素被移除。
LogService
定义日志保存接口
public interface LogService {
void saveLog(LogInfo info);
}
LogServiceImpl
具体实现日志保存接口
@Component
@Slf4j
public class LogServiceImpl implements LogService {
@Autowired
private WebClient.Builder webClientBuilder;
@Override
public void saveLog(LogInfo info) {
//WebClient根据异步发送http日志保存请求
Mono<Void> mono = webClientBuilder.build().
post().uri("http://ace-admin/api/log/save")
.body(BodyInserters.fromValue(info))
.retrieve()
.bodyToMono(Void.class);
// 输出结果
log.debug(String.valueOf(mono.block()));
}
}
LogRest
定义接口接收日志请求,并通过
@RequestMapping("/api")
@RestController
@Slf4j
public class LogRest {
@Autowired
private GateLogBiz gateLogBiz;
//接收WebClient根据发送的请求
@RequestMapping(value = "/log/save", method = RequestMethod.POST)
public @ResponseBody
void saveLog(@RequestBody LogInfo info) {
GateLog log = new GateLog();
BeanUtils.copyProperties(info, log);
log.setCrtTime(new Date(info.getCrtTime()));
gateLogBiz.insertSelective(log);
}
}
GateLogBiz
插入日志信息到数据库
@Service
@Transactional(rollbackFor = Exception.class)
public class GateLogBiz extends BaseBiz<GateLogMapper,GateLog> {
@Override
public void insert(GateLog entity) {
mapper.insert(entity);
}
@Override
public void insertSelective(GateLog entity) {
mapper.insertSelective(entity);
}
}