1.背景
傳統的基於B/S架構jsp項目一般是這樣的:客戶端(瀏覽器)向服務端發送請求,服務端接受請求後,JAVA的severlet或者jsp會從數據庫讀取數據,在後臺拼裝好後以HTML的形式傳輸給前臺。這樣也很好理解:比如一個用戶爲了獲得一個文章的信息,這些信息一般肯定是在數據庫裏面讀取,毋庸置疑。但是這樣做會有一個很明顯的問題:用戶每次一瀏覽一篇新聞都會往數據庫裏面讀取一次,即便這個文章更本沒有改變!這在有很多訪問請求對數據庫負載是一個大災難。
現在網上很大一部分解決方案是將這些頁面靜態化,每一個頁面都生成一個靜態的HTML,在每一次請求其實在請求這個HTML文件,不與數據庫打交道,當文章有所改變時,相對應的HTML也會改變。這種方法是將數據來源是數據庫改變成了磁盤文件,很大一部分減輕。
我們這邊的條件是這樣的,服務器是在網上買的虛擬主機,其對MYSQL和文件存儲都有很大的限制,如下圖
2.我們的解決方案
我們是這樣考慮的:由於本身文件硬盤大小有限制,而且具體內存服務提供商並無要求,所以我們放棄生成HTML頁面的方案,決定設計一套數據在內存裏面的解決方案,頁面顯示的數據全是從內存裏面讀出來的。
其中AsbPage是一個抽象類,提供了一個抽象方法initData,其功能由具體子類來實現,有子類調用Service將數據保存在dataMap中。
我們設計了一個專門的來管理這些Map的管理類,具體類圖如下:
在這個類中管理所有的Page類,並且提供了刷新數據的功能,這個類是隨着Spring的啓動而啓動的,大家都知道Spring在啓動的時候默認是單例,並且在內存中已經實例化出來了,這樣我們就把初始化數據放在管理類的構造函數裏面,當項目啓動的時候,內存中已經有這些數據了,在Spring中的配置如下
<bean id="dataCacheManager" class="com.cqut.cache.DataCacheManager" />
現在既然已經在內存裏面有了,那剩下的問題就是怎麼取了。我們在gardenDynamic.jsp這個JSP裏面,通過SpringUtil將Bean容器裏面的管理類(DataCacheManager)取出來,然後調用getPageData方法,其中有兩個參數,第一個你要去取的哪一個頁面,第二個參數是要去取的具體內容,其中第一個數據是在管理類裏面設置的,第二個參數在具體的Page頁面裏面設置的。
//對推薦閱讀和點擊排行的數據緩存啓動
DataCacheManager dm = (DataCacheManager)SpringUtil.getBeanByName("dataCacheManager");
//取得gardenDynamic這個頁面的recommendDynamic數據
List<Map<String,Object>> infoListTop2 = (List<Map<String,Object>>)dm.getPageData("gardenDynamic","recommendDynamic");
好了,現在已經大功告成了,可是有人問了,如果後臺管理員改變了文章內容但你的內存裏面的沒有改變啊,問得好,我們的解決方案有兩種:
1.定時刷新內存
2.通過事件機制如果有變動則刷新
第二種方式通過AJAX調用後臺方法來刷新,我們項目用的是第一種,我們採用的任務調度框架是quartz ,每6秒來調用一下DataCacheManager 中的reloadData 方法。具體配置如下:
<bean id="dataCacheManager" class="com.cqut.cache.DataCacheManager" />
<bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="simpleTrigger" />
</list>
</property>
<property name="configLocation" value="classpath:config/quartz.properties" />
</bean>
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail">
<ref bean="jobDetail" />
</property>
<property name="startDelay">
<value>0</value>
</property>
<property name="repeatInterval">
<value>6000</value>
</property>
</bean>
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="dataCacheManager" />
<property name="targetMethod" value="reloadData" />
</bean>
這樣,我們就完成了從內存讀取數據的需求,每一次訪問不涉及數據庫的讀取,遠遠加大了訪問速度。