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


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

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

  每個人對業務的設計的不同的,我只是根據自己的了解來設計的。有更好的想法,可以在下面評論區,跟我討論哦。

1.EMQ服務器配置
  本次使用2.3.5版本。直接是從github.com上拉取下來的,然后進行源代碼進行安裝的。前期開發不用涉及到插件開發,可以直接使用官網提供的二進制包。不然的話,就用源代碼自己編譯。注意這里如果自己編譯的話,Erlang要 R20+版本才可以。自己到http://www.erlang.org 下載新版的Erlang/OTP。

  一切的編譯步驟,我在前面的博客有提到,不清楚的可以看我前面的博客。雖然EMQ進行了小版本更新,但是大體的編譯流程還是差不多了。這里就不多說了。

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

  我覺得MQTT通信服務器與業務服務器是要盡量分開的,特別是不要在MQTT服務器里做過多的業務處理。所以使用EMQ服務器里面的帳號驗證和ACL權限驗證。采用的是Redis中間件驗證方式。

  由於默認的EMQ服務器是不多帳號密碼進行驗證和主題的驗證。這里我們要開啟驗證。下面的這些配置是對默認的配置進行的修改。

  (1)data/loaded_plugins 文件

  這里要增加一行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。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM