物聯網架構成長之路(21)-業務服務器設計1

0.前言
  前段時間忙了其餘事了,感受利用週末的時間效率好低哦。沒有平時上班時間的效率高。哈哈哈。這篇博客,主要是物聯網業務服務器前期的一些簡單設計。主要是設備如何進行登陸,從業務服務器那裏獲取Token後,登錄到MQTT服務器。業務服務器對設備的登陸驗證,ACL權限驗證這兩方面。主要是把業務服務器與MQTT服務器聯繫起來。javascript

  距離上次EMQ學習已經有一段時間了,測試服務器也已經換了。因爲一些緣由,只能用本身的服務器了。博客中出現的IP或者域名,因爲只是測試環境,沒有進行過多的限制,有幸看到博客的,請不要搞破壞哦。哈哈哈。/滑稽html

  每一個人對業務的設計的不一樣的,我只是根據本身的瞭解來設計的。有更好的想法,能夠在下面評論區,跟我討論哦。java

1.EMQ服務器配置
  本次使用2.3.5版本。直接是從github.com上拉取下來的,而後進行源代碼進行安裝的。前期開發不用涉及到插件開發,能夠直接使用官網提供的二進制包。否則的話,就用源代碼本身編譯。注意這裏若是本身編譯的話,Erlang要 R20+版本才能夠。本身到http://www.erlang.org 下載新版的Erlang/OTP。node

  一切的編譯步驟,我在前面的博客有提到,不清楚的能夠看我前面的博客。雖然EMQ進行了小版本更新,可是大致的編譯流程仍是差很少了。這裏就很少說了。jquery

  編譯後,會在emq-relx目錄下生成_rel/emqttd目錄。git

  我以爲MQTT通訊服務器與業務服務器是要儘可能分開的,特別是不要在MQTT服務器裏作過多的業務處理。因此使用EMQ服務器裏面的賬號驗證和ACL權限驗證。採用的是Redis中間件驗證方式。github

  因爲默認的EMQ服務器是很少賬號密碼進行驗證和主題的驗證。這裏咱們要開啓驗證。下面的這些配置是對默認的配置進行的修改。ajax

  (1)data/loaded_plugins 文件redis

  這裏要增長一行emq_auth_redis算法

  (2)etc/emq.conf

  因爲如今是單服務器,性能配置什麼都是默認的就能夠。如今我尚未了解全部的配置,性能調優之後看有沒有機會了解。

  這裏要修改的是能夠修改node.name 和 node.cookie,兩個也可使用默認就能夠。

  因爲默認的EMQ服務器是不檢查賬號密碼和ACL權限的。這裏須要修改emq.conf使其要進行驗證。還有cache_acl這個默認是true, 測試的時候最好是設置爲false,這裏若是是true的話,那麼EMQ一樣會對ACL進行驗證。可是隻驗證一次,就是在第一次發佈或者訂閱對應的Topic時會去判斷ACL,若是經過了,那麼下次對這個Topic進行sub/pub是再也不進行判斷驗證的,同理不經過了,下次也不會進行驗證。若是有cache是能夠提升性能,可是對於那些須要對權限動態修改的業務場景,這個功能就不能用了。對應的配置項是

1 mqtt.allow_anonymous = false
2 mqtt.acl_nomatch = deny
3 mqtt.cache_acl = false

  (3)etc/plugins/emq_auth_redis.conf

  修改對應的redis地址和密碼, 分別對應的配置項是 auth.redis.server=127.0.0.1:6379 和 auth.redis.password=password 剩下的cmd不用修改,使用默認的,後續開發若是須要自定義的,能夠修改這裏的cmd。

  嗯,剩下的就不用再配置了。

  ./bin/emqttd start 啓動EMQ服務器就能夠。而後經過Web管理界面進行查看,服務是否啓動。而後在裏面提供的WebSocket界面測試鏈接。

  

2.業務代碼

  若是在EMQ服務器裏作業務處理的話,不是不行,可是呢,Erlang這個語言,處理業務很差,我也不熟悉,做爲一個項目來講,招人也很差招。

  我仍是使用較爲通用的Java來作業務處理。Java的生態會好不少。本次使用Spring Boot 2.0框架。因爲本篇博客主要講的是物聯網業務方面的,關於Spring Boot的一些配置,這裏就不展開了。之後有機會再寫成博客說明。

  從上面的圖能夠看到,如今沒有賬號密碼是不能登陸到MQTT服務器了。EMQ是經過查詢Redis而後進行驗證的。因此我只須要在業務服務器增長一段往Redis服務器寫入權限控制的數據記錄便可。關於默認的格式,參考EMQ文檔。

  Login代碼段

 1     @Autowired
 2     private StringRedisTemplate stringredisTemplate;
 3     
 4     @RequestMapping(value="login")
 5     public @ResponseBody IOTDeviceModel login(HttpSession session,
 6             @RequestParam("username") String username, @RequestParam("password") String password){
 7         //檢驗賬號密碼
 8         IOTDeviceModel dev = new IOTDeviceModel();
 9         if(username.equals("demo")){
10             dev = getIOTDeviceModelDemo();
11         }else if(username.equals("test")){
12             dev = getIOTDeviceModelTest();
13         }else{
14             dev.setMsg("用戶名,密碼錯誤.");
15             return dev;
16         }
17         session.setAttribute("session_user", dev);
18         //設置到 mqtt-redis 並返回token
19         stringredisTemplate.opsForHash().put("mqtt_user:" + dev.getUUID(), "password", dev.getToken());
20         stringredisTemplate.expire("mqtt_user:" + dev.getUUID(), 100, TimeUnit.SECONDS);
21         //這裏查詢模擬數據數據庫,容許當前用戶能夠發佈和訂閱的Topic
22         stringredisTemplate.opsForHash().put("mqtt_acl:" + dev.getUUID(), "/publicroom", "3");
23         //返回成功
24         dev.setMsg("建立成功, 請使用uuid,token 登錄到mqtt服務器");
25         return dev;
26     }

  GetModel代碼段

 1     //測試用戶 Demo
 2     private IOTDeviceModel getIOTDeviceModelDemo(){
 3         IOTDeviceModel model = new IOTDeviceModel();
 4         model.setDevid(1);
 5         model.setUsername("demo");
 6         model.setPassword("demo");
 7         model.setUUID("5a53a33d-98af-4f87-bb57-cc2e21450b36");
 8         model.setToken(UUID.randomUUID().toString()); //生成臨時的Token
 9         return model;
10     }
11     //測試用戶Test
12     private IOTDeviceModel getIOTDeviceModelTest(){
13         IOTDeviceModel model = new IOTDeviceModel();
14         model.setDevid(2);
15         model.setUsername("test");
16         model.setPassword("test");
17         model.setUUID("f9a06e81-3f12-425b-b58d-21fca17b9932");
18         model.setToken(UUID.randomUUID().toString());
19         return model;
20     }

  上面的代碼很簡單了,就是判斷當前賬號密碼是否正確,若是正確的,就寫入Redis。可能會有疑問,爲何設置到Redis的密碼,不直接使用password字段,而是隨機生成Token。這是因爲業務中數據庫保存的密碼通常都是加密後的。而加密算法也是各不相同,默認的MD5可能不知足,固然也有基於一些其餘的緣由。這裏就是返回Token,而後用戶、設備經過業務服務器獲取到Token,登陸到MQTT服務器上。

  
  這樣就登陸成功了。如今這個賬號只能發佈和訂閱/publicroom這個主題。經過自帶的測試工具進行測試以下

  

  能夠發現,分別對/World 和 /publicroom這兩個主題進行訂閱和發佈,都是成功的,可是真正在服務器發送數據通訊的只有/publicroom這個主題,由於只有這個主題是被容許的,/World這個主題不容許。

  其實按照正常邏輯來講,這裏的/World最好是連訂閱都是不容許的,可是好像默認的這裏WebSocket插件沒有作處理。若是之後我基於本身開發插件的話,這裏能夠進行限制處理了。

