先森最近更新了一篇文章,發布后先森自己沒有去看,今天去看了一眼,結果突然發現這篇文章中所有的外鏈沒有自動轉成內鏈。以前優化SEO的時候,看到的優化建議基本都有不要直接跳轉到外鏈,會導致權重降低,所以先森一直都有用anylink這個插件來實現這個功能。對于anylink這個插件,先森的使用體驗還是非常好的,之前一共還發過三篇博文:WordPress為anylink插件外鏈跳轉添加漂亮的跳轉頁面WordPress:WPJAM BASIC插件與anylink沖突WordPress優化:為anylink插件增加緩存既然發現了問題,那么就得解決問題,正好是清明節放假期間,托疫情的福先森哪里也不好去,所以來會會這個bug。排查過程首先,出現沒有轉內鏈的文章只有先森最新發布的那一篇,之前的文章都是正常的。1、懷疑緩存可能是CDN有緩存,先森將本機hosts改成源站,直接訪問源站,測試依舊正常,pass。除了CDN,先森WordPress還有緩存,先森用的是插件wp-super-cache進行緩存。先森清理了插件緩存后,問題依舊;然后想是不是插件有問題,直接將wp-super-cache插件停用了,問題依舊。2、懷疑anylink插件排除了緩存的問題,先森又想是不是anylink插件自己出了什么問題,先森將anylink插件停用后再啟用,發現問題依舊,插件運行問題pass。然后先森考慮是不是插件在本篇文章的執行有問題,開debug看一看。先森在WordPress的wp-config.php中打開了dubug:define( 'WP_DEBUG', true );define( 'SAVEQUERIES', true );先森開debug主要是想看本篇文章中的SQL查詢,所以在后臺開啟了Debug Queries插件,結果開啟后并沒有看到anylink的查詢,然后先森想到了為anylink添加redis緩存這篇文章中,已經將相關的SQL查詢優化了,這樣debug確實已經看不到去MySQL的查詢了。此時先森已經開始懷疑是不是當時優化有什么bug了,但還是需要先確認一下是不是MySQL、Redis數據庫有問題。3、懷疑數據庫有問題先森的MySQL是云數據庫,Redis是部署在源站本機的。MySQL運行肯定沒有問題, 不然就不只是外鏈有問題了;Redis檢查后發現也是正常運行的。先森再去看anylink的MySQL表,發現wp_al_urls表里是有出問題這篇文章的外鏈轉內鏈對應表的,這就奇怪了,到這里MySQL的問題排除了,先森就懷疑是自己之前的優化存在問題了。4、懷疑優化存在BUG先森本來想直接在redis里查有問題文章的緩存,但是redis的key太多了,先森已經忘了key是什么規則了,所以就先去研究了一下anylink的代碼,先搞清楚之前先森的優化。首先確認到,先森優化的是插件的classes/al_filter.php文件,優化了getAllLnks()和get_slug_by_url()這兩個類中的函數,增加了先查redis緩存,沒有再查數據庫并將結果存到redis的代碼。修改的部分這兩個函數先森研究了一下,get_slug_by_url()這個函數是用在把留言者的鏈接轉換成內鏈的,所以出現問題的不可能是這個函數,那只能是getAllLnks()這個函數了。getAllLnks()這個函數保存緩存的key是‘getAllLnks:’開頭的,后面跟的是文章的id,先森就去redis查相關key。應該是redis的key不支持冒號,所以實際存儲的key是‘getAllLnks-’開頭的。先森發現有問題的文章ID是1968,所以它的key是‘getAllLnks-1968’,先森先查的是正常文章的key,查出來是一大堆的內容,而查到1968就發現明顯有問題:getAllLnks有問題這個key的內容簡直就沒有內容,按邏輯來想起碼有兩個內容:外鏈地址、內鏈地址,將這個key刪除,然后重新訪問有問題的文章,發現外鏈就已經自動轉為內鏈了,此時再來查redis的key查詢結果,正常多了:getAllLnks正常結果至此,問題已經臨時得到了解決,但是原因還需要分析一下。出現bug的原因一開始,先森以為這個問題原因是anylink在管理員登錄時不會處理外鏈,先森預覽文章被緩存了,但是找了一圈anylink的代碼,沒有找到判斷管理員是否登錄的代碼,所以應該不是這個原因。好好想了一下,發現bug的原因應該是這個:當文章還在編輯的過程中,預覽文章,由于此時文章還沒有外鏈,所以數據庫查詢結果為空,但是這個空結果被先森的代碼保存到Redis中了,由于Redis的緩存沒有設置過期或到期時間未到,先森也沒有設置redis緩存更新,導致后續anylink插件來查詢這篇文章的外鏈時一直是空的。解決方案問題原因找到了,就好解決了,先森想到了兩個解決方案:方法1:管理員登錄時anylink不處理鏈接方法2:文章發布、變更時更新anylink的緩存第一種方法,管理員登錄時,尤其是正在編輯文章時,預覽文章不會造成錯誤的key被redis緩存。但此方法還是有bug,管理員未來更新文章時新增了其他外鏈,此時由于Redis已經緩存了這篇文章的外鏈查詢情況,如果改緩存未過期,新增的外鏈在文章中會出現未被轉換的情況。且這種方法容易造成管理員視角和訪客視角不同,先森用的其它插件有類似設定,實際使用中會讓人比較頭疼,所以先森不太希望使用這種方式進行修復。相較而言,第二種方法就要合理的多,只要文章有變動,那就把anylink該文章的redis緩存刪了,有人訪問該文章時再緩存。實現也比較簡單,在自己的主體function.php最后加一個publish_post鉤子,實現這個需求:/** * 更新或發布文章清理Redis緩存 * 增加時間:2022-04-04 12:43:00 * By:http://www.cnidcc.cn/ */function Clean_Redis($post_ID){ wp_cache_delete( 'getAllLnks:'.$post_ID );}add_action('publish_post', 'Clean_Redis', 0);來測試一下,在管理后臺隨便更新了一篇已發布的文章,發布前查詢該文章的redis緩存,刷新后再去查詢,該文章的key已經查不到數據了。測試hook刪除redis緩存2020年埋下的bug終于在2022年被修復了,還好期間先森新發布的文章只有2021兩篇(有點慚愧),且不涉及外鏈,所以影響不大。按理說也會影響到舊文章的更新,但是先森最近幾年對博客這邊的關注實在太少,影響著實不大。無論怎樣,寫代碼還是的考慮周到一點,還好先森不是程序員,只是一枚小運維。