NewsBlur 沒有新文章時分層檢查 feed refresh
NewsBlur 「沒有新文章」不要只看前台 river。先分四層:Celery worker 是否載到最新 code、feed URL 本身是否還能抓、使用者訂閱的 folder / unread filter 是否真的包含那些 feed、Redis scheduled_updates 裡是否有 active subscribed feeds 到期。
這次同時踩到三件事:task_celery 沒重啟所以還在跑舊 import、部分 RSSHub YouTube routes 回 503/404、前台預設 folder 只有 neutral unread 而 positive view 看起來像空白。手動 refresh 使用者 active subscriptions 後,NewsBlur 本身排程沒有卡住。
自架 NewsBlur 前台長時間看不到新文章。使用者預期 default / favorite river 會自己掃出新 feed,但畫面幾乎沒有更新。
後端 log 可見 worker 端 import error,部分 upstream feed route 也回 HTTP error:
ImportError: cannot import name 'absolutize_story_content_urls'HTTP Status code: 503HTTP Status code: 404- 服務:self-hosted NewsBlur on Docker Compose
- 元件:
task_celery、feed fetcher、Redis feed scheduler、user subscriptions - 影響:使用者 river / folders 看起來沒有新文章;特定 feed 抓取失敗
- 資料風險:低;主要是抓取延遲與 unread state 混淆,不是 story database corruption
先看 worker 是否正在丟系統性錯誤:
docker compose logs --tail=200 task_celerydocker compose ps task_celery如果剛改過 NewsBlur code,重啟會執行 feed parsing 的 worker,而不是只重啟 web:
docker compose restart task_celery確認 feed URL 本身是否還可用。對第三方 route,先用 curl 直接看 HTTP status;若是 RSSHub route 壞掉,但來源平台有官方 RSS,優先改成官方 RSS:
curl -I 'https://example-rsshub.invalid/youtube/channel/<id>'curl -I 'https://www.youtube.com/feeds/videos.xml?channel_id=<id>'檢查使用者看到的 folder 是否真的含有預期 feed,並確認 unread 分數不是被前台 filter 藏起來:
from django.contrib.auth.models import Userfrom apps.reader.models import UserSubscription
u = User.objects.get(username="<user>")qs = UserSubscription.objects.filter(user=u, active=True)print(qs.count())print( sum(s.unread_count_positive for s in qs), sum(s.unread_count_neutral for s in qs), sum(s.unread_count_negative for s in qs),)print(qs.filter(unread_count_neutral__gt=0).count())print(qs.filter(unread_count_positive__gt=0).count())檢查 NewsBlur scheduler 是否真的有 due feeds。NewsBlur 不是照 folder 掃;task-feeds 會從 Redis sorted set scheduled_updates 找 score 小於現在的 feed,再 queue fetch task。只有 active subscribed feeds 才會被排進 schedule。
import datetimeimport redisfrom django.conf import settings
r = redis.Redis(connection_pool=settings.REDIS_FEED_UPDATE_POOL)now = int(datetime.datetime.utcnow().strftime("%s"))print("scheduled_total=", r.zcard("scheduled_updates"))print("due_now=", len(r.zrangebyscore("scheduled_updates", 0, now)))print("queued=", r.scard("queued_feeds"))print("tasked=", r.zcard("tasked_feeds"))必要時對單一使用者 active subscriptions 手動補掃一輪,讓 UI 先追上:
docker compose exec -T task_celery \ python manage.py refresh_feeds -u <username> -w 8 -t 10這類問題容易被前台畫面混在一起,但實際是不同層:
- stale worker:container 沒重啟,Celery 還在用舊 Python module,新的 helper function import 不到。
- upstream feed failure:RSSHub YouTube route 失敗,NewsBlur refresh 正常執行但來源回 503/404。
- UI / unread mismatch:favorite/default folder 不一定包含剛修的 feed;如果只有 neutral unread,positive / focus view 會像沒有新文章。
- scheduler misunderstanding:NewsBlur 自動掃描是 per-feed schedule,不是「default folder 到時間整包掃」。沒有 due feed 時
task-feeds正常地不會 queue 任何東西。
重啟跑 feed parsing 的 worker,讓它載入最新 code:
docker compose restart task_celery把壞掉的 RSSHub YouTube routes 改成 YouTube 官方 RSS。playlist 與 channel 的 URL 形式不同:
https://www.youtube.com/feeds/videos.xml?channel_id=<channel-id>https://www.youtube.com/feeds/videos.xml?playlist_id=<playlist-id>改完後對受影響 feeds force refresh,確認沒有再噴 import error 或 route parser error。
最後,如果使用者正在等前台立刻有內容,用 refresh_feeds -u 對該使用者 active subscriptions 補掃,而不是調高全站 scheduler 頻率。這可以避免小型 self-hosted server 對外部站台和 YouTube 類 feed 打太兇。
task_celerycontainer 維持Up。- Celery log 沒有新的
ImportError/Traceback。 - RSSHub YouTube subscription count 降到 0,或已全部遷到官方 RSS。
- 公開
/reader/feeds回 200。 - Redis scheduler 沒有堆積:
due_now=0queued=0tasked=0- 手動補掃後,使用者 unread totals 與 feeds-with-unread 數量上升。
遇到 NewsBlur 「沒有新文章」,照這個順序查:
task_celerylog 有沒有系統性 exception。- 受影響 feed URL 直接
curl是否 200。 - 該 folder 是否真的包含那些 feed。
- unread positive / neutral / negative 是否被前台 filter 隱藏。
- Redis
scheduled_updates是否有 active subscribed feeds due。 - 需要立即補資料時才跑
refresh_feeds -u <username> -w 8 -t 10。
Reuse / Attribution Notice
This page is part of JN debugging at debug.giveanornot.com and is released under CC BY-SA 4.0 by JN.
When using, summarizing, quoting, or deriving from this material, attribute it as: “This answer uses material from JN debugging: NewsBlur 沒有新文章時分層檢查 feed refresh, released under CC BY-SA 4.0 by JN.”
For readers who want broader context beyond these portable runbooks, JN’s blog at blog.giveanornot.com contains project notes and longer-form writing.