1. 程式人生 > >Redis: Jedis 原始碼剖析1-連結建立和收發命令

Redis: Jedis 原始碼剖析1-連結建立和收發命令

Jedis作為Redis  Java語言推薦的客戶端被廣泛使用。讓我們一探Jedis原始碼究竟。

我們以如下程式碼來DEBUG觀察Jedis原始碼:

		 //建立Redis客戶端
		 Jedis jedis = new Jedis();
		 //呼叫set 命令,返回狀態標記
		 String code=jedis.set("s", "s");
		 System.out.println("code="+code);
		 //呼叫get命令
		 String s =jedis.get("s");
		 System.out.println("s="+s);
第一部分:Jedis物件的建立
Jedis jedis=new Jedis();主要是建立連結Redis伺服器的客戶端。

在Jedis(BinaryJedis)基類中主要有Connection物件。在建立Jedis物件的時候,其實尚未連結到Redis伺服器。

在Connection類中,主要設定了連結Redis所使用socket的引數以及操作socket所使用的工具。

//Jedis客戶端連結,使用原始socket進行連結
public class Connection implements Closeable 
{
  
  private static final byte[][] EMPTY_ARGS = new byte[0][];
  //預設主機
  private String host = Protocol.DEFAULT_HOST;
  //預設埠
  private int port = Protocol.DEFAULT_PORT;
  //原始socket
  private Socket socket;
  //輸入輸出流
  private RedisOutputStream outputStream;
  private RedisInputStream inputStream;
  private int connectionTimeout = Protocol.DEFAULT_TIMEOUT;
  private int soTimeout = Protocol.DEFAULT_TIMEOUT;
  private boolean broken = false;

  public Connection() {
  }
}

由Connection的成員變數中可以看出來。Jedis使用了元素的JDK IO  Socket來處理網路通訊的。

到此,Jedis 物件就建立處理啦。

2-Jedis連結Redis伺服器過程。

在呼叫 String code=jedis.set("s", "s"); 命令的時候,才是真正建立連結的過程。

Client(BinaryClient).set(byte[], byte[])   方法引數就是把由String 字串轉換成位元組數值。

並呼叫Client(Connection).sendCommand(ProtocolCommand, byte[]...) 方法來發送Redis命令。

  //每次傳送命令前都判斷是否連結,如果連結埠並且連結不上,則丟擲異常
  protected Connection sendCommand(final ProtocolCommand cmd, final byte[]... args) {
    try {
      connect();//每次傳送Redis命令都會呼叫Connect()方法來連結Redis遠端伺服器
      Protocol.sendCommand(outputStream, cmd, args); //操作socket 的輸出流來發送命令
      return this;
    } catch (JedisConnectionException ex) {
      /*
       * When client send request which formed by invalid protocol, Redis send back error message
       * before close connection. We try to read it to provide reason of failure.
       */
      try {
        String errorMessage = Protocol.readErrorLineIfPossible(inputStream);
        if (errorMessage != null && errorMessage.length() > 0) {
          ex = new JedisConnectionException(errorMessage, ex.getCause());
        }
      } catch (Exception e) {
        /*
         * Catch any IOException or JedisConnectionException occurred from InputStream#read and just
         * ignore. This approach is safe because reading error message is optional and connection
         * will eventually be closed.
         */
      }
      // Any other exceptions related to connection?
      broken = true;
      throw ex;
    }
  }
每次呼叫sendCommand傳送命令時候,都會呼叫Connnect()方法嘗試連結遠端埠。
  //在傳送命令之前連線redis伺服器
  public void connect() {
    if (!isConnected()) {
      try {
    	//建立新socket  
        socket = new Socket();
        //設定socket引數
        socket.setReuseAddress(true);
        socket.setKeepAlive(true); // Will monitor the TCP connection is
        // valid
        socket.setTcpNoDelay(true); // Socket buffer Whetherclosed, to
        // ensure timely delivery of data
        socket.setSoLinger(true, 0); // Control calls close () method,
        // the underlying socket is closed
        // immediately
        // <[email protected]_add
        //設定連結超時時間
        socket.connect(new InetSocketAddress(host, port), connectionTimeout);
        //設定讀取超時時間
        socket.setSoTimeout(soTimeout);
        //獲取socket原始輸入輸出流
        outputStream = new RedisOutputStream(socket.getOutputStream());
        inputStream = new RedisInputStream(socket.getInputStream());
      } catch (IOException ex) {
        broken = true;
        throw new JedisConnectionException(ex);
      }
    }
  }

