[Azure]_Azure Service Bus Event Hubs應用於IoT:適用於任何裝置的傳輸使用(包含.NET以及JS的範例)

本文翻譯&整理&擴充專案做法原文來自:http://fabriccontroller.net/blog/posts/iot-with-azure-service-bus-event-hubs-authenticating-and-sending-from-any-type-of-device-net-and-js-samples/

前天發了一篇翻譯整理自Sandrino Di Mattia的文章,在:https://www.ntex.tw/wordpress/1983.html

 

如果你還沒看過上一篇,請先看上一篇,了解大概內容後再看本篇會比較清楚在幹嘛

下文有些步驟就直接省略了,只截圖重要的地方

 

本篇示範會利用多種方式,新版的.NET(HttpClient), 舊版的.NET(HttpWebRequest), JS, 這三種方式透過HTTPS認證傳輸

以及一種新版的.NET透過SERVICE BUS SDK 的AMQP傳輸,前面提到四個方式分別實作模擬成真實環境的四個不同的sensor,會隨機產生溫度資料回傳Event Hub

還有一個負責接收的Processor.

 

原文作者的SAMPLE放在:https://github.com/sandrinodimattia/AzureServiceBus.EventHubs.Samples

 

 

▼ 建立一個名為”temperature”的event hub, 且具有兩個SAP,名稱分別是SenderDevice、TemperatureProcessor,而權限分別是Send、Listen2-CREATEHUB

 

 

接著,需要為每個Device依照不同的Publisher名稱跟傳輸模式產生出回傳至EventHub的驗證key,原文作者寫了一個非常實用的產生器,位置在:

https://github.com/sandrinodimattia/RedDog/releases/tag/0.2.0.1

source code上面也有,原文作者也只有提到主要串接字串的部分,其他產稱token都是由api來處理。

這裡就不詳述其中運作的原理,但筆記一下如何使用

 

▼ 本圖我解釋一下

1-SG

左邊Hub的區塊,

  • Namespace填入你EventHubs所屬的Service Bus Name
  • HubName則是剛剛建立出來的EventHubs Name
  • Publisher代表識別拿等下產生出的key去傳送資料的裝置名稱(在這個sample case有livingroom, garage, bathroom, phone)
  • Mode則為要使用Https或者AMQP,若為後者必須要使用MICROSOFOT SERVICE BUS SDK才OK。

 

右邊的Credentials區塊,

  • Sender Key Name就是前面在Azure設定SAP的Rule Name
  • Sender Key就是對應著前面Sender Key Name的Key
  • Token TTL代表這個Key可以活多久,設定為0代表永久有效

 

按下產生就會有一長串的key出現在下方,因為是透過SHA處理,每按一次都不一樣。

 

 

那這些Key產生出來要怎麼塞回作者提供的code裡面?,以下針對四個專案的Programe需要修改的地方後面有中文註解提示(SAS KEY可能要捲動一下SYNTAX PLUG-IN的水平卷軸):

EventHubs.IoT.AuthAndSend.HttpClientSender/Programe.cs

      static void Main(string[] args)
        {
            // Generate a SAS key with the Signature Generator.: https://github.com/sandrinodimattia/RedDog/releases
            var sas = "SharedAccessSignature sr=https%3a%2f%2fdxrdaa01.servicebus.windows.net%2ftemperature%2fpublishers%2flivingroom%2fmessages&sig=ynYwdHRupw0SBWE07lOc3UGV%2biUPslMxSyZgOw6Rcfc%3d&se=1420016997&skn=SenderDevice"; //這裡要換掉key
            
            // Namespace info.
            var serviceNamespace = "dxrdaa01"; //改成Service bus name
            var hubName = "temperature";
            var deviceName = "livingroom"; //記得對應著上面的產生器所設的命名來產生對應的key

            // Create client.
            var httpClient = new HttpClient();
            httpClient.BaseAddress = new Uri(String.Format("https://{0}.servicebus.windows.net/", serviceNamespace));
            httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", sas);

            Console.WriteLine("Starting device: {0}", deviceName);

            // Keep sending.
            while (true)
            {
                var eventData = new
                {
                    Temperature = new Random().Next(20, 50)
                };

                var postResult = httpClient.PostAsJsonAsync(String.Format("{0}/publishers/{1}/messages", hubName, deviceName), eventData).Result;

                Console.WriteLine("Sent temperature using HttpClient: {0}", eventData.Temperature);
                Console.WriteLine(" > Response: {0}", postResult.StatusCode);
                Console.WriteLine(" > Response Content: {0}", postResult.Content.ReadAsStringAsync().Result);

                Thread.Sleep(new Random().Next(1000, 5000));
            }
        }

 

