YunaiV/ruoyi-vue-pro · error · IllegalStateException
会话数已达上限: {}
Error message
会话数已达上限: {} What it means
Thrown by IotUdpSessionManager.registerSession when a NEW device (not already in deviceSessionCache) attempts to register while the cache has reached maxSessions. The manager is a Guava Cache bounded by maximumSize with expireAfterAccess eviction; this check is an explicit ceiling on top of Guava's own eviction so that a full cache rejects brand-new devices instead of silently evicting an existing one. It is an IllegalStateException because the gateway is in a valid but capacity-blocked state. Note the check is a race-free compare-and-register guarded by the synchronized method.
Source
Thrown at yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/manager/IotUdpSessionManager.java:55
public IotUdpSessionManager(int maxSessions, long sessionTimeoutMs) {
this.maxSessions = maxSessions;
this.deviceSessionCache = CacheBuilder.newBuilder()
.maximumSize(maxSessions)
.expireAfterAccess(sessionTimeoutMs, TimeUnit.MILLISECONDS)
.build();
}
/**
* 注册设备会话
*
* @param deviceId 设备 ID
* @param sessionInfo 会话信息
*/
public synchronized void registerSession(Long deviceId, SessionInfo sessionInfo) {
// 检查是否为新设备,且会话数已达上限(同步方法确保检查和注册的原子性)
if (deviceSessionCache.getIfPresent(deviceId) == null
&& deviceSessionCache.size() >= maxSessions) {
throw new IllegalStateException("会话数已达上限: " + maxSessions);
}
// 注册会话
deviceSessionCache.put(deviceId, sessionInfo);
log.info("[registerSession][注册设备会话,设备 ID: {},地址: {},productKey: {},deviceName: {}]",
deviceId, buildAddressKey(sessionInfo.getAddress()),
sessionInfo.getProductKey(), sessionInfo.getDeviceName());
}
/**
* 获取会话信息
* <p>
* 注意:调用此方法会自动刷新会话的过期时间
*
* @param deviceId 设备 ID
* @return 会话信息,不存在则返回 null
*/
public SessionInfo getSession(Long deviceId) {
return deviceSessionCache.getIfPresent(deviceId);View on GitHub (pinned to 0418084e22)
Solutions
- Increase maxSessions in the IotUdpSessionManager construction (gateway config) to exceed the expected concurrent device count.
- Shorten sessionTimeoutMs so idle device sessions expire sooner, freeing slots for new devices.
- Catch IllegalStateException at the call site of registerSession and respond to the device with a 'server busy / retry later' NACK instead of crashing the handler.
- Confirm the deviceId being passed is correct and stable per physical device; a deviceId that churns (e.g. per-source-port) will exhaust the cache with duplicate logical devices.
Example fix
// before
manager.registerSession(deviceId, info); // throws when full
// after
try {
manager.registerSession(deviceId, info);
} catch (IllegalStateException e) {
log.warn("[udp][会话已满,拒绝设备 {}]", deviceId);
// reply with retry-later or drop packet
} Defensive patterns
Strategy: try-catch
Validate before calling
// No public size() accessor exists; validate by attempting registration
default void registerOrReject(IotUdpSessionManager mgr, Long deviceId, SessionInfo info) {
try {
mgr.registerSession(deviceId, info);
} catch (IllegalStateException e) {
// cache full — tell the device to retry, or shed load
}
} Try / catch
try {
sessionManager.registerSession(deviceId, sessionInfo);
} catch (IllegalStateException e) {
log.warn("[udp][session capacity reached, device {} rejected]", deviceId);
// optionally send a retry-later response; do NOT propagate and kill the UDP handler
} Prevention
- Size maxSessions above the peak concurrent device count with headroom for reconnect storms.
- Tune sessionTimeoutMs so idle devices free slots quickly.
- Monitor deviceSessionCache eviction/size metrics and alert before the cap.
- Ensure deviceId is stable per physical device so the cache does not fill with logical duplicates.
When it happens
Trigger: Calling registerSession(deviceId, sessionInfo) where deviceSessionCache.getIfPresent(deviceId) == null AND deviceSessionCache.size() >= maxSessions. Re-registration (same deviceId already cached) never trips it because the first conjunct is false. The limit is the maxSessions value passed to the constructor (IotUdpSessionManager(maxSessions, sessionTimeoutMs)).
Common situations: A UDP gateway deployed with a too-small maxSessions for the fleet size; a device fleet larger than the configured cap all reconnecting after a gateway restart; stale sessions not expiring fast enough (long sessionTimeoutMs) so live slots are consumed by idle devices; misconfiguration where maxSessions was left at a default low value.
Related errors
- [createProtocol][协议实例 %s 的协议类型 %s 暂不支持]
- MQTT Client 启动失败: 连接 Broker 失败
- Modbus 读取失败 [slaveId=%d, identifier=%s, functionCode=%d, add
- 功能码 {} 不支持写操作
- Modbus 写入失败 [slaveId=%d, identifier=%s, address=%d]
AI-assisted analysis of YunaiV/ruoyi-vue-pro@0418084e22 (2026-08-14).
Data as JSON: /api/errors/bf6b573e38d964d3.
Report an issue: GitHub.