65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 C# 和 Redis Pub/Sub 创建一个非常简单的控制台聊天应用程序

starIconstarIconstarIconstarIconstarIcon

5.00/5 (5投票s)

2017年12月21日

CPOL

5分钟阅读

viewsIcon

36083

使用 C# (.NET Core) 和 Redis 的 pub/sub 功能创建一个非常简单的控制台聊天应用程序,代码仅 55 行。

引言

本文适合 Redis Pub/Sub 的初学者。实际上,Redis 本身在其 pub/sub 文档中有一个用 Ruby 编写的不错的聊天应用程序示例 在这里(不妨看一下),但在本文中,我们将学习 Redis pub/sub,并使用 C# (.NET Core) 和 Redis 创建一个非常简单的聊天应用程序(包含换行符和注释共 55 行代码),之后我们还将讨论从中需要注意的事项。

背景

Redis 是一个开源的、内存数据结构存储,用作数据库、缓存和消息代理。它是 NoSQL 数据库之一。基本上,它是一个键值存储,并带有一些其他强大功能(其中一个就是 pub/sub)。

要求

为了跟上本文的进度并亲自动手编写代码,我们需要准备好

  • Redis。我们可以从 Microsoft AzureAmazon Web Services 获取现成的 Redis 服务器,或者,如果你不想注册任何云服务,也可以在自己的计算机上安装 Redis 服务器。这还假设你已经知道如何使用 -至少是基本操作 - redis-cli

  • .NET Core。众所周知,.NET Core 可以在 Windows、Linux 和 macOS / OS X 上运行。

  • 源代码编辑器或 IDE。当然,我们需要这个,但对于 Windows(或 Mac),推荐使用 Visual Studio。如果我们已经安装了 .NET Core,我们也可以选择 Visual Studio 以外的其他源代码编辑器。

Redis Pub/Sub 基础

引用自维基百科,发布/订阅是一种消息传递模式,消息的发送者(称为发布者)不会将消息直接编程发送给特定的接收者(称为订阅者),而是将发布的消息分类到不同的类别中,而不知道有哪些订阅者(如果有的话)。同样,订阅者表达对一个或多个类别的兴趣,并且只接收感兴趣的消息,而不知道有哪些发布者(如果有的话)。

正如我们之前提到的,Redis 不仅仅是一个键值服务器,它也是一个消息服务器。通过 Redis,我们可以实现发布/订阅。Redis 为其 pub/sub(或发布/订阅)功能提供了很好的文档 在这里。但简单来说,Redis 的发布/订阅功能使我们能够在进程之间实现快速的消息传递和通信。

需要注意的是,Redis pub/sub 与键空间无关。它的设计初衷是不在任何级别上干扰它,包括数据库编号。

Redis 提供了简单的命令来实现发布/订阅。在 Redis CLI 中,订阅频道的命令非常简单。

SUBSCRIBE channel_one channel_two

在上面的示例中,客户端订阅了名为 channel_onechannel_two 的频道。一个客户端可以订阅多个频道。

如果我们打开另一个客户端(redis-cli 或其他 Redis 交互工具),而不关闭用于订阅的客户端,然后尝试使用 PUBLISH 命令,如下所示。

PUBLISH channel_one "Hey Guys!"

消息将能被订阅者收到。我们可以从下面的示例(gif)中看到。

Redis Pub/Sub Demo (gif)

好了,关于基础知识就到这里。实际上,Redis 还有其他与 pub/sub 相关的命令,它们是用于取消订阅频道/频道的 UNSUBSCRIBE,用于频道名称的模式匹配订阅(使用 glob 风格的模式)的 PSUBSCRIBE,以及 PUNSUBSCRIBE。现在,让我们开始创建我们非常简单的聊天应用程序。

创建简单的聊天应用程序

在本节中,我们将使用 C# 创建一个非常简单的聊天应用程序,代码量仅为 55 行。本文假设您正在使用 Visual Studio。

