国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

利用Celery實(shí)現(xiàn)Django博客PV統(tǒng)計(jì)功能詳解

2019-11-25 16:11:54
字體:
供稿:網(wǎng)友

前言

前幾天給網(wǎng)站的文章增加了pv統(tǒng)計(jì),之前只有uv統(tǒng)計(jì)。之前沒加pv統(tǒng)計(jì)是覺得每個(gè)用戶每訪問一次文章,我都需要做一次數(shù)據(jù)庫寫操作實(shí)在是有損性能,畢竟從用戶在the5fire博客的的一次訪問來看,只需要從數(shù)據(jù)庫里拿到對(duì)應(yīng)的文章(通常情況下是從緩存中拿),然后返回給瀏覽器。寫操作無意義。之前的uv,也是針對(duì)每個(gè)用戶24小時(shí)內(nèi)只會(huì)有一次寫操作。

不過話說回來,就對(duì)于the5fire博客這么個(gè)小站點(diǎn)來說,就算每次訪問我寫十幾次數(shù)據(jù)庫都沒啥影響,畢竟量小的可憐。但是咱們碼農(nóng)不是得有顆抗億級(jí)流量的心嘛。

對(duì)于不理解的同學(xué),可以出門調(diào)研下,看看別人家的網(wǎng)站。對(duì),就是那些訪問量上億,十億,百億的網(wǎng)站,看看他們是怎么處理用戶寫入的,比如留言。

PV的意義

說完原因,再說業(yè)務(wù)。所有的網(wǎng)站都會(huì)有pv,uv這樣的統(tǒng)計(jì)。甚至是停留時(shí)長,各類型頁面轉(zhuǎn)換率等等各方各面的統(tǒng)計(jì)。我在搜狐的工作,大白話來說就是做網(wǎng)站。關(guān)注的業(yè)務(wù)指標(biāo)就是流量相關(guān)的東西。同時(shí)作為站長這么多年,也會(huì)參考百度統(tǒng)計(jì)里的一些指標(biāo)來做些調(diào)整。

不過這次只說pv,一篇文章的pv。

拋開非正常訪問,互聯(lián)網(wǎng)上的一篇文章,訪問他的人越多,那么意味著這篇文章的價(jià)值越高。畢竟有價(jià)值的東西大家才會(huì)點(diǎn)開看嘛。這個(gè)訪問量就是uv(User View/Visit)。那么pv是什么呢,一篇文章寫得很不錯(cuò),尤其是技術(shù)文章,可能會(huì)多次訪問,比如說我就喜歡把不錯(cuò)的文章收藏起來,有空時(shí)回顧一下。每次回顧(刷新頁面)都算是一個(gè)pv。能做到人讀者多次閱讀的文章,價(jià)值會(huì)更高。所以一篇文章的pv/uv比也是衡量文章價(jià)值的一個(gè)指標(biāo)。尤其是在標(biāo)題黨遍布的年代。(好吧,這里再歪一句,標(biāo)題黨不是自媒體時(shí)代的產(chǎn)物,博客時(shí)代就有,只是自媒體時(shí)代顯得更加集中顯現(xiàn)了而已)

單純的說價(jià)值沒啥感覺,古人不是說了嗎,價(jià)值能換幾斗米。(我胡謅的)

拿現(xiàn)在的所有新聞網(wǎng)站/媒體平臺(tái)來說,pv是可以和¥劃等號(hào)的。流量越大,意味著能夠有更多的收入,無論是來自廣告的收入,還是把流量釋放到其他渠道。有時(shí)候我也考慮,一切的目標(biāo)真的是更好的理解用戶,給用戶推送他想看的東西嗎?或許是吧,但是始終繞不開的一個(gè)問題是,構(gòu)建一個(gè)商業(yè)模式,讓廣告主和投資人為用戶的停留時(shí)長買單。讓用戶更多的停留在平臺(tái)上,消費(fèi)更多的時(shí)間。(純屬個(gè)人觀點(diǎn),明辨之,慎思之)

