(仿支付寶支付結果頁)如何實現頁面廣告隨時上下線、過期自動下線及到時自動上線?


原文鏈接:https://juejin.im/post/5c7e4907f265da2dcf62a77c
仿支付寶支付結果頁,支付完成結果頁廣告/活動鏈接配置。

背景:

最近需要實現一個功能,關於頁面廣告自動配置的。這篇隨筆是記錄對這個需求從分析到實現以及優化的過程,以免以后忘記。

需求描述:

某些頁面需要配置廣告或活動宣傳圖,廣告或活動需滿足隨時上下線、過期自動下線及到時自動上線。
如:現在時間2019-2-22 16:16:13,要在支付完成頁面配置領獎活動,活動要在2019-3-10 00:00:00准時上線,在2019-3-30 23:59:59結束活動。
要的效果是,在活動上線前的任意時刻配置完活動后,頁面到時間自動上線這個活動。
也可能會是其他的多個活動或廣告,每個頁面廣告的個數可變,不同上下線時間可不同,其他頁面也需要實現這樣的功能,頁面與頁面之間的活動不一定一樣。

需求分析:

提取關鍵詞:

【廣告或活動宣傳圖】、【隨時上下線、過期自動下線及到時自動上線】【每個頁面廣告的個數可變】【不同廣告上下線時間可不同】【頁面與頁面之間的活動不一定一樣】

分析:

1、【廣告或活動宣傳圖】
要為不同頁面設置不同的廣告,有的頁面廣告可能一樣,也就是廣告會復用,所有要有廣告表
2、【每個頁面廣告的個數可變】、【不同廣告上下線時間可不同】、【頁面與頁面之間的活動不一定一樣】
頁面可配置多個廣告,所有要有頁面配置表,以及廣告和頁面的關系表,即頁面廣告表
頁面配置表主要配置頁面的廣告個數,實現【每個頁面廣告的個數可變】,頁面廣告表主要配置頁面的每個廣告上下線時間,實現【不同廣告上下線時間可不同】
簡單分析后得出如下表結構:廣告表adv,頁面配置表page_config,頁面廣告表page_adv
VlV0SA.md.png

思考:

這些頁面配置的廣告在一段時間內是不會變的,如果頁面請求次數較多,廣告查詢次數就會很頻繁,對數據庫造成不必要的壓力。所以可以引入緩存,降低數據庫請求次數,緩解數據庫壓力。這里使用的Redis。

那么什么時候入緩存呢?
可以選擇在服務啟動時異步把已在上下線時間區間內的廣告先加載至緩存,或選擇在請求時取緩存,緩存內沒有時再查庫然后放緩存。緩存時間視情況而定。

這里選擇的是,項目啟動時異步把符合條件的頁面廣告配置信息存入Redis,那些還沒到指定時間的先不放Redis,等到訪問頁面加載廣告時,先查Redis,若無則按條件(>=nowtime)查庫,查到后存Redis。
在接口中拿到廣告配置信息后,判斷當前時間是否在配置的時間區間內,由於一個頁面配置多個廣告,不同廣告時間也不同,所以要迭代,把符合的返回,有過期的就做標記,然后把整個頁面的配置信息在Redis里刪除。
(或者不選擇在啟動時加載,就在用戶請求時加入緩存,但是下面的第1步的方法在刷新加載時會用到不能刪)

具體實現

第1步、項目啟動時先把頁面廣告配置信息存入Redis

