Teach you to write a simple redis Client Framework -. Net core

A fool is a good worker 2020-11-10 23:11:17
teach write simple redis client


Recently, boss Ye wrote a FreeRedis, It's powerful , Amazing performance , Faster than a bullet , More powerful than an engine , Just a while ago I was learning Redis, So try to follow the trend and write a simple RedisClient.

FreeRedis Project address :https://github.com/2881099/FreeRedis

This article tutorial source code Github Address :https://github.com/whuanle/RedisClientLearn

Because the code is simple , Don't think about too many features , Password login is not supported ; No clustering support ; Concurrency is not supported ;

First of all, install it on your own computer redis,Windows Version download address :https://github.com/MicrosoftArchive/redis/releases

Then download Windows Version of Redis Manager

Windows Version of Redis Desktop Manager 64 position 2019.1( Chinese version ) Download address https://www.7down.com/soft/233274.html

Download the latest version of the official legitimate version https://redisdesktop.com/download

0, About Redis RESP

RESP Full name REdis Serialization Protocol , namely Redis Serialization protocol , For protocol client use socket Connect Redis when , Data transmission rules .

The official agreement says :https://redis.io/topics/protocol

that RESP The agreement is with Redis In communication request - Respond to The way is as follows :

  • The client will command As RESP Large array of strings ( namely C# Use in byte[] Store string command ) Send to Redis The server .
  • The server is implemented according to the command RESP type Reply .

RESP The type in does not refer to Redis Basic data types for , It's about the response format of the data :

stay RESP in , The type of some data depends on the first byte :

  • about Simple string , The first byte of the reply is “ +”
  • about error , The first byte of the reply is “-”
  • about Integers , The first byte of the reply is “:”
  • about Batch string , The first byte of the reply is “ $”
  • about Array , The first byte of the reply is “ *

For these , Maybe beginners don't know much about , Now let's do it in practice .

We turn on Redis Desktop Manager , And then click on the console , Input :

set a 12
set b 12
set c 12
MGET abc

Press enter on each line of the above command .MGET yes Redis The command to retrieve the values of multiple keys at once .

The output is as follows :

 Local :0>SET a 12
"OK"
Local :0>SET b 12
"OK"
Local :0>SET c 12
"OK"
Local :0>MGET a b c
1) "12"
2) "12"
3) "12"

But this management tool and removed RESP Protocol identifier in , Let's write a demo Code , Restore RESP The essence of .