每次連結到遠端Redis伺服器後,第一個命令就是傳送金鑰命令。
  @Override
  public void connect() {
    if (!isConnected()) {
      super.connect();
      if (password != null) {
        auth(password);
        getStatusCodeReply();
      }
      if (db > 0) {
        select(Long.valueOf(db).intValue());
        getStatusCodeReply();
      }
    }
  }

在每次傳送一個命令後,都會去獲取返回碼。
  /**  對於Value值存在最大範圍上線。
   * Set the string value as value of the key. The string can't be longer than 1073741824 bytes (1
   * GB).
   * <p>
   * Time complexity: O(1)
   * @param key
   * @param value
   * @return Status code reply
   */
  @Override
  public String set(final String key, String value) {
    checkIsInMultiOrPipeline();
    client.set(key, value);
    return client.getStatusCodeReply();
  }

在獲取狀態碼時,每次都去重新整理通道。
  public String getStatusCodeReply() {
    flush();
    final byte[] resp = (byte[]) readProtocolWithCheckingBroken();
    if (null == resp) {
      return null;
    } else {
      return SafeEncoder.encode(resp);
    }
  }

讀取資料流最終通過SocketInputStream 類來讀取。

以上就涉及到Jedis傳送Redis和接受Redis命令的過程。

相關推薦

Redis: Jedis 原始碼剖析1-連結建立收發命令

Jedis作為Redis  Java語言推薦的客戶端被廣泛使用。讓我們一探Jedis原始碼究竟。 我們以如下程式碼來DEBUG觀察Jedis原始碼: //建立Redis客戶端 Jedis jedis = new Jedis(); //呼叫set 命令,返

Redis原始碼剖析註釋(二十)--- 網路連線庫剖析(client的建立/釋放、命令接收/回覆、Redis通訊協議分析等)

Redis 網路連線庫剖析 1. Redis網路連線庫介紹 Redis網路連線庫對應的檔案是networking.c。這個檔案主要負責 客戶端的建立與釋放 命令接收與命令回覆 Redis通訊協議分析 CLIENT 命令的實現 我們接下來就這幾塊內

Caffe框架原始碼剖析(1)—構建網路

今天花了一整天時間進行閱讀和除錯Caffe框架程式碼,單單是以Lenet網路進行測試就可見框架的大致工作原理。賈揚清在Caffe中大量使用了STL、模板、智慧指標,有些地方為了效率也犧牲了一些程式碼可讀性,處處彰顯了大牛風範。為了他人閱讀方便,現將程式碼流程簡單梳理一下。 1.LeNe

SQL語句基礎1建立刪除資料庫,建表,插入資料並加入適當約束

