亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第2節(jié):計數(shù)器和統(tǒng)計數(shù)據(jù)

sourcenode / 3082人閱讀

摘要:清理程序通過對記錄已知計數(shù)器的有序集合執(zhí)行命令來一個接一個的遍歷所有已知的計數(shù)器。

上一篇文章:Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第1節(jié):使用Redis來記錄日志
下一篇文章:Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第3節(jié):查找IP所屬城市以及國家

正如第三章所述,通過記錄各個頁面的被訪問次數(shù),我們可以根據(jù)基本的訪問計數(shù)信息來決定如何緩存頁面。但是第三章只是一個非常簡單的例子,現(xiàn)實情況很多時候并非是如此簡單的,特別是涉及實際網(wǎng)站的時候,尤為如此。

知道我們的網(wǎng)站在最近5分鐘內(nèi)獲得了10 000次點擊,或者數(shù)據(jù)庫在最近5秒內(nèi)處理了200次寫入和600次讀取,是非常有用的。通過在一段時間內(nèi)持續(xù)地記錄這些信息,我們可以注意到流量的突增和漸增情況,預(yù)測何時需要對服務(wù)器進行升級,從而預(yù)防系統(tǒng)因為負載超載而下線。

這一節(jié)將分別介紹使用Redis來實現(xiàn)計數(shù)器的方法以及使用Redis來進行數(shù)據(jù)統(tǒng)計的方法,并在最后討論如何簡化示例中的數(shù)據(jù)統(tǒng)計操作。本節(jié)展示的例子都是由實際的用例和需求驅(qū)動的。首先,讓我們來看看,如何使用Redis來實現(xiàn)時間序列計數(shù)器,以及如何使用這些計數(shù)器來記錄和監(jiān)視應(yīng)用程序的行文。

將計數(shù)器存儲到Redis里面

在監(jiān)控應(yīng)用程式的同時,持續(xù)的收集信息是一件非常重要的事情。那些影響網(wǎng)站響應(yīng)速度以及網(wǎng)站所能服務(wù)的頁面數(shù)量的代碼改動、新的廣告營銷活動或者是剛剛接觸系統(tǒng)的新用戶,都有可能會徹底地改變網(wǎng)站載入頁面的數(shù)量,并因此而影響網(wǎng)站的各項性能指標。但如果我們平時不記錄任何指標數(shù)據(jù)的話,我們就不可能知道指標發(fā)生了變化,也就不知道網(wǎng)站的性能是在提高還是在下降。

為了收集指標數(shù)據(jù)并進行監(jiān)視和分析,我們將構(gòu)建一個能夠持續(xù)創(chuàng)建并維護計數(shù)器的工具,這個工具創(chuàng)建的每個計數(shù)器都有自己的名字(名字帶有網(wǎng)站點擊量、銷量或者數(shù)據(jù)庫查詢字樣的計數(shù)器都是比較重要的計數(shù)器)。這些計數(shù)器會以不同的精度(如1秒、5秒、1分鐘等)存儲最新的120個數(shù)據(jù)樣本,用戶也可以根據(jù)自己的需要,對取樣的數(shù)量和精度進行修改。

實現(xiàn)計數(shù)器首先需要考慮的就是如何存儲計數(shù)器的信息,接下來將說明我們是如何將計數(shù)器信息存儲在Redis里面

對計數(shù)器進行更新

為了對計數(shù)器進行更新,我們需要存儲實際的計數(shù)器信息,對于每個計數(shù)器以及每種精度,如網(wǎng)站點擊量計數(shù)器/5秒,我們將使用一個散列來存儲網(wǎng)站在每個5秒時間片之內(nèi)獲得的點擊量,其中,散列的每個鍵都是某個時間片的開始時間,而鍵對應(yīng)的值則存儲了網(wǎng)站在該時間片之內(nèi)獲得的點擊量。下表展示了一個點擊量計數(shù)器存儲的其中一部分數(shù)據(jù),這個計數(shù)器以每5秒為一個時間片記錄著網(wǎng)站的點擊量:

鍵名:count:5:hits 類型:hash
1336376410 45
1336376405 28
1336376395 17(本行數(shù)據(jù)表示:網(wǎng)站在2012年5月7日早晨7:39:55到7:40:00總共獲得了17次點擊)
1336376400 29

