Java高手的30k之路|面试宝典|精通Netty(二)

实践应用

构建服务器和客户端

  • 掌握如何使用Netty构建TCP/UDP服务器和客户端,包括初始化、监听和连接。
  • 如何处理心跳机制,保持长连接的活跃。

TCP 服务器

1. 引入依赖

pom.xml文件中添加Netty依赖:

<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.69.Final</version>
</dependency>
2. 创建服务器引导类
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;public class TcpServer {private final int port;public TcpServer(int port) {this.port = port;}public void start() throws InterruptedException {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new TcpServerHandler());}}).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);ChannelFuture f = b.bind(port).sync();f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}public static void main(String[] args) throws InterruptedException {int port = 8080; // 端口号new TcpServer(port).start();}
}
3. 创建服务器处理器
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;public class TcpServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {// 处理收到的消息System.out.println("Server received: " + msg);ctx.write(msg);}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) {ctx.flush();}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}
}

TCP 客户端

1. 创建客户端引导类
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;public class TcpClient {private final String host;private final int port;public TcpClient(String host, int port) {this.host = host;this.port = port;}public void start() throws InterruptedException {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new TcpClientHandler());}});ChannelFuture f = b.connect(host, port).sync();f.channel().closeFuture().sync();} finally {group.shutdownGracefully();}}public static void main(String[] args) throws InterruptedException {String host = "localhost";int port = 8080; // 端口号new TcpClient(host, port).start();}
}
2. 创建客户端处理器
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;public class TcpClientHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelActive(ChannelHandlerContext ctx) {ctx.writeAndFlush("Hello Server!");}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {System.out.println("Client received: " + msg);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}
}

UDP 服务器

1. 创建服务器引导类
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;public class UdpServer {private final int port;public UdpServer(int port) {this.port = port;}public void start() throws InterruptedException {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioDatagramChannel.class).option(ChannelOption.SO_BROADCAST, true).handler(new ChannelInitializer<DatagramChannel>() {@Overrideprotected void initChannel(DatagramChannel ch) throws Exception {ch.pipeline().addLast(new UdpServerHandler());}});b.bind(port).sync().channel().closeFuture().await();} finally {group.shutdownGracefully();}}public static void main(String[] args) throws InterruptedException {int port = 8080; // 端口号new UdpServer(port).start();}
}
2. 创建服务器处理器
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;public class UdpServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) {String msg = packet.content().toString(io.netty.util.CharsetUtil.UTF_8);System.out.println("Server received: " + msg);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}
}

UDP 客户端

1. 创建客户端引导类
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.CharsetUtil;import java.net.InetSocketAddress;public class UdpClient {private final String host;private final int port;public UdpClient(String host, int port) {this.host = host;this.port = port;}public void start() throws InterruptedException {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioDatagramChannel.class).option(ChannelOption.SO_BROADCAST, true).handler(new ChannelInitializer<DatagramChannel>() {@Overrideprotected void initChannel(DatagramChannel ch) throws Exception {ch.pipeline().addLast(new UdpClientHandler());}});b.connect(new InetSocketAddress(host, port)).sync().channel().writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("Hello Server!", CharsetUtil.UTF_8),new InetSocketAddress(host, port))).sync();} finally {group.shutdownGracefully();}}public static void main(String[] args) throws InterruptedException {String host = "localhost";int port = 8080; // 端口号new UdpClient(host, port).start();}
}
2. 创建客户端处理器
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;public class UdpClientHandler extends SimpleChannelInboundHandler<DatagramPacket> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) {String msg = packet.content().toString(io.netty.util.CharsetUtil.UTF_8);System.out.println("Client received: " + msg);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}
}

在Netty中处理心跳机制以保持长连接的活跃性,可以使用Netty提供的 IdleStateHandlerIdleStateHandler 是一个 ChannelInboundHandler,用于检测连接的空闲状态。你可以在服务器和客户端中添加 IdleStateHandler 以检测和处理空闲连接。