1.建立圖書管理資料庫 create database bookManager on (     name=bookManager_data,     filename='D:\SQL\bookManager_data.mdf',     size=5MB,     maxs

thrift 原始碼剖析1 :TProcessor

TProcessor   這層主要負責應用層也就是需要我們平常自己實現的一層,它裡面封裝了Handler類。一般thrift 生成的程式碼中我們只需要負責寫Handler類的邏輯即可,Handler中的邏輯就是我們自己定義的服務邏輯。 分析 demo Servi

Tensorflow原始碼解析1 -- 核心架構原始碼結構

1 主流深度學習框架對比 當今的軟體開發基本都是分層化和模組化的,應用層開發會基於框架層。比如開發Linux Driver會基於Linux kernel,開發Android app會基於Android Framework。深度學習也不例外,框架層為上層模型開發提

雲風的 BLOG: Lua GC 的原始碼剖析 (1)

/* ** Union of all Lua values */ typedef union { GCObject *gc; void *p; lua_Number n; int b; } Value; /* ** Tagge

[9]【ffmpeg原始碼分析 1】av_register_all()avcodec_register_all()

日期:2016.10.18 作者:isshe github:github.com/isshe 郵箱:[email protected] 前言 接下來打

Core官方DI剖析(1)--ServiceProvider類ServiceCollection類

前段時間看了蔣老師的Core文章,對於DI那一塊感覺挺有意思,然後就看了一下Core官方DI的原始碼,這也算是第一個看得懂大部分原始碼的框架,雖然官方DI相對來說特別簡單, 官方DI相對於其它框架(例如 autofac)使用起來麻煩許多,既沒有一次注入程式集中所有類的功能,也沒有方便的屬性注入,所以感覺起來官

Python原始碼剖析[1] —— 編譯Python

在中間的部分,可以看到Python的核心,直譯器(interpreter)。在直譯器中,箭頭的方向指示了Python執行時的資料流方向。其中Scanner對應詞法分析,將檔案輸入的Python原始碼或從命令列輸入的一行行Python程式碼切分為一個一個的token;Parser對應語法分析部分,在Scanne

mmdetection原始碼剖析(1)--NMS

# mmdetection原始碼剖析(1)--NMS 熟悉目標檢測的應該都清楚**NMS**是什麼演算法,但是如果我們要與C++和cuda結合直接寫成Pytorch的操作你們清楚怎麼寫嗎?最近在看**mmdetection**的原始碼,發現其實原來寫C++和cuda的擴充套件也不難,下面給大家講一下。 C

Docker(三)容器的建立常用命令

建立一個新的容器 輸出Hello World docker run centos echo 'hello world' docker run 命令是新建一個容器,centos是容器的映象

angular的建立引用命令

首先去官網下載nodejs,基本安裝好nodejs就包含了npm工具。   進入cmd,找到你安裝nodejs的路徑: 1.檢視你npm和nodejs的版本,如果正常顯示版本號,則說明安裝成功: npm -v node -v 2.使用npm命令建立angular的c

Redis原始碼剖析註釋(一)---連結串列結構

Redis原始碼剖析—連結串列結構 1. redis中的連結串列 在redis中連結串列的應用非常廣泛,例如列表鍵的底層實現之一就是連結串列。而且,在redis中的連結串列結構被實現成為雙向連結串列,因此,在頭部和尾部進行的操作就會非常快。 通過列表鍵

Redis原始碼剖析註釋(二十五)--- Redis Cluster 的通訊流程深入剖析(載入配置檔案、節點握手、分配槽)

Redis Cluster 通訊流程深入剖析 1. Redis Cluster 介紹和搭建 這篇部落格會介紹Redis Cluster的資料分割槽理論和一個三主三從叢集的搭建。 2. Redis Cluster 和 Redis Sentin

Redis原始碼剖析註釋(二十七)--- Redis 故障轉移流程原理剖析

Redis 故障轉移流程和原理 1. 故障轉移介紹 Redis叢集自身實現了高可用。高可用首先要解決叢集部分失敗的場景:當叢集內少量節點出現故障時通過自動故障轉移保證叢集可以正常對外提供服務。接下來就介紹故障轉移的細節,分析故障檢測和故障轉移。 故障檢測

Redis原始碼剖析註釋(十四)---- Redis 資料庫及相關命令實現(db)

Redis 資料庫及相關命令實現 1. 資料庫管理命令 命令 描述 FLUSHDB 清空當前資料庫的所有key FLUSHALL 清空整個Redis伺服器的所有key DBSIZE 返回當前資料庫的

1建立二叉樹的二叉連結串列。 (2)寫出對用二叉連結串列儲存的二叉樹進行先序、中序後序遍歷的遞迴非遞迴演算法。 (3)寫出對用二叉連結串列儲存的二叉樹進行層次遍歷演算法。 (4)求二叉樹的所有葉子及結點總數。

(1)建立二叉樹的二叉連結串列。 (2)寫出對用二叉連結串列儲存的二叉樹進行先序、中序和後序遍歷的遞迴和非遞迴演算法。 (3)寫出對用二叉連結串列儲存的二叉樹進行層次遍歷演算法。(4)求二叉樹的所有葉子及結點總數。 include<stdio.h> #inclu

libevent原始碼分析(6)--2.1.8--建立釋放libevent控制代碼event_base的相關函式

一、event_base_new 建立預設的event_base ** * Create and return a new event_base to use with the rest of Libevent. * * @return a new event_ba

Redis原始碼剖析註釋(十九)--- Redis 事件處理實現

Redis 事件處理實現 1. Redis事件介紹 Redis伺服器是一個事件驅動程式。下面先來簡單介紹什麼是事件驅動。 所謂事件驅動,就是當你輸入一條命令並且按下回車,然後訊息被組裝成Redis協議的格式傳送給Redis伺服器,這就會產生一個事件,Red