我其实是主要做web后端的,学习这个游戏项目主要是为了学习其netty,gRpc,反射的运用和设计模式等
作为一个web后端,可以了解学习下前端js,vue/react等,但是这个游戏项目完全没必要学习前端东西了…
这个Demo项目我会一点一点敲下来,感受从原始冗余代码到优雅代码的演化过程.
W3C教程
NioEventLoopGroup 是用来处理I/O操作的多线程事件循环器,Netty 提供了许多不同的 EventLoopGroup 的实现用来处理不同的传输。在这个例子中我们实现了一个服务端的应用,因此会有2个 NioEventLoopGroup 会被使用。第一个经常被叫做‘boss’,用来接收进来的连接。第二个经常被叫做‘worker’,用来处理已经被接收的连接,一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上。如何知道多少个线程已经被使用,如何映射到已经创建的 Channel上都需要依赖于 EventLoopGroup 的实现,并且可以通过构造函数来配置他们的关系。
BossGroup负责和客户端建立链接(建立链接比较消耗资源),将客户端的链接注册到WorkerGroup,它不断的去把客户端链接注册给WG
WorkerGroup从BG收到注册的Channel后,负责读写工作,读写完消息之后会把消息放入pipeline中进行各种处理,它不断的做这件事.
NIO,Netty这些东西,什么reactor等名词,听起来很高端–高端意味着很难,其实不然.原理其实很简单,生活中也很常见,只不过Netty的设计者很牛叉,用了各种设计模式
这个模型和现代的传统餐馆的模式很像,客人是client,前台接待人员是Boss Group,服务员就是Worker Group.
一般参观会有一个前台,客人来了之后前台负责给客人分配餐桌;
客人就坐后,服务员送过来一张菜单,客人开始画对勾点菜,点完了吆喝一声,服务员过来取菜单,然后交给后厨做饭,做好后后服务员再把饭端上来.
我们看古装剧,有时候前台接待人员和服务员是同一个人–店小二,这个就相当于传统阻塞模式,店小二接待一个客人,等客人点完餐后再去接待下一个客人.这样效率略低.
首先创建个普通maven项目
io.netty
netty-all
4.1.43.Final
org.slf4j
slf4j-log4j12
1.7.29
src/main/resources
**/*.properties
false
org.apache.maven.plugins
maven-compiler-plugin
3.5.1
1.8
1.8
UTF-8
log4j.rootLogger=info,stdout,all
# ---- stdout ----
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.encoding=UTF-8
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss,SSS}] [%p] %C{1}.%M --> %m%n
# ---- all ----
log4j.appender.all=org.apache.log4j.DailyRollingFileAppender
log4j.appender.all.encoding=UTF-8
log4j.appender.all.append=true
log4j.appender.all.DatePattern='.'yyyy-MM-dd
log4j.appender.all.File=logs/all.log
log4j.appender.all.layout=org.apache.log4j.PatternLayout
log4j.appender.all.layout.ConversionPattern=[%d{HH:mm:ss,SSS}] [%p] %C{1}.%M --> %m%n
public class ServerMain {
private static final Logger log = LoggerFactory.getLogger(ServerMain.class);
private static final int SERVER_PORT = 12345;
public static void main(String[] args) {
// 加载log4j配置文件
PropertyConfigurator.configure(ServerMain.class.getClassLoader().getResourceAsStream("log4j.properties"));
// 餐馆的前台接待小姐姐
EventLoopGroup bossGroup = new NioEventLoopGroup();
// 餐馆的服务员
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup);
// 服务器信道的处理方式
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(
// Http服务编码解码器
new HttpServerCodec()
// 限制内容长度
, new HttpObjectAggregator(65535)
// WebSocket协议处理器,在这里处理握手/ping/pong等消息
, new WebSocketServerProtocolHandler("/websocket")
// 自定义的消息处理器
, new SimpleMsgHandler()
);
}
});
try {
// 绑定端口,这里硬编码了,实际项目中会根据配置文件或者main函数的参数指定端口
ChannelFuture channelFuture = serverBootstrap.bind(SERVER_PORT).sync();
if (channelFuture.isSuccess()) {
log.info("服务器启动成功~");
}
// 等待服务器信道关闭,即不让程序立即退出,而是一直提供服务
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
} finally {
// 关闭服务器
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
SimpleMsgHandler
public class SimpleMsgHandler extends SimpleChannelInboundHandler<Object> {
private static final Logger log = LoggerFactory.getLogger(SimpleMsgHandler.class);
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object msg) throws Exception {
if (channelHandlerContext == null || msg == null) {
return;
}
log.info("收到消息:{}", msg);
}
}
这时候启动ServerMain,然后用浏览器访问http://localhost:12345/websocket 就可以看到控制台打印收到消息了.