网站的统计数据有很多个,其中比较重要的是PV(Page View)、UV(Unique View)、页面停留时间、网站访问时间。
PV:指网站的页面浏览量,每刷一次页面,算一次
PV:指网站的独立访客数,同一个IP在同一天的多次访问只算一次。
页面停留时间:指用户在页面上停留的时间
网站访问时间:指用户退出网站时间与用户进入网站的时间差
其中页面停留时间的统计有多种方法
1、定时刷新后台
打开页面后,每隔一段时间刷新后台,以第一次刷新和最后一次刷新的时间差作为页面停留时间,同时设置最大页面停留时间。
优点:统计的页面停留时间相对比较准确。
缺点:当网站的访问量上来后,这种方式有很大的代价,后台日志的负载会非常大。
2、计算两个页面的访问时间差
比如T1时刻访问页面P1,T2时刻访问页面P2,那么访问页面P1的时间T=T2-T1,但访问最后一个页面的时间为0。
优点:统计方式比较简单明了。
缺点:统计的结果可能不太准确,比如打开P1页面后,再打开P2页面,然后再返回P1页面,这时统计的结果就不准确。
3、通过鼠标的滑动来刷新后台
当页面有鼠标的滑动时,就向后台刷新日志,以第一次滑动和最后一次滑动的时间差当作页面停留时间。
优点:统计的页面停留时间比较准确
缺点:后台日志的负载会比较大
下面用Java+Spring+Mysql实现第二种方式的统计
1、项目的程序结构如下:
2、核心代码
2.1 日志拦截器LogFilter.java
package com.filter; import java.io.IOException; import java.sql.Timestamp; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.UUID; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import com.bean.LogBean; import com.service.LogService; public class LogFilter implements Filter { Logger logger = Logger.getLogger(LogFilter.class); @Autowired private LogService logService; public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { if (!(servletRequest instanceof HttpServletRequest) || !(servletResponse instanceof HttpServletResponse)) { throw new ServletException( "OncePerRequestFilter just supports HTTP requests"); } HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; HttpSession session = httpRequest.getSession(); String ip = httpRequest.getRemoteAddr(); String page = httpRequest.getRequestURI(); String contextPath = httpRequest.getContextPath(); String servletPath = httpRequest.getServletPath(); logger.info("doFilter sessionId="+session.getId()+",ip="+ip+",page="+page+",contextPath="+contextPath+",servletPath="+servletPath); LogBean logBean = new LogBean(); logBean.setId(UUID.randomUUID().toString()); logBean.setSessionId(session.getId()); logBean.setIp(ip); logBean.setPage(page); logBean.setAccessTime(new Timestamp(new Date().getTime())); logBean.setStayTime(0); //通过session id 和 ip,查出最近的一条访问记录 LogBean bean = null; try { bean = logService.getLatestLog(session.getId(), ip); } catch (Exception e) { e.printStackTrace(); logger.error(e.getMessage()); } //更改最近访问记录的停留时间,这里把两次访问记录的间隔时间算成上一次页面访问的停留时间 if(bean != null){ long stayTime = (System.currentTimeMillis() - bean.getAccessTime().getTime())/1000; try { logService.updateLog(bean.getId(), stayTime); } catch (Exception e) { e.printStackTrace(); logger.error(e.getMessage()); } } //保存当前访问记录 try { logService.saveLog(logBean); } catch (Exception e) { e.printStackTrace(); logger.error(e.getMessage()); } //统计网站今天的PV(页面浏览量),UV(独立访客数) DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); String format = df.format(new Date()); Date parse = null; try { parse = df.parse(format); } catch (ParseException e1) { e1.printStackTrace(); } Timestamp startTime = new Timestamp(parse.getTime()); Timestamp endTime = new Timestamp(parse.getTime() + 24*3600*1000); try { int pv = logService.getPV(startTime, endTime); int uv = logService.getUV(startTime, endTime); logger.info("pv="+pv); logger.info("uv="+uv); } catch (Exception e) { e.printStackTrace(); logger.error(e.getMessage()); } filterChain.doFilter(servletRequest, servletResponse); return; } public void init(FilterConfig filterConfig) throws ServletException { ServletContext context = filterConfig.getServletContext(); ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(context); logService = (LogService) ctx.getBean("logService"); logger.info("init"); } public void destroy() { } }
2.2 web.xml配置
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <filter> <filter-name>logFilter</filter-name> <filter-class>com.filter.LogFilter</filter-class> </filter> <filter-mapping> <filter-name>logFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <session-config> <session-timeout>30</session-timeout> </session-config> </web-app>
3、日志表设计
CREATE TABLE log ( id varchar(64) NOT NULL, session_id varchar(64) NOT NULL, ip varchar(64) NULL, page varchar(200) NULL, access_time timestamp NULL, stay_time mediumtext NULL, PRIMARY KEY(id) ) ENGINE = InnoDB AUTO_INCREMENT = 0 ; CREATE INDEX log_index ON log(session_id, ip) ;
4、项目的下载地址如下
http://download.csdn.net/detail/brushli/7518709
5、评价与展望
该系统的设计能基本满足中小网站的数据统计,对于对准确性要求比较高的网站来说,关键统计数据的统计方法应做改进;
对于并发量比较高的网站来说,直接将日志插入数据库的方式也应做改进,比如改用消息队列MQ来处理。