Redis中介軟體與Web中介軟體

ggtc發表於2024-07-07

易混淆概念辨析

在不同的上下文中,“Redis中介軟體”可以有不同的含義,這可能導致一些混淆。讓我們來分解一下:

  1. Web中介軟體與訊息中介軟體的區別:

    • Web中介軟體:在ASP.NET Core(或類似框架)中,中介軟體是指處理HTTP請求管道的元件,例如處理請求、認證、日誌記錄等。這些中介軟體按順序構成一個請求處理管道,每個中介軟體負責對請求進行處理或轉發到下一個中介軟體。在.NET Core中,可以使用 Use 方法將中介軟體新增到請求處理管道中,例如 app.UseMiddleware()

    • 訊息中介軟體:訊息中介軟體是一種用於處理訊息傳遞的軟體服務,典型的例子包括RabbitMQ、Kafka和Redis等。這些服務允許應用程式在分散式環境中非同步傳送和接收訊息,而Redis服務作為分散式系統的一個請求處理環節,應該稱為分散式系統的中介軟體。在這種情況下,Redis可以作為一種訊息中介軟體來使用,用於釋出/訂閱模式或佇列服務。

    • Web中介軟體與分散式系統中介軟體:當我們談論一個web應用後端時,中介軟體指的是Web中介軟體,當我們談論一個由多個服務組成的分散式系統時,中介軟體指的是分散式系統的其中一個服務。

  2. Redis在.NET Core中的角色:

    • Redis作為快取或資料儲存:在.NET Core中,Redis通常被用作快取或資料儲存,透過Redis資料庫提供的快速讀寫速度來最佳化應用程式的效能。這時,Redis被視為一個託管服務,應用程式透過Redis進行資料儲存或快取訪問,而不是透過HTTP請求處理管道。
  3. 混淆的來源:

    • 可能的混淆源於術語的多義性。在某些情況下,人們可能將Redis稱為中介軟體,指的是它作為一個提供服務的中介。然而,在嚴格的技術術語中,Redis通常不被稱為Web中介軟體,而更傾向於被稱為資料儲存或快取服務。

綜上所述,確實存在一些術語上的混淆,特別是當人們在不同的上下文中使用相同的術語來指代不同的概念時。在.NET Core中,Redis通常不被稱為Web中介軟體,而更適合被稱為快取或資料儲存服務。而在訊息傳遞系統中,Redis可以作為訊息中介軟體使用,用於處理分散式系統中的訊息傳遞。

分散式系統中Redis訊息中介軟體使用

分散式系統中的通訊

不同於Web後端,分散式系統由多個獨立的程式(也稱為服務)組成。同一個程式內通訊一般透過函式呼叫進行。不同程式間通訊呢?一般採用訊息傳遞的形式,正如Win32的訊息迴圈一樣。所以像RedisRabbitMQ之類的訊息中介軟體就成了分散式系統的重要組成部分

搭建一個分散式系統

  1. 我們設想這樣一個分散式系統

    • 服務A是一個webapi系統,提供一些api介面,比如簽到介面,獲取個人資訊介面,獲取業務資料介面。平時大家訪問量不大,但是簽到集中在短時間內,並且要將資料儲存到資料庫,由於磁碟IO速度限制,所以那時候服務A可能處理不過來,要將簽到傳送給Redis暫存起來

    • Redis是一個記憶體資料庫,記憶體IO速度很快,與磁碟IO相比快了5個數量級

    • 服務B是一個模板型別為C#控制檯程式的簽到服務,專門用於從Redis取出簽到訊息,並輸出到控制檯

  2. Redis
    Redis安裝和基本使用(windows版)
    redis通訊過程為

    • 發起連線(可保持)
    • 傳送set/get請求
    • 返回結果

    redis新增一個名為MQ1的列表List資料結構存放簽到請求,使用LPUSH+BRPOP,列表表現為訊息佇列。

  3. 服務A
    服務A使用CSRedisCore作為Redis客戶端

    //Program.cs
    
    //發起Redis連線
    var csredis = new CSRedisClient("127.0.0.1:6379");
    //注入到依賴容器
    builder.Services.AddSingleton(csredis);
    

    接到Http請求後,使用LPUSH將新的請求新增到列表的開始

    //SignController .cs
    
    [ApiController]
    [Route("[controller]/[action]")]
    public class SignController : Controller
    {
    	private readonly CSRedisClient redisClient;
    
    	public SignController(CSRedisClient redisClient)
    	{
    		this.redisClient = redisClient;
    	}
    	
    	[HttpGet]
    	public IActionResult Sign(string name)
    	{
    		redisClient.LPush("MQ1", name);
    		return Ok();
    	}
    }
    
  4. 服務B
    服務B在一個迴圈中使用BRPOP阻塞式的從列表結尾取出舊的請求。設定阻塞超時時間為一個很大的秒數,直到有元素可供取出

internal class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("服務啟動!");
        //發起Redis連線
        var csredis = new CSRedisClient("127.0.0.1:6379");
        try
        {
            while (true)
            {
                //這裡阻塞時間不要大於Redis心跳時間1半
                string name = csredis.BRPop(2, "MQ1");
                Console.WriteLine(name);
            }
        }
        catch (Exception)
        {
        }
        
        Console.WriteLine("服務停止!");
    }
}

測試

  • 100客戶端*100次請求:耗時17秒,每個請求平均耗時1.69ms

image

  • 10000個請求中失敗了99次

image

  • 500客戶端*100次請求:耗時17秒,每個請求平均耗時4.6ms

image

  • 50000個請求中失敗了4120次

image

  • 我試了下把最大連線數從15增加到150,請求耗時並無改變,但失敗的請求下降到2500多次。而且我觀察到前面的幾千次都失敗了,耗時也很久,大概佔了1/2,程序記憶體在這期間上漲,後面失敗的很少,耗時也短。
    但是我註釋了Redis寫入後結果並沒太大變化。後面用讀出redis訊息佇列中的值,發現幾乎一瞬間就讀完了。看起來不是redis慢,而是api伺服器慢。也有可能是測試端的鍋,畢竟500個執行緒似乎太多了。
    總之初次接觸,一臉迷惑。

相關文章