一,数据库实现方法:(实现思路:基于Struts2.0的拦截器)
0,流程:struts.xml ->
ApplicationContext.xml
->
LoginInterceptor.java ->
OnlineAction.java ->
Online.ftl
1,
struts.xml:
<action name="index" class="indexAction">
<result name="success" type="freemarker">/WEB-INF/ftl/OnLine.ftl</result>
</action>
2,
ApplicationContext.xml:
<bean id="indexAction" class="cn.company.OnlineAction">
<property name="configService" ref="configService" />
<property name="onlineService" ref="onlineService" />
</bean>
<!-- 定期清理过期的在线用户 -->
<bean id="onlineClear" class="cn.
company.OnLineClearTimerTask">
<property name="onlineService" ref="onlineService" />
</bean>
<bean id="onlineClearTimerTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="delay">
<value>10800000</value><!-- 3小时 * 60分钟 * 60秒 * 1000毫秒 = 10800000毫秒 -->
</property>
<property name="period">
<value>10800000</value>
</property>
<property name="timerTask">
<ref local="onlineClear" />
</property>
</bean>
<bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean">
<property name="scheduledTimerTasks">
<list>
<ref local="onlineClearTimerTask" />
</list>
</property>
</bean>
3,
LoginInterceptor.java
:
public String intercept(ActionInvocation actionInvocation) throws Exception {
Object action = actionInvocation.getAction();
// log.info("登录拦截器将拦截:" + action.getClass().getName().substring(24));
ActionContext ac = actionInvocation.getInvocationContext();
ServletContext servletContext = (ServletContext) ac.get(ServletActionContext.SERVLET_CONTEXT);
WebApplicationContext wc = WebApplicationContextUtils.getWebApplicationContext(servletContext);
if (wc == null) {
log.error("找不到Spring的配置文件:ApplicationContext.xml");
} else {
String username = "";
UserSession us = (UserSession) ac.getSession().get(Constant.USER_SESSION_KEY);
if (ac.getSession().get(User.SESSION_LOGIN_NAME_KEY) == null) {
if (us == null) {
username = (String) ac.getSession().get(User.SESSION_LOGIN_NAME_KEY);
// log.info("当前用户第一次进来:" + username);
} else {
username = ((UserSession) ac.getSession().get(Constant.USER_SESSION_KEY)).getUserName();
// log.info("当前登录用户:" + username);
}
}
if (us == null) {
OnLineService onlineService = (OnLineService) wc.getBean("onlineService");
long nowTime = System.currentTimeMillis();
// log.info("添加一个游客:[email protected]" + nowTime + "(" + String.format("%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS:%1$tL", nowTime) + ")");
UserOnLine userOnLine = new UserOnLine();
userOnLine.setUserId(0);
userOnLine.setUserName("[email protected]" + nowTime);
userOnLine.setOnlineTime(nowTime);
onlineService.saveUserOnLine(userOnLine);
// 加入UserSession
us = new UserSession();
us.setUserName(userOnLine.getUserName());
us.setLastActiveTime(nowTime);
ac.getSession().put(Constant.USER_SESSION_KEY, us);
} else {
OnLineService onlineService = (OnLineService) wc.getBean("onlineService");
if (ac.getSession().get(User.SESSION_LOGIN_NAME_KEY) == null) {
username = ((UserSession) ac.getSession().get(Constant.USER_SESSION_KEY)).getUserName();
} else {
username = (String) ac.getSession().get(User.SESSION_LOGIN_NAME_KEY);
}
UserOnLine userOnLine = onlineService.findUserOnLineByUserName(username);
if (userOnLine != null) {
//log.info("OnlineTime:" + userOnLine.getOnlineTime());
}
}
}
// 继续执行 Action
return actionInvocation.invoke();
}
4,
OnlineAction.java:
public class OnlineAction extends ManageBaseAction {
/** 配置服务 */
private ConfigService configService;
/** 在线服务 */
private OnLineService onlineService;
/** 在线用户统计服务 */
private StatService statService;
/** 在线用户列表 */
private List<UserOnLine> onlineList = new ArrayList<UserOnLine>();
/** 在线会员数 */
private long onlineUserNum = 0;
/** 在线游客数 */
private long onlineGuestNum = 0;
/** 最高在线人数 */
private String onlineHighest;
/** 最高在线人数的出现时间 */
private String onlineAppear;
@Override
protected String execute(String cmd) throws Exception {
// 从游客变成注册用户
String username = "";
HttpSession session = request.getSession();
if (session.getAttribute(User.SESSION_LOGIN_NAME_KEY) == null) {
username = ((UserSession) session.getAttribute(Constant.USER_SESSION_KEY)).getUserName();
} else {
String guestName = ((UserSession) session.getAttribute(Constant.USER_SESSION_KEY)).getUserName();
// log.info("登录前的游客名:" + guestName);
if (guestName != null && !"".equals(guestName)) {
UserOnLine userOnLine = onlineService.findUserOnLineByUserName(guestName);
onlineService.removeUserOnLine(userOnLine);
}
username = (String) session.getAttribute(User.SESSION_LOGIN_NAME_KEY);
}
// log.info("当前在首页的用户:" + username);
long optTime = System.currentTimeMillis() - getTime();
// log.info("前五分钟:" + optTime + "(" + String.format("%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS:%1$tL", optTime) + ")");
onlineUserNum = onlineService.getOnLineUesrNum(optTime);
// log.info("在线会员:" + onlineUserNum);
onlineGuestNum = onlineService.getOnLineGuestNum(optTime);
// log.info("在线游客:" + onlineGuestNum);
long tmpHighest = onlineUserNum + onlineGuestNum;
// log.info("当前最高在线人数:" + tmpHighest);
int _h = Integer.valueOf(String.valueOf(tmpHighest));
UserOnLineStat userOnLineStat = statService.getUserOnLineStat();
// log.info("系统中的最高在线人数:" + userOnLineStat.getHighest());
if (tmpHighest > userOnLineStat.getHighest()) {
userOnLineStat.setHighest(_h);
userOnLineStat.setAppearTime(String.format("%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS", new Date()));
statService.updateUserOnLineStat(userOnLineStat);
// 从更新的数据库中取最新的数据
onlineHighest = String.valueOf(userOnLineStat.getHighest());
// log.info("最高在线:" + onlineHighest);
onlineAppear = userOnLineStat.getAppearTime();
// log.info("出现时间:" + onlineAppear);
} else {
onlineHighest = String.valueOf(userOnLineStat.getHighest());
// log.info("最高在线:" + onlineHighest);
onlineAppear = userOnLineStat.getAppearTime();
// log.info("出现时间:" + onlineAppear);
}
// 当前在线用户列表
onlineList = onlineService.findOnLineUser(optTime);
/*
SimpleMailMessage msg = new SimpleMailMessage(this.message);
msg.setTo("[email protected]");
msg.setSentDate(new Date());
msg.setText("当前在线用户:" + username + " [" + String.format("%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS:%1$tL", new Date()) + "]" + "[" + request.getRemoteAddr() + "]");
try {
mailSender.send(msg);
} catch(MailException ex) {
log.error(ex.getMessage());
log.info("邮件发送失败!");
}*/
return SUCCESS;
}
/**
* 获取系统配置的超时时间
*
*
@return
*/
private long getTime() {
return Integer.valueOf(getConfigValue("site.user.online.time")) * 1 * 60 * 1000; // 5 * 1分钟 * 60秒 1000毫秒 = 300000毫秒 = 5分钟
}
/**
* 根据配置 Name 得到相应的 Value
*/
private String getConfigValue(String string) {
return (String) this.configService.getConfigure().getValue(string);
}
/** 配置服务的set方法 */
public void setConfigService(ConfigService configService) {
this.configService = configService;
}
/** 在线服务的set方法 */
public void setOnlineService(OnLineService onlineService) {
this.onlineService = onlineService;
}
/** 在线用户统计服务的set方法 */
public void setStatService(StatService statService) {
this.statService = statService;
}
public List<UserOnLine> getOnlineList() {
return onlineList;
}
public long getOnlineUserNum() {
return onlineUserNum;
}
public long getOnlineGuestNum() {
return onlineGuestNum;
}
public String getOnlineHighest() {
return onlineHighest;
}
public String getOnlineAppear() {
return onlineAppear;
}
}
5,
Online.ftl:
<div>
注册用户:
${onlineUserNum!0},游客:
${onlineGuestNum!0},最高在线人数:
${onlineHighest!0},发生在:
${onlineAppear!?html}</div>
</div>
<div>
<#if onlineList??>
<#list onlineList as online>
<#assign u = Util.userById(online.userId)>
<a href="${SiteUrl}go.py?loginName=${u.loginName!}">${u.trueName!}</a>
</#list>
</#if>
</div>
6,Util.userById:
public class UserById implements TemplateMethodModel {
public Object exec(List args) throws TemplateModelException {
if (args == null || args.size() == 0)
throw new TemplateModelException("userById 需要 1 个整数型参数");
String idstr = args.get(0).toString();
if (ParamUtil.isInteger(idstr) == false)
throw new TemplateModelException("userById 需要 1 个整数型参数");
// 得到用户标识参数
int id = Integer.parseInt(idstr);
User u = jtar_ctxt.getUserService().getUserById(id);
return u == null ? NOTHING : u;
}
}
7,OnlineDaoHibernate.java
:
public UserOnLine findUserOnLineByUserName(String userName) {
List<UserOnLine> list = this.getSession().createQuery(FIND_USERONLINE_BY_USERNAME).setString(0, userName).list();
return (null == list || list.isEmpty()) ? null : (UserOnline) list.get(0);
}
8,Constant.java
:
public class Constant {
public static final String USER_SESSION_KEY = "user_session";
}
9,UserSession
:
public class UserSession implements Serializable {
/** 用户名 */
private String userName;
/** 上次活动时间 (具体的时间格式) */
private long lastActiveTime = 0;
/** 已经活动时间 (当前系统时间-上次活动时间=差值) */
private long addedOnlineTime = 0;
/**
* Default Constructor
*/
public UserSession () {
//
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public long getLastActiveTime() {
return lastActiveTime;
}
public void setLastActiveTime(long lastActiveTime) {
this.lastActiveTime = lastActiveTime;
}
public long getAddedOnlineTime() {
return addedOnlineTime;
}
public void setAddedOnlineTime(long addedOnlineTime) {
this.addedOnlineTime = addedOnlineTime;
}
}
10,UserOnLine.java
:
public class UserOnLine implements Serializable {
// 对象标识
private int Id;
// 用户Id,如为游客,则可以为空
private Integer userId;
// 用户名,如为游客,则可以为:Guest
private String userName;
// 时间的毫秒数
private long onlineTime;
/** Default Construct */
public UserOnLine() {
//
}
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public long getOnlineTime() {
return onlineTime;
}
public void setOnlineTime(long onlineTime) {
this.onlineTime = onlineTime;
}
}
二,内存实现方法:(实现思路:基于
Session
和
Application
的作用范围)
1,web.xml:
<!-- 监听 session 中的在线用户 -->
<listener>
<listener-class>cn.company.OnlineListener</listener-class>
</listener>
2,
OnlineListener.java:
public class OnlineListener implements HttpSessionListener {
@Override
// 监听 Session 的创建
public void sessionCreated(HttpSessionEvent se) {
// Do Nothing
}
@Override
// 监听 Session 的销毁,在 session.invalidate() 时自动触发
public void sessionDestroyed(HttpSessionEvent se) {
HttpSession session = se.getSession();
UserSession us = (UserSession) session.getAttribute(Constant.USER_SESSION_KEY);
// session.getServletContext() 是全局的 Application
,这个是重点!
List onlineList = (List) session.getServletContext().getAttribute("onlineList");
if (null != onlineList && 0 < onlineList.size() && us != null) {
onlineList.remove(us);
}
}
}
3,
LoginInterceptor.java
:
public String intercept(ActionInvocation actionInvocation) throws Exception {
Object action = actionInvocation.getAction();
String sessionId = ServletActionContext.getRequest().getSession().getId();
// 不拦截 AdminStatAction
if (action instanceof AdminStatAction) {
return actionInvocation.invoke();
}
ActionContext ac = actionInvocation.getInvocationContext();
UserSession us = (UserSession) ac.getSession().get(Constant.USER_SESSION_KEY);
if (null == us) {
long nowTime = System.currentTimeMillis();
// 往用户Session对象中放入一个游客对象
us = new UserSession();
us.setSessionId(sessionId);
us.setUsername("_" + nowTime);
ac.getSession().put(Constant.USER_SESSION_KEY, us);
} else {
// 系统中的用户名
String username = "";
if (null == ac.getSession().get(User.SESSION_LOGIN_NAME_KEY)) {
// 如果没有从Session中获取到用户名(几乎不可能),直接将游客Session赋给用户名
username = us.getUsername();
} else {
username = (String) ac.getSession().get(User.SESSION_LOGIN_NAME_KEY);
}
// 从游客变成注册用户
// 删除之前的游客session,如果还是游客、或已登录用户再次访问首页的就不必重复删除
us.setUsername(username);
}
// 继续执行 Action
return actionInvocation.invoke();
}
4,OnlineAction.java:
public class OnlineAction extends ManageBaseAction {
// 游客列表
private ArrayList guestList = new ArrayList();
// 注册用户列表
private ArrayList userList = new ArrayList();
/* (non-Javadoc)
*
* @see cn.edustar.jitar.action.ManageBaseAction#execute(java.lang.String)
*/
@Override
protected String execute(String cmd) throws Exception {
HttpSession session = request.getSession();
UserSession us = (UserSession) session.getAttribute(Constant.USER_SESSION_KEY);
// session.getServletContext() 是全局的 Application,这个是重点!
List onlineList = (List) session.getServletContext().getAttribute("onlineList");
if (null == onlineList) {
onlineList = new ArrayList();
session.getServletContext().setAttribute("onlineList", onlineList);
} else {
// 游客session不为空,因为可能是已登录用户再次访问首页
if (null != us) {
onlineList.remove(us);
}
}
onlineList.add(us);
guestList.clear();
userList.clear();
// for (Iterator iterator = onlineList.iterator(); iterator.hasNext();) {
// UserSession _us = (UserSession) iterator.next();
// if (null != _us) {
// if (_us.getUsername().startsWith("_")) {
// guestList.add(_us.getUsername());
// } else {
// userList.add(_us.getUsername());
// }
// }
// }
for (Object object : onlineList) {
UserSession u = (UserSession) object;
if (null != u) {
if (u.getUsername().startsWith("_")) {
guestList.add(u.getUsername());
} else {
userList.add(u.getUsername());
}
}
}
return SUCCESS;
}
public ArrayList getGuestList() {
return guestList;
}
public ArrayList getUserList() {
return userList;
}
}
5,Online.ftl:
<div>
<#assign onlineStat = Util.getUserOnLineStat(userList?size + guestList?size)>
当前在线总人数:${userList?size + guestList?size}
注册用户:${userList?size!0}
游客:${guestList?size!0}
最高在线总人数:${onlineStat.highest!0}
发生在:${onlineStat.appearTime!?html}
</div>
<#if userList??>
<#list userList as user>
<#assign u = Util.userByName(user)>
<a href="${SiteUrl}go.py?loginName=${u.loginName!}">${u.trueName!}</a>
</#list>
</#if>
6,Util.userByName:
public class UserByName implements TemplateMethodModel {
@SuppressWarnings("unchecked")
public Object exec(List args) throws TemplateModelException {
// 判断参数.
if (args == null || args.size() == 0)
throw new TemplateModelException("userByName 需要 1 个字符串参数");
String loginName = args.get(0).toString();
User u = getUserService().getUserByLoginName(loginName);
return u == null ? NOTHING : u;
}
}
7,Util.getUserOnLineStat:
public class getUserOnLineStat implements TemplateMethodModel {
@SuppressWarnings("rawtypes")
public Object exec(List args) throws TemplateModelException {
int count = 0;
// 判断参数
if (null == args || 0 == args.size()) {
count = 1;
} else {
count = Integer.valueOf((String) args.get(0));
}
// System.out.println("当前在线人数:" + count);
UserOnLineStat us = (UserOnLineStat) jtar_ctxt.getCacheProvider().getCache("online").get("online");
OnlineManage onlineManage = jtar_ctxt.getOnlineManage();
if (null == us) {
us = onlineManage.getUserOnLineStat();
// 放入缓存
jtar_ctxt.getCacheProvider().getCache("online").put("online", us);
} else {
// 如果当前在线人数大于数据库(此刻是缓存)中的人数,则更新数据库,并更新缓存
if (count > us.getHighest()) {
// 更新数据库
onlineManage.updateOnLineStat(count);
// 更新缓存
jtar_ctxt.getCacheProvider().getCache("online").put("online", onlineManage.getUserOnLineStat());
}
}
return us;
}
}
8,ehcache.xml:
<cache name="online" maxElementsInMemory="1024" eternal="false" timeToIdleSeconds="600" timeToLiveSeconds="3600" overflowToDisk="true" />
9,系统启动的时候将统计信息放到缓存中:cacheService.put("online", getUserOnLineStat());
public UserOnLineStat getUserOnLineStat() {
List<UserOnLineStat> list = this.getSession().createQuery("FROM UserOnLineStat").list();
if (null == list || list.isEmpty()) {
return new UserOnLineStat();
} else {
return ((UserOnLineStat) list.get(0));
}
}
10,更新数据库:
public void updateOnLineStat(int count) {
String hql = "UPDATE UserOnLineStat SET highest = ?, appearTime = ‘" + String.format("%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS", new Date()) + "‘ WHERE id = 1";
this.getSession().createQuery(hql).setInteger(0, count).executeUpdate();
this.getSession().flush();
}