1. 程式人生 > >多執行緒非同步操作日誌

多執行緒非同步操作日誌

上次寫的一篇部落格,多執行緒非同步操作日誌不完整,現在寫一個完整的

功能是:使用者訪問一個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

 

直接測試,可以看到檔案中增加了日誌資訊