服务器端

1. 修改服务器引导类

在服务器引导类中添加 IdleStateHandler 和自定义的心跳处理器 HeartbeatHandler

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;import java.util.concurrent.TimeUnit;public class TcpServer {private final int port;public TcpServer(int port) {this.port = port;}public void start() throws InterruptedException {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new IdleStateHandler(60, 0, 0, TimeUnit.SECONDS));ch.pipeline().addLast(new HeartbeatHandler());ch.pipeline().addLast(new TcpServerHandler());}}).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);ChannelFuture f = b.bind(port).sync();f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}public static void main(String[] args) throws InterruptedException {int port = 8080; // 端口号new TcpServer(port).start();}
}
2. 创建心跳处理器
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateEvent;public class HeartbeatHandler extends ChannelInboundHandlerAdapter {@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {if (evt instanceof IdleStateEvent) {IdleStateEvent event = (IdleStateEvent) evt;switch (event.state()) {case READER_IDLE:System.out.println("Reader Idle detected, closing connection...");ctx.close();break;case WRITER_IDLE:// Handle writer idle if neededbreak;case ALL_IDLE:// Handle all idle if neededbreak;default:break;}} else {super.userEventTriggered(ctx, evt);}}
}

客户端

1. 修改客户端引导类

在客户端引导类中添加 IdleStateHandler 和自定义的心跳处理器 HeartbeatHandler

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;import java.util.concurrent.TimeUnit;public class TcpClient {private final String host;private final int port;public TcpClient(String host, int port) {this.host = host;this.port = port;}public void start() throws InterruptedException {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new IdleStateHandler(0, 30, 0, TimeUnit.SECONDS));ch.pipeline().addLast(new HeartbeatHandler());ch.pipeline().addLast(new TcpClientHandler());}});ChannelFuture f = b.connect(host, port).sync();f.channel().closeFuture().sync();} finally {group.shutdownGracefully();}}public static void main(String[] args) throws InterruptedException {String host = "localhost";int port = 8080; // 端口号new TcpClient(host, port).start();}
}
2. 创建客户端心跳处理器
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateEvent;public class HeartbeatHandler extends ChannelInboundHandlerAdapter {@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {if (evt instanceof IdleStateEvent) {IdleStateEvent event = (IdleStateEvent) evt;switch (event.state()) {case READER_IDLE:// Handle reader idle if neededbreak;case WRITER_IDLE:System.out.println("Writer Idle detected, sending heartbeat...");ctx.writeAndFlush("HEARTBEAT");break;case ALL_IDLE:// Handle all idle if neededbreak;default:break;}} else {super.userEventTriggered(ctx, evt);}}
}

WebSocket支持

  • 理解如何使用Netty实现WebSocket协议,支持实时通信应用。

使用Netty实现WebSocket协议来支持实时通信应用是一个很好的选择。WebSocket协议提供了双向通信的能力,适合于实时通信应用,例如聊天应用、在线游戏等。以下是如何使用Netty实现WebSocket服务器的步骤。

引入依赖

pom.xml 文件中添加 Netty 的依赖:

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

WebSocket 服务器

1. 创建服务器引导类

首先,创建一个服务器引导类来初始化 Netty 服务器。

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;public class WebSocketServer {private final int port;public WebSocketServer(int port) {this.port = port;}public void start() throws InterruptedException {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new WebSocketServerInitializer());}}).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);ChannelFuture f = b.bind(port).sync();f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}public static void main(String[] args) throws InterruptedException {int port = 8080; // 端口号new WebSocketServer(port).start();}
}
2. 创建服务器初始化类

