昨天晚上连续发了几篇帖子,发现"csdn学生大本营"有限制用户连续发帖的功能,也就是服务器会存储用户上次发帖的时间,当用户再次请求发帖时检测两次时间间隔是否过短,如果是,则提示用户等待一定的时间。
不知道csdn是怎么实现的,我用的是java,实现思路,环境和具体步奏如下:
思路:
服务器使用一个HashMap来存储发表过帖子的用户,key为session的id,value为发帖时间,这里使用HashMap的理由是查找速度是恒定不变的,也就是说即使HashMap里面有999999个数据,其查询速度也和只有1个数据时一样。
我们还需要一个清理HashMap的线程,清除那些已经达到合法间隔时间的数据,否则这个HashMap会膨胀的很大。
环境:
服务器依然选择我熟悉的Tomcat6,集成开发环境MyEclipse705,浏览器选择FireFox
具体步骤:
1.配置文件config.properties
- #清理间隔时间(毫秒)
- interval=600000
- #发帖所需间隔时间(毫秒)
- issueInterval=60000
2基本信息类Config.java
- package com.jackyan.practice.util;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.Properties;
- public class Config {
- private static long interval=0;
- private static long issueInterval=0;
- private static String configName="config.properties";
- private static String intervalName="interval";
- private static String issueIntervalName="issueInterval";
- //默认的清扫间隔时间
- private static long defaultInterval=1000*60*60;
- //默认的合法发帖间隔时间
- private static long dufalutIssueInterval=1000*60;
- static{
- Properties prop=new Properties();
- InputStream is=Config.class.getResourceAsStream(configName);
- try {
- prop.load(is);
- interval=Long.parseLong(prop.getProperty(intervalName));
- issueInterval=Long.parseLong(prop.getProperty(issueIntervalName));
- } catch(Exception e){
- interval=defaultInterval;
- issueInterval=dufalutIssueInterval;
- }
- }
- public static long getInterval() {
- return interval;
- }
- public static long getIssueInterval() {
- return issueInterval;
- }
- }
3.常量类Constants.java
- package com.jackyan.practice.util;
- public class Constants {
- public static final String PRIVIEW_LIST_BEAN="priviewListBean";
- }
4.事件bean类:PriviewListBean.java(有点难听。。。。)
- package com.jackyan.practice.bean;
- import java.util.HashMap;
- import com.jackyan.practice.util.Config;
- public class PriviewListBean {
- private HashMap<String, Long> map=null;
- public PriviewListBean(){
- }
- public PriviewListBean(HashMap<String,Long> map){
- this.map=map;
- }
- public void setMap(HashMap<String, Long> map) {
- this.map=map;
- }
- //向HashMap中添加新的数据
- public void addItem(String key){
- synchronized(map){
- map.put(key, System.currentTimeMillis());
- }
- }
- //从HashMap中删除指定数据
- public void removeItem(String key){
- synchronized(map){
- map.remove(key);
- }
- }
- //HashMap中是否存在指定的数据
- public boolean isItemExist(String key){
- synchronized(map){
- return map.containsKey(key);
- }
- }
- //更新HashMap中数据的值
- public void updateItem(String key){
- synchronized(map){
- addItem(key);
- }
- }
- //HashMap中指定的数据是否已经准备好(也就是这个用户发帖的冷却时间到没到)
- public boolean isReady(String key){
- synchronized(map){
- if((System.currentTimeMillis()-map.get(key).longValue())>=Config.getIssueInterval()){
- return true;
- }
- return false;
- }
- }
- //HashMao中,指定用户的发帖冷却时间还剩多少
- public long needTime(String key){
- synchronized(map){
- return (Config.getIssueInterval()-System.currentTimeMillis()+map.get(key).longValue())/1000;
- }
- }
- }
5.清扫HaspMap的类CleanPriviewListThread.java
- package com.jackyan.practice.util;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
- import com.jackyan.practice.bean.PriviewListBean;
- public class CleanPriviewListThread implements Runnable {
- private HashMap<String, Long> map=null;
- private PriviewListBean plb=null;
- public CleanPriviewListThread(HashMap<String, Long> map,PriviewListBean plb){
- this.map=map;
- this.plb=plb;
- }
- @Override
- public void run() {
- Map.Entry<String, Long> temp=null;
- Iterator<Map.Entry<String,Long>> vector=null;
- while(true){
- try{
- Thread.sleep(Config.getInterval());
- }catch(Exception e ){
- e.printStackTrace();
- System.exit(0);
- }
- synchronized(map){
- for(vector=map.entrySet().iterator();vector.hasNext();){
- temp=vector.next();
- //方便从控制台观察
- System.out.println("正在检测"+temp.getKey());
- if(plb.isReady(temp.getKey())){
- plb.removeItem(temp.getKey());
- //方便从控制台观察
- System.out.println("移除了"+temp.getKey());
- }
- }
- }
- }
- }
- }
6.Web应用监听器类PriviewListListener.java
- package com.jackyan.practice.listener;
- import java.util.HashMap;
- import javax.servlet.ServletContextEvent;
- import javax.servlet.ServletContextListener;
- import com.jackyan.practice.bean.PriviewListBean;
- import com.jackyan.practice.util.CleanPriviewListThread;
- import com.jackyan.practice.util.Constants;
- public class PriviewListListener implements ServletContextListener {
- @Override
- public void contextDestroyed(ServletContextEvent arg0) {
- arg0.getServletContext().removeAttribute(Constants.PRIVIEW_LIST_BEAN);
- }
- @Override
- public void contextInitialized(ServletContextEvent arg0) {
- HashMap<String, Long> map=new HashMap<String, Long>();
- PriviewListBean plb=new PriviewListBean(map);
- arg0.getServletContext().setAttribute(Constants.PRIVIEW_LIST_BEAN, plb);
- new Thread(new CleanPriviewListThread(map,plb),"cleanPriviesListThread").start();
- }
- }
7.响应用户发帖请求的Servlet CheckIssueTime.java
- package com.jackyan.practice.servlet;
- import java.io.IOException;
- import java.io.PrintWriter;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import javax.servlet.http.HttpSession;
- import com.jackyan.practice.bean.PriviewListBean;
- import com.jackyan.practice.util.Constants;
- public class CheckIssueTime extends HttpServlet {
- private PriviewListBean plb=null;
- public void init() throws ServletException {
- plb=(PriviewListBean)getServletContext().getAttribute(Constants.PRIVIEW_LIST_BEAN);
- }
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- response.setContentType("text/xml;charset=utf-8");
- PrintWriter out=response.getWriter();
- HttpSession session=request.getSession(true);
- session.setMaxInactiveInterval(600);
- String sessionId=session.getId();
- if(!plb.isItemExist(sessionId)){
- plb.addItem(sessionId);
- out.print("<success>允许发帖</success>");
- out.close();
- return;
- }
- if(!plb.isReady(sessionId)){
- out.print("<failure>距离下次发帖还有"+plb.needTime(sessionId)+"秒</failure>");
- out.close();
- return;
- }
- out.println("<success>允许发帖</success>");
- out.close();
- }
- }
8.响应用户提交帖子内容的servlet Issue.java
- package com.jackyan.practice.servlet;
- import java.io.IOException;
- import java.io.PrintWriter;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import javax.servlet.http.HttpSession;
- import com.jackyan.practice.bean.PriviewListBean;
- import com.jackyan.practice.util.Constants;
- public class Issue extends HttpServlet {
- private PriviewListBean plb=null;
- public void init() throws ServletException {
- plb=(PriviewListBean)getServletContext().getAttribute(Constants.PRIVIEW_LIST_BEAN);
- }
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- response.setContentType("text/xml;charset=utf-8");
- PrintWriter out=response.getWriter();
- HttpSession session=request.getSession(false);
- if(session==null){
- out.println("<message>非法操作</message>");
- out.close();
- return;
- }
- String sessionId=session.getId();
- plb.updateItem(sessionId);
- out.println("<message>发表成功</message>");
- out.close();
- }
- }
9web.xml中的内容
- <servlet>
- <servlet-name>CheckIssueTime</servlet-name>
- <servlet-class>com.jackyan.practice.servlet.CheckIssueTime</servlet-class>
- </servlet>
- <servlet>
- <servlet-name>Issue</servlet-name>
- <servlet-class>com.jackyan.practice.servlet.Issue</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>CheckIssueTime</servlet-name>
- <url-pattern>/servlet/CheckIssueTime</url-pattern>
- </servlet-mapping>
- <servlet-mapping>
- <servlet-name>Issue</servlet-name>
- <url-pattern>/servlet/Issue</url-pattern>
- </servlet-mapping>
- <listener>
- <listener-class>com.jackyan.practice.listener.PriviewListListener</listener-class>
- </listener>
10.显示用的html页面Issue.html
- <html>
- <head>
- <title>发表帖子</title>
- <meta http-equiv="content-type" content="text/html; charset=UTF-8">
- <script type="text/javascript" src="../js/jquery.js"></script>
- <script type="text/javascript" src="../js/issue.js"></script>
- <base href="http://localhost:8080/ControlIssue/">
- </head>
- <body>
- <div align="center" ><button onclick="issue();" >发帖</button><span id="wantIM" ></span></div>
- <div align="center" ><button onclick="submit();" >提交</button><span id="issueM" ></span></div>
- </body>
- </html>
11脚本文件issue.js
- function issue(){
- $.ajax({
- type : "POST" ,
- url : "servlet/CheckIssueTime",
- dataType:"xml",
- success:function(msg){
- $("#wantIM").text($(msg).children().text());
- }
- });
- }
- function submit(){
- $.ajax({
- type : "POST" ,
- url : "servlet/Issue",
- dataType:"xml",
- success:function(msg){
- $("#issueM").text($(msg).children().text());
- }
- });
- }
测试运行:没问题(真的是一次成功,我也不敢相信。。看来还是熟能生巧啊,想当年。。。)
贴几张截图吧:。。。怎么不能加图片了。。。
PS:真的想做一个详细的说明文档,但是实在是没有时间,一会还得学英语。。。如果有不同意见的地方可一定要告诉我,我会及时改正,免得误人子弟。