游戏项目服务端(一)简单认识Netty&用Netty创建个服务器

文章目录

  • 前言
  • Netty Reactor工作模型
  • 从0创建个项目
    • pom.xml添加依赖和打包方式:
    • log4j.properties
    • 代码
      • 主启动类:
      • 简单消息处理器:打印

前言

我其实是主要做web后端的,学习这个游戏项目主要是为了学习其netty,gRpc,反射的运用和设计模式等
作为一个web后端,可以了解学习下前端js,vue/react等,但是这个游戏项目完全没必要学习前端东西了…
这个Demo项目我会一点一点敲下来,感受从原始冗余代码到优雅代码的演化过程.

Netty Reactor工作模型

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.
一般参观会有一个前台,客人来了之后前台负责给客人分配餐桌;
客人就坐后,服务员送过来一张菜单,客人开始画对勾点菜,点完了吆喝一声,服务员过来取菜单,然后交给后厨做饭,做好后后服务员再把饭端上来.

我们看古装剧,有时候前台接待人员和服务员是同一个人–店小二,这个就相当于传统阻塞模式,店小二接待一个客人,等客人点完餐后再去接待下一个客人.这样效率略低.
游戏项目服务端(一)简单认识Netty&用Netty创建个服务器_第1张图片

从0创建个项目

首先创建个普通maven项目

pom.xml添加依赖和打包方式:

    
        
            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.properties

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 就可以看到控制台打印收到消息了.

你可能感兴趣的:(游戏项目服务端(一)简单认识Netty&用Netty创建个服务器)