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

com.joe.easysocket.server.backserver.manager.ChannelManager Maven / Gradle / Ivy

The newest version!
package com.joe.easysocket.server.backserver.manager;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import com.joe.easysocket.server.backserver.mvc.data.InterfaceData;
import com.joe.easysocket.server.common.config.ClusterConfig;
import com.joe.easysocket.server.common.config.Const;
import com.joe.easysocket.server.common.config.Environment;
import com.joe.easysocket.server.common.data.ProtocolData;
import com.joe.easysocket.server.common.exception.SystemException;
import com.joe.easysocket.server.common.info.BalanceInfo;
import com.joe.easysocket.server.common.lambda.Endpoint;
import com.joe.easysocket.server.common.msg.ChannelId;
import com.joe.easysocket.server.common.msg.CustomMessageListener;
import com.joe.easysocket.server.common.msg.DataMsg;
import com.joe.easysocket.server.common.protocol.ChannelProxy;
import com.joe.easysocket.server.common.protocol.ProtocolFuture;
import com.joe.easysocket.server.common.spi.PublishCenter;
import com.joe.easysocket.server.common.spi.Serializer;
import com.joe.utils.common.Tools;
import com.joe.utils.parse.json.JsonParser;
import com.joe.utils.protocol.Datagram;
import com.joe.utils.protocol.DatagramUtil;

import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

/**
 * 虚拟channel管理器
 *
 * @author joe
 */
@Slf4j
public class ChannelManager implements Endpoint {
    private volatile boolean                 started = false;
    /**
     * 后端ID
     */
    private String                           id;
    /**
     * 所有通道
     */
    private Map  allChannels;
    /**
     * 发布中心
     */
    private PublishCenter                    publishCenter;
    /**
     * channel注销消息ACKtopic
     */
    private String                           channelChangeAckTopic;
    /**
     * channel注销消息topic
     */
    private String                           channelChangeTopic;
    /**
     * 通道监听
     */
    private CustomMessageListener channelChangeListener;
    /**
     * 用户数据监听
     */
    private DataListener                     listener;
    /**
     * 前端管理
     */
    private BalanceManager                   balanceManager;
    /**
     * 序列化器
     */
    private List                 serializers;

    /**
     * 默认构造器
     *
     * @param id          后端ID
     * @param environment 环境信息
     */
    public ChannelManager(String id, Environment environment) {
        ClusterConfig clusterConfig = environment.get(Const.CLUSTER_CONFIG);
        this.id = id;
        this.balanceManager = new BalanceManager(environment);
        this.publishCenter = environment.get(Const.PUBLISH_CENTER);
        this.channelChangeAckTopic = clusterConfig.getChannelChangeAckTopic();
        this.channelChangeTopic = clusterConfig.getChannelChangeTopic();
        this.allChannels = new ConcurrentHashMap<>();

        this.channelChangeListener = new CustomMessageListener() {
            @Override
            public void onMessage(byte[] channel, ChannelId message) {
                log.debug("收到channel [{}] 关闭广播", message);
                if (message == null) {
                    log.warn("广播的channel ID为null,不处理");
                    return;
                }

                //ACK响应
                publishCenter.pub(channelChangeAckTopic + "/" + message.getChannel() + "/"
                                  + message.getBalanceId() + "/" + id,
                    message.getChannel());

                InternalChannel internalChannel = allChannels.remove(message);
                if (internalChannel != null) {
                    log.debug("channel当前存在,关闭channel[{}]", message);
                    internalChannel.closeInternal();
                }
            }

            @Override
            public Class resolveMessageType() {
                return ChannelId.class;
            }
        };
        this.listener = this::write;
        this.serializers = environment.get(Const.SERIALIZER_LIST);
    }

    /**
     * 清空指定的前端所有的channel
     *
     * @param info 对应的前端的信息
     */
    public void clearChannel(BalanceInfo info) {
        allChannels.forEach((id, channel) -> {
            if (channel.getId().getBalanceId().equals(info.getId())) {
                log.debug("清除balance [{}] 下的channel [{}]", info.getId(), id);
                InternalChannel internalChannel = allChannels.remove(id);
                if (internalChannel != null) {
                    internalChannel.closeInternal();
                }
            }
        });
    }

    @Override
    public synchronized void start() throws SystemException {
        if (started) {
            log.debug("当前channel管理器已经启动,不能重复启动");
            return;
        }
        publishCenter.register(channelChangeTopic + "/" + id, channelChangeListener);
        balanceManager.start();
        balanceManager.addCloseListener(this::clearChannel);
        started = true;
    }

    @Override
    public synchronized void shutdown() throws SystemException {
        if (!started) {
            log.debug("当前channel管理器已经关闭,不能重复启动");
            return;
        }
        publishCenter.unregister(channelChangeTopic, channelChangeListener);
        balanceManager.shutdown();
        started = false;
    }

