@Data
public class NettyConfig implements Serializable {
private static final long serialVersionUID = 6620106410814342128L;
private static final int processors = NettyRuntime.availableProcessors();
public Boolean enable = false;// 是否开启
public Boolean isTcp = true;// 是否TCP
public String name = "Netty";// 服务名称
public Integer port = 11111;// 端口号
public Integer workerCore = processors + 2;;
public Integer businessCore = Math.max(1, processors >> 1);
public Integer readerIdleTime = 240;
public Integer writerIdleTime = 0;
public Integer allIdleTime = 0;
//标识位[2] + 消息头[21] + 消息体[1023 * 2(转义预留)] + 校验码[1] + 标识位[2]
public Integer maxFrameLength = 2 + 21 + 1023 * 2 + 1 + 2;
// netty Session管理
public SessionManager sessionManager;
// netty Channel处理列表
public ChannelHandler hander;;
}
@Slf4j
public abstract class NettyServer {
protected boolean isRunning;
protected NettyConfig config;
protected EventLoopGroup bossGroup;
protected EventLoopGroup workerGroup;
protected EventExecutorGroup businessGroup;
protected NettyServer(NettyConfig config) {
this.config = config;
}
protected AbstractBootstrap<?, ?> initializeTCP() {
bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory(config.name, Thread.MAX_PRIORITY));
workerGroup = new NioEventLoopGroup(config.workerCore, new DefaultThreadFactory(config.name, Thread.MAX_PRIORITY));
if (config.businessCore > 0) {
businessGroup = new DefaultEventExecutorGroup(config.businessCore);
}
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(NioChannelOption.TCP_NODELAY, true)
.childHandler(config.hander);
//内存泄漏检测 开发推荐PARANOID 线上SIMPLE
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.SIMPLE);
return serverBootstrap;
}
protected AbstractBootstrap<?, ?> initializeUDP() {
bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory(config.name, Thread.MAX_PRIORITY));
if (config.businessCore > 0)
businessGroup = new DefaultEventExecutorGroup(config.businessCore);
return new Bootstrap()
.group(bossGroup).channel(NioDatagramChannel.class)
.option(NioChannelOption.SO_REUSEADDR, true)
.option(NioChannelOption.SO_RCVBUF, 1024 * 1024 * 50)
.handler(config.hander);
}
public synchronized boolean start() {
if (!config.enable) {
return false;
}
if (isRunning) {
log.info("======{}已经启动,port:{}======", config.name, config.port);
return isRunning;
}
AbstractBootstrap<?, ?> bootstrap = config.isTcp ? initializeTCP() : initializeUDP();
ChannelFuture future = bootstrap.bind(config.port).awaitUninterruptibly();
future.channel().closeFuture().addListener(f -> {
if (isRunning)
stop();
});
if (future.cause() != null)
log.error("===启动失败===", future.cause());
if (isRunning = future.isSuccess())
log.info("\n\n\t\t\t\t\t\t\t\t======{}启动成功,port:{}======\n", config.name, config.port);
return isRunning;
}
public synchronized void stop() {
if (!config.enable) {
return;
}
isRunning = false;
try {
Future<?> future = this.workerGroup.shutdownGracefully().await();
if (!future.isSuccess()) {
log.error("workerGroup 无法正常停止:{}", future.cause());
}
future = this.bossGroup.shutdownGracefully().await();
if (!future.isSuccess()) {
log.error("bossGroup 无法正常停止:{}", future.cause());
}
future = this.businessGroup.shutdownGracefully().await();
if (!future.isSuccess()) {
log.error("businessGroup 无法正常停止:{}", future.cause());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("\n\n\t\t\t\t\t\t\t\t======{} 已经停止,port:{}======\n", config.name, config.port);
}
}
@Configuration
public class Netty808Server extends NettyServer {
@Resource
private Netty808Config config;
@Resource
private TCPListenHandler tcpListenHandler;
@Resource
private JT808DecoderHandler jt808DecoderHandler;
@Resource
private JT808ByteToEntityHandler jt808ByteToEntityHandler;
@Resource
private JT808EntityToByteHandler jt808EntityToByteHandler;
@Resource
private JT808EncoderHander jt808EncoderHander;
@Resource
private JT808DispatcherHandler jt808DispatcherHandler;
protected Netty808Server(Netty808Config config) {
super(config);
}
@Bean(name = "JT808", initMethod = "start", destroyMethod = "stop")
public NettyServer run() {
config.setHander(new ChannelInitializer<NioSocketChannel>() {
@Override
public void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new IdleStateHandler(config.readerIdleTime, config.writerIdleTime, config.allIdleTime));// 心跳
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(config.maxFrameLength, new Delimiter(new byte[] { 0x7e }, true)));
ch.pipeline().addLast(tcpListenHandler);// 监听器
//入栈
ch.pipeline().addLast(jt808DecoderHandler);// 转义 BCC
ch.pipeline().addLast(jt808ByteToEntityHandler);// 报文-实体
//出栈
ch.pipeline().addLast(jt808EncoderHander);//BCC 反转义
ch.pipeline().addLast(jt808EntityToByteHandler);//实体-报文
// 业务分发是入站最后一个处理器, 出站第一个处理器, 位置要放在最后
ch.pipeline().addLast(businessGroup, jt808DispatcherHandler);//业务分发 这里使用业务线程组
}
});
return new Netty808Server(config);
}
}