博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于NIO的消息路由的实现(五) 服务端连接管理
阅读量:6242 次
发布时间:2019-06-22

本文共 6152 字,大约阅读时间需要 20 分钟。

hot3.png

一、对客户端连接(也就是SocketChannel)的管理

  1. 客户端与服务端建立连接后,服务端来维护其连接,首先定义了一个GVConnection类,用来定义连接;

此类包括四个成员:token、userId、channel、lastAccessTime;如下:

public class GVConnection {    private String token;    private String userId;    private SocketChannel channel;    private int lastAccessTime;    public GVConnection(String token,String userId, SocketChannel channel, int lastAccessTime) {        this.token = token;        this.userId = userId;        this.channel = channel;        this.lastAccessTime = lastAccessTime;    }    public void setLastAccessTime(int lastAccessTime){        this.lastAccessTime = lastAccessTime;    }    public SocketChannel getChannel() {        return channel;    }    public String getToken() {        return token;    }    public String getUserId(){        return userId;    }    public int getLastAccessTime() {        return lastAccessTime;    }}

2.对GVConnection的操作单独封装一个类,GVConnTools,在这个类中,我定义了两个map,用来保存userId与GVConnection的对应关系,以及token与GVConnection的对应关系。这两个Map需要保持同步,在删除的时候,可以通过userId或者token进行删除。这两个方法有可能同步执行会遭遇异常,所以我增加了一个互斥锁(我不确定这样做的合理性,敬请指导)。其他新增和更新的方法我均采用了同步方法。

public class GVConnTools {    private static Map
 CONNECTION_BY_USERID = new ConcurrentHashMap
();    private static Map
 CONNECTION_BY_TOKEN = new ConcurrentHashMap
();    private static Lock lock = new ReentrantLock();    public static GVConnection getConnByToken(String token) {        if (CONNECTION_BY_TOKEN.get(token) != null) {            return CONNECTION_BY_TOKEN.get(token);        } else {            return null;        }    }    public static GVConnection getConnByUserId(String userId) {        return CONNECTION_BY_USERID.get(userId);    }    public static SocketChannel getChannelByUserId(String userId) {        if (CONNECTION_BY_USERID.get(userId) != null) {            return CONNECTION_BY_USERID.get(userId).getChannel();        } else {            return null;        }    }    public static SocketChannel getChannelByToken(String token) {        return CONNECTION_BY_TOKEN.get(token).getChannel();    }    public static synchronized void addConn2Cache(GVConnection conn) {        CONNECTION_BY_TOKEN.put(conn.getToken(), conn);        CONNECTION_BY_USERID.put(conn.getUserId(), conn);    }    public static void removeConnByUserId(String userId) {        lock.lock();        try {            String token = CONNECTION_BY_USERID.get(userId).getToken();            CONNECTION_BY_USERID.remove(userId);            CONNECTION_BY_TOKEN.remove(token);        } finally {            lock.unlock();        }    }    public static void removeConnByToken(String token) {        lock.lock();        try {            String userId = CONNECTION_BY_TOKEN.get(token).getToken();            CONNECTION_BY_TOKEN.remove(token);            CONNECTION_BY_USERID.remove(userId);        } finally {            lock.unlock();        }    }    public static String getUserIdByToken(String token) {        return CONNECTION_BY_TOKEN.get(token).getUserId();    }    public static String getTokenByUserId(String userId) {        return CONNECTION_BY_USERID.get(userId).getToken();    }    public static synchronized void updLastAccessTime(String token, int lastAccessTime) {        String userId = CONNECTION_BY_TOKEN.get(token).getUserId();        CONNECTION_BY_USERID.get(userId).setLastAccessTime(lastAccessTime);        CONNECTION_BY_TOKEN.get(token).setLastAccessTime(lastAccessTime);    }    public static Map getConnCache() {        return CONNECTION_BY_TOKEN;    }}

