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>() {
                 @Override
                 public 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 {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // 处理收到的消息
        System.out.println("Server received: " + msg);
        ctx.write(msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public 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>() {
                 @Override
                 public 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 {

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        ctx.writeAndFlush("Hello Server!");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("Client received: " + msg);
    }

    @Override
    public 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>() {
                 @Override
                 protected 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> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) {
        String msg = packet.content().toString(io.netty.util.CharsetUtil.UTF_8);
        System.out.println("Server received: " + msg);
    }

    @Override
    public 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>() {
                 @Override
                 protected 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> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) {
        String msg = packet.content().toString(io.netty.util.CharsetUtil.UTF_8);
        System.out.println("Client received: " + msg);
    }

    @Override
    public 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>() {
                 @Override
                 public 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 {

    @Override
    public 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 needed
                    break;
                case ALL_IDLE:
                    // Handle all idle if needed
                    break;
                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>() {
                 @Override
                 public 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 {

    @Override
    public 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 needed
                    break;
                case WRITER_IDLE:
                    System.out.println("Writer Idle detected, sending heartbeat...");
                    ctx.writeAndFlush("HEARTBEAT");
                    break;
                case ALL_IDLE:
                    // Handle all idle if needed
                    break;
                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>() {
                 @Override
                 public 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> {

    @Override
    protected 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> {

    @Override
    protected 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);
        }
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("Handler added: " + ctx.channel().id().asShortText());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("Handler removed: " + ctx.channel().id().asShortText());
    }

    @Override
    public 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>() {
    @Override
    protected 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>() {
                 @Override
                 public 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>() {
                 @Override
                 public 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> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("Server received: " + msg);
        ctx.writeAndFlush(msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

public class SecureClientHandler extends SimpleChannelInboundHandler<Object> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("Client received: " + msg);
    }

    @Override
    public 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      |
+-------------------+                    +-----------------------------+
  • 19
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
精通并发与netty视频教程(2018)视频教程 netty视频教程 Java视频教程目录: 1_学习的要义 2_Netty宏观理解 3_Netty课程大纲深度解读 4_项目环境搭建与Gradle配置 5_Netty执行流程分析与重要组件介绍 6_Netty回调与Channel执行流程分析 7_Netty的Socket编程详解 8_Netty多客户端连接与通信 9_Netty读写检测机制与长连接要素 10_Netty对WebSocket的支援 11_Netty实现服务器端与客户端的长连接通信 12_Google Protobuf详解 13_定义Protobuf文件及消息详解 14_Protobuf完整实例详解 15_Protobuf集成Netty与多协议消息传递 16_Protobuf多协议消息支援与工程最佳实践 17_Protobuf使用最佳实践与Apache Thrift介绍 18_Apache Thrift应用详解与实例剖析 19_Apache Thrift原理与架构解析 20_通过Apache Thrift实现Java与Python的RPC调用 21_gRPC深入详解 22_gRPC实践 23_Gradle Wrapper在Gradle项目构建中的最佳实践 24_gRPC整合Gradle与代码生成 25_gRPC通信示例与JVM回调钩子 26_gRPC服务器流式调用实现 27_gRPC双向流式数据通信详解 28_gRPC与Gradle流畅整合及问题解决的完整过程与思考 29_Gradle插件问题解决方案与Nodejs环境搭建 30_通过gRPC实现Java与Nodejs异构平台的RPC调用 31_gRPC在Nodejs领域中的静态代码生成及与Java之间的RPC通信 32_IO体系架构系统回顾与装饰模式的具体应用 33_Java NIO深入详解与体系分析 34_Buffer中各重要状态属性的含义与关系图解 35_Java NIO核心类源码解读与分析 36_文件通道用法详解 37_Buffer深入详解 38_NIO堆外内存与零拷贝深入讲解 39_NIO中Scattering与Gathering深度解析 40_Selector源码深入分析 41_NIO网络访问模式分析 42_NIO网络编程实例剖析 43_NIO网络编程深度解析 44_NIO网络客户端编写详解 45_深入探索Java字符集编解码 46_字符集编解码全方位解析 47_Netty服务器与客户端编码模式回顾及源码分析准备 48_Netty与NIO系统总结及NIO与Netty之间的关联关系分析 49_零拷贝深入剖析及用户空间与内核空间切换方式 50_零拷贝实例深度剖析 51_NIO零拷贝彻底分析与Gather操作在零拷贝中的作用详解 52_NioEventLoopGroup源码分析与线程数设定 53_Netty对Executor的实现机制源码分析 54_Netty服务端初始化过程与反射在其中的应用分析 55_Netty提供的Future与ChannelFuture优势分析与源码讲解 56_Netty服务器地址绑定底层源码分析 57_Reactor模式透彻理解及其在Netty中的应用 58_Reactor模式与Netty之间的关系详解 59_Acceptor与Dispatcher角色分析 60_Netty的自适应缓冲区分配策略与堆外内存创建方式 61_Reactor模式5大角色彻底分析 62_Reactor模式组件调用关系全景分析 63_Reactor模式与Netty组件对比及Acceptor组件的作用分析 64_Channel与ChannelPipeline关联关系及模式运用 65_ChannelPipeline创建时机与高级拦截过滤器模式的运用 66_Netty常量池实现及ChannelOption与Attribute作用分析 67_Channel与ChannelHandler及ChannelHandlerContext之间的关系分析 68_Netty核心四大组件关系与构建方式深度解读 69_Netty初始化流程总结及Channel与ChannelHandlerContext作用域分析 70_Channel注册流程深度解读 71_Channel选择器工厂与轮询算法及注册底层实现 72_Netty线程模型深度解读与架构设计原则 73_Netty底层架构系统总结与应用实践 74_Netty对于异步读写操作的架构思想与观察者模式的重要应用 75_适配器模式与模板方法模式在入站处理器中的应用 76_Netty项目开发过程中常见且重要事项分析 77_Java NIO Buffer总结回顾与难点拓展 78_Netty数据容器ByteBuf底层数据结构深度剖析 79_Netty

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值