项目中集成mqtt客户端查询功能,使用到了EMQX api-v5
,具体步骤:
一、准备工作
首先在EMQX dashboard
中添加API 密钥
填写密钥名称,点击确定,会生成API Key
和Secret Key
,保存起来备用。
二、配置文件
在springboot的配置文件中添加如下配置
spring:
# mqtt 配置
mqtt:
api:
# mqtt dashboard的访问地址
host: http://xxx.xxx.xxx.xxx:18083
# 上图中的apiKey
apiKey: xxxxxxxxxxxx
# 上图中的secretKey
secretKey: xxxxxxxxxxxx
三、后端实现
创建配置类MqttApiConfig
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.mqtt.api")
public class MqttApiConfig {
private String host;
private String apiKey;
private String secretKey;
}
查询参数DeviceStatusRequest
@Data
public class DeviceStatusRequest {
private String deviceId;
private int pageNum = 1;
private int pageSize = 20;
}
接收参数DeviceStatusInfo
@Data
public class DeviceStatusInfo {
private Boolean connected;
private String node;
private Integer port;
private Integer keepalive;
private String ip_address;
private String username;
private String created_at;
private String clientid;
private String connected_at;
private Boolean clean_start;
}
创建Controller的接口
@Slf4j
@RequestMapping("/mqtt/client")
@RestController
public class ClientController extends BaseController {
@Resource
private MqttApiConfig mqttConfig;
@Resource
private RestTemplate restTemplate;
/**
* 获取所有MQTT客户端列表
*/
private final String GET_ALL_MQTT_CLIENTS_URL = "%s/api/v5/clients?page=%s&limit=%s&fields=clientid,username,connected,ip_address,port,keepalive,clean_start,connected_at,node,disconnected_at,created_at";
/**
* 获取单个MQTT客户端状态信息
*/
private final String GET_MQTT_CLIENT_STATUS_URL = "%s/api/v5/clients?clientid=%s&fields=clientid,username,connected,ip_address,port,keepalive,clean_start,connected_at,node,disconnected_at,created_at";
/**
* 分页查询所有MQTT客户端
*
* @return
*/
@GetMapping("/all")
public PageResult<List<DeviceStatusInfo>> getClients(DeviceStatusRequest request) {
String deviceId = request.getDeviceId();
String url = String.format(GET_ALL_MQTT_CLIENTS_URL, mqttConfig.getHost(), request.getPageNum(), request.getPageSize());
if (StringUtils.isNotBlank(deviceId)) {
url = String.format(GET_MQTT_CLIENT_STATUS_URL, mqttConfig.getHost(), deviceId);
}
JSONObject object = exchange(url);
if (object != null) {
List<DeviceStatusInfo> list = JSONArray.parseArray(object.getString("data"), DeviceStatusInfo.class);
JSONObject meta = object.getJSONObject("meta");
Page<DeviceStatusInfo> page = new Page<>();
page.addAll(list);
page.setPageNum(meta.getInteger("page"));
page.setPageSize(meta.getInteger("limit"));
page.setTotal(meta.getLong("count"));
return PageResult.success(page);
}
return new PageResult(500, "没有查询到数据");
}
private JSONObject exchange(String url) {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setBasicAuth(mqttConfig.getApiKey(), mqttConfig.getSecretKey(), StandardCharsets.UTF_8);
HttpEntity entity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
log.info("response=>{}", response);
if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
String body = response.getBody();
return JSONObject.parseObject(body);
}
return null;
}
四、前端实现
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="68px">
<el-form-item label="客户端" prop="deviceId">
<el-input
v-model="queryParams.deviceId"
placeholder="请输入设备id"
clearable
@keyup.enter.native="handleQuery"
style="width: 160px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="list" style="width: 100%;">
<el-table-column label="序号" type="index" align="center"/>
<el-table-column label="客户端ID" align="left" prop="clientid" min-width="180" :show-overflow-tooltip="true"/>
<el-table-column label="用户名" align="center" prop="username" min-width="150" :show-overflow-tooltip="true"/>
<el-table-column label="节点" align="left" prop="node" width="180" :show-overflow-tooltip="true"/>
<el-table-column label="连接状态" align="center" width="120">
<template v-slot="scope">
<el-tag :type="scope.row.connected ? 'success' : 'danger'">{{ scope.row.connected ? '已连接' : '未连接' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="IP地址" align="left" prop="ip_address" min-width="150" :show-overflow-tooltip="true">
<template v-slot="scope">
<span>{{ scope.row.ip_address}}:{{scope.row.port}}</span>
</template>
</el-table-column>
<el-table-column label="心跳" align="center" prop="keepalive" width="120"/>
<el-table-column label="清除会话" align="center" width="120">
<template v-slot="scope">
<el-tag>{{ scope.row.clean_start }}</el-tag>
</template>
</el-table-column>
<el-table-column label="会话创建时间" align="center" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.created_at) }}</span>
</template>
</el-table-column>
<el-table-column label="连接时间" align="center" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.connected_at) }}</span>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script>
import {getMqttClient} from "@/api/mqtt";
export default {
name: "MqttClient",
data() {
return {
// 遮罩层
loading: true,
// 总条数
total: 0,
// 表格数据
list: [],
// 查询参数
queryParams: {
deviceId: undefined,
pageNum: 1,
pageSize: 20
}
};
},
created() {
this.getList();
},
methods: {
/** 查询登录日志列表 */
getList() {
this.loading = true;
getMqttClient(this.queryParams).then(response => {
this.list = response.data;
this.total = response.count;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
}
};
</script>