国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Python--Redis實戰:第五章:使用Redis構建支持程序:第4節:服務的發現與配置

jackzou / 1351人閱讀

摘要:在通常情況下,即使只更新配置中的一個標志,也會導致更新后的配置文件被強制推送至所有服務器,收到更新的服務器可能需要重新載入配置甚至可能還要重啟應用程序服務器。將被包裹函數的一些有用的元數據復制給配置處理器。

上一篇文章:Python--Redis實戰:第五章:使用Redis構建支持程序:第3節:查找IP所屬城市以及國家

隨著我們越來越多地使用Redis以及其他服務,如何存儲各項服務的配置信息將變成一個棘手的問題:對于一個Redis服務器、一個數據庫服務器以及一個Web服務器來說,存儲它們的配置信息并不困難;但如果我們使用了一個擁有好幾個從服務器的Redis主服務器,或者為不同的應用程序設置了不同的Redis服務器,甚至為數據庫也設置了主服務器和從服務器的話,那么存儲這些服務器的配置信息將變成一件讓人頭痛的事情。

用于連接其他服務器以及服務器的配置信息一般都是以配置文件的形式存儲在硬盤里面,每當機器下線、網絡連接斷開或者某些需要連接其他服務器的情況出現時,程序通常需要一次性地對不同服務器中的多個配置文件進行更新。而這一節要介紹的就是如何將大部分配置信息從文件轉移到Redis里面,使得應用程序可以自己完成絕大部分配置工作。

使用Redis存儲配置信息

為了展示配置管理方面的難題是多么的常見,來看一個非常簡單的配置例子:假設現在我們要用一個標志flag來表示web服務器是否正在進行維護,如果服務器正在進行維護,那么它就不應該發送數據庫請求,而是應該向訪問們返回一條簡短的【抱歉,我們正在進行維護,請稍后重試】的信息;相反,如果服務器并沒有進行維護,那么它就應該按照既定的程序來運行。

在通常情況下,即使只更新配置中的一個標志,也會導致更新后的配置文件被強制推送至所有Web服務器,收到更新的服務器可能需要重新載入配置、甚至可能還要重啟應用程序服務器。

與其嘗試為不斷增多的服務寫入和維護配置文件,不如讓我們直接配置寫入Redis里面。只要將配置信息存儲在Redis里面,并編寫應用程序來獲取這些信息,我們就不用再編寫工具來向服務器推送配置信息了,服務器和程序也不用再通過載入配置文件的方式 來更新配置信息了。

為了實現這個簡單的功能,讓我們假設自己已經構建了一個中間層或者插件,這個中間層額作用在于:當is_under_maintenance()函數返回True時,它將向用戶顯示維護頁面;與此相反,如何is_under_maintenance()函數返回False,它將如常地處理用戶的訪問請求。其中is_under_maintenance()函數通過檢查一個名為is-under-maintenance的鍵來判斷服務器是否正在進行維護:如果is-under-maintenance鍵非空,那么函數返回True;否則返回False,另外,因為訪客在看見維護頁面的時候通常都會不耐煩的頻繁刷新頁面,所以為了盡量降低Redis在處理高訪問量Web服務器時的負載,is_under_maintenance()函數最多只會每秒更新一次服務器維護信息。

下面代碼展示了is_under_maintenance()函數的具體定義:

import time

LAST_CHECKED=None
IS_UNDER_MAINTENANCE=False

def is_under_maintenance(conn):
    #將連個變量設置為全局變量以便在之后對它們進行寫入
    global LAST_CHECKED,IS_UNDER_MAINTENANCE
    #距離上次檢查是否以及超過1秒?
    if LAST_CHECKED

通過將is_under_maintenance()函數插入應用程序的正確位置上,我們可以在1秒內改變數以千計Web服務器的行為。為了降低Redis在處理高訪問量web服務器時的負載,is_under_maintenance()函數將服務器維護狀態信息的更新頻率限制為最多每秒1次,但如果有需要的話,我們也可以加快信息的更新頻率,甚至直接移除函數里面限制更新速度的那些代碼。雖然is_under_maintenance()函數看上去似乎并不實用,但它的確展示了將配置信息存儲在一個普通可訪問位置的威力。

接下來我們要考慮的是,怎樣才能將更復雜的配置選項存儲到Redis里面呢?

為每個應用程序組件分別配置一個Redis服務器

在我們越來越多地使用Redis的過程中,無數的開發者已經發現,最終在某個時間點上,只使用一臺Redis服務器將不能滿足我們的需要。因為我們可能需要記錄更多信息,可能需要更多用于緩存的空間,還可能會使用本書之后的章節會介紹的、使用Redis構建的高級服務。但不管何種原因,我們都需要用到更多Redis服務器。

為了平滑地從單臺服務器過渡到多臺服務器,用戶最好還是為應用程序中的每個獨立部分都分別運行一個Redis服務器,比如說,一個專門負責記錄日志、一個專門負責記錄統計數據、一個專門負責進行緩存、一個專門負責存儲cookies等。別忘了,一臺機器是可以運行多個Redis服務器的,只要這些服務器使用的端口號各不同就可以了。除此之外,在一個Redis服務器里面使用多個【數據庫】,也可以減少系統管理的工作量。以上提到的兩種方法,都是通過將不同數據劃分至不同鍵空間的方式,來或多或少的簡化遷移至更大或更多服務器時所需的工作。但遺憾的是,隨著Redis服務器的數量或者Redis數據庫的數量不斷增多,為所有Redis服務器管理和分發配置信息的工作將變得越來越煩瑣和無趣。

在上一節中,我們用了Redis來存儲表示服務器是否正在進行維護的標志,并通過這個標志來決定是否需要向訪客顯示維護頁面。而這一次,我們同樣可以使用Redis來存儲與其他Redis服務器有關的信息。說的更詳細一點,我們可以把一個已知的Redis服務器用作配置信息字典,然后通過這個字典存儲的配置信息來連接為不同應用或服務組件提供數據的其他Redis服務器。此外,這個字典還會在配置出現變更時,幫助客戶端連接至正確的服務器。字典的具體實現比這個例子所要求的更為通用一些,因為我敢肯定,當你開始使用這個字典來獲取配置信息的時候,你很快就會把它應用到其他服務器以及其他服務上面,而不僅僅用于獲取Redis服務器的配置信息。

我們將構建一個函數,該函數可以從一個鍵里面取出一個JSON編碼的配置值,其中,存儲配置值的鍵由服務的類型以及使用該服務的應用程序命名。舉個例子,如何我們想要獲取連接存儲統計數據的Redis服務器所需的信息,那么就需要獲取config:redis:statistics鍵的值。下面函數展示了設置配置值的具體方法:

def set_config(conn,type,component,config):
    conn.set("config:%s:%s"%(type,component))
    json.dumps(config)

通過這個函數,我們可以隨心所欲的設置任何JSON編碼的配置信息。因為get_config()函數和前面介紹過的is_under__maintenance()函數具有相似的結構,所以我們只要在語義上稍作修改,就可以使用get_config()函數來替代is__under_maintenance()函數。下面代碼列出了與set_config()相對應的get_config()函數,這個函數可以按照用戶的需要,對配置信息進行0秒、1秒或者10秒的局部緩存。

import json
import time

CONFIGS={}
CHECKED={}

def get_config(conn,type,component,wait=1):
    key="config:%s:%s"%(type,component)
    #檢查是否需要對這個組件的信息進行更新
    if CHECKED.get(key)

在擁有了配置信息和獲取配置信息的兩個函數之后,我們還可以在此之上更近一步。我們在前面一直考慮的都是怎樣存儲和獲取配置信息以便連接各個不同的Redis服務器,但直到目前為止,我們編寫的絕大多數函數和第一個參數都是一個連接參數。因此,為了不再需要手動獲取我們正在使用的各項服務的連接,下面讓我們來構建一個能夠幫助我們自動連接這些服務的方法。

自動Redis連接管理

手動創建和傳遞Redis連接并不是一件容易地事情,這不僅是因為我們需要重復查閱配置信息,還有一個原因就是,即使使用了 上一節介紹的配置管理函數,我們還是需要獲取配置、連接Redis,并在使用完連接之后關閉連接。為了簡化連接的管理操作,我們將編寫一個裝飾器,讓它負責連接除配置服務器之外的所有其他Redis服務器。

裝飾器

Python提供了一種語法,用于將函數X傳入另一個函數Y的內部,其中函數Y就被成為裝飾器。裝飾器給用戶提供了一個修改函數X行為的機會。有些裝飾器可以用于校驗參數,而有些裝飾器則可以用于注冊回調函數,甚至還有一些裝飾器可以用于管理連接:就像我們接下來要做的那樣。

下面代碼展示了我們定義的裝飾器,它接受一個指定的配置作為參數并生成一個包裝器,這個包裝器可以包裹一個函數,使得之后對被包裹函數的調用可以自動連接至正確的Redis服務器,并且連接Redis服務器所使用的那個連接會和用戶之后提供的其他參數一同傳遞至包裹的函數:

REDIS_CONNECTIONS={}

