Redis-py官方文件翻譯

xbynet發表於2016-12-07

官網:https://github.com/andymccurd…
當前版本:2.10.5
注:這不是完整翻譯,只提取了關鍵資訊。省略了部分內容,如lua指令碼支援。

pip install redis
pip install hiredis(解析器,可選。windows下好像不行。)

>>> import redis
>>> r = redis.StrictRedis(host=`localhost`, port=6379, db=0)
>>> r.set(`foo`, `bar`)
True
>>> r.get(`foo`)
`bar`

redis-py採取兩種client class實現redis命令:
其一、StrictRedis類儘量堅持官方語法,但是以下除外:

  • SELECT: 沒有實現,應該是執行緒安全的原因。

  • DEL: 由於del是python語法關鍵字,所用delete來代替。

  • CONFIG GET|SET: 分開用 config_get or config_set來代替

  • MULTI/EXEC: 事務作為Pipeline類的其中一部分的實現。Pipeline預設保證了MULTI,EXEC宣告。但是你可以指定transaction=False來禁用這一行為。

  • SUBSCRIBE/LISTEN:PubSub作為一個獨立的類來實現釋出訂閱機制。

  • SCAN/SSCAN/HSCAN/ZSCAN:每個命令都對應一個等價的迭代器方法scan_iter/sscan_iter/hscan_iter/zscan_iter methods for this behavior.

其二、Redis類是StrictRedis的子類,提供redis-py版本向後的相容性。

關於StrictRedis與Redis的區別:(官方推薦使用StrictRedis.)
以下幾個方法在StrictRedis和Redis類中的引數順序不同。
LREM: Order of `num` and `value` arguments reversed such that `num` can provide a default value of zero.
在Redis類中是這樣的:
lrem(self, name, value, num=0)
在StrictRedis類中是這樣的:
lrem(self, name, count, value)

ZADD: Redis specifies the `score` argument before `value`. These were swapped accidentally when being implemented and not discovered until after people were already using it. The Redis class expects *args in the form of: name1, score1, name2, score2, …
在Redis類中是這樣的:
redis.zadd(`my-key`, `name1`, 1.1, `name2`, 2.2, name3=3.3, name4=4.4)
在StrictRedis中是這樣的:
redis.zadd(`my-key`, 1.1, `name1`, 2.2, `name2`, name3=3.3, name4=4.4)

SETEX: Order of `time` and `value` arguments reversed.
在Redis類中是這樣的:
setex(self, name, value, time)
而在StrictRedis中是這樣的:
setex(self, name, time, value)

連線池

>>> pool = redis.ConnectionPool(host=`localhost`, port=6379, db=0)
>>> r = redis.Redis(connection_pool=pool)

Connections:redis-py提供兩種型別的連線:基於TCP埠的,基於Unix socket檔案的(需要redis伺服器開啟配置)。

>>> r = redis.Redis(unix_socket_path=`/tmp/redis.sock`)

如果你需要,自定義連線類,需要告知連線池。

>>> pool = redis.ConnectionPool(connection_class=YourConnectionClass,
                                your_arg=`...`, ...)

釋放連線回到連線池:可以使用Redis類的reset()方法,或者使用with上下文管理語法。

解析器:
解析器控制如何解析Redis-server的響應內容,redis-py提供兩種方式的解析器類支援:PythonParser和HiredisParser(需要單獨安裝)。它優先選用HiredisParser,如果不存在,則選用PythonParser. Hiredis是redis核心團隊開發的一個高效能c庫,能夠提高10x的解析速度。

響應回撥:
The client class使用一系列的callbacks來完成響應到對應python型別的對映。這些響應回撥,定義在 Redis client class中的RESPONSE_CALLBACKS字典中。你可以使用set_response_callback 方法來新增自定義回撥類。這個方法接受兩個引數:一個命令名字,一個回撥類。回撥類接受至少一個引數:響應內容,關鍵字引數作為命令呼叫時的引數。

執行緒安全性:

Redis client instances是執行緒安全的。由於執行緒安全原因,不提供select實現,因為它會導致資料庫的切換。
在不同執行緒間傳遞PubSub or Pipeline物件也是不安全的。

Pipelines

Pipelines是Redis類的一個子類,支援快取多個命令,然後作為單個請求傳送。通過減少TCP請求次數來達到提供效能的目的。

>>> r = redis.Redis(...)
>>> r.set(`bing`, `baz`)
>>> # Use the pipeline() method to create a pipeline instance
>>> pipe = r.pipeline()
>>> # The following SET commands are buffered
>>> pipe.set(`foo`, `bar`)
>>> pipe.get(`bing`)
>>> # the EXECUTE call sends all buffered commands to the server, returning
>>> # a list of responses, one for each command.
>>> pipe.execute()
[True, `baz`]

Pipelines的實現採用流式API,故而你可以採用以下鏈式呼叫的方式:

>>> pipe.set(`foo`, `bar`).sadd(`faz`, `baz`).incr(`auto_number`).execute()
[True, True, 6]

Pipelines預設以原子性(事務)的形式執行所有快取的命令,你也可以禁用這一行為:

>>> pipe = r.pipeline(transaction=False)

WATCH命令提供了在事務之前檢測一個或多個key值的變化。一旦在事務執行之前,某個值發生了變化,那麼事務將被取消然後丟擲WatchError 異常。
利用watch我們可以實現client-side incr命令:

>>> with r.pipeline() as pipe:
...     while 1:
...         try:
...             # put a WATCH on the key that holds our sequence value
...             pipe.watch(`OUR-SEQUENCE-KEY`)
...             # after WATCHing, the pipeline is put into immediate execution
...             # mode until we tell it to start buffering commands again.
...             # this allows us to get the current value of our sequence
...             current_value = pipe.get(`OUR-SEQUENCE-KEY`)
...             next_value = int(current_value) + 1
...             # now we can put the pipeline back into buffered mode with MULTI
...             pipe.multi()
...             pipe.set(`OUR-SEQUENCE-KEY`, next_value)
...             # and finally, execute the pipeline (the set command)
...             pipe.execute()
...             # if a WatchError wasn`t raised during execution, everything
...             # we just did happened atomically.
...             break
...        except WatchError:
...             # another client must have changed `OUR-SEQUENCE-KEY` between
...             # the time we started WATCHing it and the pipeline`s execution.
...             # our best bet is to just retry.
...             continue

不過你可以使用transaction方法來簡化這一操作:它包含handling and retrying watch errors的樣板程式碼。第一引數為callable(這個callable只能接受一個Pipeline引數),及多個需要被WATCH的keys

>>> def client_side_incr(pipe):
...     current_value = pipe.get(`OUR-SEQUENCE-KEY`)
...     next_value = int(current_value) + 1
...     pipe.multi()
...     pipe.set(`OUR-SEQUENCE-KEY`, next_value)
>>>
>>> r.transaction(client_side_incr, `OUR-SEQUENCE-KEY`)
[True]

Publish / Subscribe

PubSub物件subscribes to channels and listens for new messages。

>>> r = redis.StrictRedis(...)
>>> p = r.pubsub()

>>> p.subscribe(`my-first-channel`, `my-second-channel`, ...)
>>> p.psubscribe(`my-*`, ...)

>>> p.get_message()
{`pattern`: None, `type`: `subscribe`, `channel`: `my-second-channel`, `data`: 1L}
>>> p.get_message()
{`pattern`: None, `type`: `subscribe`, `channel`: `my-first-channel`, `data`: 2L}
>>> p.get_message()
{`pattern`: None, `type`: `psubscribe`, `channel`: `my-*`, `data`: 3L}

通過PubSub獲取訊息時返回的是一個字典,字典key有如下幾個:
type:其中一個, `subscribe`, `unsubscribe`, `psubscribe`, `punsubscribe`, `message`, `pmessage`
channel: The channel [un]subscribed to or the channel a message was published to
pattern: The pattern that matched a published message`s channel. Will be None in all cases except for `pmessage` types.
data: The message data. With [un]subscribe messages, this value will be the number of channels and patterns the connection is currently subscribed to. With [p]message messages, this value will be the actual published message.
現在來發布訊息:

# the publish method returns the number matching channel and pattern
# subscriptions. `my-first-channel` matches both the `my-first-channel`
# subscription and the `my-*` pattern subscription, so this message will
# be delivered to 2 channels/patterns
>>> r.publish(`my-first-channel`, `some data`)
2
>>> p.get_message()
{`channel`: `my-first-channel`, `data`: `some data`, `pattern`: None, `type`: `message`}
>>> p.get_message()
{`channel`: `my-first-channel`, `data`: `some data`, `pattern`: `my-*`, `type`: `pmessage`}

取消訂閱:如果沒有傳遞任何引數,那麼這個PubSub訂閱的所有的channels or patterns都會被取消。

>>> p.unsubscribe()
>>> p.punsubscribe(`my-*`)
>>> p.get_message()
{`channel`: `my-second-channel`, `data`: 2L, `pattern`: None, `type`: `unsubscribe`}
>>> p.get_message()
{`channel`: `my-first-channel`, `data`: 1L, `pattern`: None, `type`: `unsubscribe`}
>>> p.get_message()
{`channel`: `my-*`, `data`: 0L, `pattern`: None, `type`: `punsubscribe`}

回撥的方式處理髮布的訊息

redis-py還允許你通過回撥的方式處理髮布的訊息。
Message handlers接受一個引數,the message,是一個字典物件。just like the examples above.
以回撥形式訂閱:subscribe接受關鍵字引數,鍵為channels or patterns,值為回撥函式。

>>> def my_handler(message):
...     print `MY HANDLER: `, message[`data`]
>>> p.subscribe(**{`my-channel`: my_handler})

在你註冊了回撥處理的情況下, get_message()會返回None

預設情況下除了釋出訊息之外,還會傳遞 subscribe/unsubscribe成功的確認訊息,如果你不想接收它們:ignore_subscribe_messages=True

>>> p = r.pubsub(ignore_subscribe_messages=True)
>>> p.subscribe(`my-channel`)
>>> p.get_message()  # hides the subscribe message and returns None
>>> r.publish(`my-channel`)
1
>>> p.get_message()
{`channel`: `my-channel`, `data`: `my data`, `pattern`: None, `type`: `message`}

三種讀取訊息的方式

第一種:無限迴圈通過PubSub物件的get_message()讀取訊息

>>> while True:
>>>     message = p.get_message()
>>>     if message:
>>>         # do something with the message
>>>     time.sleep(0.001)  # be nice to the system :)

第二種,通過阻塞方法listen()來讀取:p.listen()返回一個generator,阻塞直到有訊息可獲取。

>>> for message in p.listen():
...     # do something with the message

第三種,開啟一個事件迴圈執行緒pubsub.run_in_thread()方法 creates a new thread and starts the event loop. 並返回執行緒物件。
但是需要注意的是:如果你沒有註冊訊息處理函式,那麼呼叫run_in_thread()將會丟擲異常redis.exceptions.PubSubError

>>> p.subscribe(**{`my-channel`: my_handler})
>>> thread = p.run_in_thread(sleep_time=0.001)
# the event loop is now running in the background processing messages
# when it`s time to shut it down...
>>> thread.stop()

關於字元編碼:

預設情況下,publish的訊息會被編碼,當你獲取訊息時得到的是編碼後的位元組,如果你需要它自動解碼,建立Redis client例項時需要指定decode_responses=True,(譯者注:不建議使用該選項,因為當存在pickle序列化的值時,client.get(key)時會出現解碼失敗的錯誤UnicodeDecodeError)

關閉釋放資源:

PubSub.close() method to shutdown the connection.

>>> p = r.pubsub()
>>> ...
>>> p.close()

LUA Scripting支援:

略。

Sentinel support與節點發現:

Redis Sentinel用於發現Redis節點。請確保至少一個Sentinel daemon 程式在執行。
你可以使用Sentinel connection to discover the master and slaves network addresses:

>>> from redis.sentinel import Sentinel
>>> sentinel = Sentinel([(`localhost`, 26379)], socket_timeout=0.1)
>>> sentinel.discover_master(`mymaster`)
(`127.0.0.1`, 6379)
>>> sentinel.discover_slaves(`mymaster`)
[(`127.0.0.1`, 6380)]

>>> master = sentinel.master_for(`mymaster`, socket_timeout=0.1)
>>> slave = sentinel.slave_for(`mymaster`, socket_timeout=0.1)
>>> master.set(`foo`, `bar`)
>>> slave.get(`foo`)
`bar`

上面的master and slave物件就是一個普通的StrictRedis物件例項。如果Sentinel配置了連線池的話,它們還會使用這個連線池。
可能丟擲的異常:MasterNotFoundError ,SlaveNotFoundError 它們都是ConnectionError的子類

Scan Iterators

Redis 2.8之後有了*SCAN命令。redis-py also exposes the following methods that return Python iterators for convenience: scan_iter, hscan_iter, sscan_iter and zscan_iter.

>>> for key, value in ((`A`, `1`), (`B`, `2`), (`C`, `3`)):
...     r.set(key, value)
>>> for key in r.scan_iter():
...     print key, r.get(key)
A 1
B 2
C 3

相關文章