让我们开始吧。首先,创建一个 .NET Core 控制台应用程序,可以将其命名为“SimpleChatApp”。之后,我们需要获取/安装这个 nuget 包:StackExchange.Redis,用于与 Redis 通信,这是 .NET 语言(C# 等)的 Redis 客户端库,我建议您查看其文档。

我们将一步一步来。第一步,我们需要建立与 Redis 的连接。

      using StackExchange.Redis;
      using System;
      
      namespace SimpleChatApp
      {
            class Program
            {
                  private const string RedisConnectionString = "localhost";
                  private static ConnectionMultiplexer connection = 
                    ConnectionMultiplexer.Connect(RedisConnectionString);
      
                  private const string ChatChannel = 
                          "Chat-Simple-Channel"; // Can be anything we want.
                  private static string userName = string.Empty;
      
                  static void Main()
                  {
                        Console.WriteLine("Hello World!");
                  }
            }
      }

需要知道的是,ConnectionMultiplexer 实现了 IDisposable,通常情况下,我们需要在应用程序的整个生命周期内重用该对象。在上面的代码中,我们没有使用 using 块,因为在关闭控制台应用程序后,它会被自动释放。

对于连接字符串配置,我们可以使用 Microsoft Azure Cache 或其他 Redis 云服务,而不是使用 localhost。有关配置文档,请在此处 查看

继续。在 Main() 方法中,编写以下代码。

      static void Main()
      {
            // Enter name and put it into variable userName
            Console.Write("Enter your name: ");
            userName = Console.ReadLine();

            // Create pub/sub
            var pubsub = connection.GetSubscriber();

            // Subscriber subscribes to a channel
            pubsub.Subscribe(ChatChannel, (channel, message) => MessageAction(message));

            // Notify subscriber(s) if you're joining
            pubsub.Publish(ChatChannel, $"'{userName}' joined the chat room.");

            // Messaging here
            while (true)
            {
                  pubsub.Publish(ChatChannel, $"{userName}: {Console.ReadLine()}  " +
                    $"({DateTime.Now.Hour}:{DateTime.Now.Minute})");
            }
      }

      private static void MessageAction(RedisValue message)
      {
            // We'll implement it later, to show the message.
            throw new NotImplementedException();
      }

从上面的代码可以看出,为了订阅,我们需要将频道名称作为第一个参数传递,然后将处理程序/回调作为第二个参数。对于第二个参数,我们传递一个带有两个参数(频道和消息)的 Action。对于这个 Action,我们创建了 MessageAction。在这里我们不需要频道对象,因为我们只关心消息。对于发布消息,就像之前的 PUBLISH 命令一样,我们需要传递频道名称,然后是消息本身。

我们使用 MessageAction 来打印消息,让我们用下面的代码来编写我们的方法。

      static void MessageAction(string message)
      {
            int initialCursorTop = Console.CursorTop;
            int initialCursorLeft = Console.CursorLeft;

            Console.MoveBufferArea(0, initialCursorTop, Console.WindowWidth, 
                                   1, 0, initialCursorTop + 1);
            Console.CursorTop = initialCursorTop;
            Console.CursorLeft = 0;

            // Print the message here
            Console.WriteLine(message);

            Console.CursorTop = initialCursorTop + 1;
            Console.CursorLeft = initialCursorLeft;
      }

让我们把它们放在一起,代码看起来会是这样。

      using StackExchange.Redis;
      using System;
      
      namespace SimpleChatApp
      {
            class Program
            {
                  private const string RedisConnectionString = "localhost";
                  private static ConnectionMultiplexer connection = 
                    ConnectionMultiplexer.Connect(RedisConnectionString);
      
                  private const string ChatChannel = "Chat-Simple-Channel";
                  private static string userName = string.Empty;
      
                  static void Main()
                  {
                        // Enter name and put it into variable userName
                        Console.Write("Enter your name: ");
                        userName = Console.ReadLine();
            
                        // Create pub/sub
                        var pubsub = connection.GetSubscriber();
            
                        // Subscriber subscribes to a channel
                        pubsub.Subscribe(ChatChannel, 
                              (channel, message) => MessageAction(message));
            
                        // Notify subscriber(s) if you're joining
                        pubsub.Publish(ChatChannel, $"'{userName}' joined the chat room.");
            
                        // Messaging here
                        while (true)
                        {
                            pubsub.Publish(ChatChannel, $"{userName}: {Console.ReadLine()}  " +
                                $"({DateTime.Now.Hour}:{DateTime.Now.Minute})");
                        }
                  }
      
                  static void MessageAction(string message)
                  {
                        int initialCursorTop = Console.CursorTop;
                        int initialCursorLeft = Console.CursorLeft;
            
                        Console.MoveBufferArea(0, initialCursorTop, Console.WindowWidth, 
                            1, 0, initialCursorTop + 1);
                        Console.CursorTop = initialCursorTop;
                        Console.CursorLeft = 0;
            
                        // Print the message here
                        Console.WriteLine(message);
            
                        Console.CursorTop = initialCursorTop + 1;
                        Console.CursorLeft = initialCursorLeft;
                  }
            }
      }

就是这样!构建项目。

请注意,如果我们使用 .NET Core,可以在输出 DLL 目录(bin/Debugbin/Release)中使用此命令通过命令提示符或终端(如果您使用的是 Linux)来运行应用程序。

      dotnet SimpleChatApp.dll

让我们看看它是如何工作的。

Simple Chat App with Redis Pub-Sub Demo (gif)

我们已经创建了一个聊天应用程序,纯粹是为了好玩!只要它连接到 Redis(或云服务中的 Redis),我们就可以聊天。

需要考虑的事项

我们需要知道,默认情况下,使用此应用程序时,Redis 连接是按应用程序打开/创建的,这并不理想。如果我们使用 Microsoft Azure 服务或其他 Redis 云服务,那里对客户端连接数是有限制的。

还值得一提的是,StackExchange.Redis 客户端库默认打开两个到 Redis 的连接,一个用于交互式命令,一个用于 pub/sub。所以,这个应用程序会创建两个到 Redis 的连接(每个应用程序)。如果我们安装了 Redis,可以使用以下命令查看 Redis 连接的统计信息。

    redis-cli --stat 

请记住,这只是一个非常简单的聊天应用程序,但我们仍然可以很有趣地使用它,同时了解其局限性。如果我们使用客户端-服务器应用程序并将其托管在某个地方,会更好。

玩得开心! :)

历史

  • 2017年12月21日:初始版本
© . All rights reserved.