- 0, About Redis RESP
- 1, Define data types
- 2, Define an asynchronous message state machine
- 3, Define command sending template
- 4, Definition Redis Client
- 5, Simple implementation RESP analysis
- 6, Realize the command sending client
- 7, How to use
- 8, More clients
- 9, More tests
- 10, Performance testing
- 11, About NCC
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 ~