為了能夠清理計數(shù)器包含的舊數(shù)據(jù),我們需要在使用計數(shù)器的同時,對被使用的計數(shù)器進行記錄。為了做到這一點,我們需要一個有序序列,這個序列不能包含任何重復(fù)元素,并且能夠讓我們一個接一個地遍歷序列中包含的所有元素。雖然同時使用列表和集合可以實現(xiàn)這種序列,但同時使用兩種數(shù)據(jù)結(jié)構(gòu)需要編寫更多代碼,并且增加客戶端和Redis之間的通信往返次數(shù)。實際上,實現(xiàn)有序序列更好的方法時使用有序集合,有序集合的各個成員分別由計數(shù)器的精度以及計數(shù)器的名字組成,而所有成員的分值都是0.因為所有成員的分值都被設(shè)置為0,所以Redis在嘗試按分值對有序集合進行排序的時候,就會發(fā)現(xiàn)這一點,并改為使用成員名進行排序,這使得一組給定的成員總是具有固定的排列順序,從而可以方便地對這些成員進行順序性的掃描。下表展示了一個有序集合,這個有序集合記錄了正在使用的計數(shù)器。

鍵名:known: 類型:zset(有序集合)
1:hits 0
5:hits 0
60:hits 0

既然我們已經(jīng)知道應(yīng)該使用什么結(jié)構(gòu)來記錄并表示計數(shù)器了,現(xiàn)在是時候來考慮一下如何使用和更新這些計數(shù)器了。

下面代碼展示了程序更新計數(shù)器的方法,對于每種時間片精度,程序都會將計數(shù)器 的精度和名字作為引用信息添加都記錄已有計數(shù)器的有序集合里面,并增加散列計數(shù)器在指定時間片內(nèi)的計數(shù)值。

#以秒為單位的計數(shù)器精度,分別為1秒/5秒/1分鐘/5分鐘/1小時/5小時/1天
#用戶可以按需調(diào)整這些精度
import time

PRECISION=[1,5,60,300,3600,18000,86400]

def update_counter(conn,name,count=1,now=None):
    #通過獲取當(dāng)前時間來判斷應(yīng)該對哪個時間片執(zhí)行自增操作。
    now=now or time.time()
    #為了保證之后的清理工作可以正確的執(zhí)行,這里需要創(chuàng)建一個事務(wù)性流水線
    pipe=conn.pipeline()
    #為我們記錄的每種精度都創(chuàng)建一個計數(shù)器
    for prec in PRECISION:
        #取得當(dāng)前時間片的開始時間
        pnow=int(now/prec)*prec
        #創(chuàng)建負責(zé)存儲計數(shù)信息的散列
        hash="%s:%s"%(prec,name)
        # 將計數(shù)器的引用信息添加到有序集合里面,并將其分值設(shè)為0,以便在之后執(zhí)行清理操作
        pipe.zadd("known:",hash,0)
        #對給定名字和精度的計數(shù)器進行更新
        pipe.hincrby("count:"+hash,pnow,count)
    pipe.execute()

更新計數(shù)器信息的過程并不復(fù)雜,程序只需要為每種時間片精度執(zhí)行zadd命令和hincrby命令就可以了。于此類似,從指定精度和名字的計數(shù)器里面獲取計數(shù)數(shù)據(jù)也是一件非常容易地事情。下面代碼展示了用于執(zhí)行這一操作的代碼:程序首先使用hgetall命令來獲取整個散列,接著將命令返回的時間片和計數(shù)器的值從原來的字符串格式轉(zhuǎn)換成數(shù)字格式,根據(jù)時間對數(shù)據(jù)進行排序,最后返回排序后的數(shù)據(jù):

def get_counter(conn,name,precision):
    #取得存儲計數(shù)器數(shù)據(jù)的鍵的名字
    hash="%s:%s"%(precision,name)
    #從Redis里面取出計數(shù)器數(shù)據(jù)
    data=conn.hgetall("count:"+hash)
    to_return=[]
    #將計數(shù)器數(shù)據(jù)轉(zhuǎn)換成指定的格式
    for key,value in data.iteritems():
        to_return.append((int(key),int(value)))
    #對數(shù)據(jù)進行排序,把舊的數(shù)據(jù)樣本排在前面
    to_return.sort()
    return to_return

