1. 程式人生 > 其它 >(仿支付寶支付結果頁)如何實現頁面廣告隨時上下線、過期自動下線及到時自動上線?

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

原文連結: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

思考:

這些頁面配置的廣告在一段時間內是不會變的,如果頁面請求次數較多,廣告查詢次數就會很頻繁,對資料庫造成不必要的壓力。

所以可以引入快取,降低資料庫請求次數,緩解資料庫壓力。這裡使用的Redis。

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

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

這裡選擇的是,專案啟動時非同步把符合條件的頁面廣告配置資訊存入Redis,那些還沒到指定時間的先不放Redis,等到訪問頁面載入廣告時,先查Redis,若無則按條件(>=nowtime)查庫,查到後存Redis


在介面中拿到廣告配置資訊後,判斷當前時間是否在配置的時間區間內,由於一個頁面配置多個廣告,不同廣告時間也不同,所以要迭代,把符合的返回,有過期的就做標記,然後把整個頁面的配置資訊在Redis裡刪除。
(或者不選擇在啟動時載入,就在使用者請求時加入快取,但是下面的第1步的方法在重新整理載入時會用到不能刪)

具體實現

第1步、專案啟動時先把頁面廣告配置資訊存入Redis

a、查詢所有pageId
SELECT pageId FROM page_config page_adv WHERE nowtime<=endtime AND GROUP BY pageId
三個表內連線,得List,得到的都是配置的有廣告的並且廣告還沒過期的pageId。
b、查詢pegeId對應的廣告圖片及跳轉連結
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快取起來,調介面時前判斷在不在快取裡。