服务器初始化类用于设置 WebSocket 服务器的管道和处理器。

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;public class WebSocketServerInitializer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new HttpServerCodec());ch.pipeline().addLast(new HttpObjectAggregator(65536));ch.pipeline().addLast(new ChunkedWriteHandler());ch.pipeline().addLast(new WebSocketServerProtocolHandler("/websocket"));ch.pipeline().addLast(new WebSocketFrameHandler());}
}
3. 创建WebSocket协议处理器
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;public class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {if (frame instanceof TextWebSocketFrame) {String request = ((TextWebSocketFrame) frame).text();System.out.println("Received: " + request);ctx.channel().writeAndFlush(new TextWebSocketFrame(request.toUpperCase()));} else {String message = "Unsupported frame type: " + frame.getClass().getName();throw new UnsupportedOperationException(message);}}@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {System.out.println("Handler added: " + ctx.channel().id().asShortText());}@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {System.out.println("Handler removed: " + ctx.channel().id().asShortText());}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}
}

客户端

在客户端中,可以使用浏览器中的 WebSocket API 或者用其他语言的 WebSocket 客户端库来连接和测试 WebSocket 服务器。

浏览器端 JavaScript 客户端示例
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>WebSocket Client</title>
</head>
<body><h1>WebSocket Client</h1><button onclick="connect()">Connect</button><button onclick="sendMessage()">Send Message</button><div id="messages"></div><script>var ws;function connect() {ws = new WebSocket("ws://localhost:8080/websocket");ws.onopen = function() {console.log("Connected");document.getElementById("messages").innerHTML += "<p>Connected</p>";};ws.onmessage = function(event) {console.log("Message received: " + event.data);document.getElementById("messages").innerHTML += "<p>Message received: " + event.data + "</p>";};ws.onclose = function() {console.log("Disconnected");document.getElementById("messages").innerHTML += "<p>Disconnected</p>";};}function sendMessage() {var message = "Hello WebSocket Server!";console.log("Sending: " + message);ws.send(message);}</script>
</body>
</html>

性能调优

  • 学习如何监控和调优Netty应用的性能,包括线程配置、内存管理策略等。

监控和调优Netty应用的性能是确保其高效运行的重要环节。Netty本身提供了许多配置选项和工具,结合这些以及外部的监控工具,可以实现对Netty应用的全面性能监控和优化。以下是一些关键策略和建议:

1. 线程配置

Netty使用的线程池是由EventLoopGroup管理的。可以通过合理配置EventLoopGroup来优化Netty的性能。

BossGroup和WorkerGroup配置
  • BossGroup: 负责处理连接请求。
  • WorkerGroup: 负责处理I/O操作。

一般情况下,BossGroup的线程数可以设置为可用处理器的数量,而WorkerGroup的线程数可以设置为2倍的可用处理器数量或更多,这取决于具体的应用需求和负载情况。

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors() * 2);

2. 内存管理策略

Netty的内存管理策略包括池化内存和非池化内存。使用池化内存可以减少频繁的GC和内存分配操作,提高性能。

使用池化内存

默认情况下,Netty已经启用了池化内存。你可以通过配置PooledByteBufAllocator来进一步优化。

ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT).childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);

3. TCP参数调优

Netty提供了多种TCP参数选项,可以通过ChannelOption进行配置。这些选项可以帮助优化网络通信的性能。

bootstrap.option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true).childOption(ChannelOption.TCP_NODELAY, true).childOption(ChannelOption.SO_RCVBUF, 32 * 1024).childOption(ChannelOption.SO_SNDBUF, 32 * 1024);

4. 优化Pipeline

在Netty中,数据流通过ChannelPipeline进行处理。合理配置和优化Pipeline中的Handler可以显著提高性能。

合理配置Handler

尽量减少Handler的数量,每个Handler只处理特定的任务。避免在Handler中进行耗时操作,可以将这些操作交给单独的线程池处理。

ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
ch.pipeline().addLast(new HttpServerCodec());
ch.pipeline().addLast(new ChunkedWriteHandler());
ch.pipeline().addLast(new WebSocketServerProtocolHandler("/websocket"));
ch.pipeline().addLast(new WebSocketFrameHandler());

