??提升网站性能的方式有很多,例如有效的使用缓存,生成静态页面等等。今天要说的就是生成静态页面的方式。这个也是我近期一直在搞的一个问题,近期在做使用html + servlet做个人网站,为什么是这2个东西呢? |
??? 这其中的缺点是显而易见的:因为每次请求服务器都会进行处理,如果有太多的高并发请求,那么就会加重应用服务器的压力,弄不好就把服务器 搞down 掉了。那么如何去避免呢?如果我们把对 test.do 请求后的结果保存成一个 html 文件,然后每次用户都去访问 http://abc.com/test.html ,这样应用服务器的压力不就减少了?
那么静态页面从哪里来呢?总不能让我们每个页面都手动处理吧?这里就牵涉到我们要讲解的内容了,静态页面生成方案... 我们需要的是自动的生成静态页面,当用户访问 http://abc.com/test.do,会自动生成 test.html ,然后显示给用户。
二、下面我们在简单介绍一下要想掌握页面静态化方案应该掌握的知识点
1、 基础― URL Rewrite
??什么是 URL Rewrite 呢 ? URL 重写。用一个简单的例子来说明问题:输入网址http://www.abc.com/test.do ,但是实际上访问的却是 http://www.abc.com/test.action,那我们就可以说 URL 被重写了。这项技术应用广泛,有许多开源的工具可以实现这个功能。
2、 基础― Servlet web.xml
???如果你还不知道 web.xml 中一个请求和一个 servlet 是如何匹配到一起的,那么请搜索一下 servlet 的文档。这可不是乱说呀,有很多人就认为 /xyz/*.do 这样的匹配方式能有效。
如果你还不知道怎么编写一个 servlet ,那么请搜索一下如何编写 servlet。这可不是说笑呀,在各种集成工具漫天飞舞的今天,很多人都不会去从零编写一个 servlet了。
三、基本的方案介绍
其中,对于 URL Rewriter的部分,可以使用收费或者开源的工具来实现,如果 url不是特别的复杂,可以考虑在 servlet 中实现,那么就是下面这个样子:?
?
三、实现一个servlet生成html的基本功能,包含了URL 重定向功能。
现在我们来看看一个普通的url分页请求怎么生成静态页面。
??假如现在要显示第一页的数据信息 ,那么通常的链接会是这个样子 : http://abc.com/xx.do?pageNumber=1 。
我们说一下这个最简单的方式,打个比方:当我们访问 http://abc.com/xx_pageNumber_1.shtml 的时候 ,就是在访问 http://abc.com/xx.do?pageNumber=1 。规律就是请求的 action(动作),和参数的名称,参数的值,都用下滑线分开,而且请求的网页的最终后缀是 .shtm 。然后呢?我们需要编写一个servlet,去处理 .shtm 的请求:
<servlet>
?? <servlet-name>creatorHtmlServlet</servlet-name>
?? <servlet-class>com.jlins.CreatorHtmlServlet</servlet-class>
?? <load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
?? <servlet-name>creatorHtmlServlet</servlet-name>
?? <url-pattern>*.shtm</url-pattern>
</servlet-mapping>
下面就是 servlet 的内容了:
import javax.servlet.http.*; import java.io.*; import org.apache.log4j.*; import javax.servlet.*; public class CreatorHtmlServlet extends HttpServlet { private Logger logger=Logger.getLogger(CreatorHtmlServlet.class); public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String encoding = "UTF-8"; //获得请求address String templatePath = URLReWrite(request); //获得相对路径 String realPath= request.getSession().getServletContext().getRealPath("/"); //获得要生成的静态html文件的名字 String htmlName = getHtmlFileName(request); //获得详细路径的 html文件的名字 String cachhtmlFileName = realPath + File.separator + htmlName; File cacheFile = new File(cachhtmlFileName); boolean load = true; //文件存在的话就可以直接返回了,不需要做任何处理 if(cacheFile.exists()) { load = false; } if(load) { final ByteArrayOutputStream os = new ByteArrayOutputStream(); final ServletOutputStream stream = new ServletOutputStream(){ public void write(byte[] data,int offset, int length){ os.write(data, offset, length); } public void write(int b) throws IOException{ os.write(b); } }; final PrintWriter pw = new PrintWriter(new OutputStreamWriter(os, encoding)); HttpServletResponse rep = new HttpServletResponseWrapper(response){ public ServletOutputStream getOutputStream() { return stream; } public PrintWriter getWriter() { return pw; } }; logger.debug("HtmlCreatorServlet RequestDispatcher = " + templatePath); // 使用 RequestDispatcher转发请求,请求真是的地址 // 例如 index.shtm ,则转发到 index.do RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(templatePath); dispatcher.include(request, rep); pw.flush(); FileOutputStream fos = null; try { if(os.size() == 0) { // 验证一下用户转发的地址是否有效,无效的话就提示错误 response.sendError(HttpServletResponse.SC_NOT_FOUND, ""); } else { // servlet调用其他命令在相应的目录生成html文件,并且把文件返回给客户端 fos = new FileOutputStream(cachhtmlFileName); os.writeTo(fos); dispatcher = getServletContext(). getRequestDispatcher("/"+htmlName); dispatcher.include(request, response); } } finally { if(fos != null) { fos.close(); } } } else { RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/"+htmlName); dispatcher.include(request, response); } } // 主要的功能就是把http://abc.com/xx_pageNumber_1.shtm // 转换成 http://abc.com/xx.do?pageNumber=1 形式 protected String URLReWrite(HttpServletRequest request) throws ServletException, IOException { String uri = request.getRequestURI(); String contextPath = request.getContextPath(); logger.debug("HtmlCreator contextPath = " + contextPath); if (contextPath != null && contextPath.length() > 0) uri = uri.substring(contextPath.length()); uri = uri.substring(0, uri.length()-5); String[] urls = uri.split("_"); uri = urls[0] + ".do"; if(urls.length > 1) { for(int i = 1; i < urls.length; i += 2) { if(i==1) { uri += "?" + urls[i] + "=" + urls[i+1]; } else { uri += "&" + urls[i] + "=" + urls[i+1]; } } } logger.debug("HtmlCreatorServlet get uri = " + uri); return uri; } // 主要功能根据 http://abc.com/xx_pageNumber_1.shtm // 来得到即将要生成的html文件名字,也就是 xx_pageNumber_1.html private String getHtmlFileName(HttpServletRequest request) throws ServletException, IOException{ String uri = request.getRequestURI(); String contextPath = request.getContextPath(); if (contextPath != null && contextPath.length() > 0) uri = uri.substring(contextPath.length()); uri = uri.substring(1, uri.length()-5); uri += ".html"; return uri; } }
?
上面就是整个代码了,非常的简单吧
我们这章要讨论一下如何让用户遵守访问方式和如何更新生成的html文件
四、如何让用户遵守访问方式
??前面我们已经通过代码讲解,用户访问 xx_pageNumber_1.shtm,那么我们可以直接返回已经存在的html文件给用户,大大的减轻应用服务器的压力。可是如果客户直接访问 xx.do?pageNumber=1 呢?浏览器是客户的,如果他们要想恶意的请求你的网站你是没有办法控制的,难道我们要在页面上告诉访问者“请遵守访问方式,否则您将被如何如何”吗?
??针对上面这种情况,我们需要在 index.do 中做点手脚。简单的说就是在我们自己转发请求的时候设置一个变量告诉被请求的地址我们是合法的,例如:xx.do?pageNumber=1后面追加一个 &my=true,这样的话就可以在被请求的action中检测是否有my这个变量,如果有说明合法,该怎么操作都行,如果没有就是违法的,直接跳转到错误页面。这样就能用最简单的方式控制用户的非法请求了.
接着我们讲解一下如何刷新这些html
??因为是一次性的生成Html,如果对应的数据发生变化,比如update或者delete情况,我们就应该更新相应的html文件,那如何对html文件进行更新呢?
大体上有几个方式
1、最笨的方式,手动刷新.
例如:网站首页,当管理员更新一个功能之后,手动点击后台的一个按钮直接更新对应的html文件
2、定时刷新。
每隔一段指定的时间去更新所有已经存在的html文件,当然前提是你要先验证哪些已经生成了html文件.至于怎么验证?你可以在每生成一个文件的时候往内存的某个list或者某个文件,或者某个表写入当前生成的文件名..
3、智能刷新
先说一下,这个不好做,但是做好了很牛方便。
管理员在后台的每个操作 增删改查,都进行监听,并在操作完成后去验证一下对应的文件是否存在,存在就更新,不存在就不操作. (要想实现这种方式,在进行框架设计的时候就要把这一模块考虑好,让后台的每一步操作都实现一个已经公共的被观察的对象.--可以查考一下观察者模式)