All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.cm.heclouds.adapter.mqttadapter.ProxySessionManager Maven / Gradle / Ivy

There is a newer version: 1.0.4
Show newest version
package com.github.cm.heclouds.adapter.mqttadapter;

import com.github.cm.heclouds.adapter.api.ConfigUtils;
import com.github.cm.heclouds.adapter.config.IDeviceConfig;
import com.github.cm.heclouds.adapter.config.impl.ConfigConsts;
import com.github.cm.heclouds.adapter.core.consts.CloseReason;
import com.github.cm.heclouds.adapter.core.entity.Device;
import com.github.cm.heclouds.adapter.core.entity.Response;
import com.github.cm.heclouds.adapter.core.logging.ILogger;
import com.github.cm.heclouds.adapter.core.utils.DeviceUtils;
import com.github.cm.heclouds.adapter.entity.ConnectionType;
import com.github.cm.heclouds.adapter.entity.DeviceSession;
import com.github.cm.heclouds.adapter.entity.ProxySession;
import com.github.cm.heclouds.adapter.mqttadapter.handler.UpLinkChannelHandler;
import com.github.cm.heclouds.adapter.mqttadapter.mqtt.promise.MqttConnectResult;
import com.github.cm.heclouds.adapter.utils.ConnectSessionNettyUtils;
import com.github.cm.heclouds.adapter.utils.SasTokenGenerator;
import io.netty.channel.Channel;
import javafx.util.Pair;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;

import static com.github.cm.heclouds.adapter.core.logging.LoggerFormat.Action.*;

/**
 * 代理连接Session管理类
 */
public final class ProxySessionManager {

    private static final ILogger LOGGER = ConfigUtils.getLogger();
    private static final IDeviceConfig DEVICE_CONFIG = ConfigUtils.getDeviceConfig();

    /**
     * 上一个代理连接创建时间
     */
    private static final AtomicLong LAST_PROXY_CONNECTION_CREATION_TIME = new AtomicLong(0);

    private static final UpLinkChannelHandler UP_LINK_CHANNEL_HANDLER = UpLinkChannelHandler.INSTANCE;

    /**
     * 代理连接Session池
     * 代理连接ID与代理连接Session的映射
     */
    private static final ConcurrentMap PROXY_SESSION_POOL = new ConcurrentHashMap<>();

    public static ConcurrentMap getProxySessionPool() {
        return PROXY_SESSION_POOL;
    }