5. 使用异步处理

Netty本身是异步的,但在处理耗时任务时,最好使用单独的线程池来执行这些任务,避免阻塞EventLoop线程。

ch.pipeline().addLast(new SimpleChannelInboundHandler<Object>() {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {ctx.executor().execute(() -> {// 处理耗时任务});}
});

6. 监控和日志

监控和日志对于诊断和优化Netty应用的性能至关重要。

使用Netty内置的日志功能

Netty提供了内置的日志功能,可以通过配置LoggingHandler来记录Netty的运行状态。

ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
使用外部监控工具

结合外部监控工具如Prometheus、Grafana、Java Flight Recorder(JFR)等,可以对Netty应用进行更全面的监控。

Prometheus + Grafana

使用Micrometer来将Netty的指标暴露给Prometheus,然后使用Grafana进行可视化。

// 添加依赖
<dependency><groupId>io.micrometer</groupId><artifactId>micrometer-registry-prometheus</artifactId><version>1.7.0</version>
</dependency>// 配置PrometheusMeterRegistry
PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);// 将Netty的指标注册到Micrometer
InstrumentedEventLoopGroup bossGroup = new InstrumentedEventLoopGroup(new NioEventLoopGroup(), registry);
InstrumentedEventLoopGroup workerGroup = new InstrumentedEventLoopGroup(new NioEventLoopGroup(), registry);ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup);

7. GC调优

频繁的GC会影响Netty应用的性能,尤其是Full GC。可以通过调整JVM参数来优化GC性能。

-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintHeapAtGC
-XX:+PrintTenuringDistribution
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintPromotionFailure
-XX:+PrintSafepointStatistics
-XX:PrintSafepointStatisticsCount=1
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=10
-XX:GCLogFileSize=20M

安全和加密

  • 了解如何在Netty中集成SSL/TLS以实现安全的网络通信。

在Netty中集成SSL/TLS以实现安全的网络通信,可以通过使用Netty的 SslContextSslHandler 来实现。以下是如何在Netty中添加SSL/TLS支持的步骤:

1. 准备SSL证书和密钥

首先,需要一个SSL证书和相应的密钥。如果没有,可以使用OpenSSL生成一个自签名证书:

openssl req -newkey rsa:2048 -nodes -keyout netty.key -x509 -days 365 -out netty.crt

这将生成一个密钥文件 (netty.key) 和一个证书文件 (netty.crt)。

2. 创建 SslContext

在Netty中使用 SslContext 来配置SSL/TLS。可以从证书和密钥文件创建一个 SslContext

import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;import java.io.File;public class SslContextProvider {public static SslContext createSslContext() throws Exception {File certChainFile = new File("path/to/netty.crt");File keyFile = new File("path/to/netty.key");return SslContextBuilder.forServer(certChainFile, keyFile).build();}
}

3. 在服务器引导类中配置SSL/TLS

在服务器引导类中,使用 SslContext 创建一个 SslHandler 并将其添加到 ChannelPipeline

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.SslContext;public class SecureNettyServer {private final int port;public SecureNettyServer(int port) {this.port = port;}public void start() throws Exception {SslContext sslContext = SslContextProvider.createSslContext();EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addFirst(sslContext.newHandler(ch.alloc()));ch.pipeline().addLast(new SecureServerHandler());}}).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);ChannelFuture f = b.bind(port).sync();f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}public static void main(String[] args) throws Exception {int port = 8443; // 安全端口new SecureNettyServer(port).start();}
}

4. 客户端配置SSL/TLS

客户端也需要配置SSL/TLS。与服务器类似,需要使用 SslContext 并将 SslHandler 添加到 ChannelPipeline

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslContext;public class SecureNettyClient {private final String host;private final int port;public SecureNettyClient(String host, int port) {this.host = host;this.port = port;}public void start() throws Exception {SslContext sslContext = SslContextProvider.createSslContext();EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addFirst(sslContext.newHandler(ch.alloc()));ch.pipeline().addLast(new SecureClientHandler());}});ChannelFuture f = b.connect(host, port).sync();f.channel().closeFuture().sync();} finally {group.shutdownGracefully();}}public static void main(String[] args) throws Exception {String host = "localhost";int port = 8443; // 安全端口new SecureNettyClient(host, port).start();}
}