get_counter()函數(shù)的工作方式就和之前描述的一樣,它獲取計數(shù)器數(shù)據(jù)并將其轉(zhuǎn)換成整數(shù),然后根據(jù)時間先后對轉(zhuǎn)換后的數(shù)據(jù)進行排序。

在弄懂了獲取計數(shù)器存儲的數(shù)據(jù)之后,接下來我們要考慮的是如何防止這些計數(shù)器存儲過多的數(shù)據(jù)。

清理舊計數(shù)器

經(jīng)過前面的介紹,我們已經(jīng)知道了怎樣將計數(shù)器存儲到Redis里面,已經(jīng)怎樣從計數(shù)器里面取出數(shù)據(jù)。但是,如果我們只是一味地對計數(shù)器進行更新而不執(zhí)行任何清理操作的話,那么程序最終將會因為存儲了過多的數(shù)據(jù)而導(dǎo)致內(nèi)存不足。好在我們事先已將所有已知的計數(shù)器記錄到了一個有序集合里面,所以對計數(shù)器進行清理只需要遍歷有序集合并刪除其中的舊計數(shù)器舊可以了。

為什么不使用expire?

expire命令的其中一個限制就是它只能應(yīng)用整個鍵,而不能只對鍵的某一部分數(shù)據(jù)進行過期處理。并且因為我們將同一個計數(shù)器在不同精度下的所有計數(shù)器數(shù)據(jù)都存放到了同一個鍵里面,所以我們必須定期地對計數(shù)器進行清理。如果讀者感興趣的話,也可以試試改變計數(shù)器組織數(shù)據(jù)的方式,使用Redis的過期鍵功能來代替手工的清理操作。

在處理和清理舊數(shù)據(jù)的時候,有幾件事情是需要我們格外留心的,其中包括以下幾件:

任何時候都可能會有新的計數(shù)器被添加進來

同一時間可能會有多個不同的清理操作在執(zhí)行

對于一個每天只更新一次的計數(shù)器來說,以每分鐘一次的頻率嘗試清理這個計數(shù)器只會浪費計算資源。

如果一個計數(shù)器不包含任何數(shù)據(jù),那么程序就不應(yīng)該嘗試對它進行清理。

我們接下來要構(gòu)建一個守護進程函數(shù),這個函數(shù)的工作方式和第三章中展示的守護進程函數(shù)類似,并且會嚴格遵守上面列出的各個注意事項。和之前展示的守護進程函數(shù)一樣,這個守護進程函數(shù)會不斷地重復(fù)循環(huán)知道系統(tǒng)終止這個進程為止。為了盡可能地降低清理操作的執(zhí)行負載,守護進程會以每分鐘一次的頻率清理那些每分鐘更新一次或者每分鐘更新多次的計數(shù)器,而對于那些更新頻率低于每分鐘一次的計數(shù)器,守護進程則會根據(jù)計數(shù)器自身的更新頻率來決定對他們進行清理的頻率。比如說,對于每秒更新一次或者每5秒更新一次的計數(shù)器,守護進程將以每分鐘一次的頻率清理這些計數(shù)器;而對于每5分鐘更新一次的計數(shù)器,守護進程將以每5分鐘一次的頻率清理這些計數(shù)器。

清理程序通過對記錄已知計數(shù)器的有序集合執(zhí)行zrange命令來一個接一個的遍歷所有已知的計數(shù)器。在對計數(shù)器執(zhí)行清理操作的時候,程序會取出計數(shù)器記錄的所有計數(shù)樣本的開始時間,并移除那些開始時間位于指定截止時間之前的樣本,清理之后的計數(shù)器最多只會保留最新的120個樣本。如果一個計數(shù)器在執(zhí)行清理操作之后不再包含任何樣本,那么程序?qū)挠涗浺阎嫈?shù)器的有序集合里面移除這個計數(shù)器的引用信息。以上給出的描述大致地說明了計數(shù)器清理函數(shù)的運作原理,至于程序的一些邊界情況最好還是通過代碼來說明,要了解該函數(shù)的所有細節(jié),請看下面代碼:

import bisect
import time

import redis

QUIT=True
SAMPLE_COUNT=1

