項目實戰之中小網站數據緩存的設計與實現

1.背景

傳統的基於B/S架構jsp項目一般是這樣的:客戶端(瀏覽器)向服務端發送請求,服務端接受請求後,JAVAseverlet或者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裏面,通過SpringUtilBean容器裏面的管理類(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>

這樣,我們就完成了從內存讀取數據的需求,每一次訪問不涉及數據庫的讀取,遠遠加大了訪問速度。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章