3.通道清理线程,目前的连接清理采用的是超时清理:此处我遇到一个问题,就是我根本无法判断客户端的异常中断,所以我只能依靠客户端发给服务端的心跳,不断的更新GVConnection中的lastAccessTime,在我的超时线程中,对GVConnection的集合进行遍历,一旦发现有channel关闭的以及超时的,就将其进行清理。而客户端主动关闭,我是可以获取的,在GVServer

public class ConnTimeoutCleanThread implements Runnable {    /**     * 超时时间     */    private int outTime;    /**     * 执行周期     */    private int cycle;    private static Logger logger = LogManager.getLogger(ConnTimeoutCleanThread.class.getName());    public ConnTimeoutCleanThread(int outTime, int cycle) {        this.outTime = outTime;        this.cycle = cycle;    }    public void run() {        logger.info("定期通道清理线程启动……");        while (true) {            logger.info("定期通道清理开启……");            Map
 connCache = GVConnTools.getConnCache();            for (String token : connCache.keySet()) {                int currentTime = CommonTools.systemTimeUtc();                GVConnection gvConn = GVConnTools.getConnByToken(token);                SocketChannel socketChannel = gvConn.getChannel();                if (!socketChannel.isOpen()) {                    try {                        socketChannel.close();                        GVConnTools.removeConnByToken(token);                        logger.info("清理了关闭的连接:token:<" + token + ">");                    } catch (IOException e) {                        e.printStackTrace();                    }                }else if (currentTime - gvConn.getLastAccessTime() > outTime) {                    if (socketChannel.isOpen()) {                        try {                            socketChannel.close();                            GVConnTools.removeConnByToken(token);                            logger.info("清理了超时的连接:token:<" + token + ">");                        } catch (IOException e) {                            e.printStackTrace();                        }                    }                }                try {                    Thread.sleep(3);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            logger.info("定期通道清理执行完毕!");            try {                Thread.sleep(cycle * 1000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

三、再谈发博文目的:

我之所以发博文是因为:

  1. 我的代码其实大部分是我四处拼拼凑凑,有些是我自己想的,毕竟9年没有写过代码了,我的java思维仍然停留在jdk1.4刚刚出现的时候,所以我希望大家能够一起学习进步,代码中有很多幼稚的、不科学的、不确定的、甚至我自己也不明白的地方,但是是可执行,并且经过我测试的。希望有时间、有精力的朋友能够帮助我,或者想学习的可以跟我一起学习。

  2. 我的代码仍然在不断完善中,因为我本身不是搞开发的,完全是一种兴趣,所以更新的频率不会太快。可能会出现4、5天都不更新的情况。

  3. 自从看了少帮主的znet之后,觉得自己做的简直不堪入目,所以我会对我的代码进行改写。不过改写之前会讲之前已经做的都整理出来,至少可以看看成长的经历。

转载于:https://my.oschina.net/u/2397619/blog/497326

你可能感兴趣的文章
C# yeild使用
查看>>
MapReduce-Hadoop分布式计算模型
查看>>
StrokePlus
查看>>
joisino's travel
查看>>
组合游戏-博弈论中经典模型题目
查看>>
浅谈HTTP的GET和POST
查看>>
点灯笼
查看>>
try{}catch{}
查看>>
[Aaronyang] 写给自己的WPF4.5 笔记11[自定义控件-AyImageButton的过程 1/4]
查看>>
Linux VMware新添加网络适配器找不到配置文件问题
查看>>
Javascript百学不厌 - this
查看>>
机器学习中的数学(1)-回归(regression)、梯度下降(gradient descent)
查看>>
实用算法实现-第 14 篇 启发式搜索
查看>>
c#常用的排序算法
查看>>
论文阅读——Visual inertial odometry using coupled nonlinear optimization
查看>>
Office插件编程[转]
查看>>
读代码还是读文档,来自知乎
查看>>
Linux 常见编译错误
查看>>
ASP.NET MVC 3 Controller
查看>>
Vs中调试MVC源代码步骤
查看>>