上次写的一篇博客,多线程异步操作日志不完整,现在写一个完整的
功能是:用户访问一个controller,将访问的记录保存到队列中去,在开启定时器,消费掉记录保存到文件中(可改为保存到数据库)
我的idea目录:
controller中的代码:
package com.tencent.concurrent_log.controller;
import com.tencent.concurrent_log.model.LogEntity;
import com.tencent.concurrent_log.service.UrlVisitService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UrlVisitController {
private static final Logger LOG = LoggerFactory.getLogger(UrlVisitController.class);
@Autowired
private UrlVisitService urlVisitService;
@RequestMapping(value = "/visit/{url}",method = RequestMethod.GET)
@ResponseBody
public String urlvisit(@PathVariable String url){
Integer userId = 666;
String ip = null;
String cookieValue = null;
LogEntity entity = initEntity(userId, ip, cookieValue, url);
try {
urlVisitService.operLog(entity);
return "ok";
} catch (Exception e) {
LOG.error("urlvisit<|>url:"+url,e.getMessage(),e);
return "fail";
}
}
private LogEntity initEntity(Integer userId,String ip,String cookieValue,String url){
LogEntity entity = new LogEntity(null, null, url, ip, cookieValue, userId);
return entity;
}
}
service的实现类:
package com.tencent.concurrent_log.service.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tencent.concurrent_log.logthread.LogThreadPool;
import com.tencent.concurrent_log.model.LogEntity;
import com.tencent.concurrent_log.service.UrlVisitService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.Date;
@Service
public class UrlVisitServiceImpl implements UrlVisitService {
private static final Logger LOG = LoggerFactory.getLogger("db");
private static ObjectMapper mapper = new ObjectMapper();
/**
获取到线程池
*/
@Autowired
private LogThreadPool logThreadPool;
@Override
public LogEntity operLog(LogEntity entity) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = dateFormat.format(new Date());
try {
entity.setCreateTime(dateFormat.parse(dateString));
//将日志信息保存到队列中
logThreadPool.pushLog(entity);
} catch (Exception e) {
e.printStackTrace();
}
return entity;
}
}
实体类:
package com.tencent.concurrent_log.model;
import java.io.Serializable;
import java.util.Date;
public class LogEntity implements Serializable {
/**
* 指定序列化id
*/
private static final long serialVersionUID = -5809782578272943999L;
private Integer id;
private Date createTime;
private String url;
private String ip;
private String cookieValue;
private Integer userId;
public LogEntity() {
}
public LogEntity(Integer id, Date createTime, String url, String ip, String cookieValue, Integer userId) {
this.id = id;
this.createTime = createTime;
this.url = url;
this.ip = ip;
this.cookieValue = cookieValue;
this.userId = userId;
}
@Override
public String toString() {
return "LogEntity{" +
"id=" + id +
", createTime=" + createTime +
", url='" + url + '\'' +
", ip='" + ip + '\'' +
", cookieValue='" + cookieValue + '\'' +
", userId=" + userId +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getCookieValue() {
return cookieValue;
}
public void setCookieValue(String cookieValue) {
this.cookieValue = cookieValue;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
}
线程池的类:
package com.tencent.concurrent_log.logthread;
import com.tencent.concurrent_log.executor.LogWorker;
import com.tencent.concurrent_log.model.LogEntity;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Component
public class LogThreadPool {
private ThreadPoolExecutor poolExecutor;
BlockingQueue blockingQueue = null;
public ThreadPoolExecutor getPoolExecutor(){
return this.poolExecutor;
}
/**
* 初始化线程池
*/
@PostConstruct
public void initPool(){
blockingQueue = new LinkedBlockingQueue<>(5);
poolExecutor = new ThreadPoolExecutor(10,15,100,TimeUnit.MILLISECONDS,blockingQueue);
}
/**
* 大小
* @return
*/
public int getPoolSize(){
return poolExecutor.getPoolSize();
}
/**
* 入队
* @param entity
* @throws Exception
*/
public void pushLog(LogEntity entity) throws Exception {
//将日志信息保存到队列中去,使用put(会造成阻塞)
blockingQueue.put(new LogWorker(entity));
}
/**
* 出队
* @return
*/
public Runnable poll(){
//从队列中取出任务,使用poll(会造成阻塞)
Runnable poll = (Runnable) blockingQueue.poll();
return poll;
}
}
队列中的任务类:
package com.tencent.concurrent_log.executor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tencent.concurrent_log.model.LogEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
/**
* 执行日志的操作
*/
public class LogWorker implements Runnable{
private static final Logger LOG = LoggerFactory.getLogger(LogWorker.class);
private LogEntity entity;
public LogWorker(LogEntity entity) {
this.entity = entity;
}
public LogWorker() {
}
private static ObjectMapper mapper = new ObjectMapper();
@Override
public void run() {
//将日志保存到文件中去
FileWriter writer = null;
String jsonLog = null;
String filePath = "D:\\test\\logTest.txt";
try {
File file = new File(filePath);
writer = new FileWriter(file,true);
jsonLog = mapper.writeValueAsString(entity);
writer.write(jsonLog);
writer.write("\r\n");
} catch (Exception e) {
LOG.error("run<|>jsonLog:"+jsonLog,e.getMessage(),e);
}finally {
if(writer != null){
try {
writer.close();
} catch (IOException e) {
LOG.error("run<|>jsonLog:"+jsonLog,e.getMessage(),e);
}
}
}
}
}
定时器类:
package com.tencent.concurrent_log.timer;
import com.tencent.concurrent_log.logthread.LogThreadPool;
import com.tencent.concurrent_log.service.UrlVisitService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.concurrent.ThreadPoolExecutor;
@EnableScheduling
@Component
public class LogTime {
@Autowired
private LogThreadPool logThreadPool;
@Autowired
private UrlVisitService urlVisitService;
@Scheduled(cron = "0 0/1 * * * ?")
public void logOper(){
ThreadPoolExecutor poolExecutor = logThreadPool.getPoolExecutor();
Runnable runnable = logThreadPool.poll();
System.out.println(poolExecutor.hashCode());
if(null != runnable){
poolExecutor.submit(runnable);
}
}
}
log4j的配置,log4j.properties
log4j.rootLogger=DEBUG,console
#自定义路径
log.rootdir =/data/logs/shortlink/
#自定义日志输出文件
log4j.logger.db=INFO,db
log4j.appender.db.Threshold = INFO
#log4j.appender.db.Append=false
#配置文件保存的时间是每天更新
log4j.appender.db=org.apache.log4j.DailyRollingFileAppender
#配置路径(使用自定义路径)
log4j.appender.db.File = ${log.rootdir}/visit
#log4j.appender.db.File = D:/logs/visit
#自定义文件名
log4j.appender.db.DatePattern= '_'yyyy-MM-dd'.log'
log4j.appender.db.Encoding=UTF-8
log4j.appender.db.layout=org.apache.log4j.PatternLayout
log4j.appender.db.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss}[ %p ]%m%n
直接测试,可以看到文件中增加了日志信息