a、查詢所有pageId
SQL:SELECT pageId FROM page_config page_adv WHERE nowtime<=endtime AND GROUP BY pageId
三個表內連接,得List ,得到的都是配置的有廣告的並且廣告還沒過期的pageId。
b、查詢pegeId對應的廣告圖片及跳轉鏈接
SQL:SELECT 字段名 FROM page_adv及adv WHERE begintime<=nowtime<=endtime AND pageId={#pageId}
然后把查到的配置信息List (為空時不做操作)以pageId為KEY放入緩存。

第2步、給前端寫接口查詢頁面廣告

按標准的控制層,業務層,數據訪問層寫,第一步中的邏輯就是在業務層完成的
控制層代碼:
接參數pageId,調用業務層查詢對應頁面配置的廣告信息,判空,直接返回狀態碼0,即無廣告前端不展示。
不為空就根據業務邏輯處理數據(如img的URL加域名),然后返回狀態碼1,前端展示廣告。
這里控制層還可以加邏輯,迭代廣告list,把當前時間在廣告起始時間內的返回,不在的不返回,並且只要有一個廣告過期,就把這個頁面的廣告list緩存清掉。這個邏輯是把過期的清掉。
業務層:
先取緩存,沒有再查庫判斷不為空(本頁面配置的有廣告),放入緩存(pageId為KEY),然后返回
數據訪問層:
SQL:SELECT 字段名 FROM page_config adv及page_adv WHERE pageId=#{pageId} AND begintime<=nowtime<=endtime

第3步、刷新加載

為什么使用刷新加載?
因為有這樣的場景:給頁面A配置了一個廣告(當前時間在廣告的起始時間內),那么這個頁面的廣告已經在緩存里了,假如此時A頁面要新加一個廣告,在后台配置后如果不做其他操作,這個廣告不會顯示(假設緩存時間較長,為一天),因為庫更新了,緩存沒有同步更新。
解決方案:
使用Redis的發布訂閱機制實現緩存的刷新加載,使新配置的廣告及時能夠顯示。
刷新加載的回調方法即1中的方法。


思考:
假如有頁面需要配置廣告,但是還沒有配,數據庫查不到,緩存也沒有,假如這個頁面訪問量很大,那么緩存沒命中就查庫,這樣對庫的壓力就會很大,這就是緩存穿透,請求上來了很容易擊垮數據庫。那怎么辦呢?

優化

解決:
當頁面沒有配置廣告時,在緩存存標志,查詢時先看標志,在決定是否往下走。

具體方案:
這時,上面的第1步就要改了。首先改步驟a的SQL,把所有的pageId都查詢出來
SQL:SELECT pageId FROM page_config LEFT JOIN page_adv表 ON ... GROUP BY pageId
使用左連接或者直接SELECT pageId FROM page_config
步驟b的SQL改為SELECT 字段名 FROM page_adv及adv WHERE nowtime<=endtime AND pageId={#pageId}
然后把查到的配置信息放入緩存之前判斷【為空時的不做操作】改為【為空時存入一個標志】假如這個標志KEY為pageId+"EMPTY_FLAG",value為"DB_IS_NULL"

為什么只判斷小於結束時間
因為如果該頁面配置的廣告開始時間大於當前時間,那么這個是查不到的,會被處理為DB_IS_NULL,如果在這個標志還沒失效就到配置的開始時間了,那么這個廣告不會被展示。讓控制層去判斷在不在時間區間。

然后在第2步要修改一下,在業務層里取緩存中的廣告列表之前,先從緩存取pageId+"EMPTY_FLAG"的value判斷為"DATABASE_IS_NULL"直接返回空
繼續修改業務層,查庫的SQL同樣要改
SQL:SELECT 字段名 FROM page_config adv及page_adv WHERE pageId=#{pageId} AND nowtime<=endtime
然后判斷為空的話,同上面的黃字那樣處理。

刷新加載調的是第1步,不用改。

當然這個緩存穿透的優化方案只是其中一種。還可以這樣:
1、控制層攔截:根據pageId查詢page_adv表,查不到說明沒配置,直接返回。
2、page_config 表增加字段,表示當前頁面已經配置的廣告個數,默認0,每配置一個該字段加1,把大於0的pageId緩存起來,調接口時前判斷在不在緩存里。

總結:

實現這個功能並不是太難,主要用到了Redis的緩存技術,Redis發布訂閱機制,關鍵就是細節的把控,以及緩存穿透的處理。

【END】


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM