<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 服務器與客戶端 [TOC=2,3] 前面的章節介紹了所有 Redis 的重要功能組件:數據結構、數據類型、事務、Lua 環境、事件處理、數據庫、持久化,等等,但是我們還沒有對 Redis 服務器本身做任何介紹。 不過,服務器本身并沒有多少需要介紹的新東西,因為服務器除了維持服務器狀態之外,最重要的就是將前面介紹過的各個功能模塊組合起來,而這些功能模塊在前面的章節里已經介紹過了,所以本章將焦點放在服務器的初始化過程,以及服務器對命令的處理過程上。 本章首先介紹服務器的初始化操作,觀察一個 Redis 服務器從啟動到可以接受客戶端連接,需要經過什么步驟。 然后介紹客戶端是如何連接到服務器的,而服務器又是如何維持多個客戶端的不同狀態的。 文章最后將介紹命令從發送到處理的整個過程,并列舉了一個 `SET` 命令的執行過程作為例子。 ### 初始化服務器 從啟動 Redis 服務器,到服務器可以接受外來客戶端的網絡連接這段時間,Redis 需要執行一系列初始化操作。 整個初始化過程可以分為以下六個步驟: 1. 初始化服務器全局狀態。 1. 載入配置文件。 1. 創建 daemon 進程。 1. 初始化服務器功能模塊。 1. 載入數據。 1. 開始事件循環。 以下各個小節將介紹 Redis 服務器初始化的各個步驟。 ### 1. 初始化服務器全局狀態 `redis.h/redisServer` 結構記錄了和服務器相關的所有數據,這個結構主要包含以下信息: - 服務器中的所有數據庫。 - 命令表:在執行命令時,根據字符來查找相應命令的實現函數。 - 事件狀態。 - 服務器的網絡連接信息:套接字地址、端口,以及套接字描述符。 - 所有已連接客戶端的信息。 - Lua 腳本的運行環境及相關選項。 - 實現訂閱與發布(pub/sub)功能所需的數據結構。 - 日志(log)和慢查詢日志(slowlog)的選項和相關信息。 - 數據持久化(AOF 和 RDB)的配置和狀態。 - 服務器配置選項:比如要創建多少個數據庫,是否將服務器進程作為 daemon 進程來運行,最大連接多少個客戶端,壓縮結構(zip structure)的實體數量,等等。 - 統計信息:比如鍵有多少次命令、不命中,服務器的運行時間,內存占用,等等。 Note 為了簡潔起見,上面只列出了單機情況下的 Redis 服務器信息,不包含 SENTINEL 、 MONITOR 、 CLUSTER 等功能的信息。 在這一步,程序創建一個 `redisServer` 結構的實例變量 `server` 用作服務器的全局狀態,并將 `server` 的各個屬性初始化為默認值。 當 `server` 變量的初始化完成之后,程序進入服務器初始化的下一步:讀入配置文件。 ### 2. 載入配置文件 在初始化服務器的上一步中,程序為 `server` 變量(也即是服務器狀態)的各個屬性設置了默認值,但這些默認值有時候并不是最合適的: - 用戶可能想使用 AOF 持久化,而不是默認的 RDB 持久化。 - 用戶可能想用其他端口來運行 Redis ,以避免端口沖突。 - 用戶可能不想使用默認的 16 個數據庫,而是分配更多或更少數量的數據庫。 - 用戶可能想對默認的內存限制措施和回收策略做調整。 等等。 為了讓使用者按自己的要求配置服務器,Redis 允許用戶在運行服務器時,提供相應的配置文件(config file)或者顯式的選項(option),Redis 在初始化完 `server` 變量之后,會讀入配置文件和選項,然后根據這些配置來對 `server` 變量的屬性值做相應的修改: 1. 如果單純執行 `redis-server` 命令,那么服務器以默認的配置來運行 Redis 。 1. 另一方面, 如果給 Redis 服務器送入一個配置文件, 那么 Redis 將按配置文件的設置來更新服務器的狀態。 比如說, 通過命令 `redis-server /etc/my-redis.conf` , Redis 會根據 `my-redis.conf` 文件的內容來對服務器狀態做相應的修改。 1. 除此之外, 還可以顯式地給服務器傳入選項, 直接修改服務器配置。 舉個例子, 通過命令 `redis-server --port 10086` , 可以讓 Redis 服務器端口變更為 `10086` 。 1. 當然, 同時使用配置文件和顯式選項也是可以的, 如果文件和選項有沖突的地方, 那么優先使用選項所指定的配置值。 舉個例子, 如果運行命令 `redis-server /etc/my-redis.conf --port 10086` , 并且 `my-redis.conf` 也指定了 `port` 選項, 那么服務器將優先使用 `--port 10086` (實際上是選項指定的值覆蓋了配置文件中的值)。 ### 3. 創建 daemon 進程 Redis 默認以 daemon 進程的方式運行。 當服務器初始化進行到這一步時,程序將創建 daemon 進程來運行 Redis ,并創建相應的 pid 文件。 ### 4. 初始化服務器功能模塊 在這一步,初始化程序完成兩件事: - 為 `server` 變量的數據結構子屬性分配內存。 - 初始化這些數據結構。 為數據結構分配內存,并初始化這些數據結構,等同于對相應的功能進行初始化。 比如說,當為訂閱與發布所需的鏈表分配內存之后,訂閱與發布功能就處于就緒狀態,隨時可以為 Redis 所用了。 在這一步,程序完成的主要動作如下: - 初始化 Redis 進程的信號功能。 - 初始化日志功能。 - 初始化客戶端功能。 - 初始化共享對象。 - 初始化事件功能。 - 初始化數據庫。 - 初始化網絡連接。 - 初始化訂閱與發布功能。 - 初始化各個統計變量。 - 關聯服務器常規操作(cron job)到時間事件,關聯客戶端應答處理器到文件事件。 - 如果 AOF 功能已打開,那么打開或創建 AOF 文件。 - 設置內存限制。 - 初始化 Lua 腳本環境。 - 初始化慢查詢功能。 - 初始化后臺操作線程。 完成這一步之后,服務器打印出 Redis 的 ASCII LOGO 、服務器版本等信息,表示所有功能模塊已經就緒,可以等待被使用了: ~~~ _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 2.9.7 (7a47887b/1) 32 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in stand alone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 | `-._ `._ / _.-' | PID: 6717 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' ~~~ 雖然所有功能已經就緒,但這時服務器的數據庫還是一片空白,程序還需要將服務器上一次執行時記錄的數據載入到當前服務器中,服務器的初始化才算真正完成。 ### 5. 載入數據 在這一步,程序需要將持久化在 RDB 或者 AOF 文件里的數據,載入到服務器進程里面。 如果服務器有啟用 AOF 功能的話,那么使用 AOF 文件來還原數據;否則,程序使用 RDB 文件來還原數據。 當執行完這一步時,服務器打印出一段載入完成信息: ~~~ [6717] 22 Feb 11:59:14.830 * DB loaded from disk: 0.068 seconds ~~~ ### 6. 開始事件循環 到了這一步,服務器的初始化已經完成,程序打開事件循環,開始接受客戶端連接。 以下是服務器在這一步打印的信息: ~~~ [6717] 22 Feb 11:59:14.830 * The server is now ready to accept connections on port 6379 ~~~ 以下是初始化完成之后,服務器狀態和各個模塊之間的關系圖: ![../_images/server.png](https://box.kancloud.cn/2015-09-13_55f4effef32ea.png) ### 客戶端連接到服務器 當 Redis 服務器完成初始化之后,它就準備好可以接受外來客戶端的連接了。 當一個客戶端通過套接字函數 `connect` 到服務器時,服務器執行以下步驟: 1. 服務器通過文件事件無阻塞地 `accept` 客戶端連接,并返回一個套接字描述符 `fd` 。 1. 服務器為 `fd` 創建一個對應的 `redis.h/redisClient` 結構實例,并將該實例加入到服務器的已連接客戶端的鏈表中。 1. 服務器在事件處理器為該 `fd` 關聯讀文件事件。 完成這三步之后,服務器就可以等待客戶端發來命令請求了。 Redis 以多路復用的方式來處理多個客戶端,為了讓多個客戶端之間獨立分開、不互相干擾,服務器為每個已連接客戶端維持一個 `redisClient` 結構,從而單獨保存該客戶端的狀態信息。 `redisClient` 結構主要包含以下信息: - 套接字描述符。 - 客戶端正在使用的數據庫指針和數據庫號碼。 - 客戶端的查詢緩沖(query buffer)和回復緩存(reply buffer)。 - 一個指向命令函數的指針,以及字符串形式的命令、命令參數和命令個數,這些屬性會在命令執行時使用。 - 客戶端狀態:記錄了客戶端是否處于 SLAVE 、 MONITOR 或者事務狀態。 - 實現事務功能(比如 [MULTI](http://redis.readthedocs.org/en/latest/transaction/multi.html#multi "(in Redis 命令參考 v2.8)") 和 [WATCH](http://redis.readthedocs.org/en/latest/transaction/watch.html#watch "(in Redis 命令參考 v2.8)") )所需的數據結構。 - 實現阻塞功能(比如 [BLPOP](http://redis.readthedocs.org/en/latest/list/blpop.html#blpop "(in Redis 命令參考 v2.8)") 和 [BRPOPLPUSH](http://redis.readthedocs.org/en/latest/list/brpoplpush.html#brpoplpush "(in Redis 命令參考 v2.8)") )所需的數據結構。 - 實現訂閱與發布功能(比如 [PUBLISH](http://redis.readthedocs.org/en/latest/pub_sub/publish.html#publish "(in Redis 命令參考 v2.8)") 和 [SUBSCRIBE](http://redis.readthedocs.org/en/latest/pub_sub/subscribe.html#subscribe "(in Redis 命令參考 v2.8)") )所需的數據結構。 - 統計數據和選項:客戶端創建的時間,客戶端和服務器最后交互的時間,緩存的大小,等等。 Note 為了簡潔起見,上面列出的客戶端結構信息不包含復制(replication)的相關屬性。 ### 命令的請求、處理和結果返回 當客戶端連上服務器之后,客戶端就可以向服務器發送命令請求了。 從客戶端發送命令請求,到命令被服務器處理、并將結果返回客戶端,整個過程有以下步驟: 1. 客戶端通過套接字向服務器傳送命令協議數據。 1. 服務器通過讀事件來處理傳入數據,并將數據保存在客戶端對應 `redisClient` 結構的查詢緩存中。 1. 根據客戶端查詢緩存中的內容,程序從命令表中查找相應命令的實現函數。 1. 程序執行命令的實現函數,修改服務器的全局狀態 `server` 變量,并將命令的執行結果保存到客戶端 `redisClient` 結構的回復緩存中,然后為該客戶端的 `fd` 關聯寫事件。 1. 當客戶端 `fd` 的寫事件就緒時,將回復緩存中的命令結果傳回給客戶端。至此,命令執行完畢。 ### 命令請求實例: SET 的執行過程 為了更直觀地理解命令執行的整個過程,我們用一個實際執行 [SET](http://redis.readthedocs.org/en/latest/string/set.html#set "(in Redis 命令參考 v2.8)") 命令的例子來講解命令執行的過程。 假設現在客戶端 C1 是連接到服務器 S 的一個客戶端,當用戶執行命令 `SET YEAR 2013` 時,客戶端調用寫入函數,將協議內容 `*3\r\n$3\r\nSET\r\n$4\r\nYEAR\r\n$4\r\n2013\r\n"` 寫入連接到服務器的套接字中。 當 S 的文件事件處理器執行時,它會察覺到 C1 所對應的讀事件已經就緒,于是它將協議文本讀入,并保存在查詢緩存。 通過對查詢緩存進行分析(parse),服務器在命令表中查找 `SET` 字符串所對應的命令實現函數,最終定位到 `t_string.c/setCommand` 函數,另外,兩個命令參數 `YEAR` 和 `2013` 也會以字符串的形式保存在客戶端結構中。 接著,程序將客戶端、要執行的命令、命令參數等送入命令執行器:執行器調用 `setCommand` 函數,將數據庫中 `YEAR` 鍵的值修改為 `2013` ,然后將命令的執行結果保存在客戶端的回復緩存中,并為客戶端 `fd` 關聯寫事件,用于將結果回寫給客戶端。 因為 `YEAR` 鍵的修改,其他和數據庫命名空間相關程序,比如 AOF 、REPLICATION 還有事務安全性檢查(是否修改了被 `WATCH` 監視的鍵?)也會被觸發,當這些后續程序也執行完畢之后,命令執行器退出,服務器其他程序(比如時間事件處理器)繼續運行。 當 C1 對應的寫事件就緒時,程序就會將保存在客戶端結構回復緩存中的數據回寫給客戶端,當客戶端接收到數據之后,它就將結果打印出來,顯示給用戶看。 以上就是 `SET YEAR 2013` 命令執行的整個過程。 ### 小結 - 服務器經過初始化之后,才能開始接受命令。 - 服務器初始化可以分為六個步驟: 1. 初始化服務器全局狀態。 1. 載入配置文件。 1. 創建 daemon 進程。 1. 初始化服務器功能模塊。 1. 載入數據。 1. 開始事件循環。 - 服務器為每個已連接的客戶端維持一個客戶端結構,這個結構保存了這個客戶端的所有狀態信息。 - 客戶端向服務器發送命令,服務器接受命令然后將命令傳給命令執行器,執行器執行給定命令的實現函數,執行完成之后,將結果保存在緩存,最后回傳給客戶端。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看