我正在开发基于netty的多人游戏服务器。 传输到客户端的大多数消息都是特定于单个客户端的 但有时我需要向所有客户广播相同的消息。
我不确定是否有充分理由在我自己的Map上使用ChannelGroup。 所以现在我有:
public class GameSession {
/* a map of all the players part of this game session */
private ConcurrentHashMap<String, PlayerHandler> players = new ConcurrentHashMap<String, PlayerHandler>();
private final ChannelGroup playersChannels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
PlayerHandler在哪里:
public class PlayerHandler extends SimpleChannelInboundHandler<IncomingMessage>
并包含有状态成员变量。
当一个新玩家加入时(经过一些逻辑)后,他被添加到GameSession中:
public void addPlayer(PlayerHandler p) {
if (p.getGameSessionID().equals(this.gameId)) {
players.put(p.getPlayerID(), p); //add this player to our game session
playersChannels.add(p.getChannelFromCtx()); //get channel and add to ChannelGroup
}
}
假设玩家要求离开(或关闭连接)
,我想播放一条消息public void notifyPlayerLeft(String exPlayer) {
//Broadcast message with the id of the player that left
for (Map.Entry<String, PlayerHandler> entry : players.entrySet()) {
PlayerHandler player = entry.getValue();
player.sendPlayerLeft(exPlayer);
}
}
其中sendPlayerLeft()是一个简单的方法,它可以执行以下操作:
ctx.writeAndFlush(outgoingMsg)
如果我使用 ChannelGroup ,我可以这样做:
playersChannels.writeAndFlush(outgoingMsg, matcher)
但我不确定为什么这是一个更好的主意。 Netty表示它是异步发生的 但由于PlayerHandler没有自己的线程,因此不会迭代对象 就像我在NotifyPlayerLeft()中做的那样也会异步?请注意,整个场景将由一个频道/用户/线程触发。
我希望我的问题足够清楚。 谢谢!
答案 0 :(得分:2)
如您所见here,Netty默认写入群组再次是一个简单的迭代。从这个意义上讲,您的方法在性能和并发性方面应该没有区别。主要区别在于它将所有期货收集到地图中,这种分组可以帮助您跟踪引发的任何问题。但是,为什么要使用你的代码已经实现了呢?
答案 1 :(得分:1)
使用信息更新它。因此,虽然ChannelGroup
对于前面所述的简单迭代很有用,但您也可以从API文档中获益。
Netty.io 4.1 ChannelGroup API Documents
自动从集合中删除已关闭的频道,这样您就不必担心添加频道的生命周期。频道可以属于多个频道组。
您可以通过扩展ChannelGroup.class
来创建新的DefaultChannelGroup.class
,并添加一些方法以在频道注销期间添加其他行为,以通知某些代码未注册频道,然后通知其他球员,或做清理。
答案 2 :(得分:0)
请小心Netty的ConcurrentHashMap;它实际上是越野车;它不是线程安全的。您可以使用一个线程上的并发流同时放置另一个线程来验证此错误。它并非总是失败,因为它与非原子内部数组突变有关(快速并发的读/写操作应该因netty而失败,而不是jdk。)