def clean_counters(conn):
    pipe=conn.pipeline(True)
    #為了平等的處理更新頻率各不相同的多個計數(shù)器,程序需要記錄清理操作執(zhí)行的次數(shù)
    passes=0
    #持續(xù)地對計數(shù)器進行清理,知道退出為止
    while not QUIT:
        #記錄清理操作開始執(zhí)行的時間,這個值將被用于計算清理操作的執(zhí)行時長
        start=time.time()
        index=0
        #漸進的遍歷所有已知計數(shù)器
        while index

正如之前所說,clean_counters()函數(shù)會一個接一個地遍歷有序集合里面記錄的計數(shù)器,查找需要進行清理的計數(shù)器。程序在每次遍歷時都會對計數(shù)器進行檢查,確保只清理應(yīng)該清理的計數(shù)器。當(dāng)程序嘗試清理一個計數(shù)器的時候,它會取出計數(shù)器記錄的所有數(shù)據(jù)樣本,并判斷哪些樣本是需要被刪除的。如果程序在對一個計數(shù)器執(zhí)行清理操作之后,然后這個計數(shù)器已經(jīng)不再包含任何數(shù)據(jù),那么程序會檢查這個計數(shù)器是否已經(jīng)被清空,并在確認了它已經(jīng)被清空之后,將它從記錄已知計數(shù)器的有序集合中移除。最后,在遍歷完所有計數(shù)器之后,程序會計算此次遍歷耗費的時長,如果為了執(zhí)行清理操作而預(yù)留的一分鐘時間沒有完全耗盡,那么程序?qū)⑿菝咧钡竭@一分鐘過去為止,然后繼續(xù)進行下次遍歷。

現(xiàn)在我們已經(jīng)知道怎樣記錄、獲取和清理計數(shù)器數(shù)據(jù)了,接下來要做的視乎就是構(gòu)建一個界面來展示這些數(shù)據(jù)了。遺憾的是,這些內(nèi)容設(shè)計到前端,并不在本內(nèi)容介紹范圍內(nèi),如果感興趣,可以試試jqplot、Highcharts、dygraphs已經(jīng)D3,這幾個JavaScript繪圖庫無論是個人使用還是專業(yè)使用都非常合適。

在和一個真實的網(wǎng)站打交道的時候,知道頁面每天的點擊可以幫助我們判斷是否需要對頁面進行緩存。但是,如果被頻繁訪問的頁面只需要花費2毫秒來進行渲染,而其他流量只要十分之一的頁面卻需要花費2秒來進行渲染,那么在緩存被頻繁訪問的頁面之前,我們可以先將注意力放到優(yōu)化渲染速度較慢的頁面上去。在接下來的一節(jié)中,我們將不再使用計數(shù)器來記錄頁面的點擊量,而是通過記錄聚合統(tǒng)計數(shù)據(jù)來更準確地判斷哪些地方需要進行優(yōu)化。

使用Redis存儲統(tǒng)計數(shù)據(jù)

首先需要說明的一點是,為了統(tǒng)計數(shù)據(jù)存儲到Redis里面,筆者曾經(jīng)實現(xiàn)過5種不同的方法,本節(jié)介紹的方法綜合了這5種方法里面的眾多優(yōu)點,具有非常大的靈活性和可擴展性。

本節(jié)所展示的存儲統(tǒng)計數(shù)據(jù)的方法,在工作方式上與上節(jié)介紹的log_common()函數(shù)類似:這兩者存儲的數(shù)據(jù)記錄的都是當(dāng)前這一小時以及前一小時所產(chǎn)生的事情。另外,本節(jié)介紹的方法會記錄最小值、最大值、平均值、標準差、樣本數(shù)量以及所有被記錄值之和等眾多信息,以便不時之需。

對于一種給定的上下文和類型,程序?qū)⑹褂靡粋€有序集合來記錄這個上下文以及這個類型的最小值、最大值、樣本數(shù)量、值的和、值的平方之和等信息,并通過這些信息來計算平均值以及標準差。程序?qū)⒅荡鎯υ谟行蚣侠锩娌⒎鞘菫榱税凑辗种祵Τ蓡T進行排序、而是為了對存儲著統(tǒng)計信息的有序集合和其他有序集合進行并集計算,并通過min和max這兩個聚合函數(shù)來篩選相交的元素。下表展示了一個存儲統(tǒng)計數(shù)據(jù)的有序集合實例,它記錄了ProfilePage(個人簡歷)上下文的AccessTime(訪問時間)統(tǒng)計數(shù)據(jù)。