EventHubs.IoT.AuthAndSend.Sender\Programe.cs

   static void Main(string[] args)
        { 
            // Generate a SAS key with the Signature Generator.: https://github.com/sandrinodimattia/RedDog/releases
            var sas = "SharedAccessSignature sr=https%3a%2f%2fdxrdaa01.servicebus.windows.net%2ftemperature%2fpublishers%2fbathroom%2fmessages&sig=ZDXAFS3Rcr51a7gGbSU1lw6ifBCcJ3LHAo%2br7ul5bao%3d&se=1420016981&skn=SenderDevice"; //這裡要換key

            // Namespace info.
            var serviceNamespace = "dxrdaa01"; //改成Service bus name
            var hubName = "temperature";
            var deviceName = "bathroom"; //記得對應著上面的產生器所設的命名來產生對應的key
            
            
            Console.WriteLine("Starting device: {0}", deviceName);

            var uri = new Uri(String.Format("https://{0}.servicebus.windows.net/{1}/publishers/{2}/messages", serviceNamespace, hubName, deviceName));

            // Keep sending.
            while (true)
            {
                var eventData = new
                {
                    Temperature = new Random().Next(20, 50)
                };

                var req = WebRequest.CreateHttp(uri);
                req.Method = "POST";
                req.Headers.Add("Authorization", sas);
                req.ContentType = "application/atom+xml;type=entry;charset=utf-8";

                using (var writer = new StreamWriter(req.GetRequestStream()))
                {
                    writer.Write("{ Temperature: " + eventData.Temperature + "}");
                }

                using (var response = req.GetResponse() as HttpWebResponse)
                {
                    Console.WriteLine("Sent temperature using legacy HttpWebRequest: {0}", eventData.Temperature);
                    Console.WriteLine(" > Response: {0}", response.StatusCode);
                    
                }

                Thread.Sleep(new Random().Next(1000, 5000));
            }
        }

 

EventHubs.IoT.AuthAndSend.WinJsSender\default.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>EventHubs.IoT.AuthAndSend.WinJsSender</title>

    <!-- WinJS references -->
    <!-- At runtime, ui-themed.css resolves to ui-themed.light.css or ui-themed.dark.css
    based on the user’s theme setting. This is part of the MRT resource loading functionality. -->
    <link href="/css/ui-themed.css" rel="stylesheet" />
    <script src="//Microsoft.Phone.WinJS.2.1/js/base.js"></script>
    <script src="//Microsoft.Phone.WinJS.2.1/js/ui.js"></script>

    <!-- EventHubs.IoT.AuthAndSend.WinJsSender references -->
    <link href="/css/default.css" rel="stylesheet" />
    <script src="/js/default.js"></script>
</head>
<body class="phone">
<input type="text" value="0" id="temp" />
<input type="button" value="Send Temperature" onclick="sendTemperature()" />
<label id="status"></label>
<script type="text/javascript">
function sendTemperature() {
    // Generate a SAS key with the Signature Generator.: https://github.com/sandrinodimattia/RedDog/releases
    // Could be provided by a Web API.
    var sas = "SharedAccessSignature sr=https%3a%2f%2fdxrdaa01.servicebus.windows.net%2ftemperature%2fpublishers%2fphone%2fmessages&sig=Vn82Im04c8RisIZS8zNj1LfQH6aJvfWeOO7BCK62C2A%3d&se=1420016964&skn=SenderDevice" //這裡要換key;

    var serviceNamespace = "dxrdaa01"; //改成Service bus name
    var hubName = "temperature";
    var deviceName = "phone"; //記得對應著上面的產生器所設的命名來產生對應的key

    var xmlHttpRequest = new XMLHttpRequest();
    xmlHttpRequest.open("POST", "https://" +
        serviceNamespace + ".servicebus.windows.net/" + hubName + "/publishers/" + deviceName + "/messages", true);
    xmlHttpRequest.setRequestHeader('Content-Type',
        "application/atom+xml;type=entry;charset=utf-8");
    xmlHttpRequest.setRequestHeader("Authorization", sas);

    xmlHttpRequest.onreadystatechange = function () {
        if (this.readyState == 4) {

            if (this.status == 201) {
                document.getElementById('status').innerText =
                    'Sent: ' + document.getElementById('temp').value;
            } else {
                document.getElementById('status').innerText =
                    this.status;
            }
        }
    };

    xmlHttpRequest.send("{ Temperature: " + document.getElementById('temp').value + " }");
}
</script>
</body>
</html>

 