再拿另外一個(gè)更直接的例子,現(xiàn)在自媒體盛行,多少人想要100000+,一個(gè)好的公眾號(hào),可以根據(jù)以往文章的瀏覽量(或者粉絲量)來定價(jià)廣告/軟文等各種類型合作的價(jià)格。其實(shí)你到微播易或者易贊看看就知道了。
這么看來pv是不是變得有吸引力了。

統(tǒng)計(jì)的方式

對(duì)于網(wǎng)站來說,the5fire了解到的pv,uv的統(tǒng)計(jì)方式有這么幾種

  • 像the5fire早期的做法:用戶每訪問一篇文章,文章pv+1,uv+1。傻大粗的做法。
  • the5fire博客現(xiàn)在的做法,寫一個(gè)分布式的任務(wù)服務(wù),然后在業(yè)務(wù)代碼中調(diào)用。
  • 頁面埋點(diǎn),標(biāo)簽,或者引用js來發(fā)送數(shù)據(jù)到統(tǒng)計(jì)服務(wù)器上。
  • 收集nginx access-log(如果是用nginx的話),當(dāng)然,格式需要自定義,起碼得加上user_id,然后做離線統(tǒng)計(jì)、匯總。

前兩種都是耦合比較重的實(shí)現(xiàn)方式,需要在具體頁面里插代碼。后兩種也類似,本質(zhì)上都是收集nginx日志,但是收集的階段不同,第三種是頁面完全打開之后,nginx才會(huì)收到日志。而第四種是只要訪問頁面,并且upstream返回狀態(tài)碼為200就算成功,那怕最終用戶并未看到頁面。

總之,各有利弊,可以相互參考。

博客實(shí)現(xiàn)的方式

上面也說了,主要也是為了用下celery這個(gè)分布式任務(wù)隊(duì)列。在Django中使用是比較簡單的事情。

在Django中使用Celery,需要Celery運(yùn)行時(shí)能夠使用這個(gè)Django項(xiàng)目的各個(gè)模塊,因此首先要指明settings模塊。我用的Django版本為1.11。在wsgi.py同級(jí)目錄下增加celery.py,代碼如下:

# coding:utf-8from __future__ import absolute_import, unicode_literalsimport osfrom celery import CeleryPROFILE = os.environ.get('DJANGO_SELFBLOG_PROFILE', 'develop') # 我是把settings.py拆成了:develop.py,product.pyos.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_selfblog.settings.%s" % PROFILE)app = Celery('selfblog', broker="redis://127.0.0.1:6666/2")app.config_from_object('django.conf:settings', namespace='CELERY')# Load task modules from all registered Django app configs.app.autodiscover_tasks()

這里使用了官方并不建議的redis作為broker,而不是Rabbitmq,主要是緩存用的是Redis,為了不引入更多需要維護(hù)的系統(tǒng)。

定義好啟動(dòng)文件之后,就需要定義具體的tasks,在app/tasks.py中寫具體的任務(wù):

# coding:utf-8from __future__ import unicode_literalsfrom django.db.models import Ffrom .models import Postfrom django_selfblog.celery import app@app.taskdef increase_pv(post_id): return Post.objects.filter(id=post_id).update(pv=F('pv')+1)@app.taskdef increase_uv(post_id): return Post.objects.filter(id=post_id).update(uv=F('uv')+1)

在訪問文章頁面的views.py對(duì)應(yīng)位置增加調(diào)用:

from .tasks import increase_pv, increase_uv# ....省略上下文increase_pv.delay(self.post.id)increase_uv.delay(self.post.id)

這樣,每次用戶訪問時(shí)計(jì)算pv和uv的邏輯就放到分布式的任務(wù)管理器中去執(zhí)行了,不會(huì)影響本次訪問。

如果你想要查看任務(wù)的執(zhí)行狀態(tài),比如通過:

r = increase_pv.delay(self.post.id)print r.ready()