表名:starts:ProfilePage:AccessTime 類型:zset
min 0.035
max 4.958
sunsq 194.268
sum 258.973
count 2323

既然我們已經(jīng)知道了程序要存儲的是什么類型的數(shù)據(jù),那么接下來要考慮的就是如何將這些數(shù)據(jù)寫到數(shù)據(jù)結(jié)構(gòu)里面了。

下面代碼展示了負責(zé)更新統(tǒng)計數(shù)據(jù)的代碼。和之前介紹過的常見日志程序一樣,統(tǒng)計程序在寫入數(shù)據(jù)之前會進行檢查,確保被記錄的是當(dāng)前這小時的統(tǒng)計數(shù)據(jù),并將不屬于當(dāng)前這一小時的舊數(shù)據(jù)進行歸檔。在此之后,程序會構(gòu)建兩個臨時有序集合,其中一個用于保存最小值,而另一個則用于保存最大值然后使用zunionstore命令以及它的兩個聚合函數(shù)min和max,分別計算兩個臨時有序集合與記錄當(dāng)前統(tǒng)計數(shù)據(jù)的有序集合之前的并集結(jié)果。通過使用zunionstore命令,程序可以快速的更新統(tǒng)計數(shù)據(jù),而無須使用watch去監(jiān)視可能會頻繁進行更新的存儲統(tǒng)計數(shù)據(jù)的鍵,因為這個鍵可能會頻繁地進行更新。程序在并集計算完畢之后就會刪除那些臨時有序集合,并使用zincrby命令對統(tǒng)計數(shù)據(jù)有序集合里面的count、sum、sumsq這3個成員進更新。

import datetime
import time
import uuid

import redis


def update_status(conn,context,type,value,timeout=5):
    #負責(zé)存儲統(tǒng)計數(shù)據(jù)的鍵
    destination="stats:%s:%s"%(context,type)
    #像common_log()函數(shù)一樣,處理當(dāng)前這一個小時的數(shù)據(jù)和上一個小時的數(shù)據(jù)
    start_key=destination+":start"
    pipe=conn.pipeline(True)
    end=time.time()+timeout
    while time.time()<=end:
        try:
            pipe.watch(start_key)
            now=datetime.utcnow().timetuple()
            # 像common_log()函數(shù)一樣,處理當(dāng)前這一個小時的數(shù)據(jù)和上一個小時的數(shù)據(jù)
            hour_start=datetime(*now[:4]).isoformat()

            existing=pipe.get(start_key)
            pipe.multi()
            if existing and existing

update__status()函數(shù)的前半部分代碼基本上可以忽略不看,因為它們和上節(jié)介紹的log_common()函數(shù)用來輪換數(shù)據(jù)的代碼幾乎一模一樣,而update__status()函數(shù)的后半部分則做了我們前面描述過的事情:程序首先創(chuàng)建兩個臨時有序集合,然后使用適當(dāng)?shù)木酆虾瘮?shù),對存儲統(tǒng)計數(shù)據(jù)的有序集合以及兩個臨時有序集合分別執(zhí)行zunionstore命令;最后,刪除臨時有序集合,并將并集計算所得的統(tǒng)計數(shù)據(jù)更新到存儲統(tǒng)計數(shù)據(jù)的有序集合里面。update__status()函數(shù)展示了將統(tǒng)計數(shù)據(jù)存儲到有序集合里面的方法,但如果想要獲取統(tǒng)計數(shù)據(jù)的話,又應(yīng)該怎么做呢?

下面代碼展示了程序取出統(tǒng)計數(shù)據(jù)的方法:程序會從記錄統(tǒng)計數(shù)據(jù)的有序集合里面取出所有被存儲的值,并計算出平均值和標準差。其中,平均值可以通過值的和(sum)除以取樣數(shù)量(count)來計算得出;而標準差的計算則更復(fù)雜一些,程序需要多做一些工作才能根據(jù)已有的統(tǒng)計信息計算出標注差,但是為了簡潔起見,這里不會解釋計算標準差時用到的數(shù)學(xué)知識。