EventHubs.IoT.AuthAndSent.ClientSender\Program.cs

static void Main(string[] args)
        {
            // Generate a SAS key with the Signature Generator.: https://github.com/sandrinodimattia/RedDog/releases
            // Be sure to choose the AMQP option.
            var sas = "SharedAccessSignature sr=sb%3a%2f%2fdxrdaa01.servicebus.windows.net%2ftemperature%2fpublishers%2fgarage&sig=nRzLDYPRBAb%2f0Us26BLCTS%2b%2bxcDA%2bjY4C4dJxWVsqgs%3d&se=1420016929&skn=SenderDevice"; //這裡要換key

            // Namespace info.
            var serviceNamespace = "dxrdaa01"; //改成Service bus name
            var hubName = "temperature";
            var deviceName = "garage"; //記得對應著上面的產生器所設的命名來產生對應的key


            Console.WriteLine("Starting device: {0}", deviceName);

            // Keep sending.
            while (true)
            {
                var eventData = new
                {
                    Temperature = new Random().Next(20, 50)
                };
                
                var factory = MessagingFactory.Create(ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespace, ""), new MessagingFactorySettings
                {
                    TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(sas),
                    TransportType = TransportType.Amqp
                });

                var client = factory.CreateEventHubClient(String.Format("{0}/publishers/{1}", hubName, deviceName));

                var data = new EventData(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(eventData)));
                data.PartitionKey = deviceName;

                client.Send(data);

                Console.WriteLine("Sent temperature using EventHubClient: {0}", eventData.Temperature);

                Thread.Sleep(new Random().Next(1000, 5000));
            }
        }

 

 

這樣ok之後前面四個Sender的對應資訊就set好了,而reciver的部分相對單純些,把SAP設定成Listener的那條Rule,在這就是TemperatureProcessor,而key再使用對應的那組就可以:

EventHubs.IoT.AuthAndSend.Receiver/Program.cs

  static void Main(string[] args)
        {
            var partitionCount = 8;
            var serviceNamespace = "dxrdaa01";
            var hubName = "temperature";
            var receiverKeyName = "TemperatureProcessor";
            var receiverKey = "WkkWm3yJ57oMaFEASrFChuvheJRr404rdLDalnsQ+vU="; //這裡要換key

            Console.WriteLine("Starting temperature processor with {0} partitions.", partitionCount);

            CancellationTokenSource cts = new CancellationTokenSource();

            for (int i = 0; i <= partitionCount - 1; i++)
            {
                Task.Factory.StartNew((state) =>
                {
                    Console.WriteLine("Starting worker to process partition: {0}", state);

                    var factory = MessagingFactory.Create(ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespace, ""), new MessagingFactorySettings()
                    {
                        TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(receiverKeyName, receiverKey),
                        TransportType = TransportType.Amqp
                    });

                    var receiver = factory.CreateEventHubClient(hubName)
                        .GetDefaultConsumerGroup()
                        .CreateReceiver(state.ToString(), DateTime.UtcNow);


                    while (true)
                    {
                        // Receive could fail, I would need a retry policy etc...
                        var messages = receiver.Receive(10);
                        foreach (var message in messages)
                        {
                            var eventBody = Newtonsoft.Json.JsonConvert.DeserializeObject<TemperatureEvent>(Encoding.Default.GetString(message.GetBytes()));

                            Console.WriteLine("{0} [{1}] Temperature: {2}", DateTime.Now, message.PartitionKey, eventBody.Temperature);
                        }

                        if (cts.IsCancellationRequested)
                        {
                            Console.WriteLine("Stopping: {0}", state);
                            receiver.Close();
                        }
                    }
                }, i);
            }

            Console.ReadLine();
            cts.Cancel();
            Console.WriteLine("Wait for all receivers to close and then press ENTER.");
            Console.ReadLine();
        }

 

▼ 到這個步驟,方案按下右鍵,選Set as StartUp Project,然後把五個專案全部選擇同時start。2015-01-07_11-37-23

 

▼ 如果你都有設定ok,會發現有三個sender開始傳送資料到event hubs, 而一台由js寫的windows phone也會跟著起來運作,可以手動傳值,再來可以看到圖片中右下角的視窗為Processor,也會跟著開始接收訊息。

FINAL

 

 

依照SAMPLE可以發現EVENT HUBS在SENDER的支援度非常高,支援HTTPS的裝置就可以運用結合各種字串來傳送資料,但要注意一次的POST不能超過256KB

 

以上。

Leave a comment

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料