Springboot整合Netty的启动方式(二)
本篇介绍下Springboot整合Netty的启动方式二,将Netty交由Spring进行管理。
启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;@SpringBootApplication
@EnableAsync
public class NettyApplication {public static void main(String[] args) {SpringApplication.run(NettyApplication.class, args);System.out.println("Springboot 应用启动成功......");}
}
服务器端
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;/*** * @Desc:Netty服务器端* @create: 2025-09-24 11:03**/@Order(1)
@Component
@Slf4j
public class NettyServer implements CommandLineRunner {@Value("${netty.server.port}")private int port;private NioEventLoopGroup bossGroup;private NioEventLoopGroup workerGroup;private Channel channel;/*** 启动Netty服务器*/public void start() {// 创建两个NioEventLoopGroup,一个用于监听客户端连接,一个用于处理客户端读写事件bossGroup = new NioEventLoopGroup();workerGroup = new NioEventLoopGroup();// 创建ServerBootstrap,用于完成Netty整个程序的组件初始化,启动,服务器连接ServerBootstrap serverBootstrap = new ServerBootstrap();// 设置两个EventLoopGroupserverBootstrap.group(bossGroup, workerGroup)// 设置ServerSocketChannel的处理器为NioServerSocketChannel.channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<>() {// 给每个客户端连接都创建一个ChannelInitializer,用于初始化Channel,添加ChannelHandler@Overrideprotected void initChannel(Channel channel) throws Exception {//取出channelPipelineChannelPipeline pipeline = channel.pipeline();// 添加ChannelHandlerpipeline.addLast(new MySimpleInBoundHandler());}});// 绑定端口,启动服务器ChannelFuture future = null;try {/* ** bind(port).sync()阻塞,是短暂阻塞,当Netty完成初始化启动完成后,就会释放,不再阻塞* @return void* @author shensh* @create 2025/9/24**/future = serverBootstrap.bind(port).sync();log.info("Netty服务器监听的端口:{}",port);if (future.isSuccess()) {log.info("Netty服务器启动成功......");}/* ** closeFuture().sync()方法将Netty服务器端的线程设置为wait状态,* Springboot主线程就不会执行下面finally块中的代码,直到Netty服务器端的线程结束才会执行***/future.channel().closeFuture().sync();} catch (InterruptedException e) {throw new RuntimeException(e);}finally {System.out.println(">>>>>>>>>>>>>>>>>><<<<<Netty服务器关闭<<<<<<<<<<");// 关闭EventLoopGroup,释放资源if (bossGroup != null) {bossGroup.shutdownGracefully();}if (workerGroup != null) {workerGroup.shutdownGracefully();}if (channel != null) {channel.closeFuture();}}}/*** 服务器关闭前释放资源*/@PreDestroypublic void destroy() {try {// 关闭EventLoopGroup,释放资源if (bossGroup != null) {bossGroup.shutdownGracefully().sync();}if (workerGroup != null) {workerGroup.shutdownGracefully().sync();}if (channel != null) {channel.closeFuture().sync();}} catch (InterruptedException e) {throw new RuntimeException(e);}}@Async@Overridepublic void run(String... args) throws Exception {start();}
}
要点:
- 实现implements CommandLineRunner接口;
- 添加@Component注解,将Netty服务器端交由Spring进行管理;
- 添加@Order注解,控制Spring启用时,组件的启动顺序,数据越小,启动的优先级越高。
客户端:
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;@Order(2)
@Component
@Slf4j
public class NettyClient implements CommandLineRunner {@Value("${netty.server.host}")private String host;@Value("${netty.server.port}")private int port;private NioEventLoopGroup eventLoopGroup;private Channel channel;/*** 启动Netty客户端*/public void start() {eventLoopGroup = new NioEventLoopGroup();// 创建客户端引导器Bootstrap,用于完成Netty整个程序的组件初始化,启动Bootstrap serverBootstrap = new Bootstrap();//serverBootstrap.group(eventLoopGroup)// 设置ServerSocketChannel的处理器为NioServerSocketChannel.channel(NioSocketChannel.class).handler(new ChannelInitializer<>() {// 给每个客户端连接都创建一个ChannelInitializer,用于初始化Channel,添加ChannelHandler@Overrideprotected void initChannel(Channel channel) throws Exception {//取出channelPipelineChannelPipeline pipeline = channel.pipeline();// 添加ChannelHandlerpipeline.addLast(new MySimpleInBoundHandler());}});ChannelFuture future = null;try {future = serverBootstrap.connect(host,port).sync();log.info("Netty客户端启动,监听的端口是:{}" ,port);if (future.isSuccess()) {log.info("Netty服务器启动成功......");}future.channel().closeFuture().sync();} catch (InterruptedException e) {throw new RuntimeException(e);}finally {// 关闭EventLoopGroup,释放资源if (eventLoopGroup != null) {eventLoopGroup.shutdownGracefully();}if (channel != null) {channel.closeFuture();}}}/*** 客户端关闭前释放资源*/@PreDestroypublic void destroy() {try {// 关闭EventLoopGroup,释放资源if (eventLoopGroup != null) {eventLoopGroup.shutdownGracefully().sync();}if (channel != null) {channel.closeFuture().sync();}} catch (InterruptedException e) {throw new RuntimeException(e);}}@Async@Overridepublic void run(String... args) throws Exception {start();}
}
这里把Netty 客户端组件加载顺序设为2:@Order(2),让它在服务器端加载以后再加载,启动服务,控制台打印:
Springboot 应用启动成功......
2025 [INFO] Netty服务器监听的端口:1919
2025 [INFO] Netty服务器启动成功......
2025 [INFO] SimpleChannelInboundHandler:有新的客户端连接到服务器: 53546b35
2025 [INFO] Netty客户端启动,监听的端口是:1919