import datetime
import time
import uuid

import redis

def get_stats(conn,context,type):
    #程序?qū)倪@個鍵里面取出統(tǒng)計數(shù)據(jù)
    key="stats:%s:%s"%(context,type)
    #獲取基本的統(tǒng)計數(shù)據(jù),并將它們都放到一個字典里面
    data=dict(conn.zrange(key,0,-1,withscores=True))
    #計算平均值
    data["average"]=data["sum"]/data["count"]
    #計算標準差的第一個步驟
    numerator=data["sumsq"]-data["sun"]**2/data["count"]
    #完成標準差的計算工作
    data["stddev"]=(numerator/data["count"]-1 or 1)** .5
    return data

除了用于計算標準差的代碼之外,get_stats()函數(shù)并沒有什么難懂的地方,如果讀者愿意花些時間在網(wǎng)上了解什么叫標準差的話,那么讀懂這些標準差的代碼應(yīng)該也不是什么難事。盡管有了那么多統(tǒng)計數(shù)據(jù),但我們可能還不太清楚自己應(yīng)該觀察哪些數(shù)據(jù),而接下來的一節(jié)就會解答這個問題。

簡化統(tǒng)計數(shù)據(jù)的記錄與發(fā)現(xiàn)

在將統(tǒng)計數(shù)據(jù)存儲到Redis里面之后,接下來我們該做些什么呢?說的更詳細一點,在知道了訪問每個頁面所需的時間之后,我們要怎樣才能找到那些生成速度較慢的網(wǎng)頁?或者說,當(dāng)某個頁面的生成速度變得比以往要慢的時候,我們?nèi)绾尾拍苤み@一情況?簡單的說,為了發(fā)現(xiàn)以上提到的這些情況,我們需要存儲更多信息,而具體的方法將這一節(jié)里面介紹。

要記錄頁面的訪問時長,程序就必須在頁面被訪問時進行計時。為了做到這一點,我們可以在各個不同的頁面設(shè)置計時器,并添加代碼來記錄計時的結(jié)果,但最好的辦法是直接實現(xiàn)一個能夠進行計時并將計時結(jié)果存儲起來的東西,讓它將平均訪問速度最慢的頁面都記錄到一個有序集合里面,并向我們報告哪些頁面的載入時間變得比以前更長了。

為了計算和記錄訪問時長,我們會編寫一個Python上下文管理器,并使用這個上下文管理器來包裹那些需要計算并記錄訪問時長的代碼。

在Python里面,一個上下文管理器就是一個專門定義的函數(shù)或者類,這個函數(shù)或者類的不同部分可以在一段代碼執(zhí)行之前以及執(zhí)行之后分別執(zhí)行。上下文管理器使得用戶可以很容易地實現(xiàn)類似【自動關(guān)閉已打開的文件】這樣的功能。

下面代碼展示了用于計算和記錄訪問時長的上下文管理器:程序首先會取得當(dāng)前時間,接著執(zhí)行被包裹的代碼,然后計算這些代碼的執(zhí)行時長,并將結(jié)果記錄到
Redis里面;除此之外,程序還會對記錄當(dāng)前上下文最大訪問的時間的有序集合進行更新。

import contextlib
import time

#將這個Python生成器用作上下文管理器
@contextlib.contextmanager
def access__time(conn,context):
    #記錄代碼塊執(zhí)行前的時間
    start=time.time()
    #運行被包裹的代碼塊
    yield

    #計算代碼塊的執(zhí)行時長
    data=time.time()-start
    #更新這一上下文的統(tǒng)計數(shù)據(jù)
    stats=update_stats(conn,context,"AccessTime",data)
    #計算頁面的平局訪問時長
    average=stats[1]/stats[0]

    pipe=conn.pipeline(True)
    #將頁面的平均訪問時長添加到記錄最長訪問時間的有序集合里面
    pipe.zadd("slowest:AccessTime",context,average)
    #AccessTime有序集合只會保留最慢的100條記錄
    pipe.zremrangebyrank("slowessTime",0,-101)
    pipe.execute()

