<em id="3iliw"></em>
  • <progress id="3iliw"></progress>
  • <tbody id="3iliw"><pre id="3iliw"></pre></tbody><dd id="3iliw"><noscript id="3iliw"></noscript></dd>

    <progress id="3iliw"></progress>

    杭州.net培訓
    達內杭州.net培訓中心

    13175137725

    如何在.NETCore實現RedisClient

    • 時間:2018-05-17 14:19
    • 發布:杭州.NET培訓
    • 來源:企業筆試題

    要想自行實現redisClient,則必須先要了解Redis的socket能信協議。新版統一請求協議在Redis 1.2版本中引入,并最終在Redis 2.0版本成為Redis服務器通信的標準方式。在這個協議中,所有發送至Redis服務器的參數都是二進制安全(binary safe)的。

    以下是這個協議的一般形式

    *<參數數量> CR LF

    $<參數1的字節數量> CR LF

    <參數1的數據> CR LF

    ...

    $<參數N的字節數量> CR LF

    <參數N的數據> CR LF

    注:命令本身也作為協議的其中一個參數來發送。舉個例子,以下是一個命令協議的打印版本:

    *3

    $3

    SET

    $5

    mykey

    $7

    myvalue

    這個命令的實際協議值如下:

    "*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"

    稍后看到,這種格式除了用作命令請求協議之外,也用在命令的回復協議中:這種只有一個參數的回復格式被稱為批量回復(Bulk Reply)。

    統一協議請求原本是用在回復協議中,用于將列表的多個項返回給客戶端的,這種回復格式被稱為多條批量回復(Multi Bulk Reply)。一個多條批量回復以*<argc>\r\n為前綴,后跟多條不同的批量回復,其中argc為這些批量回復的數量。

    Redis命令會返回多種不同類型的回復。一個狀態回復(或者單行回復,single line reply)是一段以"+"開始、"\r\n"結尾的單行字符串。通過檢查服務器發回數據的第一個字節,可以確定這個回復是什么類型:

    ·        狀態回復(status reply)的第一個字節是"+"

    ·        錯誤回復(error reply)的第一個字節是"-"

    ·        整數回復(integer reply)的第一個字節是":"

    ·        批量回復(bulk reply)的第一個字節是"$"

    ·        多條批量回復(multi bulk reply)的第一個字節是"*"

    二 .NET Core Socket

    說起socket,就不得不說IOCP了,這個方案本身就是為了解決多連接、高并發而設計的;但是話又說回來,任何方案都有局限性,不可能解決所有問題;這里不去討論用在這里是否合適,反正本人就是想這么試一把:用一個簡單的ioc模式實現SAEA.Socket,并為此設定各種場景,反過來優化SAEA.Socket本身。下面是一段服務器接收連接的代碼:

    private void ProcessAccept(SocketAsyncEventArgs args)

    {

    if (args == null)

    {

    args = new SocketAsyncEventArgs();

    args.Completed += ProcessAccepted;

    }

    else

    {

    args.AcceptSocket = null;

    }

    if (!_listener.AcceptAsync(args))

    {

    ProcessAccepted(_listener, args);

    }

    }

    項目結構

    在網上找到redis的命令文檔后,本人覺的準備工作差不多了,可以初步定一下項目結構:

    ·        Core:定義的是Redisclient相關最基本的業務

    ·        Interface:定義的是一些需要抽象出來的接口

    ·        Model:定義的是redis的數據模型及其請求、回復的類型枚舉

    ·        Net:這里就是將繼承實現SAEA.Socket而來的RedisConnection通信基礎

    三 命令解碼器

    通過前面的準備工作了解到redisClient的關鍵在于命令的編解碼,至于高大上算法或redis官方算法的實現,本人沒有去詳細了解,一沖動就自行實現了自定義版的解碼器。

    public string Coder(RequestType commandName, params string[] @params)

    {

    _autoResetEvent.WaitOne();

    _commandName = commandName;

    var sb = new StringBuilder();

    sb.AppendLine("*" + @params.Length);

    foreach (var param in @params)

    {

    sb.AppendLine("$" + param.Length);

    sb.AppendLine(param);

    }

    return sb.ToString();

    }

    public ResponseData Decoder()

    {

    var result = new ResponseData();

    string command = ull;

    string error = null;

    var len = 0;

    switch (_commandName)

    {

    case RequestType.PING:

    command = BlockDequeue();

    if (GetStatus(command, out error))

    {

    result.Type = ResponseType.OK;

    result.Data = "PONG";

    }

    else

    {

    result.Type = ResponseType.Error;

    result.Data = error;

    }

    break;

    case RequestType.AUTH:

    case RequestType.SELECT:

    case RequestType.SLAVEOF:

    case RequestType.SET:

    case RequestType.DEL:

    case RequestType.HSET:

    case RequestType.HDEL:

    case RequestType.LSET:

    command = BlockDequeue();

    if (GetStatus(command, out error))

    {

    result.Type = ResponseType.OK;

    result.Data = "OK";

    }

    else

    {

    result.Type = ResponseType.Error;

    result.Data = error;

    }

    break;

    case RequestType.TYPE:

    command = BlockDequeue();

    if (GetStatusString(command, out string msg))

    {

    result.Type = ResponseType.OK;

    }

    else

    {

    result.Type = ResponseType.Error;

    }

    result.Data = msg;

    break;

    case RequestType.GET:

    case RequestType.GETSET:

    case RequestType.HGET:

    case RequestType.LPOP:

    case RequestType.RPOP:

    case RequestType.SRANDMEMBER:

    case RequestType.SPOP:

    len = GetWordsNum(BlockDequeue(), out error);

    if (len == -1)

    {

    result.Type = ResponseType.Empty;

    result.Data = error;

    }

    else

    {

    result.Type = ResponseType.String;

    result.Data += BlockDequeue();

    }

    break;

    case RequestType.KEYS:

    case RequestType.HKEYS:

    case RequestType.LRANGE:

    case RequestType.SMEMBERS:

    result.Type = ResponseType.Lines;

    var sb = new StringBuilder();

    var rn = GetRowNum(BlockDequeue(), out error);

    if (!string.IsNullOrEmpty(error))

    {

    result.Type = ResponseType.Error;

    result.Data = error;

    break;

    }

    //再嘗試讀取一次,發現有回車行出現

    if (rn == -1) rn = GetRowNum(BlockDequeue(), out error);

    if (!string.IsNullOrEmpty(error))

    {

    result.Type = ResponseType.Error;

    result.Data = error;

    break;

    }

    if (rn > 0)

    {

    for (int i = 0; i < rn; i++)

    {

    len = GetWordsNum(BlockDequeue(), out error);

    sb.AppendLine(BlockDequeue());

    }

    }

    result.Data = sb.ToString();

    break;

    case RequestType.HGETALL:

    case RequestType.ZRANGE:

    case RequestType.ZREVRANGE:

    result.Type = ResponseType.KeyValues;

    sb = new StringBuilder();

    rn = GetRowNum(BlockDequeue(), out error);

    if (!string.IsNullOrEmpty(error))

    {

    result.Type = ResponseType.Error;

    result.Data = error;

    break;

    }

    if (rn > 0)

    {

    for (int i = 0; i < rn; i++)

    {

    len = GetWordsNum(BlockDequeue(), out error);

    sb.AppendLine(BlockDequeue());

    }

    }

    result.Data = sb.ToString();

    break;

    case RequestType.DBSIZE:

    case RequestType.EXISTS:

    case RequestType.EXPIRE:

    case RequestType.PERSIST:

    case RequestType.SETNX:

    case RequestType.HEXISTS:

    case RequestType.HLEN:

    case RequestType.LLEN:

    case RequestType.LPUSH:

    case RequestType.RPUSH:

    case RequestType.LREM:

    case RequestType.SADD:

    case RequestType.SCARD:

    case RequestType.SISMEMBER:

    case RequestType.SREM:

    case RequestType.ZADD:

    case RequestType.ZCARD:

    case RequestType.ZCOUNT:

    case RequestType.ZREM:

    case RequestType.PUBLISH:

    var val = GetValue(BlockDequeue(), out error);

    if (!string.IsNullOrEmpty(error))

    {

    result.Type = ResponseType.Error;

    result.Data = error;

    break;

    }

    if (val == 0)

    {

    result.Type = ResponseType.Empty;

    }

    else

    {

    result.Type = ResponseType.OK;

    }

    result.Data = val.ToString();

    break;

    case RequestType.INFO:

    var rnum = GetWordsNum(BlockDequeue(), out error);

    if (!string.IsNullOrEmpty(error))

    {

    result.Type = ResponseType.Error;

    result.Data = error;

    break;

    }

    var info = "";

    while (info.Length < rnum)

    {

    info += BlockDequeue();

    }

    result.Type = ResponseType.String;

    result.Data = info;

    break;

    case RequestType.SUBSCRIBE:

    var r = "";

    while (IsSubed)

    {

    r = BlockDequeue();

    if (r == "message\r\n")

    {

    result.Type = ResponseType.Sub;

    BlockDequeue();

    result.Data = BlockDequeue();

    BlockDequeue();

    result.Data += BlockDequeue();

    break;

    }

    }

    break;

    case RequestType.UNSUBSCRIBE:

    var rNum = GetRowNum(BlockDequeue(), out error);

    var wNum = GetWordsNum(BlockDequeue(), out error);

    BlockDequeue();

    wNum = GetWordsNum(BlockDequeue(), out error);

    var channel = BlockDequeue();

    var vNum = GetValue(BlockDequeue(), out error);

    IsSubed = false;

    break;

    }

    _autoResetEvent.Set();

    return result;

    }

    命令的封裝與測試

    有了socket、redisCoder之后,現在就可以按照官方的redis命令來進行.net core的封裝了。

    本人將這些操作封裝到RedisClient、RedisDataBase兩個類中,然后又想到連接復用的問題,簡單實現了一個連接池RedisClientFactory的類。這樣一來就可以好好的來實驗一把,看看之前的設想最終能不能實現了:

    /****************************************************************************

    *Copyright (c) 2018 Microsoft All Rights Reserved.

    *CLR版本:4.0.30319.42000

    *機器名稱:WENLI-PC

    *公司名稱:Microsoft

    *命名空間:SAEA.RedisSocketTest

    *文件名:Program

    *版本號:V1.0.0.0

    *唯一標識:3d4f939c-3fb9-40e9-a0e0-c7ec773539ae

    *當前的用戶域:WENLI-PC

    *創建人:yswenli

    *電子郵箱:wenguoli_520@qq.com

    *創建時間:2018/3/17 10:37:15

    *描述:

    *

    *=====================================================================

    *修改標記

    *修改時間:2018/3/19 10:37:15

    *修改人:yswenli

    *版本號:V1.0.0.0

    *描述:

    *

    *****************************************************************************/

    using SAEA.Commom;

    using SAEA.RedisSocket;

    using System;

    namespace SAEA.RedisSocketTest

    {

    class Program

    {

    static void Main(string[] args)

    {

    ConsoleHelper.Title = "SAEA.RedisSocketTest";

    ConsoleHelper.WriteLine("輸入ip:port連接RedisServer");

    var ipPort = ConsoleHelper.ReadLine();

    if (string.IsNullOrEmpty(ipPort))

    {

    ipPort = "127.0.0.1:6379";

    }

    RedisClient redisClient = new RedisClient(ipPort);

    redisClient.Connect(); 

    //redisClient.Connect("wenli"); 

    var info = redisClient.Info();

    if (info.Contains("NOAUTH Authentication required."))

    {

    while (true)

    {

    ConsoleHelper.WriteLine("請輸入redis連接密碼");

    var auth = ConsoleHelper.ReadLine();

    if (string.IsNullOrEmpty(auth))

    {

    auth = "yswenli";

    }

    var a = redisClient.Auth(auth);

    if (a.Contains("OK"))

    {

    break;

    }

    else

    {

    ConsoleHelper.WriteLine(a);

    }

    }

    }

    //redisConnection.SlaveOf();

    //redisConnection.Ping();

    redisClient.Select(1);

    //ConsoleHelper.WriteLine(redisConnection.Type("key0"));

    ConsoleHelper.WriteLine("dbSize:{0}", redisClient.DBSize().ToString());

    RedisOperationTest(redisClient, true);

    ConsoleHelper.ReadLine();

    }

    private static void RedisOperationTest(object sender, bool status)

    {

    RedisClient redisClient = (RedisClient)sender;

    if (status)

    {

    ConsoleHelper.WriteLine("連接redis服務器成功!");

    #region key value

    ConsoleHelper.WriteLine("回車開始kv插值操作...");

    ConsoleHelper.ReadLine();

    for (int i = 0; i < 1000; i++)

    {

    redisClient.GetDataBase().Set("key" + i, "val" + i);

    }

    //redisConnection.GetDataBase().Exists("key0");

    ConsoleHelper.WriteLine("kv插入完成...");

    ConsoleHelper.WriteLine("回車開始獲取kv值操作...");

    ConsoleHelper.ReadLine();

    var keys = redisClient.GetDataBase().Keys().Data.ToArray(false, "\r\n");

    foreach (var key in keys)

    {

    var val = redisClient.GetDataBase().Get(key);

    ConsoleHelper.WriteLine("Get val:" + val);

    }

    ConsoleHelper.WriteLine("獲取kv值完成...");

    ConsoleHelper.WriteLine("回車開始開始kv移除操作...");

    ConsoleHelper.ReadLine();

    foreach (var key in keys)

    {

    redisClient.GetDataBase().Del(key);

    }

    ConsoleHelper.WriteLine("移除kv值完成...");

    #endregion

    #region hashset

    string hid = "wenli";

    ConsoleHelper.WriteLine("回車開始HashSet插值操作...");

    ConsoleHelper.ReadLine();

    for (int i = 0; i < 1000; i++)

    {

    redisClient.GetDataBase().HSet(hid, "key" + i, "val" + i);

    }

    ConsoleHelper.WriteLine("HashSet插值完成...");

    ConsoleHelper.WriteLine("回車開始HashSet插值操作...");

    ConsoleHelper.ReadLine();

    var hkeys = redisClient.GetDataBase().GetHKeys(hid).Data.ToArray();

    foreach (var hkey in hkeys)

    {

    var val = redisClient.GetDataBase().HGet(hid, hkey);

    ConsoleHelper.WriteLine("HGet val:" + val.Data);

    }

    var hall = redisClient.GetDataBase().HGetAll("wenli");

    ConsoleHelper.WriteLine("HashSet查詢完成...");

    ConsoleHelper.WriteLine("回車開始HashSet移除操作...");

    ConsoleHelper.ReadLine();

    foreach (var hkey in hkeys)

    {

    redisClient.GetDataBase().HDel(hid, hkey);

    }

    ConsoleHelper.WriteLine("HashSet移除完成...");

    #endregion

    //redisConnection.GetDataBase().Suscribe((c, m) =>

    //{

    //    ConsoleHelper.WriteLine("channel:{0} msg:{1}", c, m);

    //    redisConnection.GetDataBase().UNSUBSCRIBE(c);

    //}, "c39654");

    ConsoleHelper.WriteLine("測試完成!");

    }

    else

    {

    ConsoleHelper.WriteLine("連接失敗!");

    }

    }

    }

    }

    預約申請免費試聽課

    怕錢不夠?就業掙錢后再付學費!    怕學不會?從入學起,達內定制課程!     擔心就業?達內多家實踐企業供你挑選!

    上一篇:隨時隨地編寫 .NET應用程序
    下一篇:Core中的并發編程
    • 掃碼領取資料

      回復關鍵字:視頻資料

      免費領取 達內課程視頻學習資料

    • 視頻學習QQ群

      添加QQ群:1143617948

      免費領取達內課程視頻學習資料

    Copyright ? 2018 Tedu.cn All Rights Reserved 京ICP備08000853號-56 京公網安備 11010802029508號 達內時代科技集團有限公司 版權所有

    選擇城市和中心
    江西省

    貴州省

    廣西省

    海南省

    国拍自产亚洲 2019国拍自产在线,国拍自产亚洲,国产a在线不卡 百度 好搜 搜狗
    <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>