5. 处理器实现

实现基本的处理器来处理消息。在这个例子中,处理器只是简单地打印消息。

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;public class SecureServerHandler extends SimpleChannelInboundHandler<Object> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {System.out.println("Server received: " + msg);ctx.writeAndFlush(msg);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}
}public class SecureClientHandler extends SimpleChannelInboundHandler<Object> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {System.out.println("Client received: " + msg);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}
}

Netty核心组件与SpringWeb核心组件映射

Netty和Spring Web是两个流行的Java网络框架,它们用于处理不同类型的网络通信。尽管它们的设计目标和应用场景不同,但它们都有类似的核心组件,可以找到一定的映射关系。

  1. Bootstrap (Netty) vs DispatcherServlet (Spring Web)

    • Bootstrap 是Netty中的引导类,用于配置和启动客户端或服务器。
    • DispatcherServlet 是Spring Web中的前端控制器,用于接收并分派HTTP请求。
  2. Channel (Netty) vs HttpServletRequest/HttpServletResponse (Spring Web)

    • Channel 代表一个开放的连接,比如一个TCP/IP socket或UDP socket,用于读写数据。
    • HttpServletRequestHttpServletResponse 代表HTTP请求和响应,用于处理HTTP协议的输入输出。
  3. ChannelPipeline (Netty) vs Handler Chain (Spring Web)

    • ChannelPipeline 是Netty中的处理链,每个Channel都有一个Pipeline,包含一系列的ChannelHandler来处理I/O事件。
    • Handler Chain 在Spring Web中通常由一系列的过滤器(Filter)和拦截器(Interceptor)组成,用于处理HTTP请求和响应。
  4. ChannelHandler (Netty) vs Controller/Interceptor/Filter (Spring Web)

    • ChannelHandler 是Netty中的处理器接口,用于处理入站和出站事件。
      • ChannelInboundHandler 处理入站事件,如读取数据。
      • ChannelOutboundHandler 处理出站事件,如写入数据。
    • Controller 在Spring Web中处理特定的HTTP请求,并生成响应。
    • Interceptor 在Spring Web中用于请求处理的前后拦截。
    • Filter 在Spring Web中用于预处理和后处理请求/响应。
  5. EventLoop (Netty) vs Task Executor (Spring Web)

    • EventLoop 在Netty中负责处理Channel的所有I/O操作。它包含一个选择器(Selector)和一个任务队列,绑定到一个线程。
    • Task Executor 在Spring Web中用于处理异步任务和并发操作,通常由线程池实现。
  6. ByteBuf (Netty) vs HttpMessage (Spring Web)

    • ByteBuf 是Netty中用于高效管理和操作字节数据的容器。
    • HttpMessage 在Spring Web中是HTTP请求和响应的表示,包含请求体和响应体的数据。
  7. Transport Layer (Netty) vs Server Abstraction (Spring Web)

    • Transport Layer 是Netty对底层网络传输的抽象,包括NIO、Epoll、KQueue等。
    • Server Abstraction 在Spring Web中包括Servlet容器(如Tomcat、Jetty)和嵌入式服务器(如Spring Boot的嵌入式Tomcat)。

核心组件映射关系图示