因為access__time()上下文管理器里面有一些沒辦法只用三言兩語來解釋的概念,所以我們最好還是直接通過使用這個管理器來了解它是如何運作的。接下來的這段代碼展示了使用access__time()上下文管理器記錄web頁面訪問時長的方法,負責(zé)處理被記錄頁面的是一個回調(diào)函數(shù):

#這個視圖接收一個Redis連接以及一個生成內(nèi)容的回調(diào)函數(shù)作為參數(shù)
def process_view(conn,callback):
    #計算并記錄訪問時長的上下文管理器就是這一包裹代碼塊的
    with access_time(conn,request.path):
        #當(dāng)上下文管理器中的yield語句被執(zhí)行時,這個語句就會被執(zhí)行
        return callback()

如果還不理解,看下面簡單的實例:

import contextlib


@contextlib.contextmanager
def mark():
    print("1")
    yield
    print(2)

def test(callback):
    with mark():
        return callback()

def xxx():
    print("xxx")

if __name__ == "__main__":
    test(xxx)

運行結(jié)果:

1
xxx
2

在看過這個例子之后,即使讀者沒有學(xué)過上下文管理器的創(chuàng)建方法,但是至少也已經(jīng)知道該如何去使用它了。這個例子使用了訪問時間上下文管理器來計算生成一個頁面需要花費時多長時間,此外,同樣的上下文管理器還可以用于計算數(shù)據(jù)庫查詢花費的時長,或者用來計算渲染一個模板所需的時長。作為練習(xí),你能否構(gòu)思一些其他種類的上下文管理器,并使用它們來記錄有用的統(tǒng)計信息呢?另外,你能否讓程序在頁面的訪問時長比平均情況要高出兩個標注差或以上時,在recent_log()函數(shù)里面記錄這一情況呢?

對現(xiàn)實世界中的統(tǒng)計數(shù)據(jù)進行收集和計數(shù)

盡管本書已經(jīng)花費了好幾頁篇幅來講述該如何收集生產(chǎn)系統(tǒng)運作時產(chǎn)生的相當(dāng)重要的統(tǒng)計信息,但是別忘了已經(jīng)有很多現(xiàn)成的軟件包可以用于收集并繪制計數(shù)器以及統(tǒng)計數(shù)據(jù),我個人最喜歡的是Graphite,在時間嘗試構(gòu)建自己的數(shù)據(jù)繪圖庫之前,不妨先試試這個。

在學(xué)會了如何將應(yīng)用程序相關(guān)的各種重要信息存儲到Redis之后,在接下來一節(jié)中,我們將了解更多與訪客有關(guān)的信息,這些信息可以幫助我們處理其他問題。

上一篇文章:Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第1節(jié):使用Redis來記錄日志
下一篇文章:Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第3節(jié):查找IP所屬城市以及國家

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/42733.html

相關(guān)文章

  • Python--Redis實戰(zhàn)五章使用Redis構(gòu)建支持程序3節(jié):查找IP所屬城市以及國家

    摘要:下面清單展示了地址所屬地查找程序的具體實現(xiàn)方法將地址轉(zhuǎn)換為分值以便執(zhí)行命令查找唯一城市方法用來根據(jù)指定的分隔符將字符串進行分割。 上一篇文章:Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第2節(jié):計數(shù)器和統(tǒng)計數(shù)據(jù)下一篇文章:Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第4節(jié):服務(wù)的發(fā)現(xiàn)與配置 通過將統(tǒng)計數(shù)據(jù)和日志存儲到Redis里面,我們...

    fengxiuping 評論0 收藏0
  • Python--Redis實戰(zhàn)五章使用Redis構(gòu)建支持程序1節(jié)使用Redis來記錄日志

    摘要:包括在內(nèi)的很多軟件都使用這種方法來記錄日志。在這一節(jié)中,我們將介紹如何使用來存儲于時間緊密相關(guān)的日志,從而在功能上替代那些需要在短期內(nèi)被存儲的消息。 上一篇文章:Python--Redis實戰(zhàn):第四章:數(shù)據(jù)安全與性能保障:第8節(jié):關(guān)于性能方面的注意事項下一篇文章:Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第2節(jié):計數(shù)器和統(tǒng)計數(shù)據(jù) 在構(gòu)建應(yīng)用程序和服務(wù)的過程中...

    mdluo 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<