多執行緒非同步操作日誌
阿新 • • 發佈:2018-11-16
上次寫的一篇部落格,多執行緒非同步操作日誌不完整,現在寫一個完整的
功能是:使用者訪問一個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
直接測試,可以看到檔案中增加了日誌資訊