Spring Boot 整合 Netty-Boot

本指南将演示如何在 Spring Boot 应用程序中集成 Netty-Boot,使您能够在 Spring Boot 的上下文中使用 Netty 来处理 HTTP 和 WebSocket 请求。

前提条件

  • JDK 版本:8 或更高
  • 构建工具:Maven 或 Gradle
  • 知识准备:具备 Spring Boot 和 Netty 的基本知识

步骤一:配置 Netty 服务器端口

首先,在 app.properties 配置文件中指定 Netty 服务器运行的端口。

netty.server.port=8869

这将设置 Netty 服务器监听在 8869 端口上。

步骤二:创建 HTTP 处理器

创建一个名为 OkHandler 的处理器类,用于管理 HTTP 请求。

package com.netty.handler;

import com.litongjava.model.body.RespBodyVo;
import com.litongjava.netty.boot.utils.HttpRequestUtils;
import com.litongjava.netty.boot.utils.HttpResponseUtils;

import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;

public class OkHandler {

    // 处理文本响应
    public FullHttpResponse txt(ChannelHandlerContext ctx, FullHttpRequest httpRequest) throws Exception {
        String responseContent = "Hello, this is the HTTP response!";
        return HttpResponseUtils.txt(responseContent);
    }

    // 处理 JSON 响应
    public FullHttpResponse json(ChannelHandlerContext ctx, FullHttpRequest httpRequest) throws Exception {
        RespBodyVo ok = RespBodyVo.ok();
        return HttpResponseUtils.json(ok);
    }

    // 回显请求内容
    public FullHttpResponse echo(ChannelHandlerContext ctx, FullHttpRequest httpRequest) {
        String fullHttpRequestString = HttpRequestUtils.getFullHttpRequestAsString(httpRequest);
        return HttpResponseUtils.txt(fullHttpRequestString);
    }
}

说明:

  • txt 方法返回纯文本的 HTTP 响应。
  • json 方法返回一个带有成功消息的 JSON 响应。
  • echo 方法将接收到的 HTTP 请求内容原样返回给客户端。

步骤三:创建 WebSocket 处理器

创建一个名为 WsHandler 的处理器类,用于管理 WebSocket 连接。

package com.netty.handler;

import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.*;

public class WsHandler {

    public void handle(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
        if (frame instanceof TextWebSocketFrame) {
            String requestText = ((TextWebSocketFrame) frame).text();
            // 返回接收到的消息
            ctx.channel().writeAndFlush(new TextWebSocketFrame("Server received: " + requestText));

        } else if (frame instanceof CloseWebSocketFrame) {
            ctx.channel().close();

        } else if (frame instanceof PingWebSocketFrame) {
            ctx.channel().writeAndFlush(new PongWebSocketFrame(frame.content().retain()));

        } else {
            throw new UnsupportedOperationException("不支持的帧类型: " + frame.getClass().getName());
        }
    }
}

说明:

  • 处理不同类型的 WebSocket 帧:
    • TextWebSocketFrame:将文本消息回传给客户端。
    • CloseWebSocketFrame:关闭连接。
    • PingWebSocketFrame:响应一个 PongWebSocketFrame

步骤四:配置 Netty-Boot 服务器

创建一个名为 NettyBootConfig 的配置类,设置 Netty 服务器和处理器。

package com.netty;

import com.litongjava.context.BootConfiguration;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.netty.boot.http.HttpRequestRouter;
import com.litongjava.netty.boot.listener.ChannelConnectionListener;
import com.litongjava.netty.boot.server.NettyBootServer;
import com.litongjava.netty.boot.websocket.WebsocketRouter;
import com.netty.handler.ChatHandler;
import com.netty.handler.OkHandler;
import com.netty.handler.WsHandler;
import com.netty.listener.MyChannelConnectionListener;

public class NettyBootConfig implements BootConfiguration {