    /**
     * 根据channel信息获取channel(只应该由系统内调用,只调用一次,并且在生成session的时候调用)
     *
     * @param channel   channel的ID
     * @param balanceId 前端的ID
     * @param remoteAdd channel的客户端IP
     * @param port      channel的客户端的端口
     * @return 不会返回null
     */
    public ChannelProxy getChannel(String channel, String balanceId, String remoteAdd, int port) {
        log.debug("获取channel:{}:{}:{}:{}", channel, balanceId, remoteAdd, port);
        ChannelId id = new ChannelId(channel, balanceId);
        InternalChannel internalChannel = allChannels.get(id);
        if (internalChannel != null) {
            log.debug("当前缓存中有对应的channel");
            return internalChannel;
        } else {
            log.debug("当前缓存中没有对应的channel,构建一个");
            internalChannel = new InternalChannel(channel, balanceId, remoteAdd, port, listener,
                balanceManager.getBalance(balanceId).getTopic(), serializers);
            allChannels.put(id, internalChannel);
            return internalChannel;
        }
    }

    /**
     * 主动往前端写数据
     *
     * @param data    要写的数据
     * @param topic   要写入的topic
     * @param channel 对应的通道
     * @return 写入状态
     */
    private ProtocolFuture write(byte[] data, String topic, String channel) {
        log.debug("后端主动往前端{}发送消息{}", topic, data);
        DataMsg dataMsg = new DataMsg();
        ProtocolData protocolData = new ProtocolData();
        long time = System.currentTimeMillis();

        Datagram datagram = DatagramUtil.build(data, (byte) 4, (byte) 1);
        log.debug("生成的数据报为:{}", datagram);
        protocolData.setData(datagram.getData());
        protocolData.setChannel(channel);
        protocolData.setReqTime(time);
        dataMsg.setId(Tools.createUUID());
        dataMsg.setTopic(topic);
        dataMsg.setCreateTime(time);
        dataMsg.setData(protocolData);
        dataMsg.setSrc(id);
        log.debug("生成的待发送数据为:{},将数据发送到:{}", dataMsg, topic);
        publishCenter.pub(topic, dataMsg);
        return ProtocolFuture.SUCCESS;
    }

    /**
     * 内部channel实现(只有write、id、getRemoteHost、getPort、isClosed、addCloseCallback方法可以使用,同时write方法
     * 无论写入是否成功都返回成功ProtocolFuture.SUCCESS)
     * 

* 使用该channel的时候应该先注册关闭监听,然后调用isClose方法判断是否关闭,如果关闭那么就认为channel失效 */ @Slf4j private static class InternalChannel implements ChannelProxy { private static final JsonParser parser = JsonParser.getInstance(); private ChannelId id; private String remoteAdd; private int port; private List callbacks; private volatile boolean close = false; /** * 数据监听器,当用户调用write后实际会调用该方法 */ private DataListener listener; private String topic; private List serializers; /** * 虚拟channel的构造器 * * @param channel 该虚拟channel对应的实际channel的ID * @param balanceId 该channel的归属前端ID * @param remoteAdd 该channel的客户端IP * @param port 该channel的客户端端口 * @param listener 数据监听(用于将用户的数据发送到前端) * @param topic 该channel对应的前端监听的topic */ private InternalChannel(@NonNull String channel, @NonNull String balanceId, @NonNull String remoteAdd, int port, @NonNull DataListener listener, @NonNull String topic, @NonNull List serializers) { this.id = new ChannelId(channel, balanceId); this.remoteAdd = remoteAdd; this.port = port; this.callbacks = new CopyOnWriteArrayList<>(); this.listener = listener; this.topic = topic; this.serializers = serializers; } @Override public ProtocolFuture write(String invoke, String data) { InterfaceData interfaceData = new InterfaceData(Tools.createUUID(), invoke, data); return listener.write(write(interfaceData).getBytes(), topic, id.getChannel()); } private String write(Object obj) { if (serializers.isEmpty()) { return parser.toJson(obj); } else { for (Serializer serializer : serializers) { if (serializer.writeable(obj)) { return new String(serializer.write(obj)); } } return parser.toJson(obj); } } @Override public String id() { return id.getChannel(); } @Override public String getRemoteHost() { return remoteAdd; } @Override public int getPort() { return port; } @Override public boolean isClosed() { return close; } /** * 当收到注销事件时调用该方法 */ void closeInternal() { close = true; callbacks.forEach(callback -> { try { callback.close(id.getChannel()); } catch (Throwable e) { log.debug("调用channel [{}] 的关闭回调 [{}] 时出错", id, this, e); } }); } ChannelId getId() { return this.id; } @Override public void addCloseCallback(ChannelCloseCallback callback) { callbacks.add(callback); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy