---
# System prepended metadata

title: 以 MQTT RabbitMQ 服務安裝在 docker 並搭配 .NET 6 進行實作發佈與訂閱

---

# 以 MQTT RabbitMQ 服務安裝在 docker 並搭配 .NET 6 進行實作發佈與訂閱

[toc]

![](https://hackmd.io/_uploads/H1deCjTa3.png)


## 安裝 RabbitMQ on Docker for Windows

![](https://hackmd.io/_uploads/S1qs6ja6h.png)

[docker hub rabbitmq](https://hub.docker.com/_/rabbitmq)

1. 輸入 pull 指令下載 image 
`$ docker pull rabbitmq`
2. 建立 Container
`$ docker run -d --hostname my-rabbit --name rock-rabbit rabbitmq:3`
`$ docker run -d --name rock-rabbit -p 5672:5672 -p 15672:15672 rabbitmq:management`
> 預設 Port:5672
> --name rock-rabbit 表示將 Container 名稱設定為 rock-rabbit
> rabbitmq:3 表示使用 rabbitmq tag 等於 3 的版本來建 Container

3. 設置預設用戶和密碼(請參考 docker 官方文件)
> 預設使用者名稱和密碼都是 `guest`

4. 進入管理網站
> http://127.0.0.1:15672/

![](https://hackmd.io/_uploads/r1IQQhTan.png)

登入成功

![](https://hackmd.io/_uploads/r1347naan.png)


> 當然你也可以綁定本機某一個 Port 號，來對應 15672 Container(請參考 docker 官網)

## 建立 Publisher 發佈者和 Consumer 訂閱者的帳號與密碼

![](https://hackmd.io/_uploads/BJX3D0TT2.png)

![](https://hackmd.io/_uploads/SkCedA6ph.png)


![](https://hackmd.io/_uploads/ByhCDApp3.png)


## 建立訊息通道
![](https://hackmd.io/_uploads/B1pNe066h.png)

![](https://hackmd.io/_uploads/H1jwxAaa2.png)


# 撰寫一個 RabbitMQ 的 .NET 範例

## 安裝套件 RabbitMQ.Client

![](https://hackmd.io/_uploads/B1_QD3p6h.png)

## 程式範例

### Publisher 發佈者程式
```csharp=
internal class Program
{
    static readonly string queueName = "stocktick";
    static void Main(string[] args)
    //傳送端代碼

    //創建連線工廠（ConnectionFactory）物件：這個物件用於設定與 RabbitMQ 伺服器的連線參數，包括使用者名稱、密碼、虛擬主機、主機名稱和埠號等。
    ConnectionFactory factory = new ConnectionFactory();
    factory.UserName = "send";
    factory.Password = "send";
    factory.VirtualHost = "/";
    factory.HostName = "localhost";
    factory.Port = 5672; // AmqpTcpEndpoint.UseDefaultPort;
                         //創建連線（IConnection）物件：使用連線工廠創建一個連線至 RabbitMQ 伺服器。
    IConnection connection = factory.CreateConnection();

    int i = 0;
    //創建通道（IModel）物件：使用連線創建一個通道，通道用於執行 RabbitMQ 的各種操作，如隊列宣告、訊息發佈等。
    using (var channel = connection.CreateModel())
    {
        //宣告隊列：使用通道的 QueueDeclare 方法宣告一個隊列，指定隊列的名稱（queue）、是否持久化（durable）、是否獨佔（exclusive）、是否自動刪除（autoDelete）、和額外的參數（arguments）。
        channel.QueueDeclare(queue: queueName,
            durable: true,
            exclusive: false,
            autoDelete: false,
            arguments: null);

        while (i < 5)
        {
            string message = "Hello World! " + i;
            var body = Encoding.UTF8.GetBytes(message);

            channel.BasicPublish(exchange: "", routingKey: queueName, basicProperties: null, body: body);
            Console.WriteLine(" [x] Sent {0}", message);
            Thread.Sleep(1000);
            i++;
        }

    }
    Console.WriteLine(" Press [enter] to exit.");
    Console.ReadLine();
}
```
### Consumer 訂閱者程式

```csharp=
internal class Program
{
    static readonly string queueName = "stocktick";
    static void Main(string[] args)
    {
        Task threadTask = Task.Run(() => t1());
        //threadTask.Wait(); // 等待任務完成


        Console.WriteLine(" Press [enter] to exit.");
        Console.ReadLine();
    }

    static bool isQuitThread = false;

    private static void t1()
    {
        //建立接收者連線資訊
        ConnectionFactory factory = new ConnectionFactory();
        factory.UserName = "receive";
        factory.Password = "receive";
        factory.VirtualHost = "/";
        factory.HostName = "localhost";
        factory.Port = 5672;//AmqpTcpEndpoint.UseDefaultPort;
        IConnection connection = factory.CreateConnection();

        //產生通道
        using var channel = connection.CreateModel();

        //宣告隊列：使用通道的 QueueDeclare 方法宣告一個隊列，指定隊列的名稱（queue）、是否持久化（durable）、是否獨佔（exclusive）、是否自動刪除（autoDelete）、和額外的參數（arguments）。
        channel.QueueDeclare(queue: queueName,
            durable: true,
            exclusive: false,
            autoDelete: false,
            arguments: null);
        Console.WriteLine(" [*] Waiting for messages.");
        var consumer = new EventingBasicConsumer(channel);
        consumer.Received += (model, ea) =>
        {
            var body = ea.Body.ToArray();
            var message = Encoding.UTF8.GetString(body);
            Console.WriteLine(" [x] Received {0}", message);
        };
        channel.BasicConsume(queue: queueName, autoAck: true, consumer: consumer);

        //讓程式不要直接結束
        while (isQuitThread) ;
        Debug.WriteLine("thread is quit!!");
    }
}
```

# 支援的程式語言

https://www.rabbitmq.com/devtools.html

# 參考資料

[.NET/C# Client API Guide](https://www.rabbitmq.com/dotnet-api-guide.html)

[MQTT教學（一）：認識MQTT](https://swf.com.tw/?p=1002)

[【RabbitMQ】五分鐘輕鬆了解 RabbitMQ 運作](https://zamhuang.medium.com/rabbitmq-%E4%BA%94%E5%88%86%E9%90%98%E8%BC%95%E9%AC%86%E4%BA%86%E8%A7%A3-rabbitmq-%E9%81%8B%E4%BD%9C-fcaecbaa69d4)

[用 Docker 玩 RabbitMQ](https://hackmd.io/@SuFrank/rJ5Tgyb6q)

###### tags: `MQTT` `RabbitMQ`