Netty writeAndFlushのループでの同時問題
必要なものは次のとおりです.
ユーザーがログインした後、現在のユーザーがバインドしているデバイス番号(1つのみで番号が一意)に基づいてwebsocket接続を行い、デバイス番号(deviceNumber)をwebsocketサーバにアップロードします.サーバはデバイスのリアルタイムメッセージを受信して一定のパッケージ処理を行い,最後にwebsocketクライアントに戻る
最初のアプローチは、サーバ側に2つのキャッシュオブジェクトが作成され、それぞれ
DeviceSocketMapは、デバイス番号とそのデバイス番号をアップロードしたすべてのwebsocket接続(異なるユーザーが同じデバイス番号をバインドできるため)を保存するwebsocket接続に使用されます.
SOcketDeviceMapは、Websocket接続とデバイス番号とのマッピング関係を保持し、Websocketが切断されたときに接続(ChannelHandlerContext)によってデバイス番号を検索し、さらにdeviceSocketMapでそのデバイス番号に基づいて対応するすべてのWebsocket接続を検索し、削除することで、deviceSocketMapに常にオンラインのデバイス番号と対応するすべての接続のマッピングが保存される
このやり方は論理的には問題ないが、問題はサービス側がset中のChannelHandlerContextを遍歴し、それぞれ複数のクライアントにメッセージを送信する際に同時発生する問題であり、メッセージを受信できるのは1つのクライアントだけであることが多い.
解決策
変更後のコードは次のとおりです.
問題解決!
ユーザーがログインした後、現在のユーザーがバインドしているデバイス番号(1つのみで番号が一意)に基づいてwebsocket接続を行い、デバイス番号(deviceNumber)をwebsocketサーバにアップロードします.サーバはデバイスのリアルタイムメッセージを受信して一定のパッケージ処理を行い,最後にwebsocketクライアントに戻る
最初のアプローチは、サーバ側に2つのキャッシュオブジェクトが作成され、それぞれ
Map<String,Set<ChannelHandlerContext>>deviceSocketMap=newConcurrentHashMap<>();
Map<Channel,String>socketDeviceMap=newConcurrentHashMap<>();
DeviceSocketMapは、デバイス番号とそのデバイス番号をアップロードしたすべてのwebsocket接続(異なるユーザーが同じデバイス番号をバインドできるため)を保存するwebsocket接続に使用されます.
SOcketDeviceMapは、Websocket接続とデバイス番号とのマッピング関係を保持し、Websocketが切断されたときに接続(ChannelHandlerContext)によってデバイス番号を検索し、さらにdeviceSocketMapでそのデバイス番号に基づいて対応するすべてのWebsocket接続を検索し、削除することで、deviceSocketMapに常にオンラインのデバイス番号と対応するすべての接続のマッピングが保存される
このやり方は論理的には問題ないが、問題はサービス側がset中のChannelHandlerContextを遍歴し、それぞれ複数のクライアントにメッセージを送信する際に同時発生する問題であり、メッセージを受信できるのは1つのクライアントだけであることが多い.
// websocket
if (deviceSocketMap.containsKey(deviceNumber)) {
Set<ChannelHandlerContext> set = deviceSocketMap.get(deviceNumber);
for (ChannelHandlerContext context : set) {
context.channel().writeAndFlush(tws);
}
}
解決策
ChannelGroup
は、Nettyがサーバによって確立されたウェブチャネルchannelを管理するために提供するものであり、本質的には高度にカプセル化されたsetセットであり、サーバがメッセージをブロードキャストする際に、そのwriteAndFlush
を直接介してセット内のすべてのチャネルにメッセージを送信することができる.変更後のコードは次のとおりです.
/**
* socket
* String:
* ChannelGroup:socket
*/
public static Map<String, ChannelGroup> deviceSocketMap = new ConcurrentHashMap<>();
/**
* socket
* Channel:socket
* String:
*/
public static Map<Channel, String> socketDeviceMap = new ConcurrentHashMap<>();
// websocket
String msg = JSON.toJSONString(webSocketBean);
TextWebSocketFrame tws = new TextWebSocketFrame(msg);
// websocket
if (deviceSocketMap.containsKey(deviceNumber)) {
deviceSocketMap.get(deviceNumber).writeAndFlush(tws);
}
問題解決!