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

9次阅读

共计 2491 个字符,预计需要花费 7 分钟才能阅读完成。

作者:南城之南

出处: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 开发手册(嵩山版)》最新公布,速速下载!

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

正文完
 0