+-------------------+                    +-----------------------------+
|       Netty       |                    |         Spring Web          |
+-------------------+                    +-----------------------------+
|   Bootstrap       | <------------->    |     DispatcherServlet       |
+-------------------+                    +-----------------------------+
|     Channel       | <------------->    | HttpServletRequest/Response |
+-------------------+                    +-----------------------------+
| ChannelPipeline   | <------------->    |       Handler Chain         |
+-------------------+                    +-----------------------------+
|  ChannelHandler   | <------------->    | Controller/Interceptor/Filter|
+-------------------+                    +-----------------------------+
|    EventLoop      | <------------->    |       Task Executor         |
+-------------------+                    +-----------------------------+
|     ByteBuf       | <------------->    |        HttpMessage          |
+-------------------+                    +-----------------------------+
| Transport Layer   | <------------->    |      Server Abstraction      |
+-------------------+                    +-----------------------------+

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/33508.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

KANO模型概述

理解KANO模型 要理解和应用KANO模型&#xff0c;我们需要更详细地探讨其每个组成部分&#xff0c;以及如何在实际设计过程中有效切入和实施这个模型。 KANO模型的详细分类 1. 基本需求&#xff08;Must-be Requirements&#xff09; 描述: 这些需求是产品的基础功能&#x…

Bootstrap和Bagging算法以及衍生算法

1. Bootstrap算法 实际上就是一种针对小样本的无放回式的抽样方法&#xff0c;通过方差的估计可以构造置信区间。 其核心思想和基本步骤如下&#xff1a;   &#xff08;1&#xff09; 采用重抽样技术从原始样本中抽取一定数量&#xff08;自己给定&#xff09;的样本&#…

Android集成高德地图SDK(2)

1.解压下载的压缩包&#xff0c;找到AMap_Android_SDK_All\AMap3DMap_DemoDocs\AMap_Android_API_3DMap_Demo\AMap3DDemo\app\libs&#xff0c;复制libs里的所有文件&#xff0c;将其粘贴到Android工程的libs目录下&#xff0c;如图所示。 2.打开app下的build.gradle&#xff0…

记录react实现选择框一二级联动出现的问题

需求&#xff1a;用户在选择第一个选择框的选项后&#xff0c;第二个选择框的选项会根据第一个选择框的选择动态更新。如图所示 出现的问题 一级分类选择之后二级分类没有数据&#xff0c;第二次重新选择一级分类的时候&#xff0c;二级分类就会有值。 第一次点击截图&#x…

024.两两交换链表中的节点,用递归和 while 循环

题意 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 难度 中等 示例 输入&#xff1a;head [1,2,3,4] 输出&#xff1a;[…

什么是车载测试?车载测试怎么学!

1、车载测试是什么&#xff1f; 车载测试分很多种&#xff0c;有软件测试、硬件测试、性能测试、功能测试等等&#xff0c;每一项测试的内容都不一样&#xff0c;我们所说的车载测试主要指的是汽车软件的功能测试&#xff0c;也就是针对汽车实现的某一个功能&#xff0c;而进行…

vue3 vxe-grid列中绑定vxe-switch实现数据更新

1、先上一张图&#xff1a; <template #valueSlot"{ row }"><vxe-switch :value"getV(row.svalue)" change"changeSwitch(row)" /></template>function getV(value){return value 1;};function changeSwitch(row) {console.l…

Trilium windows上修改笔记目录,创建多个笔记空间方法

一开始使用trilium会非常的不舒服&#xff0c;不像是obsidian可以创建多个笔记空间&#xff0c;指定多个笔记目录。这里摸索到了解决方案 修改目录的方法一 ——修改系统环境变量 打开控制面板-系统-高级系统设置 新增如上条目 修改目录的方法二——直接写bat脚本运行 新建位…

深入理解Kubernetes:CNI源码解析

在容器化环境中&#xff0c;有效管理网络是至关重要的。容器网络接口&#xff08;CNI&#xff09;是一个标准&#xff0c;定义了容器应如何配置网络。本文将深入探讨 CNI 的基础知识&#xff0c;并带你了解 CNI 与 CRI 的关系。 什么是 CNI&#xff1f; CNI&#xff08;容器网…

Redis哨兵集群搭建