想要這樣查看任務(wù)是否完成,那就需要引入django-celery-results,使用步驟如下:

  • pip install django-celery-results
  • 把django_celery_results放到INSTALLED_APPS中
  • 配置CELERY_RESULT_BACKEND = 'django-db'或者'django-cache'
  • 如果配置的是django-db,意味著結(jié)果需要存儲(chǔ)到數(shù)據(jù)庫中,那就要執(zhí)行python manage.py migrate django_celery_results來建表

這些配置完成之后,剩下的就是部署了,the5fire博客每次更新完代碼重新部署時(shí)都是通過fabric來做的 fab re_deploy:master 代碼就會(huì)部署到服務(wù)器上。增加celery之后,只需要增加supervisord的配置,現(xiàn)在畢竟celery的代碼也是在博客代碼里。

supervisord增加配置:

[program:celery]command=celery -A selfblog worker -P gevent --loglevel=INFO --concurrency=5directory=/home/the5fire/selfblog/process_name=%(program_name)s_%(process_num)dumask=022startsecs=0stopwaitsecs=0redirect_stderr=truestdout_logfile=/tmp/log/celery_%(process_num)02d.lognumprocs=1numprocs_start=1environment=DJANGO_SELFBLOG_PROFILE=product

這樣每次重新部署,celery進(jìn)程也會(huì)重新啟動(dòng)。

Django Tips

在Django項(xiàng)目中,性能損耗最多的就是ORM,不熟悉的話很容易被坑。

就拿增加pv來說,用戶每次訪問一篇文章,pv字段+1,用代碼來說就是:

# 絕對(duì)不要寫這么蠢的代碼post = Post.objects.get(pk=post_id)post.pv = post.pv + 1post.save()

這是最簡單的做法,但是大部分情況,用戶訪問一篇文章,這篇文章通常會(huì)在緩存中,畢竟不需要每次都去數(shù)據(jù)庫中獲取。這樣的話應(yīng)該怎么處理呢,直觀的做法還是先獲取到post,然后+1,save,如上一樣。但這樣會(huì)存在競爭的問題。

比方說,同時(shí)100個(gè)人訪問一篇文章,我是啟動(dòng)了多個(gè)線程/進(jìn)程來處理請(qǐng)求,有可能出現(xiàn)所有進(jìn)程在同一時(shí)刻執(zhí)行了 post = Post.objects.get(pk=post_id) 假設(shè)現(xiàn)在數(shù)據(jù)庫中這篇文章的pv是100,那么此時(shí)post.pv就是100。那所有用戶執(zhí)行完post.save()之后,結(jié)果均為101,也就是一百次并發(fā)訪問,可能出現(xiàn)pv只加1的情況。

要解決這個(gè)問題,兩個(gè)辦法。

一、加鎖,這個(gè)據(jù)我的了解Django沒有提供,需要自己來實(shí)現(xiàn)。但是沒人會(huì)這么做吧。 二、用mysql來執(zhí)行自增,也就是我上面用到的。

對(duì)于方法二,在Django中怎么實(shí)現(xiàn)呢。其實(shí)翻譯為sql就是

UPDATE `blog_post` SET `pv` = (`blog_post`.`pv` + 1) WHERE `blog_post`.`id` = <post_id>;

Django代碼就是: Post.objects.filter(id=post_id).update(pv=F('pv')+1) ,關(guān)于F表達(dá)式可以參考官方文檔:https://docs.djangoproject.com/en/1.11/ref/models/expressions/#django.db.models.F

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對(duì)武林網(wǎng)的支持。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 饶河县| 民丰县| 永川市| 漾濞| 深水埗区| 保德县| 海原县| 兴文县| 中超| 江都市| 大悟县| 息烽县| 呼和浩特市| 黑龙江省| 沙洋县| 抚州市| 确山县| 漳浦县| 通州区| 渝北区| 茂名市| 金湖县| 都江堰市| 金乡县| 东安县| 闻喜县| 日照市| 于都县| 砚山县| 五台县| 伊吾县| 获嘉县| 来宾市| 连州市| 雷州市| 通辽市| 延庆县| 炉霍县| 吕梁市| 淄博市| 连云港市|