3.測試結果

  主要從新梳理一下流程。

  (1)一個簡答的測試界面

 1 <!DOCTYPE html>
 2 <html xmlns:th="http://www.thymeleaf.org"
 3       xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
 4 <head>
 5     <meta name="viewport" content="width=device-width,initial-scale=1"/>
 6     <meta charset="UTF-8"/>
 7     <title>EMQ-測試</title>
 8 </head>
 9 <body>
10 如下是模擬用戶/設備登陸業務服務獲取Token<br>
11 用戶名: <input id="username" />
12 密碼: <input id="password" /> 
13 <button id="login-btn">登陸業務服務器</button> <br>
14 登錄後信息: <div id='login-ret'></div>
15 
16 <hr>
17 如下是用戶/設備獲取Token後登陸到MQTT服務器<br>
18 用戶名: <input id="mqtt-username"/>
19 密碼: <input id="mqtt-password"/>
20 <button id="mqtt-login-btn">登陸MQTT</button> <br>
21 登錄後信息: <div id="mqtt-login-ret"></div>
22 
23 <hr>
24 登陸到MQTT後
25 <script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js" type="text/javascript"></script>
26 <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>
27 <script th:inline="javascript">
28     jQuery(function($){
29         bindBtnLogin();
30     });
31     
32     function bindBtnLogin(){
33         $("#login-btn").bind('click', function(){
34             var username = $("#username").val();
35             var password = $("#password").val();
36             $.post("/login", {
37                 username: username,
38                 password: password
39             }, function(ret){
40                 console.log(ret);
41                 $("#login-ret").html(JSON.stringify(ret));
42             });
43         });
44     }
45 </script>
46 </body>
47 </html>

 

  

  輸入demo demo 進行登陸,而後返回Token,而後我使用MQTT客戶端去鏈接。這裏的客戶端使用的是 Eclipse Paho MQTT Utility 工具。

  填寫對應的服務器地址,而後在選項裏寫入賬號密碼,這裏的賬號就是上圖的UUID,密碼就是上圖的Token了。而後登陸成功。

  

  (2)查看Redis數據

  這裏使用Redis客戶端進行查看,方便調試

  

  這裏的mqtt_user:** 因爲設置了TTL,因此會在一段時間後自動刪除掉。

  (3)客戶端訂閱主題

 

  依次在下面訂閱/publicroom和/TTT兩個主題,會發現兩個都訂閱成功(具體緣由,上面有說到)。而後在下面的發佈,分別對/publicroom和/TTT主題進行發佈信息。下圖是運行結果。注意這裏的訂閱要分開進行訂閱,不要兩個選中而後訂閱。這裏的/TTT訂閱不成功了?這麼說,EMQ服務器應該是有處理返回的。

  

  (4)再用另外的賬號

  這裏同理,按照上面的流程,再用另外的賬號,進行登陸,而後兩個賬號互發信息。經測試,基本符合預期的。

  (5)Web管理界面

  這裏看一下幾個管理界面。

  

  

  兩個用戶,ClientID分別就是上面業務服務器模擬的兩臺設備。

4.簡單流程圖

  

5.其餘

  最後多說兩句,一個產品的業務邏輯和業務需求的前期確認是跟一開始的Topic設計是相關的。Topic若是一開始設計的很差,後面的業務就很難進行擴展了。只能往EMQ增長邏輯判斷,我以爲是很沒有必要的。不少能夠靠Topic的巧妙設計來避開復雜的業務邏輯。

  另外,須要不少對設備的統計,操做的處理.例如統計當前主題下訂閱的用戶,踢出主題下的某個用戶,系統推送下發消息等.好比統計用戶,這個我以爲是業務相關的,須要從業務服務器中獲取,可是業務服務器通常沒有這些數據,因此就須要業務服務器Hook到MQTT服務獲取實時在線數.又好比消息下發推送,這個確定是業務服務器下發消息,好比廣播等,一樣要寫MQTT插件.

  固然,這個是我的的想法。期待下一篇博客 業務系統設計2。

相關文章
相關標籤/搜索