#將應用組件的名字傳遞給裝飾器
def redis_connection(component,wait=1):
    #因為函數每次被調用都需要獲取這個配置鍵,所以我們干脆把它緩存起來
    key="config:redis:"+component
    #包裝器接受一個函數作為參數,并使用另一個函數來包裹這個函數。
    def wrapper(function):
        #將被包裹函數的一些有用的元數據復制給配置處理器。
        @function.wraps(function)
        def call(*args,**kwargs):#創建負責管理連接信息的函數
            #如果有就配置存在,那么獲取它
            old_config=CONFIGS.get(key,object())
            #如果有新配置存在,那么獲取它
            _config=get_config(config_connection,"redis",component,wait)

            config={}
            #對配置進行處理并將其用于創建Redis連接
            for k,v in _config.iteritems():
                config[k.encode("utf-8")]=v

            #如果新舊配置并不相同,那么創建新的連接
            if config!=old_config:
                REDIS_CONNECTIONS[key]=redis.Redis(**config)

            #將Redis連接以及其他匹配的參數傳遞給包裹函數,然后調用該函數并返回它的執行結果。
            return function(REDIS_CONNECTIONS.get(key),*args,**kwargs)
        #返回被包裹的函數
        return call
    #返回用于包裹Redis函數的包裝器
    return wrapper
同時使用*args和**kwargs

在Python中,函數定義的args變量用于獲取所有位置參數,而kwargs變量則用于獲取所有命令出納和素,這兩種參數傳遞方式都可以將給定的參數傳入被調用的函數里面。

上面戰術的一系列嵌套函數初看上去可能會讓人感動頭昏目眩,但它們實際上并沒有想象中的那么復雜。redis_connection()裝飾器接受一個應用組件的名字作為參數并返回一個包裝器。這個包裝器接受一個我們想要將連接傳遞給它的函數為參數,然后對函數進行包裹并返回被包裹函數的調研器。這個調用器負責處理所有獲取配置信息的工作,除此之外,它還負責連接Redis服務器并調用被包裹的函數。盡管redis_connecition()函數描述起來相當復雜,但實際使用起來卻是非常方便的,下面代碼就展示了怎樣將redis_connection()函數應用到之間介紹的log_recent()函數上面。

@redis_connection("logs")
def log_recent(conn,app,message,severity=logging.INFO,pipe=None):
    # 嘗試將日志的安全級別準還為簡單的字符串
    severity = str(SEVERITY.get(severity, severity)).lower()
    # 創建負責存儲消息的鍵
    destination = "recent:%s:%s" % (name, severity)
    # 將當前時間添加到消息里面,用于記錄消息的發送時間
    message = time.asctime() + "  " + message
    # 使用流水線來將通信往返次數降低為一次
    pipe = pipe or conn.pipeline()
    # 將消息添加到日志列表的最前面
    pipe.lpush(destination, message)
    # 對日志列表進行修建,讓它只包含最新的100條消息
    pipe.ltrim(destination, 0, 99)
    # 執行兩個命令
    pipe.execute()

log_recent("main","User 235 logged in")

現在你已經看到怎樣使用redis_connection()來裝飾log_recent()函數,這個裝飾器還是蠻有用的,不是嗎?通過使用這個改良后的方法來處理鏈接和配置,我們幾乎可以把我們要調用的所有函數的代碼都刪去好幾行。

作為練習,請嘗試使用redis_connection()去裝飾之前介紹的access_time()上下文管理器,使得這個上下文管理器可以在不必手動傳遞Redis服務器連接的情況下執行。

本章小結

本章介紹的所有主題都直接或間接地用于對應用程序進行幫助和支持,這里展示的函數和裝飾器都旨在幫助讀者學會如何使用Redis來支撐應用程序的不用部分:日志、計數器以及統計數據可以幫助用戶直觀地了解應用程序的性能,而IP所屬地查找程序則可以告訴你客戶所在的地點。除此之外,存儲服務的發現和配置信息可以幫助我們減少大量需要手動處理連接的工作。

現在我們已經知道了怎樣使用Redis來對應用程序進行支持了,在接下來的第6章,我們將學習如何使用Redis來構建應用程序組件。

上一篇文章:Python--Redis實戰:第五章:使用Redis構建支持程序:第3節:查找IP所屬城市以及國家

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/42741.html

相關文章

  • Python--Redis實戰五章使用Redis構建支持程序3:查找IP所屬城市以及國家

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

    fengxiuping 評論0 收藏0
  • Python--Redis實戰五章使用Redis構建支持程序1使用Redis來記錄日志

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

    mdluo 評論0 收藏0

發表評論

0條評論

jackzou

|高級講師

TA的文章

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