导航:从零开始手写mmo游戏从框架到爆炸(零)—— 导航-CSDN博客
从第六章到第八章,都是在为消息路由准备,现在我们已经距离目标不远了。我们已经在HandlerFactory中注册了所有的handler,此时只需要把投递过来的消息根据topic和tag找到对应的handler去执行就可以了,但是这里还有一个新的问题,就是投递过来的消息body都是string,但是我们很多时候传递过来的应该是对象的json字符串,所以我们要考虑反序列化的问题。同时为了尽可能的使用服务器的资源,处理的时候应该放到线程池中去处理,所以还需要增加一个线程池。
改造NetworkListener
package com.loveprogrammer.base.network.support;
import com.alibaba.fastjson2.JSON;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.loveprogrammer.base.bean.session.Session;
import com.loveprogrammer.base.factory.SpringContextHelper;
import com.loveprogrammer.base.network.command.HandlerFactory;
import com.loveprogrammer.base.network.command.anotation.TagListener;
import com.loveprogrammer.base.network.listener.INetworkEventListener;
import com.loveprogrammer.constants.CommonValue;
import com.loveprogrammer.pojo.StringMessage;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Component
public class NetworkListener implements INetworkEventListener {
protected static final Logger logger = LoggerFactory.getLogger(NetworkListener.class);
private final ThreadPoolExecutor executor = new ThreadPoolExecutor(
1,
10,
0L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(20480),
new ThreadFactoryBuilder()
.setNameFormat("worker-pool-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
@Autowired
private HandlerFactory handlerFactory;
@Override
public void onConnected(ChannelHandlerContext ctx) {
logger.info("建立连接");
SessionManager.getInstance().create(ctx.channel());
}
@Override
public void onDisconnected(ChannelHandlerContext ctx) {
logger.info("建立断开");
SessionManager.getInstance().close(ctx.channel());
}
@Override
public void onExceptionCaught(ChannelHandlerContext ctx, Throwable throwable) {
logger.warn("异常发生", throwable);
}
@Override
public void channelRead(ChannelHandlerContext ctx, StringMessage msg) {
// logger.info("数据内容:data=" + msg.getBody());
// String result = "我是服务器,我收到了你的信息:" + msg.getBody();
//
// Session session = SessionManager.getInstance().getSessionByChannel(ctx.channel());
// result += ",sessionId = " + session.getId();
//
// StringMessage message = StringMessage.create(1,1);
// message.setStatusCode(CommonValue.MSG_STATUS_CODE_SUCCESS);
// message.setBody(result);
//
// SessionManager.getInstance().sendMessage(ctx.channel(),message);
// 消息分发 对于消息的处理要增加多线程
int topicId = msg.getTopicId();
int tagId = msg.getTagId();
Class handler = handlerFactory.handlerMap.get(topicId);
if (handler == null) {
logger.warn("未获取到指定的消息监听对象,topicId {}", topicId);
return;
}
String bodyValue = msg.getBody();
executor.execute(() -> {
try {
Object bean = SpringContextHelper.getBean(handler);
// 找到tag 遍历methods
Method[] methods = handler.getMethods();
for (Method method : methods) {
TagListener mqListener = method.getAnnotation(TagListener.class);
if (tagId == mqListener.tag()) {
Class> aClass = mqListener.messageClass();
String name = aClass.getName();
// 先处理基本类型
if ("java.lang.String".equals(name)) {
method.invoke(bean, ctx, bodyValue);
} else if ("java.lang.Long".equals(name)) {
Long object = Long.parseLong(bodyValue);
method.invoke(bean, ctx, object);
} else if ("java.lang.Integer".equals(name)) {
Integer object = Integer.parseInt(bodyValue);
method.invoke(bean, ctx, object);
} else if ("java.lang.Short".equals(name)) {
Short object = Short.parseShort(bodyValue);
method.invoke(bean, ctx, object);
} else if ("java.lang.Byte".equals(name)) {
Byte object = Byte.parseByte(bodyValue);
method.invoke(bean, ctx, object);
} else if ("java.lang.Double".equals(name)) {
Double object = Double.parseDouble(bodyValue);
method.invoke(bean, ctx, object);
} else if ("java.lang.Float".equals(name)) {
Float object = Float.parseFloat(bodyValue);
method.invoke(bean, ctx, object);
}
// 转对象类型
else {
Object object = JSON.parseObject(bodyValue, aClass);
method.invoke(bean, ctx, object);
}
break;
}
}
} catch (Exception e) {
logger.error("发生异常", e);
String result = "我是服务器,我收到了你的信息:" + msg.getBody();
result += ",发生异常 = " + e.getMessage();
StringMessage message = StringMessage.create(0, 0);
message.setStatusCode(CommonValue.MSG_STATUS_CODE_SUCCESS);
message.setBody(result);
SessionManager.getInstance().sendMessage(ctx.channel(), message);
}
});
}
}
改造HelloHandler
package com.loveprogrammer.base.network.command.handler;
import com.loveprogrammer.base.bean.session.Session;
import com.loveprogrammer.base.network.command.BaseHandler;
import com.loveprogrammer.base.network.command.HandlerFactory;
import com.loveprogrammer.base.network.command.anotation.TagListener;
import com.loveprogrammer.base.network.command.anotation.TopicListener;
import com.loveprogrammer.base.network.support.SessionManager;
import com.loveprogrammer.command.server.ServerTag;
import com.loveprogrammer.command.server.ServerTopic;
import com.loveprogrammer.constants.CommonValue;
import com.loveprogrammer.pojo.StringMessage;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* @ClassName HelloHandler
* @Description 测试handler
* @Author admin
* @Date 2024/2/5 15:49
* @Version 1.0
*/
@Component
@TopicListener(topic = ServerTopic.TOPIC_HELLO)
public class HelloHandler extends BaseHandler {
public static final Logger log = LoggerFactory.getLogger(HelloHandler.class);
@TagListener(tag = ServerTag.TAG_HELLO_HI,messageClass = String.class)
public void sayHi(ChannelHandlerContext ctx, String msg){
log.info("数据内容:data=" + msg);
String result = "我是服务器,我收到了你的信息:" + msg;
Session session = SessionManager.getInstance().getSessionByChannel(ctx.channel());
result += ",sessionId = " + session.getId();
StringMessage message = StringMessage.create(0,0);
message.setStatusCode(CommonValue.MSG_STATUS_CODE_SUCCESS);
message.setBody(result);
SessionManager.getInstance().sendMessage(ctx.channel(),message);
}
}
全部源码详见:
gitee : eternity-online: 多人在线mmo游戏 - Gitee.com
分支:step-06