这里有些点只提供一些思路,具体实现可以自行去做。
启动方式
- 利用注解@PostConstruct,加载启动方法上即可。
@PostConstruct
public void start() {
//创建接收请求和处理请求的实例(默认线程数为 CPU 核心数乘以2也可自定义)
bossGroup = new NioEventLoopGroup(1);
workerGroup = new NioEventLoopGroup(2);
try {
//创建服务端启动辅助类(boostrap 用来为 Netty 程序的启动组装配置一些必须要组件,例如上面的创建的两个线程组)
ServerBootstrap socketBs = new ServerBootstrap();
//channel 方法用于指定服务器端监听套接字通道
//socket配置
socketBs.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(nettyServerChannelInitializer)
//ChannelOption.SO_BACKLOG对应的是tcp/ip协议listen函数中的backlog参数,函数listen(int socketfd,int backlog)用来初始化服务端可连接队列,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接,多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小
.option(ChannelOption.SO_BACKLOG, 1000);
//默认的心跳间隔是7200s即2小时。Netty默认关闭该功能。
//.childOption(ChannelOption.SO_KEEPALIVE, true);
future = socketBs.bind(8688).sync();
future.addListener(future1 -> log.info("Netty服务端启动成功"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
- 利用ApplicationRunner接口
@Component
public class NettyServerInit implements ApplicationRunner {
/**
* 注入NettyServer
*/
@Autowired
private NettyServer nettyServer;
@Autowired
private AlarmHk alarmHk;
@Override
public void run(ApplicationArguments args) throws InterruptedException {
nettyServer.start();
}
}
- 利用SpringContextUtils在启动类中启动
@Slf4j
//导入SpringContextUtils类,防止空指针
@Import(SpringContextUtils.class)
@SpringBootApplication
public class CarComputeApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(CarComputeApplication.class);
}
public static void main(String[] args) throws UnknownHostException, InterruptedException {
ConfigurableApplicationContext application = SpringApplication.run(CarComputeApplication.class, args);
Environment env = application.getEnvironment();
String ip = InetAddress.getLocalHost().getHostAddress();
String port = env.getProperty("server.port");
String path = env.getProperty("server.servlet.context-path");
log.info("\n----------------------------------------------------------\n\t" +
"Application is running! Access URLs:\n\t" +
"Local: \t\thttp://localhost:" + port + path + "/\n\t" +
"External: \thttp://" + ip + ":" + port + path + "/\n\t" +
"----------------------------------------------------------");
NettyServer nettyServer = SpringContextUtils.getBean(NettyServer.class);
nettyServer.start();
}
}
netty集群
可以利用redis或者zookeeper来实现
这里只讲一下redis的用法,每个服务端都缓存自己的客户端到本地,用通道id做唯一标识,利用redis发布订阅的能力,当服务端发现客户端A要找的客户端B不在本服务的时候,通知redis,redis根据客户端id找到服务端,然后通知服务端,服务端拿到客户端A的信息,发送给客户端B。
具体可参考这里:Netty集群部署实现跨服务端通信的落地方案
SSL
1、如果是websocket可以利用SSL保证单方面验证,将SSL证书配置到nginx即可实现wss连接。
参考链接:这里是原生的websocket,没有用netty集成,做参考即可
2、如果是soket,可以利用SSL保证双方验证,客户端和服务端都配置证书,利用SslContext实现。
核心代码:
@Component
public class NettyServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@Autowired
private NettyServerHandler nettyServerHandler;
@Autowired
private HeartBeatServerHandler heartBeatServerHandler;
@Override
protected void initChannel(SocketChannel ch) throws SSLException {
ChannelPipeline pipeline = ch.pipeline();
File certChainFile = new File("E:\\itstack\\GIT\\itstack.org\\itstack-demo-netty\\itstack-demo-netty-2-13\\src\\main\\java\\org\\itstack\\demo\\netty\\ssl\\client\\client.crt");
File keyFile = new File("E:\\itstack\\GIT\\itstack.org\\itstack-demo-netty\\itstack-demo-netty-2-13\\src\\main\\java\\org\\itstack\\demo\\netty\\ssl\\client\\pkcs8_client.key");
File rootFile = new File("E:\\itstack\\GIT\\itstack.org\\itstack-demo-netty\\itstack-demo-netty-2-13\\src\\main\\java\\org\\itstack\\demo\\netty\\ssl\\client\\ca.crt");
SslContext sslCtx = SslContextBuilder.forClient().keyManager(certChainFile, keyFile).trustManager(rootFile).build();
pipeline.addLast(sslCtx.newHandler(ch.alloc()));
String delimiter = "@_";
pipeline.addLast(new DelimiterBasedFrameDecoder(1024*8, Unpooled.wrappedBuffer(delimiter.getBytes())));
pipeline.addLast(new DelimiterBasedFrameEncoder(delimiter));
//设置心跳机制 0永不超时
pipeline.addLast(new IdleStateHandler(10,0,0, TimeUnit.SECONDS));
pipeline.addLast(new StringDecoder(Charset.forName("UTF-8")));
pipeline.addLast(new StringEncoder(Charset.forName("UTF-8")));
pipeline.addLast(heartBeatServerHandler);
pipeline.addLast(nettyServerHandler);
}
}
具体可参考:Netty基于SSL实现信息传输过程中双向加密验证
堆外内存溢出
简单介绍一下:主要引起的原因是我们使用了netty的引用计数对象,结果却没有释放造成的,并且这个隐藏的问题不会立刻显现出来,所以如果我们不对项目进行压测,很难会发现这个问题。
举个例子,ByteBuf就使用了引用计数对象,在handler中,如果不主动释放,时间长了就会产生这个问题,还有涉及到http相关的协议通信也会涉及到。
所以,在使用完消息之后,如果你不确定是否需要释放,**可以直接在finally里调用ReferenceCountUtil.release(msg)**方法,这个方法判断如果引用了就释放,如果没引用就跳过。
同时,你还可以进行堆外内存监控——具体描述可参考这里