    /**
     * 创建新的代理连接Session
     * 

* 注意:仅当控制连接有效时才会建立 * * @param proxyId 代理连接ID * @param channel 连接channel * @param mqttClient Mqtt Client * @return 新的代理连接Session */ private static ProxySession createProxySession(String proxyId, Channel channel, MqttClient mqttClient) { ProxySession proxySession = ProxySession.newBuilder() .mqttClient(mqttClient) .proxyId(proxyId) .channel(channel) .proxyDevAssociation(new ConcurrentHashMap<>()) .connected(true) .isDevicesReachedLimit(false) .build(); ConnectSessionNettyUtils.setConnectionType(channel, ConnectionType.PROXY_CONNECTION); ConnectSessionNettyUtils.setProxySession(channel, proxySession); return proxySession; } /** * 选择代理连接Session,用于新设备选择合适的代理连接,默认为选择当前代理设备最少的代理连接 * * @return 代理连接Session */ public static ProxySession chooseProxySession() { if (ControlSessionManager.config == null) { LOGGER.logInnerWarn(ConfigUtils.getName(), RUNTIME, "choose proxy session failed as control session was not initialized"); return null; } return chooseMinimumDeviceProxySession() .orElseGet(ProxySessionManager::initNewProxyConnection); } /** * 连接断开情况处理 */ public static void handleConnectionLost(ProxySession proxySession) { LOGGER.logPxyConnWarn(ConfigUtils.getName(), DISCONNECT, null, proxySession.getProxyId()); Iterator, DeviceSession>> iterator = proxySession.getProxyDevAssociation().entrySet().iterator(); // 所有代理设备下线 while (iterator.hasNext()) { Map.Entry, DeviceSession> entry = iterator.next(); DeviceSession deviceSession = entry.getValue(); Device device = Device.newBuilder() .productId(deviceSession.getProductId()) .deviceName(deviceSession.getDeviceName()).build(); DeviceUtils.setDeviceCloseReason(device, CloseReason.CLOSE_DUE_TO_PROXY_CONNECTION_LOST); DeviceSessionManager.handleDeviceOffline(deviceSession); // 主动通知设备下线 ConfigUtils.getConfig().getDeviceDownLinkHandler() .onDeviceNotifiedLogout(device, new Response(null, 1061, "disconnected proxy connection")); iterator.remove(); } removeProxySession(proxySession.getProxyId()); } /** * 代理连接代理的设备数量是否超过限制 * * @param deviceSession deviceSession * @param response 登录响应 * @return 是否超过限制 */ public static boolean isProxiedDevicesReachedLimit(DeviceSession deviceSession, Response response) { ProxySession proxySession = deviceSession.getProxySession(); if (response.getCode() == 0x8B) { proxySession.setDevicesReachedLimit(true); deviceSession.setProxySession(null); Device deviceEntity = DEVICE_CONFIG.getDeviceEntity(DEVICE_CONFIG.getOriginalIdentity(deviceSession.getProductId(), deviceSession.getDeviceName())); // 重新选择代理连接登录 UP_LINK_CHANNEL_HANDLER.doDeviceOnline(deviceEntity); return true; } return false; } /** * 放入代理连接Session * * @param proxySession 代理连接Session */ private static void putProxySession(ProxySession proxySession) { PROXY_SESSION_POOL.put(proxySession.getProxyId(), proxySession); } /** * 从Session池中移除 * * @param proxyId 代理连接id */ private static void removeProxySession(String proxyId) { PROXY_SESSION_POOL.remove(proxyId); } /** * 当前代理设备最少的代理连接 * * @return 当前代理设备最少的代理连接 */ private static Optional chooseMinimumDeviceProxySession() { return PROXY_SESSION_POOL.values().stream() .filter(proxySession -> !proxySession.isDevicesReachedLimit()) .min(Comparator.comparingInt(ProxySession::size)); } /** * 初始化代理连接,当控制连接不存在或者已经断开时不会进行初始化 * * @return 代理连接Session */ private static ProxySession initNewProxyConnection() { if (!ControlSessionManager.isControlSessionActive()) { LOGGER.logPxyConnWarn(ConfigUtils.getName(), INIT, "init new proxy connection failed as control session is inactive", null); return null; } long currentTimeMillis = System.currentTimeMillis(); if (currentTimeMillis - LAST_PROXY_CONNECTION_CREATION_TIME.getAndSet(currentTimeMillis) < ConfigConsts.MAX_PROXY_CONNECTION_CREATION_INTERVAL_MS) { LOGGER.logPxyConnWarn(ConfigUtils.getName(), INIT, "init new proxy connection failed as proxy connections create too fast", null); return null; } String clientId = genClientId(); if (PROXY_SESSION_POOL.containsKey(clientId)) { LOGGER.logPxyConnWarn(ConfigUtils.getName(), INIT, "existed proxy connection", clientId); return null; } MqttClient mqttClient = new MqttClient(ConfigUtils.getConfig()); MqttConnectResult result; try { String serviceId = ControlSessionManager.config.getServiceId(); String sasToken = SasTokenGenerator.adapterSasToken(ControlSessionManager.config); if (sasToken == null) { LOGGER.logPxyConnWarn(ConfigUtils.getName(), INIT, "init new proxy connection failed due to generate sasToken failed", clientId); return null; } result = mqttClient.connect(clientId, serviceId, sasToken); } catch (ExecutionException | InterruptedException e) { LOGGER.logPxyConnWarn(ConfigUtils.getName(), INIT, "initialize mqtt client failed whiling choose proxy session due to " + e.getLocalizedMessage(), clientId); Thread.currentThread().interrupt(); return null; } ProxySession proxySession = null; switch (result.returnCode()) { case CONNECTION_ACCEPTED: proxySession = createProxySession(clientId, mqttClient.getChannel(), mqttClient); putProxySession(proxySession); LOGGER.logPxyConnInfo(ConfigUtils.getName(), INIT, "init new proxy connection succeed", clientId); break; case CONNECTION_REFUSED_NOT_AUTHORIZED: LOGGER.logPxyConnWarn(ConfigUtils.getName(), INIT, "init new proxy connection failed due to proxy connections reached limit", clientId); break; case CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD: case CONNECTION_REFUSED_SERVER_UNAVAILABLE: default: LOGGER.logPxyConnWarn(ConfigUtils.getName(), INIT, "init new proxy connection failed due to " + result.returnCode().toString(), clientId); break; } return proxySession; } /** * 生成用于建立代理连接的clientID * * @return clientID */ private static String genClientId() { if (ControlSessionManager.config == null) { throw new IllegalStateException("control session was not initialized"); } return ControlSessionManager.config.getInstanceName() + "/" + UUID.randomUUID().toString(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy