Skip to content

本篇将根据Netty服务端模板代码编写启动类,并实现标准启动和支持websocket协议的服务端;服务端的端口、线程数等参数将用过yml文件动态传递,yml文件将通过main方法的args数组在启动时传递。

xml
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.92.Final</version>
</dependency>

Netty启动服务模板代码

java
public class XimServer {
    private final static Logger logger = LoggerFactory.getLogger(XimServer.class);

    private int port;

    public XimServer(int port) {
        this.port = port;
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap server = new ServerBootstrap();
        server.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 10240) // 服务端可连接队列大小
                .option(ChannelOption.SO_REUSEADDR, true) // 允许重复使用本地地址和端口
                .option(ChannelOption.TCP_NODELAY, true) // 禁用Nagle算法,简单理解成是否允许批量发送数据,开启会影响消息及时性
                .option(ChannelOption.SO_KEEPALIVE, true) //保活
                .childHandler(new ChannelInitializer<SocketChannel>() {

                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {

                    }
                });
        server.bind(port);
    }
}

使用WebSocket协议

Netty自带对WebSocket的支持,添加Netty提供Handler的即可。

java
public class XimWebSocketServer {
    private final static Logger logger = LoggerFactory.getLogger(XimWebSocketServer.class);

    private int port;

    public XimWebSocketServer(int port) {
        this.port = port;
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap server = new ServerBootstrap();
        server.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 10240) // 服务端可连接队列大小
                .option(ChannelOption.SO_REUSEADDR, true) // 允许重复使用本地地址和端口
                .option(ChannelOption.TCP_NODELAY, true) // 禁用Nagle算法,简单理解成是否允许批量发送数据,开启会影响消息及时性
                .option(ChannelOption.SO_KEEPALIVE, true) //保活
                .childHandler(new ChannelInitializer<SocketChannel>() {

                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        // websocket 基于http协议,所以要有http编解码器
                        pipeline.addLast("http-codec", new HttpServerCodec());
                        // 对写大数据流的支持
                        pipeline.addLast("http-chunked", new ChunkedWriteHandler());
                        // 几乎在netty中的编程,都会使用到此hanler
                        pipeline.addLast("aggregator", new HttpObjectAggregator(65535));
                        /*
                          websocket 服务器处理的协议,用于指定给客户端连接访问的路由 : /ws
                          本handler会帮你处理一些繁重的复杂的事
                          会帮你处理握手动作: handshaking(close, ping, pong) ping + pong = 心跳
                          对于websocket来讲,都是以frames进行传输的,不同的数据类型对应的frames也不同
                         */
                        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
                    }
                });
        server.bind(port);
    }
}

Main方法

java
public class Starter {
    public static void main(String[] args) {
        new XimServer(9001);
        new XimWebSocketServer(19001);
    }
}

使用yaml动态配置

在resources目录下新建config.yml文件

yaml
xim:
  tcpPort: 9000
  webSocketPort: 19000
  bossThreadSize: 1
  workThreadSize: 4

创建对象BootstrapConfig,这里面的数据将通过yml文件在启动时传入。

java
@Data
public class BootstrapConfig {
    
    private TcpConfig xim;

    @Data
    public static class TcpConfig {
        private Integer tcpPort;
        private Integer webSocketPort;
        private Integer bossThreadSize;
        private Integer workThreadSize;
    }
}

先配置依赖文件:

xml
<!-- yaml解析 -->
<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.33</version>
</dependency>

在Main文件读取

java
public class Starter {
    public static void main(String[] args) throws FileNotFoundException {
        Yaml yaml = new Yaml();
        FileInputStream inputStream = new FileInputStream("D:\\code\\javaCode\\Year2023\\month02\\im-system\\im-tcp\\src\\main\\resources\\config.yml");
        BootstrapConfig bootstrapConfig = yaml.loadAs(inputStream, BootstrapConfig.class);
        System.out.println(bootstrapConfig);
    }
}

使用yaml动态配置后启动程序改造

java
public class XimServer {
    private final static Logger logger = LoggerFactory.getLogger(XimServer.class);
    private final EventLoopGroup bossGroup;
    private final EventLoopGroup workerGroup;
    private final ServerBootstrap server;
    private final BootstrapConfig.TcpConfig config;

    public XimServer(BootstrapConfig.TcpConfig config) {
        this.config = config;
        bossGroup = new NioEventLoopGroup(config.getBossThreadSize());
        workerGroup = new NioEventLoopGroup(config.getWorkThreadSize());

        server = new ServerBootstrap();
        server.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 10240) // 服务端可连接队列大小
                .option(ChannelOption.SO_REUSEADDR, true) // 允许重复使用本地地址和端口
                .option(ChannelOption.TCP_NODELAY, true) // 禁用Nagle算法,简单理解成是否允许批量发送数据,开启会影响消息及时性
                .option(ChannelOption.SO_KEEPALIVE, true) //保活
                .childHandler(new ChannelInitializer<SocketChannel>() {

                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {

                    }
                });
    }

    public void start() {
        this.server.bind(this.config.getTcpPort());
    }
}

websocket也类似

java
public class XimWebSocketServer {
    private final static Logger logger = LoggerFactory.getLogger(XimWebSocketServer.class);

    private final EventLoopGroup bossGroup;
    private final EventLoopGroup workerGroup;
    private final ServerBootstrap server;
    private final BootstrapConfig.TcpConfig config;

    public XimWebSocketServer(BootstrapConfig.TcpConfig config) {
        this.config = config;
        bossGroup = new NioEventLoopGroup(config.getBossThreadSize());
        workerGroup = new NioEventLoopGroup(config.getWorkThreadSize());

        server = new ServerBootstrap();
        server.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 10240) // 服务端可连接队列大小
                .option(ChannelOption.SO_REUSEADDR, true) // 允许重复使用本地地址和端口
                .option(ChannelOption.TCP_NODELAY, true) // 禁用Nagle算法,简单理解成是否允许批量发送数据,开启会影响消息及时性
                .option(ChannelOption.SO_KEEPALIVE, true) //保活
                .childHandler(new ChannelInitializer<SocketChannel>() {

                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        // websocket 基于http协议,所以要有http编解码器
                        pipeline.addLast("http-codec", new HttpServerCodec());
                        // 对写大数据流的支持
                        pipeline.addLast("http-chunked", new ChunkedWriteHandler());
                        // 几乎在netty中的编程,都会使用到此hanler
                        pipeline.addLast("aggregator", new HttpObjectAggregator(65535));
                        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
                    }
                });
    }

    public void start() {
        this.server.bind(this.config.getTcpPort());
    }
}

启动类如下,配置文件路径通过启动参数传递。

java
public class Starter {

    public static void main(String[] args) throws FileNotFoundException {
        if (args.length > 0) {
            start(args[0]);
        }
    }

    private static void start(String path) {
        try {
            Yaml yaml = new Yaml();
            FileInputStream inputStream = new FileInputStream(path);
            BootstrapConfig bootstrapConfig = yaml.loadAs(inputStream, BootstrapConfig.class);
            System.out.println(bootstrapConfig);
            new XimServer(bootstrapConfig.getXim()).start();
            new XimWebSocketServer(bootstrapConfig.getXim()).start();
        } catch (Exception e) {
            e.printStackTrace();
            // 如果有错误,直接退出整个程序
            System.exit(500);
        }
    }
}

IDEA中如何配置启动参数?

image-20230820165502860