windows下Redis初探4

基本用法

  • StackExchange.Redis 的核心对象是 ConnectionMultiplexer , 在 StackExchange.Redis 命名空间下面; 这个对象隐藏(封装)了多个服务器细节。

  • 你不用每个操作都创建ConnectionMultiplexer,因为ConnectionMultiplexer被设计成可以在多个调用着之间共享和复用.

  • ConnectionMultiplexer是线程安全的

  • 使用 ConnectionMultiplexer.Connect or ConnectionMultiplexer.ConnectAsync
    传递配置字符串或ConfigurationOptions对象来创建ConnectionMultiplexer对象

  • 配置字符串采用以逗号分隔

下面是创建一个ConnectionMultiplexer并连接到本地redis服务器默认端口(6379)的例子

1
2
3
4
using StackExchange.Redis;
...
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost");
// ^^^ store and re-use this!!!

注意ConnectionMultiplexer实现了IDisposable,会在你不再使用这个对象的时候自动销毁。这是故意设计不显示用using销毁,因为你可以很方便的使用并重用它。

更复杂的情况可能涉及主/从设置; 对于此用法,只需指定组成该逻辑redis层的所有所需节点(它将自动标识主节点):

1
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("server1:6379,server2:6379");

如果发现两个节点都是主节点,a tie-breaker key can optionally be specified that can be used to resolve the issue,但幸运的是这样的条件非常罕见。

一旦你有一个ConnectionMultiplexer,你可能想要做的3件主要事情:

  • 使用 redis database(请注意,在群集的情况下,单个逻辑数据库可能会分布在多个节点上)
  • 使用redis 的put/sub功能
  • 访问单个服务器进行维护/监控

使用 redis database

使用 redis database的一个简单例子:

1
IDatabase db = redis.GetDatabase();

The object returned from GetDatabase is a cheap pass-thru object, and does not need to be stored. Note that redis supports multiple databases (although this is not supported on “cluster”); this can be optionally specified in the call to GetDatabase. Additionally, if you plan to make use of the asynchronous API and you require the Task.AsyncState to have a value, this can also be specified:

GetDatabase的返回对象是

1
2
3
int databaseNumber = ...
object asyncState = ...
IDatabase db = redis.GetDatabase(databaseNumber, asyncState);

Once you have the IDatabase, it is simply a case of using the redis API. Note that all methods have both synchronous and asynchronous implementations. In line with Microsoft’s naming guidance, the asynchronous methods all end ...Async(...), and are fully await-able etc.

The simplest operation would be to store and retrieve a value:

1
2
3
4
5
string value = "abcdefg";
db.StringSet("mykey", value);
...
string value = db.StringGet("mykey");
Console.WriteLine(value); // writes: "abcdefg"

Note that the String... prefix here denotes the String redis type, and is largely separate to the .NET String type, although both can store text data. However, redis allows raw binary data for both keys and values - the usage is identical:

1
2
3
4
byte[] key = ..., value = ...;
db.StringSet(key, value);
...
byte[] value = db.StringGet(key);

The entire range of redis database commands covering all redis data types is available for use.

Using redis pub/sub

Another common use of redis is as a pub/sub message distribution tool; this is also simple, and in the event of connection failure, the ConnectionMultiplexer will handle all the details of re-subscribing to the requested channels.

1
ISubscriber sub = redis.GetSubscriber();

Again, the object returned from GetSubscriber is a cheap pass-thru object that does not need to be stored. The pub/sub API has no concept of databases, but as before we can optionally provide an async-state. Note that all subscriptions are global: they are not scoped to the lifetime of the ISubscriber instance. The pub/sub features in redis use named “channels”; channels do not need to be defined in advance on the server (an interesting use here is things like per-user notification channels, which is what drives parts of the realtime updates on Stack Overflow). As is common in .NET, subscriptions take the form of callback delegates which accept the channel-name and the message:

1
2
3
sub.Subscribe("messages", (channel, message) => {
Console.WriteLine((string)message);
});

Separately (and often in a separate process on a separate machine) you can publish to this channel:

1
sub.Publish("messages", "hello");

This will (virtually instantaneously) write "hello" to the console of the subscribed process. As before, both channel-names and messages can be binary.

Please also see Pub / Sub Message Order for guidance on sequential versus concurrent message processing.

Accessing individual servers

For maintenance purposes, it is sometimes necessary to issue server-specific commands:

1
IServer server = redis.GetServer("localhost", 6379);

The GetServer method will accept an EndPoint.aspx) or the name/value pair that uniquely identify the server. As before, the object returned from GetServer is a cheap pass-thru object that does not need to be stored, and async-state can be optionally specified. Note that the set of available endpoints is also available:

1
EndPoint[] endpoints = redis.GetEndPoints();

From the IServer instance, the Server commands are available; for example:

1
2
DateTime lastSave = server.LastSave();
ClientInfo[] clients = server.ClientList();

Sync vs Async vs Fire-and-Forget

There are 3 primary usage mechanisms with StackExchange.Redis:

  • Synchronous - where the operation completes before the methods returns to the caller (note that while this may block the caller, it absolutely does not block other threads: the key idea in StackExchange.Redis is that it aggressively shares the connection between concurrent callers)
  • Asynchronous - where the operation completes some time in the future, and a Task or Task<T> is returned immediately, which can later:
    • be .Wait()ed (blocking the current thread until the response is available)
    • have a continuation callback added (ContinueWith.aspx) in the TPL)
    • be awaited (which is a language-level feature that simplifies the latter, while also continuing immediately if the reply is already known)
  • Fire-and-Forget - where you really aren’t interested in the reply, and are happy to continue irrespective of the response

The synchronous usage is already shown in the examples above. This is the simplest usage, and does not involve the TPL.

For asynchronous usage, the key difference is the Async suffix on methods, and (typically) the use of the await language feature. For example:

1
2
3
4
5
string value = "abcdefg";
await db.StringSetAsync("mykey", value);
...
string value = await db.StringGetAsync("mykey");
Console.WriteLine(value); // writes: "abcdefg"

The fire-and-forget usage is accessed by the optional CommandFlags flags parameter on all methods (defaults to none). In this usage, the method returns the default value immediately (so a method that normally returns a String will always return null, and a method that normally returns an Int64 will always return 0). The operation will continue in the background. A typical use-case of this might be to increment page-view counts:

1
db.StringIncrement(pageKey, flags: CommandFlags.FireAndForget);
坚持技术分享,您的支持将鼓励我更好的创作!