Netty創建通信服務時使用Nio異步通信, 配置代碼(bootstrap.channel(NioSocketChannel.class);),要怎樣實現這樣一個同步發送消息並接收消息功能,雖然這樣做覺得很沒必要。
public class ChannelUtil {
public static Object writeMsgSync(Object msg, Channel channel, AttributeKey<Object> attrKey, int timeout) throws Exception{
if(channel.isActive()){
synchronized(channel){
Attribute<Object> attr = channel.attr(attrKey);
synchronized(attr){
attr.set(msg);
System.out.println("發消息:"+msg);
channel.writeAndFlush(msg);
attr.wait(timeout * 1000);
Object ret = attr.get();
System.out.println("收消息:"+ret);
attr.set(null);
if(ret == msg){
return null;
}
return ret;
}
}
}
return null;
}
}
下面的方法需要加入到收消息的Handler(如SimpleChannelInboundHandler)中
@Override
public void onRead(ChannelHandlerContext ctx, CmdMsg msg) throws Exception {
System.out.println("收到消息:"+msg);
Channel channel = ctx.channel();
Attribute<Object> attr = channel.attr(ClientGlobal.CHANNEL_SYNC_KEY);
synchronized(attr){
Object lastMsg = attr.get();
if (lastMsg != null && lastMsg instanceof CmdMsg) {
CmdMsg lastCmdMsg = (CmdMsg) lastMsg;
if (msg.getCmdCode() == lastCmdMsg.getCmdCode()) {
// 當前消息就是正在等待返回的響應消息,同步消息
attr.set(msg);
attr.notify();
System.out.println("此消息是同步響應消息:" + msg);
return;
}
else{
if(msg.getCmdCode() == CmdConst.CMD_RET){
RetMsg retMsg = (RetMsg)PackUtil.unpackCmdMsg(msg, RetMsg.class);
if(retMsg.getOpCode() == lastCmdMsg.getCmdCode()){
attr.set(msg);
attr.notify();
System.out.println("此消息是同步響應消息:" + msg);
return;
}
}
}
}
}
System.out.println("消息加入到隊列中:"+msg);
MsgTask msgTask = new MsgTask();
msgTask.setCtx(ctx);
msgTask.setMsg(msg);
BaseProcessor<?> processor = msgModule.getMsgProcessor(msg.getCmdCode());
if(processor == null){
//沒有註冊對應的消息加工器
System.out.println("沒有註冊對應的消息加工器:msg code is "+msg.getCmdCode());
return;
}
msgTask.setProcessor(processor);
msgGueueMgr.pushMsgTask(msgTask);
}
實現原理:通過Channel和Channel中的Attribute,利用wait、Notify,實現線程間通信
1: 發送消息時,將channel中的屬性設值,然後提交到Netty內部消息隊列,等待一個超時時間,如果這段時間內,有人將其喚醒,則取出channel的屬性值,這個就是返回的消息;
2:接收消息時,判斷channel中的屬性是否有設值,若有,判斷當前消息是否是此屬性中消息的響應消息,若是,則喚醒channel屬性。