    @Override
    public void config() {
        NettyBootServer nettyBootServer = NettyBootServer.me();

        // 创建并设置自定义的 ChannelConnectionListener
        ChannelConnectionListener myChannelConnectionListener = new MyChannelConnectionListener();
        nettyBootServer.setChannelConnectionListener(myChannelConnectionListener);

        // 设置 HTTP 路由
        HttpRequestRouter httpRouter = nettyBootServer.getHttpRequestRouter();
        OkHandler okHandler = Aop.get(OkHandler.class);
        httpRouter.add("/txt", okHandler::txt);
        httpRouter.add("/json", okHandler::json);
        httpRouter.add("/echo", okHandler::echo);

        // 设置 WebSocket 路由
        WebsocketRouter websocketRouter = nettyBootServer.getWebsocketRouter();
        WsHandler wsHandler = Aop.get(WsHandler.class);
        websocketRouter.add("/ws", wsHandler::handle);

        // 额外的处理器(如果需要)
        ChatHandler chatHandler = new ChatHandler();
    }
}

说明:

  • 实现 BootConfiguration 接口来配置 Netty 服务器。
  • 设置自定义的 ChannelConnectionListener 来管理连接事件。
  • 使用 HttpRequestRouter 配置 HTTP 路由,将 URL 映射到处理方法。
  • 使用 WebsocketRouter 配置 WebSocket 路由。

步骤五:将 Netty-Boot 集成到 Spring Boot 中

创建一个名为 NettyServerConfig 的 Spring Boot 配置类,将 Netty 服务器运行在 Spring 上下文中。

package com.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.litongjava.context.Context;
import com.litongjava.netty.boot.NettyApplication;
import com.netty.NettyBootConfig;

@Configuration
public class NettyServerConfig {

    @Bean(destroyMethod = "close")
    public Context nettyContext() {
        NettyBootConfig bootConfig = new NettyBootConfig();
        return NettyApplication.run(NettyServerConfig.class, bootConfig, new String[] {});
    }
}

说明:

  • 使用 @Configuration 注解,表示这是一个 Spring 配置类。
  • 定义了一个名为 nettyContext 的 Bean,使用 NettyApplication.run 方法来初始化和运行 Netty 服务器。
  • 设置 destroyMethod = "close",确保在应用程序上下文关闭时,Netty 服务器能够正确关闭。

步骤六:启动应用程序并进行测试

所有配置就绪后,以正常方式启动您的 Spring Boot 应用程序。Netty 服务器将被初始化,并开始监听指定的端口(8869)。

测试 HTTP 端点

  • 纯文本响应

    发送 GET 请求到 http://localhost:8869/txt

    curl http://localhost:8869/txt
    

    响应:

    Hello, this is the HTTP response!
    
  • JSON 响应

    发送 GET 请求到 http://localhost:8869/json

    curl http://localhost:8869/json
    

    响应:

    { "status": "ok", "message": "Success" }
    
  • Echo 端点

    发送 POST 请求到 http://localhost:8869/echo,并附带数据:

    curl -X POST http://localhost:8869/echo -d "测试数据"
    

    响应:

    [包含 "测试数据" 的完整 HTTP 请求详情]
    

测试 WebSocket 端点

使用 WebSocket 客户端连接到 ws://localhost:8869/ws

  • 发送文本消息,例如 "Hello WebSocket"。

  • 服务器将响应:

    Server received: Hello WebSocket
    

结论

您已经成功地将 Netty-Boot 集成到了 Spring Boot 应用程序中,能够处理 HTTP 和 WebSocket 请求。通过这种设置,您可以在熟悉的 Spring Boot 环境中利用 Netty 的高性能网络能力。

附加说明

  • 依赖注入:处理器通过 Aop.get(Class) 方法获取,意味着使用了 AOP 框架进行依赖注入。请确保您的应用程序中已正确配置 AOP(面向切面编程)。
  • ChannelConnectionListener:自定义实现的 ChannelConnectionListener 允许您管理与通道连接相关的事件,例如通道何时激活或不活跃。
  • 线程安全:如果处理器维护状态,请确保它们是线程安全的,因为 Netty 可能会并发处理多个请求。

参考资料