关于java:Netty-通道怎么区分对应的用户

作者:南城之南

出处:https://www.cnblogs.com/liang…

前言

思考一个性能业务,在web程序中向指定的某个用户进行实时通信

在Web使用的Socket通信性能中(如在线客服),为保障点对点通信.而这个看似简略的依据用户寻到起channel通道理论会碰到不少问题

  1. web程序中的Http协定是无状态的
  2. 个别我的项目中socket服务和web我的项目是独立部署的
  3. socket连贯存在重连的状况,而Channel对象每次都不一样
  4. Channel是面向网卡绑定的,无奈序列化

解决方案

通过治理一个线程平安的用户标识(如用户主键)和对应channelmap链表

private final ConcurrentHashMap<String, Channel> channelMap = new ConcurrentHashMap<>();

那么问题来了,

  • netty模块中怎么失去这个用户标识?
  • 又如何保障netty socket模块能够平安的辨认某个通道属于某个用户?(这个能够像下面一样的形式解决)
  • netty socket模块接管到一条音讯又任何证实这条通道是可信的?

netty的实现中是没有认证也没有HttpSession这个货色的,也就是说.在netty程序线程中是无奈失去web我的项目登录的用户状况的.

出于这点,参考web我的项目集群的session共享计划.能够在Redis等缓存中保留登录信息.

  1. web我的项目中登录之后在redis中在这个以用户id为名的key中保留一个token,
  2. 在客户端socket通道建设之后立马发送蕴含一个用户标识ASKsocket服务端,
  3. 服务端依据ASK计算一个tokenredis比对.一旦比对胜利,则绑定以后channel和用户之间的关系;
  4. 之后server每接管到一条音讯就检测以后通道有没有绑定用户信息

这个key一次性的.这点十分重要,试想一下.在你前台我的项目可能因为cookie过期或者后盾曾经主动将该用户下线,而你的用户标识ASK裸露.那么就可能被歹意连贯发送音讯;

另外对于tokenASK之类的验证传输如果仅仅是为了辨认和绑定用户与channel的关系,这点也是能够疏忽的,只有redis中保留该用户的登录状态即可,通道建设的第一次通信就传输以后浏览器的登录用户标识,再去redis中比对即可,然而redis中的这个key还是一次性的好,防止一个用户建设多条socket通道

正确的绑定通道Channel和用户之间的关系

如果咱们仅仅有一个ConcurrentHashMap<String, Channel>,是无奈疾速优雅的判断以后channel是属于哪个用户的;我看到他人绝大多数的实现是在创立一个channelId用户标识的Map来治理

//key为channel的长id,channel.id().asLongText();value为用户id
private final ConcurrentHashMap<String, String> channelAndUserMap = new ConcurrentHashMap<>();

其实这不是最正当的做法,正确的做法是利用Channel对象提供的AttributeMap来保留该通道的附带信息,很多人不晓得Channel对象提供了一个绑定自定义数据的Map

应用示例:

//用户id=>channel示例
private final ConcurrentHashMap<String, Channel> channelMap = new ConcurrentHashMap<>();


/**
 * 判断一个通道是否有用户在应用
 * 可做信息转发时判断该通道是否非法
 * @param channel
 * @return
 */
public boolean hasUser(Channel channel) {
    AttributeKey<String> key = AttributeKey.valueOf("user");
    return (channel.hasAttr(key) || channel.attr(key).get() != null);//netty移除了这个map的remove办法,这里的判断审慎一点
}

/**
 * 上线一个用户
 *
 * @param channel
 * @param userId
 */
public void online(Channel channel, String userId) {
    //先判断用户是否在web零碎中登录?
    //这部分代码集体实现,参考下面redis中的验证

        this.channelMap.put(userId, channel);
        AttributeKey<String> key = AttributeKey.valueOf("user");
        channel.attr(key).set(userId);


}

/**
 * 依据用户id获取该用户的通道
 *
 * @param userId
 * @return
 */
public Channel getChannelByUserId(String userId) {
    return this.channelMap.get(userId);
}

/**
 * 判断一个用户是否在线
 *
 * @param userId
 * @return
 */
public Boolean online(String userId) {
    return this.channelMap.containsKey(userId) && this.channelMap.get(userId) != null;
}

留神!!

很多人拿channel.id().asShortText()来记录标识channel,这是谬误的!!!!!短id不保障全局惟一!!

近期热文举荐:

1.1,000+ 道 Java面试题及答案整顿(2021最新版)

2.终于靠开源我的项目弄到 IntelliJ IDEA 激活码了,真香!

3.阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!

4.Spring Cloud 2020.0.0 正式公布,全新颠覆性版本!

5.《Java开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞+转发哦!

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理