如何实时获取设备的延迟

如何实时获取设备的延迟

今天了解到一个功能,如何获取一个设备延迟,并加以监控。搜索了一下资源,大概了解可以通过两个方式获取。

  • Ping请求+Springboot定时任务
  • Http请求+Springboot定时任务

前提:实时获取设备延迟的前提是目标设备必须在线且可通过网络访问(支持ICMP或HTTP/HTTPS等协议),并且你拥有必要的访问权限。设备和本地网络应允许Ping或HTTP请求,并确保网络环境稳定、时间同步准确。

1. 测试Ping和HTTP获取设备延迟


1.1 使用Ping测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.net.InetAddress;

public class PingTest {
public static void main(String[] args) {
while (true) {
try {
String ipAddress = "camelliaxiaohua.online"; // 要ping的主机地址
InetAddress inet = InetAddress.getByName(ipAddress); // 将主机名转换为IP地址
long startTime = System.currentTimeMillis(); // 记录开始时间
boolean reachable = inet.isReachable(5000); // 使用Ping操作,超时设为5秒
long endTime = System.currentTimeMillis(); // 记录结束时间
if (reachable) {
System.out.println("Ping成功,延迟时间: " + (endTime - startTime) + " 毫秒");
} else {
System.out.println("Ping失败,主机不可达");
}
} catch (Exception e) {
e.printStackTrace(); // 捕获异常并打印堆栈信息
}
}
}
}

image-20240910205848354

正常的实时 ping 请求对服务器的资源消耗通常很小,如上图测试所示(两核cpu)。ping 命令使用的是 ICMP(Internet Control Message Protocol)协议,它的主要作用是测试两台设备之间的网络连通性。ICMP 请求非常轻量,且不涉及复杂的计算或数据处理,因此对服务器的资源占用(CPU、内存、带宽等)很少。

下面就是不正常的操作,请勿模仿。

1. 频繁的大量 ping 请求:如果你以非常高的频率(如每秒数百次)持续向服务器发送 ping 请求,可能会占用网络带宽,尤其是在高并发的情况下,可能导致网络流量增加,影响其他服务。

2. 大规模分布式 ping(DDoS 攻击):如果有大量的设备同时向服务器发送 ping 请求(例如,DDoS 攻击中的 ping flood),可能会使服务器的网络资源被耗尽,导致服务变慢甚至宕机。

1.2 使用HTTP测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.net.HttpURLConnection;
import java.net.URL;

public class HttpPingByHttpURLConnection {
public static void main(String[] args) {
while (true) {
try {
String urlString = "https://camelliaxiaohua.online";
URL url = new URL(urlString);
long startTime = System.currentTimeMillis();
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000); // 设置连接超时5秒
connection.setReadTimeout(5000); // 设置读取超时5秒
connection.connect();
int responseCode = connection.getResponseCode();
long endTime = System.currentTimeMillis();

if (responseCode == 200) {
System.out.println("请求成功,延迟时间: " + (endTime - startTime) + " 毫秒");
} else {
System.out.println("请求失败,响应码: " + responseCode);
}
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

2. 使用定时任务实现实时监控


思路:后端使用SpringBoot定时任务获取设备延时。然后使用WebSocket定时将网络延迟数据实时推送给所有已连接的WebSocket客户端。在前端通过JavaScript与服务器建立WebSocket连接(如ws://localhost:8080/latency-ws),接收延迟数据。

2.1 实现WebSocket的配置

1. 创建一个WebSocket配置类,用于注册WebSocket处理器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package networklatency.config;

import networklatency.controller.LatencyWebSocketHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

private final LatencyWebSocketHandler latencyWebSocketHandler;

public WebSocketConfig(LatencyWebSocketHandler latencyWebSocketHandler) {
this.latencyWebSocketHandler = latencyWebSocketHandler;
}

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 注册 WebSocket 处理器,前端通过 ws://localhost:8080/latency-ws 连接
registry.addHandler(latencyWebSocketHandler, "/latency-ws").setAllowedOrigins("*");
}
}

2.2 实现WebSocket处理器

2. 创建一个LatencyWebSocketHandler来管理WebSocket连接,并在定时任务中推送延迟结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package networklatency.controller;

import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
public class LatencyWebSocketHandler extends TextWebSocketHandler {

private List<WebSocketSession> sessions = new ArrayList<>();

@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// 新的客户端连接建立时,添加到session列表中
sessions.add(session);
}


public void afterConnectionClosed(WebSocketSession session, boolean status) throws Exception {
// 客户端断开连接时,移除session
sessions.remove(session);
}

public void broadcastLatency(String message) {
// 向所有已连接的客户端广播消息
for (WebSocketSession session : sessions) {
try {
session.sendMessage(new TextMessage(message));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

2.3 获取设备延迟

3. 实现SpringBoot定时任务,获取设备延迟并进行WebSocket消息推送。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package networklatency.service.impl;

import networklatency.controller.LatencyWebSocketHandler;
import networklatency.service.NetworkMonitorService;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;

@Service
public class NetworkMonitorServiceImpl implements NetworkMonitorService {

private final LatencyWebSocketHandler webSocketHandler;

public NetworkMonitorServiceImpl(LatencyWebSocketHandler webSocketHandler) {
this.webSocketHandler = webSocketHandler;
}

// 定期ping某个设备
@Override
@Scheduled(fixedRate = 1000) // 每隔1秒执行一次
public Long pingDevice() {
String host = "camelliaxiaohua.online"; // 目标设备
try {
InetAddress address = InetAddress.getByName(host);

long startTime = System.currentTimeMillis();
boolean reachable = address.isReachable(2000); // 2秒的超时时间
long endTime = System.currentTimeMillis();

long latency = endTime - startTime;
if (reachable) {
System.out.println("Ping成功,延迟时间: " + latency + " 毫秒");
} else {
System.out.println("无法到达 " + host);
}
// 推送消息到WebSocket客户端
webSocketHandler.broadcastLatency("Ping Latency: " + latency + " ms");
return latency;
} catch (Exception e) {
e.printStackTrace();
}
return 50000000L;
}

// 定期发送HTTP请求,获取服务器响应时间
@Override
@Scheduled(fixedRate = 1000) // 每隔1秒执行一次
public Long checkHttpLatency() {
String urlString = "https://camelliaxiaohua.online"; // 目标URL
try {
URL url = new URL(urlString);
long startTime = System.currentTimeMillis();

HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(2000); // 设置2秒连接超时
connection.setReadTimeout(2000); // 设置2秒读取超时
connection.connect();
int responseCode = connection.getResponseCode();
long endTime = System.currentTimeMillis();
long latency = endTime - startTime;

if (responseCode == 200) {
System.out.println("请求成功,延迟时间: " + latency + " 毫秒");
} else {
System.out.println("请求失败,响应码: " + responseCode);
}
connection.disconnect();

// 推送消息到WebSocket客户端
webSocketHandler.broadcastLatency("HTTP Latency: " + latency + " ms");
return latency;
} catch (Exception e) {
e.printStackTrace();
}
return 50000000L;
}
}

2.4 其他配置

  • 定时任务需要在启动类增加@EnableScheduling注解

  • 定时任务配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 启用定时任务相关配置
    spring:
    task:
    scheduling:
    pool:
    size: 5 # 定时任务线程池的大小,5表示最多同时执行5个定时任务
    shutdown:
    await-termination: true # 应用程序关闭时是否等待所有任务完成
    await-termination-period: 30s # 等待终止任务的最大时间,30秒