问题描述
前段时间给客户做了一个会议签到系统系统中涉及到这么一个问题:用户签到的时候有一个页面显示当前签到人的照片和最近签到的6个人的照片,照片数据都以blob对象存储在数据库中的,为了在页面上显示照片,我直接写了一个servlet来处理照片的请求,servlet中直接把blob二进制流输出给jsp界面。jsp以<img src="/conference/image" height="280" width="200">的形式向servlet发送请求来获取照片servlet中以ServletOutputStream out = res.getOutputStream();Blob temp = photo.getPhoto();InputStream is = temp.getBinaryStream();byte buffer[] = new byte[256];while (is.read(buffer, 0, 256) != -1) {out.write(buffer);}out.flush();的形式响应。每当有人签到,jsp页面就自动刷新一次来获取新的6张照片这样设计有几个瓶颈1、照片本身大小比较大,每张都是200+k,每次刷新所有的照片都要重新到后台去获取,对网络的要求比较高,而且如果多个人同时使用这个页面刷新的话,更是问题2、每次请求的blob数据都需要到后台数据库中取数据,而且都是 并发的请求,对数据库的连接和数据库都有很大的压力,容易使服务down了试着改进过,因为每次请求的6张照片中正常情况有5张是重复的,所以我想用hashmap把查出来的结果缓存起来,下次有请求来了,如果在缓存中有了,就直接在缓存中取。没有再到数据库中取,缓存中到了一定数量,再把缓存全部清了,否则肯定会占用大量内存。但是这样又有问题,java的回收机制自动把我hashmap中的blob对象给清掉了。每次取到的persistence是有的可是到persistence中取blob却是空的了。所以jsp界面显示的照片都是空的。不知道有没有高人能提出一个好的解决方案。探讨探讨,如何改进。或者重新设计。不胜感激!问题补充:HashMap问题blob对象被清 是这样的HashMap本来就是放在单子实例中的,肯定也是staticpublic class AnalyseDAO extends BasicDAO {private static AnalyseDAO _instance= new AnalyseDAO();;public static AnalyseDAO getInstance() { if (_instance == null) { _instance = new AnalyseDAO(); } return _instance;}private static HashMap photohash = new HashMap();public TCifPhoto getCifPhoto(String stuempNo){if(photohash.size()>=50){ photohash.clear();}if(photohash!=null && photohash.get(stuempNo)!=null){ TCifPhoto tmp= (TCifPhoto)photohash.get(stuempNo); if(tmp.getPhoto()==null){ System.out.println("omg that's it lob is empty");}return tmp;}else{ Session s = getSession(); String sql = " from TCifPhoto cif where cif.stuempNo= '"+stuempNo+"'";try { Query q = s.createQuery(sql); List list = q.list(); if(list!=null&&list.size()>0){ return (TCifPhoto)list.get(0);}else{ return null;}} catch (HibernateException he) { throw he;}}}这样的设计在tomcat上跑一点问题都没有 HashMap中的数据也能取到,可是一但到websphere上HashMap中存储的TCifPhoto 这个persistence还在,就是persistence里面的二进制lob对象没有了,从代码里看出系统输出了“omg that's it lob is empty”就很明显知道问题的状况。可以判定websphere把lob字段给清了。或者另一种架设是websphere用到了对lob字段的lazyload的方式或者别的。没有找到相应的文档资料。问题补充:又补充讨论一下:现在看来armorking说的应该是对的,blob的操作是逻辑指针,数据库连接释放后就不能取到值了。而tomcat和websphere数据库连接的管理方式又别,所以同样的代码,tomcat能正常运行,websphere就不行。(这有待继续挖掘区别,o(∩_∩)o...)。其实把blob缓存,我觉得不是最好的方案吧,即时做成byte[]缓存解决了原先的问题,但是,每张图片有300k,在签到峰值时期,每个用户客户端与服务器的网络流量将达到很高的程度,假设刷新为2s一次 300K*6=1.8M ,接近1M/s。再加上如果有多个客户端在刷。服务器流量将受到考验。所以我考虑是不是可以把照片缓存到每个客户端ie缓存中。首先把第一次请求的照片数据从数据库中取出缓存到服务器硬盘上。而客户端直接通过url来存取服务器上的照片。每次刷新后,相同的照片ie会自动取ie缓存中的照片,而不会再到服务器上取,这样就能解决原来的两个问题了。不知道大家有什么建议?
解决方案
从lz提供的source来看blob数据检索出来以后,并没有被放到photohash中另外,根据sql的Blob的文档引用默认情况下,驱动程序使用 SQL locator(BLOB) 实现 Blob,这意味着 Blob 对象包含一个指向 SQL BLOB 数据而不是数据本身的逻辑指针。Blob 对象在它被创建的事务处理期间有效。 TCifPhoto.getPhoto()的类型如果是Blob型而不是 byte[]的话,一旦检索事务处理结束了,的确有可能变成null
解决方案二:
这样的情况就有点妖了,按理说只要不是操作系统不一致,java虚拟机的本地实现应该是一致的。缓存图片信息的字节数组看看?
解决方案三:
以我的理解,这个问题跟垃圾回收无关一般来说,对象在servlet中有三种scope:request, session, application其中request < session < application而lz想做的应该是个对象缓存但是每个用户的session是不同的,也就是说不同用户的session是无法共享的所以,应当使用application级别的缓存策略从实现手段上来讲,最简单的,就是一个单例类就能搞定只需要注意两点:1、既然是个“会议签到系统”,那么这个缓存的结构就应该是一个会议对应一个照片缓存2、每次用户签到后缓存的6个相片中最早签到的一张需要被最新签到的用户照片替换这个替换处理需要加上同步处理3、根据实际应用场景,可能需要对缓存做清除处理比如说这个“会议签到系统”应当会对会议的状态管理中有一个“会议结束”的处理那么,在这个“会议结束”处理的最后,应当把对应的缓存清除
解决方案四:
一般情况下,垃圾回收不会去主动清除仍然存在Map中引用的对象,除非是WeakHashMap等特殊的Map实现。建议你检查下代码,看缓存的HashMap是否static。
解决方案五:
每个不同的照片都有个名字吧?相同的相片的名字总是相同吧?用客户端缓存不行吗?相片缓存久点就是了嘛。这样只有初次访问的时候可能会消耗些时间。当每个人的相片都缓存了之后,就不会消耗多少资源了。
解决方案六:
引用但是这样又有问题,java的回收机制自动把我hashmap中的blob对象给清掉了。每次取到的persistence是有的可是到persistence中取blob却是空的了。所以jsp界面显示的照片都是空的。你肯定是把HashMap丢在Servlet中了, 丢在某个单件类中 [就一个系统运行的话] 或者 丢在Session中[多个系统的话].
解决方案七:
把照片放在server的硬盘上吧,显示照片根据签会人的id显示6张照片好了。不过现在你可以把硬盘上不存在的照片从库里读出来,要是硬盘上有,下次就不用读库了。