最近由於我的網站的文章瀏覽量計數在Chrome瀏覽器下有BUG,因此打算從新實現這個功能。前端
本來的實現很簡單,每次點擊文章詳情頁的時候,前端會發送一個GET請求articles/id
獲取一篇文章詳情。這個時候,會把這篇文章的瀏覽量+1,再存進數據庫裏。redis
這個實現本來能夠實現這個功能,可是後來我才發現,我犯了一個很致命的錯誤:在GET請求的業務邏輯裏進行了數據的寫操做!sql
原則來說,GET請求應該具備冪等性,即短期內同時兩個如出一轍的GET請求,返回的結果也應該是同樣的。而我本來的實現就破壞了GET請求的冪等性。數據庫
剛好,在Chrome瀏覽器裏,個人文章詳情頁會發送兩次GET請求。這疑似Chrome瀏覽器和nuxt服務端渲染之間的一個BUG,目前尚未定位到具體緣由。後端
但不管如何,後端應該是能夠避免這樣的BUG,即便某用戶短期內請求兩次或者屢次,也應該只增長一次瀏覽量計數。瀏覽器
因爲最近在學習高併發方面的知識,因此這裏也考慮一下,若是一個高併發的文章瀏覽量計數系統,應該如何設計?緩存
先來理一下需求。多線程
若是要知足高併發,那首先考慮用異步和緩存。因此考慮使用多線程加Redis的解決方案。併發
請求流程:異步
PUT
請求/articles/{id:\\d+}/view
。200
響應。200
響應後,當即把當前文章的瀏覽量+1,知足需求3。後端主要邏輯:
後端的主要思路是暫時把增長的瀏覽量(假設某篇文章爲n)放進Redis裏,而後每隔一段時間刷新到Mysql數據庫和ElasticSearch存儲裏,讓這篇文章的瀏覽量在現有的基礎上加n,而後把Redis這篇文章的瀏覽量清零。
isViewd:articleId:ip
。若是有,就說明以前瀏覽過,就什麼也不作,直接返回。若是沒有,就加上這個key。時間能夠設置爲1小時過時,防止佔用過多內存。這裏使用Redis的string類型。viewCount:articleId
,value爲緩存的瀏覽量。完成後當前線程任務就結束了。這裏使用Redis的string類型。這些key應該沒有過時時間。若是併發量特別大,能夠考慮不把瀏覽量存在數據庫裏,而僅存在Redis裏,這樣能夠獲得近乎實時的瀏覽量存儲,並且需求8排序也是實時的(使用zset),但這樣可能會耗費大量的內存資源。
雖然最後權衡了併發量和複雜性,個人我的網站的文章瀏覽邏輯並無徹底按照上述設計思路來作,但上述思路是我對一個高併發文章瀏覽量計數系統設計的思考,之後若是有機會能夠寫一個開源的版本。
可能實現起來會更復雜,根據併發量的不一樣,代碼也會有一些差異,以上思路僅供參考。