using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp
{
class Program
{
static async Task Main(string[] args)
{
IPAddress IP = IPAddress.Parse("127.0.0.1");
IPEndPoint IPEndPoint = new IPEndPoint(IP, 6379);
Socket client = new Socket(IP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
await client.ConnectAsync(IPEndPoint);
if (!client.Connected)
{
Console.WriteLine(" Connect Redis Server failed !");
Console.Read();
}
Console.WriteLine(" Congratulations , Connect Redis Server success ");
// Receiving messages in the background
new Thread(() =>
{
while (true)
{
byte[] data = new byte[100];
int size = client.Receive(data);
Console.WriteLine();
Console.WriteLine(Encoding.UTF8.GetString(data));
Console.WriteLine();
}
}).Start();
while (true)
{
Console.Write("$> ");
string command = Console.ReadLine();
// The order sent must be in the form of \r\n ending
int size = client.Send(Encoding.UTF8.GetBytes(command + "\r\n"));
Thread.Sleep(100);
}
}
}
}

Input and output results :

$> SET a 123456789
+OK
$> SET b 123456789
+OK
$> SET c 123456789
+OK
$> MGET a b c
*3
$9
123456789
$9
123456789
$9
123456789

so ,Redis The message content of the response , In order to $、*、+ At the beginning of a character , And use \r\n Separate .

We write Redis Client The way to do this is to receive socket Content , And then we can parse out the actual data .

Each time the setting command is sent successfully , Will return to +OK;*3 Indicates that there are three arrays ;$9 Indicates that the received data length is 9;

That's about it , Let's write a simple Redis Client frame , Then go to bed .

Remember to use netstandard2.1, Because there are some byte[] 、string、ReadOnlySpan<T> Transformation , need netstandard2.1 To be more convenient .

1, Define data types

According to the preceding demo, Let's define a type , Store those special symbols :

 /// <summary>
/// RESP Response type
/// </summary>
public static class RedisValueType
{
public const byte Errors = (byte)'-';
public const byte SimpleStrings = (byte)'+';
public const byte Integers = (byte)':';
public const byte BulkStrings = (byte)'$';
public const byte Arrays = (byte)'*';
public const byte R = (byte)'\r';
public const byte N = (byte)'\n';
}

2, Define an asynchronous message state machine

Create a MessageStrace class , It works as an asynchronous state machine in response to a message , And it has the function of parsing data stream .

 /// <summary>
/// Custom message queuing state machine
/// </summary>
public abstract class MessageStrace
{
protected MessageStrace()
{
TaskCompletionSource = new TaskCompletionSource<string>();
Task = TaskCompletionSource.Task;
}
protected readonly TaskCompletionSource<string> TaskCompletionSource;
/// <summary>
/// Mark whether the task is completed , And receive redis Response string data stream
/// </summary>
public Task<string> Task { get; private set; }
/// <summary>
/// Receiving data streams
/// </summary>
/// <param name="stream"></param>
/// <param name="length"> Actual length </param>
public abstract void Receive(MemoryStream stream, int length);
/// <summary>
/// The response has been completed
/// </summary>
/// <param name="data"></param>
protected void SetValue(string data)
{
TaskCompletionSource.SetResult(data);
}
/// <summary>
/// analysis $ or * The number after the symbol , The subscript of the last digit after the character must be passed
/// </summary>
/// <param name="data"></param>
/// <param name="index"> Resolved to the location </param>
/// <returns></returns>
protected int BulkStrings(ReadOnlySpan<byte> data, ref int index)
{
int start = index;
int end = start;
while (true)
{
if (index + 1 >= data.Length)
throw new ArgumentOutOfRangeException(" overflow ");
// \r\n
if (data[index].CompareTo(RedisValueType.R) == 0 && data[index + 1].CompareTo(RedisValueType.N) == 0)
{
index += 2; // Point to \n Next to
break;
}
end++;
index++;
}
// Intercept $2 *3 The number after the symbol
return Convert.ToInt32(Encoding.UTF8.GetString(data.Slice(start, end - start).ToArray()));
}
}

3, Define command sending template

because Redis There are so many orders , For better packaging , We define a message sending template , Five types are specified to be sent with five types respectively Client.

Define a unified template class :

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace CZGL.RedisClient
{
/// <summary>
/// Command send template
/// </summary>
public abstract class CommandClient<T> where T : CommandClient<T>
{
protected RedisClient _client;
protected CommandClient()
{
}
protected CommandClient(RedisClient client)
{
_client = client;
}
/// <summary>
/// Reuse
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
internal virtual CommandClient<T> Init(RedisClient client)
{
_client = client;
return this;
}
/// <summary>
/// Is the request successful
/// </summary>
/// <param name="value"> Response message </param>
/// <returns></returns>
protected bool IsOk(string value)
{
if (value[0].CompareTo('+') != 0 || value[1].CompareTo('O') != 0 || value[2].CompareTo('K') != 0)
return false;
return true;
}
/// <summary>
/// dispatch orders
/// </summary>
/// <param name="command"> Orders sent </param>
/// <param name="strace"> Data type client </param>
/// <returns></returns>
protected Task SendCommand<TStrace>(string command, out TStrace strace) where TStrace : MessageStrace, new()
{
strace = new TStrace();
return _client.SendAsync(strace, command);
}
}
}

4, Definition Redis Client

RedisClient Class is used to send Redis command , Then put the task in the queue ; receive Redis Data content returned , And write the data stream to memory , Call up the queue , Set the return value of the asynchronous task .

Send Processes can be concurrent , But receiving message content uses a single thread . To ensure the sequence of messages , Using queues to record Send - Receive The order of .

C# Of Socket Comparison of egg exercises , Want to do concurrency and high performance Socket It's not that easy .

The following code is annotated in three places , I'll use it later to write other code .

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CZGL.RedisClient
{
/// <summary>
/// Redis client
/// </summary>
public class RedisClient
{
private readonly IPAddress IP;
private readonly IPEndPoint IPEndPoint;
private readonly Socket client;
//private readonly Lazy<StringClient> stringClient;
//private readonly Lazy<HashClient> hashClient;
//private readonly Lazy<ListClient> listClient;
//private readonly Lazy<SetClient> setClient;
//private readonly Lazy<SortedClient> sortedClient;
// Data flow request queue
private readonly ConcurrentQueue<MessageStrace> StringTaskQueue = new ConcurrentQueue<MessageStrace>();
public RedisClient(string ip, int port)
{
IP = IPAddress.Parse(ip);
IPEndPoint = new IPEndPoint(IP, port);
//stringClient = new Lazy<StringClient>(() => new StringClient(this));
//hashClient = new Lazy<HashClient>(() => new HashClient(this));
//listClient = new Lazy<ListClient>(() => new ListClient(this));
//setClient = new Lazy<SetClient>(() => new SetClient(this));
//sortedClient = new Lazy<SortedClient>(() => new SortedClient(this));
client = new Socket(IP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
}
/// <summary>
/// Start connecting Redis
/// </summary>
public async Task<bool> ConnectAsync()
{
await client.ConnectAsync(IPEndPoint);
new Thread(() => { ReceiveQueue(); })
{
IsBackground = true
}.Start();
return client.Connected;
}
/// <summary>
/// Send a command , Add it to the queue
/// </summary>
/// <param name="task"></param>
/// <param name="command"></param>
/// <returns></returns>
internal Task<int> SendAsync(MessageStrace task, string command)
{
var buffer = Encoding.UTF8.GetBytes(command + "\r\n");
var result = client.SendAsync(new ArraySegment<byte>(buffer, 0, buffer.Length), SocketFlags.None);
StringTaskQueue.Enqueue(task);
return result;
}
/*
Microsoft Input different sizes of data into the buffer , Test response time .
1024 - real 0m0,102s; user 0m0,018s; sys 0m0,009s
2048 - real 0m0,112s; user 0m0,017s; sys 0m0,009s
8192 - real 0m0,163s; user 0m0,017s; sys 0m0,007s
256 - real 0m0,101s; user 0m0,019s; sys 0m0,008s
16 - real 0m0,144s; user 0m0,016s; sys 0m0,010s
.NET Socket, The default buffer size is 8192 byte .
Socket.ReceiveBufferSize: An Int32 that contains the size, in bytes, of the receive buffer. The default is 8192.
But a lot of the response was just "+OK\r\n" Such a response , also MemoryStream The default is 256( Of course , You can set your own size ), The buffer is too large , Waste of memory ;
exceed 256 This size ,MemoryStream Will continue to allocate new 256 The size of the memory area , Will consume performance .
BufferSize Set to 256 , It's a better way to do it .
*/
private const int BufferSize = 256;
/// <summary>
/// Single thread serial receive data stream , Call up the task queue to complete the task
/// </summary>
private void ReceiveQueue()
{
while (true)
{
MemoryStream stream = new MemoryStream(BufferSize); // Memory cache
byte[] data = new byte[BufferSize]; // Fragmentation , Every time I receive N Bytes
int size = client.Receive(data); // Waiting to receive a message
int length = size; // Total length of data stream
while (true)
{
stream.Write(data, 0, size); // The data stream received by fragmentation is written to the memory buffer
// Data stream received
if (size < BufferSize) // There is Bug , When the size of the data stream or the byte size of the last slice of the data stream is just as good as BufferSize Big hour , Can't jump out of Receive
{
break;
}
length += client.Receive(data); // It's not finished yet , Continue to receive
}
stream.Seek(0, SeekOrigin.Begin); // Reset cursor position
// Call up the queue
StringTaskQueue.TryDequeue(out var tmpResult);
// Process the tasks in the queue
tmpResult.Receive(stream, length);
}
}
/// <summary>
/// Reuse
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="client"></param>
/// <returns></returns>
public T GetClient<T>(T client) where T : CommandClient<T>
{
client.Init(this);
return client;
}
///// <summary>
///// Get string request client
///// </summary>
///// <returns></returns>
//public StringClient GetStringClient()
//{
// return stringClient.Value;
//}
//public HashClient GetHashClient()
//{
// return hashClient.Value;
//}
//public ListClient GetListClient()
//{
// return listClient.Value;
//}
//public SetClient GetSetClient()
//{
// return setClient.Value;
//}
//public SortedClient GetSortedClient()
//{
// return sortedClient.Value;
//}
}
}

5, Simple implementation RESP analysis

The following use of code to achieve the Redis RESP Message parsing , Time issues , I only realize +、-、$、* The analysis of the four symbols , Other symbols can be improved by reference .

Create a MessageStraceAnalysis`.cs , The code is as follows :

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace CZGL.RedisClient
{
/// <summary>
/// RESP Parse data flow
/// </summary>
public class MessageStraceAnalysis<T> : MessageStrace
{
public MessageStraceAnalysis()
{
}
/// <summary>
/// Parsing protocols
/// </summary>
/// <param name="data"></param>
public override void Receive(MemoryStream stream, int length)
{
byte firstChar = (byte)stream.ReadByte(); // First character , Because the cursor has reached 1, So behind .GetBuffer(), from 1 Start cutting off , The first character is discarded ;
if (firstChar.CompareTo(RedisValueType.SimpleStrings) == 0) // Simple string
{
SetValue(Encoding.UTF8.GetString(stream.GetBuffer()));
return;
}
else if (firstChar.CompareTo(RedisValueType.Errors) == 0)
{
TaskCompletionSource.SetException(new InvalidOperationException(Encoding.UTF8.GetString(stream.GetBuffer())));
return;
}
// No + and - start
stream.Position = 0;
int index = 0;
ReadOnlySpan<byte> data = new ReadOnlySpan<byte>(stream.GetBuffer());
string tmp = Analysis(data, ref index);
SetValue(tmp);
}
// Enter the recursive processing flow
private string Analysis(ReadOnlySpan<byte> data, ref int index)
{
// *
if (data[index].CompareTo(RedisValueType.Arrays) == 0)
{
string value = default;
index++;
int size = BulkStrings(data, ref index);
if (size == 0)
return string.Empty;
else if (size == -1)
return null;
for (int i = 0; i < size; i++)
{
var tmp = Analysis(data, ref index);
value += tmp + ((i < (size - 1)) ? "\r\n" : string.Empty);
}
return value;
}
// $..
else if (data[index].CompareTo(RedisValueType.BulkStrings) == 0)
{
index++;
int size = BulkStrings(data, ref index);
if (size == 0)
return string.Empty;
else if (size == -1)
return null;
var value = Encoding.UTF8.GetString(data.Slice(index, size).ToArray());
index += size + 2; // Before leaving , Move the pointer to \n after
return value;
}
throw new ArgumentException(" Parse error ");
}
}
}

6, Realize the command sending client

because Redis Too many orders , If you encapsulate all commands directly into RedisClient in , Must make API Yes , And the code is hard to maintain . therefore , We can split , according to string、hash、set etc. redis type , To design clients .

Let's design a StringClient:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CZGL.RedisClient
{
/// <summary>
/// String type
/// </summary>
public class StringClient : CommandClient<StringClient>
{
internal StringClient()
{
}
internal StringClient(RedisClient client) : base(client)
{
}
/// <summary>
/// set key value
/// </summary>
/// <param name="key">key</param>
/// <param name="value">value</param>
/// <returns></returns>
public async Task<bool> Set(string key, string value)
{
await SendCommand<MessageStraceAnalysis<string>>($"{StringCommand.SET} {key} {value}", out MessageStraceAnalysis<string> strace);
var result = await strace.Task;
return IsOk(result);
}
/// <summary>
/// Get the value of a key
/// </summary>
/// <param name="key"> key </param>
/// <returns></returns>
public async Task<string> Get(string key)
{
await SendCommand($"{StringCommand.GET} {key}", out MessageStraceAnalysis<string> strace);
var result = await strace.Task;
return result;
}
/// <summary>
/// Intercepts data of specified length from the value of the specified key
/// </summary>
/// <param name="key">key</param>
/// <param name="start"> Start subscript </param>
/// <param name="end"> End subscript </param>
/// <returns></returns>
public async Task<string> GetRance(string key, uint start, int end)
{
await SendCommand($"{StringCommand.GETRANGE} {key} {start} {end}", out MessageStraceAnalysis<string> strace);
var result = await strace.Task;
return result;
}
/// <summary>
/// Set a value and return the old value
/// </summary>
/// <param name="key"></param>
/// <param name="newValue"></param>
/// <returns></returns>
public async Task<string> GetSet(string key, string newValue)
{
await SendCommand($"{StringCommand.GETSET} {key} {newValue}", out MessageStraceAnalysis<string> strace);
var result = await strace.Task;
return result;
}
/// <summary>
/// Get the value of a bit in binary data
/// </summary>
/// <param name="key"></param>
/// <param name="index"></param>
/// <returns>0 or 1</returns>
public async Task<int> GetBit(string key, uint index)
{
await SendCommand($"{StringCommand.GETBIT} {key} {index}", out MessageStraceAnalysis<string> strace);
var result = await strace.Task;
return Convert.ToInt32(result);
}
/// <summary>
/// Set a bit to 1 or 0
/// </summary>
/// <param name="key"></param>
/// <param name="index"></param>
/// <param name="value">0 or 1</param>
/// <returns></returns>
public async Task<bool> SetBit(string key, uint index, uint value)
{
await SendCommand($"{StringCommand.SETBIT} {key} {index} {value}", out MessageStraceAnalysis<string> strace);
var result = await strace.Task;
return IsOk(result);
}
/// <summary>
/// Get the value of multiple keys
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public async Task<string[]> MGet(params string[] key)
{
await SendCommand($"{StringCommand.MGET} {string.Join(" ", key)}", out MessageStraceAnalysis<string> strace);
var result = await strace.Task;
return result.Split("\r\n");
}
private static class StringCommand
{
public const string SET = "SET";
public const string GET = "GET";
public const string GETRANGE = "GETRANGE";
public const string GETSET = "GETSET";
public const string GETBIT = "GETBIT";
public const string SETBIT = "SETBIT";
public const string MGET = "MGET";
// ... ... more String command
}
}
}

StringClient Realized 7 individual Redis String Command of type , Other commands are similar .

We turn on RedisClient.cs, The following part of the release code :

private readonly Lazy<StringClient> stringClient; // 24 That's ok
stringClient = new Lazy<StringClient>(() => new StringClient(this)); // 38 That's ok
// 146 That's ok
/// <summary>
/// Get string request client
/// </summary>
/// <returns></returns>
public StringClient GetStringClient()
{
return stringClient.Value;
}

7, How to use

RedisClient Examples of use :

 static async Task Main(string[] args)
{
RedisClient client = new RedisClient("127.0.0.1", 6379);
var a = await client.ConnectAsync();
if (!a)
{
Console.WriteLine(" Failed to connect to server ");
Console.ReadKey();
return;
}
Console.WriteLine(" Successfully connected to the server ");
var stringClient = client.GetStringClient();
var result = await stringClient.Set("a", "123456789");
Console.Read();
}

Encapsulated message commands support asynchronous .

8, More clients

light String The type is not good , We continue to package more clients .

Hash :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CZGL.RedisClient
{
public class HashClient : CommandClient<HashClient>
{
internal HashClient(RedisClient client) : base(client)
{
}
/// <summary>
/// Set hash
/// </summary>
/// <param name="key"> key </param>
/// <param name="values"> Field - List of values </param>
/// <returns></returns>
public async Task<bool> HmSet(string key, Dictionary<string, string> values)
{
await SendCommand($"{StringCommand.HMSET} {key} {string.Join(" ", values.Select(x => $"{x.Key} {x.Value}").ToArray())})", out MessageStraceAnalysis<string> strace);
var result = await strace.Task;
return IsOk(result);
}
public async Task<bool> HmSet<T>(string key, T values)
{
Dictionary<string, string> dic = new Dictionary<string, string>();
foreach (var item in typeof(T).GetProperties())
{
dic.Add(item.Name, (string)item.GetValue(values));
}
await SendCommand($"{StringCommand.HMSET} {key} {string.Join(" ", dic.Select(x => $"{x.Key} {x.Value}").ToArray())})", out MessageStraceAnalysis<string> strace);
var result = await strace.Task;
return IsOk(result);
}
public async Task<object> HmGet(string key, string field)
{
await SendCommand($"{StringCommand.HMGET} {key} {field}", out MessageStraceAnalysis<string> strace);
var result = await strace.Task;
return IsOk(result);
}
private static class StringCommand
{
public const string HMSET = "HMSET ";
public const string HMGET = "HMGET";
// ... ... more String command
}
}
}

list :

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace CZGL.RedisClient
{
public class ListClient : CommandClient<ListClient>
{
internal ListClient(RedisClient client) : base(client)
{
}
/// <summary>
/// set key value
/// </summary>
/// <param name="key">key</param>
/// <param name="value">value</param>
/// <returns></returns>
public async Task<bool> LPush(string key, string value)
{
await SendCommand($"{StringCommand.LPUSH} {key} {value}", out MessageStraceAnalysis<string> strace);
var result = await strace.Task;
return IsOk(result);
}
public async Task<string> LRange(string key, int start, int end)
{
await SendCommand($"{StringCommand.LRANGE} {key} {start} {end}", out MessageStraceAnalysis<string> strace);
var result = await strace.Task;
return result;
}
private static class StringCommand
{
public const string LPUSH = "LPUSH";
public const string LRANGE = "LRANGE";
// ... ... more String command
}
}
}

aggregate :

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace CZGL.RedisClient
{
public class SetClient : CommandClient<SetClient>
{
internal SetClient() { }
internal SetClient(RedisClient client) : base(client)
{
}
public async Task<bool> SAdd(string key, string value)
{
await SendCommand($"{StringCommand.SADD} {key} {value}", out MessageStraceAnalysis<string> strace);
var result = await strace.Task;
return IsOk(result);
}
public async Task<string> SMembers(string key)
{
await SendCommand($"{StringCommand.SMEMBERS} {key}", out MessageStraceAnalysis<string> strace);
var result = await strace.Task;
return result;
}
private static class StringCommand
{
public const string SADD = "SADD";
public const string SMEMBERS = "SMEMBERS";
// ... ... more String command
}
}
}

Ordered set :

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace CZGL.RedisClient
{
public class SortedClient : CommandClient<SortedClient>
{
internal SortedClient(RedisClient client) : base(client)
{
}
public async Task<bool> ZAdd(string key, string value)
{
await SendCommand($"{StringCommand.ZADD} {key} {value}", out MessageStraceAnalysis<string> strace);
var result = await strace.Task;
return IsOk(result);
}
private static class StringCommand
{
public const string ZADD = "ZADD";
public const string SMEMBERS = "SMEMBERS";
// ... ... more String command
}
}
}

such , We have a simple function RedisClient The framework .

9, More tests

To verify that functionality is available , Let's write some examples :

 static RedisClient client = new RedisClient("127.0.0.1", 6379);
static async Task Main(string[] args)
{
var a = await client.ConnectAsync();
if (!a)
{
Console.WriteLine(" Failed to connect to server ");
Console.ReadKey();
return;
}
Console.WriteLine(" Successfully connected to the server ");
await StringSETGET();
await StringGETRANGE();
await StringGETSET();
await StringMGet();
Console.ReadKey();
}
static async Task StringSETGET()
{
var stringClient = client.GetStringClient();
var b = await stringClient.Set("seta", "6666");
var c = await stringClient.Get("seta");
if (c == "6666")
{
Console.WriteLine("true");
}
}
static async Task StringGETRANGE()
{
var stringClient = client.GetStringClient();
var b = await stringClient.Set("getrance", "123456789");
var c = await stringClient.GetRance("getrance", 0, -1);
if (c == "123456789")
{
Console.WriteLine("true");
}
var d = await stringClient.GetRance("getrance", 0, 3);
if (d == "1234")
{
Console.WriteLine("true");
}
}
static async Task StringGETSET()
{
var stringClient = client.GetStringClient();
var b = await stringClient.Set("getrance", "123456789");
var c = await stringClient.GetSet("getrance", "987654321");
if (c == "123456789")
{
Console.WriteLine("true");
}
}
static async Task StringMGet()
{
var stringClient = client.GetStringClient();
var a = await stringClient.Set("stra", "123456789");
var b = await stringClient.Set("strb", "123456789");
var c = await stringClient.Set("strc", "123456789");
var d = await stringClient.MGet("stra", "strb", "strc");
if (d.Where(x => x == "123456789").Count() == 3)
{
Console.WriteLine("true");
}
}

10, Performance testing

Because it's just easy to write , And it's single threaded , And it's a waste of memory , I think the performance will be worse . But what about the truth ? So let's test that out :

 static RedisClient client = new RedisClient("127.0.0.1", 6379);
static async Task Main(string[] args)
{
var a = await client.ConnectAsync();
if (!a)
{
Console.WriteLine(" Failed to connect to server ");
Console.ReadKey();
return;
}
Console.WriteLine(" Successfully connected to the server ");
var stringClient = client.GetStringClient();
Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < 3000; i++)
{
var guid = Guid.NewGuid().ToString();
_ = await stringClient.Set(guid, guid);
_ = await stringClient.Get(guid);
}
watch.Stop();
Console.WriteLine($" The total time is :{watch.ElapsedMilliseconds/10} ms");
Console.ReadKey();
}

Time consuming :

 The total time is :1003 ms

Is probably 1s,3000 individual SET and 3000 individual GET common 6000 A request . It seems that single thread performance is also very strong .

Before you know it 11 O 'clock , No more , I went to bed .

I have other Redis article :

Build distributed Redis Cluster Cluster and Redis introduction

Redis Introduction and ASP.NET Core cache

11, About NCC

.NET Core Community (.NET Central Community , abbreviation NCC) It's based on and around .NET The unofficial organization and activities of the technology stack 、 Non profit private open source community . We hope that through us NCC Community efforts , Working with open source communities for .NET Ecology infuses more energy .

Join in NCC, There's a bunch of frames inside , Teach you to write frames , Participate in open source projects , Make your contribution . Remember to join NCC yo ~

版权声明
本文为[A fool is a good worker]所创,转载请带上原文链接,感谢

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云