0.前言
上一篇博客講的這個圖,說到,設備主動上報數據到應用服務器端,只說到基於MQ來實現,只講到安裝篇,至於代碼篇,等后面實際用到再繼續深入了解。本篇博客主要講,客戶端(手機或者PC端)命令下發到設備,然后設備應答,返回結果到客戶端。常用於掃碼支付類應用。這該是如何設計呢?以前有說到,把手機或者PC客戶端當作一個設備,同樣接入MQTT Broker,這樣,設備和客戶端,同時訂閱和發布相同Topic,就可以兩者之間進行通信。這種方式實現比較簡單。效率高。適用范圍比較單一。一般也就用於自家設備與自家手機應用客戶端。適合公司做自己項目的公司。因為這種方式,平台端還需要管理終端用戶,這對於互聯網公司來說,一般是不太樂意接受的。
下面講到的這種異步調用與同步調用,比較適合一些硬件廠商,或者一些想做大平台的公司。就是提供API給第三方客戶接入。完全分離設備端廠商與應用端廠商,兩者只需要按照流程接入即可,不用管Topic的事情。這樣做的好處是,可以做成一個類似小米一樣的生態系統。定好協議,左邊是給設備廠商接入,右邊是給第三方公司,特別是互聯網公司接入。平台只做好設備認證和客戶應用接入。客戶的終端客戶我們平台端就不需要管了。專心做好平台,做好生態即可。
應用場景:
假如我是一個賣設備模塊的公司,假如這個模塊就一個開關功能。現在定義好模塊的通信協議。開放給硬件產品公司模塊,就一個功能,開跟關。至於硬件產品公司,把開關應用到消防安防的開關,還是閘門、門禁的開關,還是智能插座的開關等等開關類應用。然后這批模塊,按照不同的產品公司,在物聯網平台創建好模型,創建好產品。完成設備接入。
下一步,就是開放API給第三方軟件公司。軟件公司,只需要與平台對接,通過產品key,設備sn,命令參數,就可以下發命令到設備。第三方軟件公司,開發自己的APP應用。接收命令后通過RPC調用物聯網平台,平台下發命令,設備應答,平台返回命令下發結果。完成整個調用。(以上應用場景,均可用於共享儀器,掃碼支付等場景)
1. 異步調用

2. 同步調用

注:本時序圖,最后為什么會有輪詢呢?因為在一個同步調用過程中,會設置超時,如果超時時間內,設備還無法回復。那么就返回超時並返回請求的Msgid給應用服務器。應用服務根據MsgID再去定時輪詢。(也有可能是硬件故障無法回復)。
當然上面的應用服務器與客戶端之間,除了Http請求外,可以采用其他的請求方式。
3. 同步調用參考代碼
同步調用,是基於Java Servlet 3.x 提供的DeferredResult實現的。
MessageQueueDeferredService.java
1 package com.wunaozai.demo.deferred; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 6 import org.springframework.stereotype.Service; 7 import org.springframework.web.context.request.async.DeferredResult; 8 9 @Service 10 public class MessageQueueDeferredService { 11 12 /** 13 * 內存級別消息隊列 14 */ 15 private final Map<String, DeferredResult<String>> results = 16 new HashMap<String, DeferredResult<String>>(); 17 18 /** 19 * 設置應答 20 * @param key 21 * @param value 22 * @return 23 */ 24 public boolean setResponse(String key, String value) { 25 if(results.containsKey(key)) { 26 DeferredResult<String> res = results.get(key); 27 res.setResult(value); 28 results.remove(key); 29 return true; 30 } 31 //可能超時或者不存在對應key 32 return false; 33 } 34 /** 35 * 設置請求 36 * @param key 37 * @param result 38 * @return 39 */ 40 public DeferredResult<String> setRequest(String key) { 41 DeferredResult<String> result = new DeferredResult<String>(5000L, "not response"); 42 results.put(key, result); 43 result.onTimeout(new Runnable() { 44 @Override 45 public void run() { 46 results.remove(key, result); 47 } 48 }); 49 result.onCompletion(new Runnable() { 50 @Override 51 public void run() { 52 results.remove(key, result); 53 } 54 }); 55 return result; 56 } 57 58 }
DeferredResultController.java
1 package com.wunaozai.demo.deferred; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.web.bind.annotation.GetMapping; 5 import org.springframework.web.bind.annotation.RequestMapping; 6 import org.springframework.web.bind.annotation.RestController; 7 import org.springframework.web.context.request.async.DeferredResult; 8 9 @RestController 10 @RequestMapping(value="/deferredresult/") 11 public class DeferredResultController { 12 13 @Autowired 14 private MessageQueueDeferredService messagequeuedeferredService; 15 16 @GetMapping(value="/request") 17 public DeferredResult<String> setRequest(String msgid){ 18 DeferredResult<String> result = messagequeuedeferredService.setRequest(msgid); 19 return result; 20 } 21 @GetMapping(value="/response") 22 public boolean setResponse(String msgid, String value) { 23 boolean flag = messagequeuedeferredService.setResponse(msgid, value); 24 return flag; 25 } 26 27 }
運行結果

前三次請求,由於沒有調用response方法,所以等到5秒超時。並返回not response。等第四次,在5秒內訪問 http://127.0.0.1:8080/deferredresult/reponse?msgid=001&value=test ,然后就在 http://127.0.0.1:8080/deferredresult/request?msgid=003 頁面看到返回結果test了。
參考資料:
https://www.cnblogs.com/coderxiaohei/p/14061468.html
本文地址:https://www.cnblogs.com/wunaozai/p/14078660.html
本系列目錄: https://www.cnblogs.com/wunaozai/p/8067577.html
個人主頁:https://www.wunaozai.com/


