大型网站架构和优化策略和常见处理方案实例
2014-08-29 22:20
766 查看
大型在线商务网站的构架设计
本项目是一个在线商品交易平台,平台的主要目的是让企业在平台上发布商品及资讯,用户可以在此平台上购买商品并参与商品的评论。围绕这一目的,系统需要实现商品管理,商品订购,多部门订单处理,商品促销,内容管理等功能。一、平台技术架构
使用EJB是因为当时3G已经出来,如果以后3G普及了,很多用户可能会通过手机去购物,这时候商城可定要提供两个终端,一种针对电脑,一种针对手机。
采用EJB的话可以把业务层的功能做成EJB部署到一台机器上面,再分别开发PC机的客户端和手机的客户端来远程调用EJB的业务层。
如果使用SSH架构的话,需要维护多份代码。
二、服务器架构
系统有多类服务器,分别为:数据库服务器、JBOSS应用服务器(运行EJB程序)、Tomcat服务器(EJB客户端)、Apache的http服务器(专门用来解析静态文件,减轻Tomcat压力)。关于Apache的http服务器与Tomcat的集成,请在网上搜一下。
随着访问量的增大,需要分别对这三类服务器进行配置集群。在集群环境下,需要加入负载均衡器(可以用硬件来做,也可以用软件来做,最好是用硬件来做。采用软件来做的一般是没钱的公司。)将用户的请求交给集群环境中的某一台机器来处理。
集群下文件的处理方式:1、通过软件方法,开一个socket,每台机器都传送一下,这种方式不大保险。2、硬件方法,磁盘阵列。效率很高,有些硬盘可以作为服务硬盘,有些可以作为备份硬盘。可以把数据根据raid方案放在不同的扇区里面,读取的时候,读取光头会同时读取。访问硬盘的速度可以成倍提高。
多台服务器间Session同步:如果集群环境下服务器不多(一般不超过5台,最好不超过3台。因为让每个Session都分布在各个服务器上会占用很大性能)可以通过Tomcat来配置集群的Session同步。如果服务器超过5台,最好单独搞一个Session服务器。
像这种集群方案,IBM这类公司已经提供了一套一系列的硬件方案,不过一套方案要卖个上百万。有四台WEB服务器,最少能同时支撑三万多人在线。一天的访问量就有三十来万。像当当网,一天也就十八万。
另一种方案是:COOKIE+DB来实现Session的功能,他会把数据存放到数据库,给用户写入个COOKIE,COOKIE存入随机生成唯一ID,标识用户身份,卓越网和当当网用的就是这种方案。
三、用到的几个性能优化的技术
OSCatch:缓存
Velocity:页面静态化
SSI:包含页面
大型电商网站,性能优化
问题:1)当大型网站系统>10万人
一个小时内,会跟数据库交互10万次(国内有京东,淘宝),这就会出现数据库瓶颈,每个数据库最大连接数(socket)2000
在某一段短暂时间内1万人,会跟数据库发生1万次交互,2000-8000【30秒】 5000 3000
2000个用户很快就可以到页面
5000个用户访问页面比较慢
还有3000个用户会提示超时/服务器出现例外
这是访问性能的问题,原因是数据库瓶颈。
解决方案:
1>页面静态化
解决方案:使用模板技术(Velocity[9-10年]/Freemarket[5-6年])
2>2>缓存技术 (当数据更新比较快,几秒钟就更新一次,或者需要实时反映数据变化,或者页面具有很多种风格,不便于生成静态页面。如BBS)
A.页面缓存(view,html代码)
缺点:不能做到实时更新,优点:比二级缓存效率更高。
在缓存的有效期内,显示的数据是没有变化的,只有当缓存过期以后才会有变化。
页面缓存分为局部缓存和全局缓存,全局缓存是缓存整个页面的html代码,页面缓存是缓存页面中的某一块区域的html代码。
缓存的范围:application范围(所有人都能共享,比如说产品列表显示)session(只针对某一个访问者,比如说缓存某个用户的个人信息)
OSCache默认的范围是application范围,可以通过scope属性来修改。
缓存默认的有效时间是3600秒,也就是一小时。可以通过time属性来修改。
refresh属性h如果设置为true,则可以强行清楚缓存。
key属性的作用:如果不设置key属性,就根据用户输入的url来做缓存,如果用户输入的url改变,缓存也会改变。oscatche会把所有的值放到一个map中,通过 key value来做判断。
缓存的key将以请求的uri+查询字符窜组成,如果你访问/oscache/index.jsp?name=tt和/oscache/index.jsp?name=ppp将得到两分缓存。
缓存在初次访问页面时进行,后续的请求将会返回缓存中的内容。缓存中存放的内容为页面返回给用户的html代码。
OSCache配置属性介绍
cache.memory=true指定是否使用内存缓存,配置默认为true,即使用内存缓存。
cache.capacity=1000指定缓存的容量,默认的容量是无限的,我们可以为他设置缓存数量。
如果要使用硬盘缓存,我们可以这样设置:
cache.memory=false
cache.path=d:\\cache(指定缓存保存的路径,注意:路径应该采用双\\符号)
cache.persistence.class=com.opensymphony.oscache.plugins.diskpersistence.DiskPersistenceListener
cache.persistence.class用于设计持久化类
我们既要考虑页面的数量,也要考虑机器的内存,如果内存不多的话容易引起内存耗尽,系统的性能反而下降。
但是最好还是用内存缓存,因为速度比较快,一般服务器的内存都大于10g,用于缓存产品列表页面够了。因为产品列表页面不会太多,假设我们有几万个的话,有一两个g就够了。
CacheFilter的实现原理:
1 CacheFilter{ 2 doFilter(request,response,chain){ 3 String urlpath=req....; 4 if(oscache.contains(urlpath)){ 5 String content=OsCache.getKey(urlpath); 6 response.write(content); 7 }else{ 8 CacheHttpServletResponseWrapper wrapper=new CacheHttpServletResponseWrapper(response) 9 chain.doFilter(request,wrapper); 10 String content=wrapper.getContent();//获取服务器网客户端输出的html代码 11 OScache.put(urlpath,content); 12 response.write(content); 13 } 14 } 15 16 public CacheHttpServletResponseWrapper entends HttpServletResponseWrapper{ 17 private String content; 18 public CacheHttpServletResponseWrapper(HttpServletResponse response){ 19 .... 20 } 21 public void write(String content){ 22 this.content=content; 23 } 24 public String getContent(){ 25 return content; 26 } 27 } 28 }
页面缓存比二级缓存快的原因:当请求来之后系统就会交给过滤器,过滤器得到路径以后就会把缓存返回给客户端。
二级缓存要经过action service 和jsp
B.二级缓存(model/业务层,domain对象)优点:实时更新
EHCache OSCache jbossCache(分布式缓存)
第一步:导入ehcache的ehcache.jar文件(hibernate中有)
第二部:在persistence.xml文件中添加下面配置项:
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 <property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/> 2 <property name="hibernate.cache.use_second_level_cache" value="true"/> 3 <property name="hibernate.cache.use_query_cache" value="false"/>
第三步:在实体类上面标注@Cache(region="cn.aaron.bean.Person",usage=CacheConcurrencyStrategy.READ_WRITE)
第四步:在classpath下面放入ehcache.xml,内容模板如下:
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <ehcache> 3 4 <diskStore path="D:\cache" /> 5 6 <defaultCache maxElementsInMemory="10000" eternal="false" overflowToDisk="true" 7 timeToIdleSeconds="120" 8 timeToLiveSeconds="180" 9 diskPersistent="false" 10 diskExpiryThreadIntervalSeconds="60"/> 11 12 <cache name="cn.aaron.bean.Person" maxElementsInMemory="100" 13 eternal="false" overflowToDisk="true" 14 timeToIdleSeconds="300" 15 timeToLiveSeconds="600" diskPersistent="false" /> 16 </ehcache>
注意<cache>节点中的name属性要和@Cache(region="cn.aaron.bean.Person",usage=CacheConcurrencyStrategy.READ_WRITE)中的region相同
ehcache.xml文件中各项属性说明如下:
defaultCache节点为缺省的缓存策略
maxElementsInMemory 内存中最大允许的对象数量
eternal 设置缓存中的对象是否永远不过期
overflowToDisk 把溢出的对象放到硬盘上(对于本例而言,第1001个对象将存放在硬盘上)
timeToIdleSeconds 指定缓存对象空闲多长时间会过期,过期的对象会被清除掉
timeToLiveSeconds 指定缓存对象总的存活时间
diskPersistent 当jvm结束时是否持久化对象
diskExpiryThreadIntervalSeconds 指定专门用于清除过期对象的监听线程的轮询时间
3>数据源 连接池里面放一些连接对象
每次都能跟数据库建立连接socket(client)----socket(数据库)
4>SSI 对性能提升不是那么明显(有一点点作用)
Server Side Include, 通常称为“服务器端包含”技术。
使用了SSI技术的文件默认的后缀名为.shtml,SSI技术通过在html文件中加入SSI指令让web服务器在输出标准HTML代码之前先解释SSI指令,
并把解释完成后的输出结果和HTML代码一起返回给客户端。
在大部分项目中,我们主要使用了SSI的包含指令<!-#include virtual="global/foot.jsp"-->,
他的作用类似于JSP中的<jsp:include page="/global/foot.jsp"/>标签。
使用SSI主要有如下两点优势:
1 SSI技术是通用技术,它不受限于运行环境,在java,.net,CGI,ASP,PHP下都可以使用SSI技术
2 解释SSI指令的效率比解释JSP的效率快很多,因为Servlet规范提供了太多的功能,这些功能都需要servlet引擎进行解释,所以效率比较低。
在目前,大部分的门户网站都是用SSI技术,解释SSI文件最佳的服务器是Apache HTTP Server。
大型门户网站基本都使用这个来解释SSI文件。
配置实用SSI
目前主流的web服务器都提供SSI实现,我们只需要打开SSI功能就可以使用。
tomcat也可以,但是并不会提高性能,因为使用的还是servlet引擎 .
电子商务网站分页的封装
首先创建一个QueryResult类来存放结果集和总记录数:packagecn.wuxing.bean;
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 import java.util.List; 2 3 public class QueryResult<T> { 4 private List<T> resultlist; 5 private long totalrecord; 6 7 public List<T> getResultlist() { 8 return resultlist; 9 } 10 public void setResultlist(List<T> resultlist) { 11 this.resultlist = resultlist; 12 } 13 public long getTotalrecord() { 14 return totalrecord; 15 } 16 public void setTotalrecord(long totalrecord) { 17 this.totalrecord = totalrecord; 18 } 19 }
接下来在DaoSupport类里面添加如下方法:
![](http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
View
Code
getScrollData是最主要的方法,可以对分页进行排序、加条件等等。
我们可以讲getScrollData得到的值存放到下面这个类中,请注意这个类的注释以及相关方法的注释:
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 import java.util.List; 2 3 /** 4 * 封装了Action向jsf所存放的一些数据 5 * 该类最重要的是调用setQueryResult方法,他会连环设置该类其他属性的值。 6 * @author bruce 7 * @param <T> 8 */ 9 public class PageView<T> { 10 /** 分页数据 **/ 11 private List<T> records; 12 /** 页码开始索引和结束索引 **/ 13 private PageIndex pageindex; 14 /** 总页数 **/ 15 private long totalpage = 1; 16 /** 每页显示记录数 **/ 17 private int maxresult = 12; 18 /** 当前页 **/ 19 private int currentpage = 1; 20 /** 总记录数 **/ 21 private long totalrecord; 22 /** 页码数量 **/ 23 private int pagecode = 10; 24 /** 要获取记录的开始索引 **/ 25 public int getFirstResult() { 26 return (this.currentpage-1)*this.maxresult; 27 } 28 public int getPagecode() { 29 return pagecode; 30 } 31 32 public void setPagecode(int pagecode) { 33 this.pagecode = pagecode; 34 } 35 36 /** 37 * 构造方法,要使用这个类,必须传进每页显示记录数和当前页。 38 * @param maxresult 39 * @param currentpage 40 */ 41 public PageView(int maxresult, int currentpage) { 42 this.maxresult = maxresult; 43 this.currentpage = currentpage; 44 } 45 46 /** 47 * 该方法存在一个连环的调用 48 * 将从数据库分页查询出来的总结果集和总记录数设置进来。 49 * 在设置总记录数的同时,会换算出总页数。详情参看setTotalrecord方法。 50 * 在设置总页数的同时,包含分页的开始索引和结束索引的PageIndex对象也确定了下来。详情参看setTotalpage方法。 51 * @param qr 52 */ 53 public void setQueryResult(QueryResult<T> qr){ 54 setRecords(qr.getResultlist()); 55 setTotalrecord(qr.getTotalrecord()); 56 } 57 58 public long getTotalrecord() { 59 return totalrecord; 60 } 61 /** 62 * 在设置总记录数的同时,会将总页数也设置进来。 63 * 在设置总页数的同时,包含分页的开始索引和结束索引的PageIndex对象也确定了下来。 64 * @param totalrecord 65 */ 66 public void setTotalrecord(long totalrecord) { 67 this.totalrecord = totalrecord; 68 setTotalpage(this.totalrecord%this.maxresult==0? this.totalrecord/this.maxresult : this.totalrecord/this.maxresult+1); 69 } 70 public List<T> getRecords() { 71 return records; 72 } 73 public void setRecords(List<T> records) { 74 this.records = records; 75 } 76 public PageIndex getPageindex() { 77 return pageindex; 78 } 79 public long getTotalpage() { 80 return totalpage; 81 } 82 public void setTotalpage(long totalpage) { 83 this.totalpage = totalpage; 84 this.pageindex = PageIndex.getPageIndex(pagecode, currentpage, totalpage); 85 } 86 public int getMaxresult() { 87 return maxresult; 88 } 89 public int getCurrentpage() { 90 return currentpage; 91 } 92 }
PageView用到了一个类PageIndex:
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 /** 2 * 此类有一个静态方法,返回带有分页的开始索引和结束索引值的对象 3 */ 4 public class PageIndex { 5 private long startindex; 6 private long endindex; 7 8 public PageIndex(long startindex, long endindex) { 9 this.startindex = startindex; 10 this.endindex = endindex; 11 } 12 public long getStartindex() { 13 return startindex; 14 } 15 public void setStartindex(long startindex) { 16 this.startindex = startindex; 17 } 18 public long getEndindex() { 19 return endindex; 20 } 21 public void setEndindex(long endindex) { 22 this.endindex = endindex; 23 } 24 25 /** 26 * 静态方法来获取分页的开始索引和结束索引的值 27 * @param viewpagecount 分页栏总共显示多少个页码 28 * @param currentPage 当前页码值 29 * @param totalpage 总共页码值 30 * @return 带有开始索引和结束索引的页码对象 31 */ 32 public static PageIndex getPageIndex(long viewpagecount, int currentPage, long totalpage){ 33 long startpage = currentPage-(viewpagecount%2==0? viewpagecount/2-1 : viewpagecount/2); 34 long endpage = currentPage+viewpagecount/2; 35 if(startpage<1){ 36 startpage = 1; 37 if(totalpage>=viewpagecount) endpage = viewpagecount; 38 else endpage = totalpage; 39 } 40 if(endpage>totalpage){ 41 endpage = totalpage; 42 if((endpage-viewpagecount)>0) startpage = endpage-viewpagecount+1; 43 else startpage = 1; 44 } 45 return new PageIndex(startpage, endpage); 46 } 47 }
前台jsp的封装如下:
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 <%@ page language="java" pageEncoding="UTF-8"%> 2 <font color="#FFFFFF"> 3 当前页:第${pageView.currentpage}页 | 总记录数:${pageView.totalrecord}条 | 每页显示:${pageView.maxresult}条 | 总页数:${pageView.totalpage}页</font> 4 <c:forEach begin="${pageView.pageindex.startindex}" end="${pageView.pageindex.endindex}" var="wp"> 5 <c:if test="${pageView.currentpage==wp}"><b><font color="#FFFFFF">第${wp}页</font></b></c:if> 6 <c:if test="${pageView.currentpage!=wp}"><a href="javascript:topage('${wp}')" class="a03">第${wp}页</a></c:if> 7 </c:forEach>
倒数第二行用到了a
href="javascript:topage('${wp}')"而不是直接写链接地址,这是因为这个jsp是封装好的,以后别的jsp调用就可以直接写一个topage的js方法来将链接地址写入。
大型网站对图片的下载,存放,及压缩管理
构建保存图片的路径:![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 String pathdir = "/images/product/"+ productTypeId+ "/"+ productId+ "/prototype";//构建文件保存的目录
为什么要有那么多个目录,因为java本身不会去获取图片,而是调用了操作系统的一些接口来获取图片,如果一个目录下图片太多的话,操作系统获取图片的速度会变慢 ,所以巴巴运动网在构建图片路径的时候搞了多个目录,分散保存图片。
有了这个pathdir就可以得到图片保存目录的真实路径:
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 String realpathdir = request.getSession().getServletContext().getRealPath(pathdir);
获取了图片的真实路径后,就可以开始保存图片了:
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 File savedir = new File(realpathdir); 2 File file = saveFile(savedir, filename, imagefile.getFileData());
imagefile为struts的FormFile类的对象,filename为文件名,这两个属性都可以从前台获取过来。以下是saveFile方法的代码:
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 /** 2 * 保存文件 3 * @param savedir 存放目录 4 * @param fileName 文件名称 5 * @param data 保存的内容 6 * @return 保存的文件 7 * @throws Exception 8 */ 9 public static File saveFile(File savedir, String fileName, byte[] data) throws Exception{ 10 if(!savedir.exists()) savedir.mkdirs();//如果目录不存在就创建 11 File file = new File(savedir, fileName); 12 FileOutputStream fileoutstream = new FileOutputStream(file); 13 fileoutstream.write(data); 14 fileoutstream.close(); 15 return file; 16 }
保存完图片后还要保存一张图片的缩略图,宽度为140px
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 String pathdir140 = "/images/product/"+ productTypeId+ "/"+ productId+ "/140x";//140宽度的图片保存目录 2 String realpathdir140 = request.getSession().getServletContext().getRealPath(pathdir140); 3 File savedir140 = new File(realpathdir140); 4 if(!savedir140.exists()) savedir140.mkdirs();//如果目录不存在就创建 5 File file140 = new File(realpathdir140, filename); 6 ImageSizer.resize(file, file140, 140, ext);
这里我们用到了一个从网上下的用于压缩图片的ImageSizer工具类的静态方法,resize方法传进去的四个参数分别代表原始图片对象,需要被压缩的图片对象,压缩宽度的大小,图片后缀名。这个工具类只能压缩jpg, png, gif(非动画)三种格式,如果想压缩更多的格式需要付费。以下是该工具类:
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 /** 2 * 图像压缩工具 3 * @author lihuoming@sohu.com 4 * 5 */ 6 public class ImageSizer { 7 public static final MediaTracker tracker = new MediaTracker(new Component() { 8 private static final long serialVersionUID = 1234162663955668507L;} 9 ); 10 /** 11 * @param originalFile 原图像 12 * @param resizedFile 压缩后的图像 13 * @param width 图像宽 14 * @param format 图片格式 jpg, png, gif(非动画) 15 * @throws IOException 16 */ 17 public static void resize(File originalFile, File resizedFile, int width, String format) throws IOException { 18 if(format!=null && "gif".equals(format.toLowerCase())){ 19 resize(originalFile, resizedFile, width, 1); 20 return; 21 } 22 FileInputStream fis = new FileInputStream(originalFile); 23 ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); 24 int readLength = -1; 25 int bufferSize = 1024; 26 byte bytes[] = new byte[bufferSize]; 27 while ((readLength = fis.read(bytes, 0, bufferSize)) != -1) { 28 byteStream.write(bytes, 0, readLength); 29 } 30 byte[] in = byteStream.toByteArray(); 31 fis.close(); 32 byteStream.close(); 33 34 Image inputImage = Toolkit.getDefaultToolkit().createImage( in ); 35 waitForImage( inputImage ); 36 int imageWidth = inputImage.getWidth( null ); 37 if ( imageWidth < 1 ) 38 throw new IllegalArgumentException( "image width " + imageWidth + " is out of range" ); 39 int imageHeight = inputImage.getHeight( null ); 40 if ( imageHeight < 1 ) 41 throw new IllegalArgumentException( "image height " + imageHeight + " is out of range" ); 42 43 // Create output image. 44 int height = -1; 45 double scaleW = (double) imageWidth / (double) width; 46 double scaleY = (double) imageHeight / (double) height; 47 if (scaleW >= 0 && scaleY >=0) { 48 if (scaleW > scaleY) { 49 height = -1; 50 } else { 51 width = -1; 52 } 53 } 54 Image outputImage = inputImage.getScaledInstance( width, height, java.awt.Image.SCALE_DEFAULT); 55 checkImage( outputImage ); 56 encode(new FileOutputStream(resizedFile), outputImage, format); 57 } 58 59 /** Checks the given image for valid width and height. */ 60 private static void checkImage( Image image ) { 61 waitForImage( image ); 62 int imageWidth = image.getWidth( null ); 63 if ( imageWidth < 1 ) 64 throw new IllegalArgumentException( "image width " + imageWidth + " is out of range" ); 65 int imageHeight = image.getHeight( null ); 66 if ( imageHeight < 1 ) 67 throw new IllegalArgumentException( "image height " + imageHeight + " is out of range" ); 68 } 69 70 /** Waits for given image to load. Use before querying image height/width/colors. */ 71 private static void waitForImage( Image image ) { 72 try { 73 tracker.addImage( image, 0 ); 74 tracker.waitForID( 0 ); 75 tracker.removeImage(image, 0); 76 } catch( InterruptedException e ) { e.printStackTrace(); } 77 } 78 79 /** Encodes the given image at the given quality to the output stream. */ 80 private static void encode( OutputStream outputStream, Image outputImage, String format ) 81 throws java.io.IOException { 82 int outputWidth = outputImage.getWidth( null ); 83 if ( outputWidth < 1 ) 84 throw new IllegalArgumentException( "output image width " + outputWidth + " is out of range" ); 85 int outputHeight = outputImage.getHeight( null ); 86 if ( outputHeight < 1 ) 87 throw new IllegalArgumentException( "output image height " + outputHeight + " is out of range" ); 88 89 // Get a buffered image from the image. 90 BufferedImage bi = new BufferedImage( outputWidth, outputHeight, 91 BufferedImage.TYPE_INT_RGB ); 92 Graphics2D biContext = bi.createGraphics(); 93 biContext.drawImage( outputImage, 0, 0, null ); 94 ImageIO.write(bi, format, outputStream); 95 outputStream.flush(); 96 } 97 98 /** 99 * 缩放gif图片 100 * @param originalFile 原图片 101 * @param resizedFile 缩放后的图片 102 * @param newWidth 宽度 103 * @param quality 缩放比例 (等比例) 104 * @throws IOException 105 */ 106 private static void resize(File originalFile, File resizedFile, int newWidth, float quality) throws IOException { 107 if (quality < 0 || quality > 1) { 108 throw new IllegalArgumentException("Quality has to be between 0 and 1"); 109 } 110 ImageIcon ii = new ImageIcon(originalFile.getCanonicalPath()); 111 Image i = ii.getImage(); 112 Image resizedImage = null; 113 int iWidth = i.getWidth(null); 114 int iHeight = i.getHeight(null); 115 if (iWidth > iHeight) { 116 resizedImage = i.getScaledInstance(newWidth, (newWidth * iHeight) / iWidth, Image.SCALE_SMOOTH); 117 } else { 118 resizedImage = i.getScaledInstance((newWidth * iWidth) / iHeight, newWidth, Image.SCALE_SMOOTH); 119 } 120 // This code ensures that all the pixels in the image are loaded. 121 Image temp = new ImageIcon(resizedImage).getImage(); 122 // Create the buffered image. 123 BufferedImage bufferedImage = new BufferedImage(temp.getWidth(null), temp.getHeight(null), 124 BufferedImage.TYPE_INT_RGB); 125 // Copy image to buffered image. 126 Graphics g = bufferedImage.createGraphics(); 127 // Clear background and paint the image. 128 g.setColor(Color.white); 129 g.fillRect(0, 0, temp.getWidth(null), temp.getHeight(null)); 130 g.drawImage(temp, 0, 0, null); 131 g.dispose(); 132 // Soften. 133 float softenFactor = 0.05f; 134 float[] softenArray = {0, softenFactor, 0, softenFactor, 1-(softenFactor*4), softenFactor, 0, softenFactor, 0}; 135 Kernel kernel = new Kernel(3, 3, softenArray); 136 ConvolveOp cOp = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null); 137 bufferedImage = cOp.filter(bufferedImage, null); 138 // Write the jpeg to a file. 139 FileOutputStream out = new FileOutputStream(resizedFile); 140 // Encodes image as a JPEG data stream 141 JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); 142 JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bufferedImage); 143 param.setQuality(quality, true); 144 encoder.setJPEGEncodeParam(param); 145 encoder.encode(bufferedImage); 146 } 147 }
允许用户上传文件,那么我们一定要注意安全,如果用户上传了一个jsp文件,而这个文件的上传路径恰好能被用户访问到,那么用户可能会在这个jsp文件里面做一个对网站其他文件的文件操作,可以将文件保存到web-inf下面,如果用户需要下载,我们就写一个servlet读取这个文件,以流的方式返回给用户。
在线交易系统的购物车的实现
在购物车中,我们可以删除购物项,修改产品的购买数量,清空购物车,进入结算中心。以下是购物车的代码
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 /** 2 * 购物车 3 */ 4 public class BuyCart { 5 /* 购物项 */ 6 private List<BuyItem> items = new ArrayList<BuyItem>(); 7 /* 配送信息 */ 8 private OrderDeliverInfo deliverInfo; 9 /* 购买者联系信息 */ 10 private OrderContactInfo contactInfo; 11 /* 支付方式 */ 12 private PaymentWay paymentWay; 13 /* 购买者与收货人是否相同 */ 14 private Boolean buyerIsrecipients; 15 /* 配送费 */ 16 private float deliveFee = 10f; 17 /* 附言 */ 18 private String note; 19 20 public String getNote() { 21 return note; 22 } 23 24 public void setNote(String note) { 25 this.note = note; 26 } 27 28 public float getDeliveFee() { 29 return deliveFee; 30 } 31 32 public void setDeliveFee(float deliveFee) { 33 this.deliveFee = deliveFee; 34 } 35 36 public PaymentWay getPaymentWay() { 37 return paymentWay; 38 } 39 40 public void setPaymentWay(PaymentWay paymentWay) { 41 this.paymentWay = paymentWay; 42 } 43 44 public Boolean getBuyerIsrecipients() { 45 return buyerIsrecipients; 46 } 47 48 public void setBuyerIsrecipients(Boolean buyerIsrecipients) { 49 this.buyerIsrecipients = buyerIsrecipients; 50 } 51 52 public OrderContactInfo getContactInfo() { 53 return contactInfo; 54 } 55 56 public void setContactInfo(OrderContactInfo contactInfo) { 57 this.contactInfo = contactInfo; 58 } 59 60 public OrderDeliverInfo getDeliverInfo() { 61 return deliverInfo; 62 } 63 64 public void setDeliverInfo(OrderDeliverInfo deliverInfo) { 65 this.deliverInfo = deliverInfo; 66 } 67 68 public List<BuyItem> getItems() { 69 return items; 70 } 71 72 /** 73 * 添加购物项 74 * @param item 购物项 75 */ 76 public void add(BuyItem item){ 77 if(this.items.contains(item)){ 78 for(BuyItem it : this.items){ 79 if(it.equals(item)){ 80 it.setAmount(it.getAmount()+1); 81 break; 82 } 83 } 84 }else{ 85 this.items.add(item); 86 } 87 } 88 /** 89 * 删除指定购物项 90 * @param item 购物项 91 */ 92 public void delete(BuyItem item){ 93 if(this.items.contains(item)) this.items.remove(item); 94 } 95 /** 96 * 清空购物项 97 */ 98 public void deleteAll(){ 99 this.items.clear(); 100 } 101 102 /** 103 * 计算商品总销售价 104 * @return 105 */ 106 public float getTotalSellPrice(){ 107 float totalprice = 0F; 108 for(BuyItem item : this.items){ 109 totalprice += item.getProduct().getSellprice() * item.getAmount(); 110 } 111 return totalprice; 112 } 113 /** 114 * 计算商品总市场价 115 * @return 116 */ 117 public float getTotalMarketPrice(){ 118 float totalprice = 0F; 119 for(BuyItem item : this.items){ 120 totalprice += item.getProduct().getMarketprice() * item.getAmount(); 121 } 122 return totalprice; 123 } 124 /** 125 * 计算总节省金额 126 * @return 127 */ 128 public float getTotalSavedPrice(){ 129 return this.getTotalMarketPrice() - this.getTotalSellPrice(); 130 } 131 /** 132 * 计算订单的总费用 133 * @return 134 */ 135 public float getOrderTotalPrice(){ 136 return this.getTotalSellPrice()+ this.deliveFee; 137 } 138 }
以下是购物项的代码:
在线商品系统的最近浏览商品列表 --- Cookie实现
实现方式 Cookie1.对最近浏览商品的实现需要用到cookie,一下代码可以获取cookie:
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 /** 2 * 获取cookie的值 3 * @param request 4 * @param name cookie的名称 5 * @return 6 */ 7 public static String getCookieByName(HttpServletRequest request, String name) { 8 Map<String, Cookie> cookieMap = WebUtil.readCookieMap(request); 9 if(cookieMap.containsKey(name)){ 10 Cookie cookie = (Cookie)cookieMap.get(name); 11 return cookie.getValue(); 12 }else{ 13 return null; 14 } 15 } 16 17 protected static Map<String, Cookie> readCookieMap(HttpServletRequest request) { 18 Map<String, Cookie> cookieMap = new HashMap<String, Cookie>(); 19 Cookie[] cookies = request.getCookies(); 20 if (null != cookies) { 21 for (int i = 0; i < cookies.length; i++) { 22 cookieMap.put(cookies[i].getName(), cookies[i]); 23 } 24 } 25 return cookieMap; 26 }
readCookieMap先获取request里面的所有cookie,然后以键值对的形式存放到一个Map中。在getCookieByName方法中,先调用readCookieMap方法来获取所有的cookie,然后在查询自己想要查找的那个cookie的值。
2.完成了获取cookie值之后,我们就可以对cookie里面的浏览记录进行修改排序:
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 public String buildViewHistory(HttpServletRequest request, Integer currentProductId){ 2 //23-2-6-5 3 //1.如果当前浏览的id已经在浏览历史里了,我们要把移到最前面 4 //2.如果浏览历史里已经达到了10个产品了,我们需要把最选进入的元素删除 5 String cookieValue = WebUtil.getCookieByName(request, "productViewHistory"); 6 LinkedList<Integer> productids = new LinkedList<Integer>(); 7 if(cookieValue!=null && !"".equals(cookieValue.trim())){ 8 String[] ids = cookieValue.split("-"); 9 for(String id : ids){ 10 productids.offer(new Integer(id.trim())); 11 } 12 if(productids.contains(currentProductId)) productids.remove(currentProductId); 13 if(productids.size()>=10) productids.poll(); 14 } 15 productids.offer(currentProductId); 16 StringBuffer out = new StringBuffer(); 17 for(Integer id : productids){ 18 out.append(id).append('-'); 19 } 20 out.deleteCharAt(out.length()-1); 21 return out.toString(); 22 }
为了方便排序 cookie的值是:用户浏览过的商品的id加横杠分隔符结合而成的。如:23-2-6-5。
3.排序结束后我们又可以将新的cookie存入浏览器中:
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 /** 2 * 添加cookie 3 * @param response 4 * @param name cookie的名称 5 * @param value cookie的值 6 * @param maxAge cookie存放的时间(以秒为单位,假如存放三天,即3*24*60*60; 如果值为0,cookie将随浏览器关闭而清除) 7 */ 8 public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) { 9 Cookie cookie = new Cookie(name, value); 10 cookie.setPath("/"); 11 if (maxAge>0) cookie.setMaxAge(maxAge); 12 response.addCookie(cookie); 13 }
该方法的调用如下:
在线购物系统权限模块
相关文章推荐
- 【转】分布式环境下5种session处理策略(大型网站技术架构:核心原理与案例分析 里面的方案)
- [技术架构] 大型互联网站解决高并发的常见策略
- 星箭:大型网站优化策略
- 疯狂代码,大型网站架构系列之三,多对多关系的优化设计
- 基于Java技术的大型网站架构方案
- 大型视频网站的技术架构方案
- 大型网站架构系列之三,多对多关系的优化设计
- 从LiveJournal后台发展看 大型网站系统架构以及性能优化方法
- 大型网站的SEO网站优化策略
- PHP大型网站的架构实例分析
- 大型网站架构系列之五,缓存策略设计概要
- 基于Java技术的大型网站架构方案
- seo搜索引擎优化在大型网站的策略
- 大型网站系统架构以及性能优化方法
- 从LiveJournal后台发展看 大型网站系统架构以及性能优化方法(转)
- 大型网站架构技术方案集锦
- 大型网站架构技术方案集锦
- 从LiveJournal后台发展看 大型网站系统架构以及性能优化方法
- 大型网站数据库优化及浅析大型网站的架构(转)
- 大型网站架构系列之四,缓存策略设计概要