一、安装Redis 1.安装依赖 yum install -y gcc tcl2.将Redis压缩包解压到对应的目录 tar -zxvf redis-2.8.0.tar.gz mv redis-2.8.0 /usr/local3.编译 cd /usr/local/redis-2.8.0 make && make install4.配置redis.conf # 任意ip都可以访问 bind 0.0.0.0 # 关闭保…

网安大咖说·镜鉴(下)| 把握安全新脉搏:企业CSO的领航之道

网安大咖说镜鉴栏目通过对网安大咖说嘉宾访谈内容的深度提炼&#xff0c;撷取群英论道之精髓&#xff0c;汇聚众智谋策之高远&#xff0c;为从业者提供宝贵的经验和启迪。集思广益、博采众长&#xff0c;意在以镜为鉴&#xff0c;观网安之百态&#xff0c;立防范之策略&#xf…

Linux 内核缓存一致性相关机制和接口梳理

From 程序员秘书 缓存一致性是一个非常关键的问题&#xff0c;特别是在多核处理器和直接内存访问&#xff08;DMA&#xff09;场景下。原因如下&#xff1a; 多核CPU与cache的缓存一致性问题&#xff1a;每个CPU core都有自己的cache&#xff0c;由于cache的写回机制&#xf…

AI助力科研:自动化科学构思生成系统初探

科学研究作为推动创新和知识进步的关键活动&#xff0c;在解决复杂问题和提升人类生活水平方面发挥着至关重要的作用。然而&#xff0c;科学研究的固有复杂性、缓慢的进展速度以及对专业专家的需求&#xff0c;限制了其生产力的提升。为了增强科研效率&#xff0c;本文提出了一…

力扣78 子集

给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的 子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[],[1],[2],[1,2],[3],…

Linux使用pthread多线程时cmake新增语句

https://cmake.org/cmake/help/latest/module/FindThreads.html 在子文件夹中的CMakeList.txt&#xff1a; aux_source_directory(. DIR_SUB_SRCS) add_library(echatlib SHARED ${DIR_SUB_SRCS}) # STATIC SHARED# 方法一 find_package(Threads REQUIRED) target_link_librar…

鸿蒙开发Ability Kit(程序框架服务):【FA模型绑定Stage模型ServiceExtensionAbility】

FA模型绑定Stage模型ServiceExtensionAbility 本文介绍FA模型的三种应用组件如何绑定Stage模型的ServiceExtensionAbility组件。 PageAbility关联访问ServiceExtensionAbility PageAbility关联访问ServiceExtensionAbility和PageAbility关联访问ServiceAbility的方式完全相同…

重学java 84.Java枚举

那些你暗自努力的时光&#xff0c;终究会照亮你前行的路 —— 24.6.24 一、枚举介绍&#xff08;开发中表示状态&#xff09; 1.概述&#xff1a; 五大引用数据类型&#xff1a;类型、数组、接口、注解、枚举 2.定义&#xff1a; public enum 枚举类名{} 所有的枚举类父类…

集成Stata分布式事务

一.事务协调器TC 1&#xff0e;安装Seata-server 下载&#xff1a;https://github.com/seata/seata/tags 启动&#xff1a;seata-server.bat -p 8091 -h 127.0.0.1 -m file 关闭其他服务&#xff0c;先启动它 2&#xff0e;配置文件修改 二.主业务端TM 1&#xff0e;导入依赖…

贝锐花生壳内网穿透

贝锐花生壳内网穿透使用步骤 首先你得去官网购买一个域名配置一下内网穿透映射官网下载一个客户端修改代码配置 首先你得去官网购买一个域名 配置一下内网穿透映射 官网下载一个客户端 注意&#xff0c;一定要下载客户端&#xff0c;不然用不了 当然&#xff0c;本地我已经提前…

SpringBoot-配置文件中使用随机值和使用变量

1、配置文件中使用随机值 2.在配置文件使用引用变量 如果没定义还可以设置默认值