Netty实现HTTP服务器案例

wen java案例 11

本文目录导读:

Netty实现HTTP服务器案例

  1. Maven依赖配置
  2. 服务器主类
  3. 服务器初始化器
  4. HTTP请求处理器
  5. 完整示例:支持RESTful API的服务器
  6. 测试方法
  7. 高级特性扩展
  8. 测试工具类

我来为您展示一个完整的Netty实现HTTP服务器的案例,包含代码和详细说明。

Maven依赖配置

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

服务器主类

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class HttpServer {
    private static final int PORT = 8080;
    public static void main(String[] args) throws InterruptedException {
        // 创建boss线程组:接收连接
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        // 创建worker线程组:处理I/O
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ServerInitializer());
            // 绑定端口并启动服务
            ChannelFuture channelFuture = bootstrap.bind(PORT).sync();
            System.out.println("HTTP Server started on port " + PORT);
            // 关闭服务器
            channelFuture.channel().closeFuture().sync();
        } finally {
            // 优雅关闭
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

服务器初始化器

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpServerExpectContinueHandler;
public class ServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        // HTTP编解码器
        pipeline.addLast("httpCodec", new HttpServerCodec());
        // HTTP聚合器:将HTTP消息聚合成完整的请求
        pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
        // HTTP应答处理
        pipeline.addLast("httpServerHandler", new HttpServerHandler());
        // 添加压缩支持(可选)
        // pipeline.addLast("compressor", new HttpContentCompressor());
    }
}

HTTP请求处理器

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import java.util.HashMap;
import java.util.Map;
public class HttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        if (msg instanceof HttpRequest) {
            HttpRequest request = (HttpRequest) msg;
            // 处理HTTP请求
            handleRequest(ctx, request);
        } else if (msg instanceof HttpContent) {
            // 处理请求内容(如POST body)
            HttpContent content = (HttpContent) msg;
            handleContent(ctx, content);
        }
    }
    private void handleRequest(ChannelHandlerContext ctx, HttpRequest request) {
        // 获取请求方法
        HttpMethod method = request.method();
        // 获取请求URI
        String uri = request.uri();
        System.out.println("Received " + method + " request for: " + uri);
        // 获取请求头
        HttpHeaders headers = request.headers();
        // 处理不同路径的请求
        String responseContent;
        if ("/".equals(uri)) {
            responseContent = generateHomePage();
        } else if ("/api/user".equals(uri) && method == HttpMethod.GET) {
            responseContent = handleUserApi();
        } else if ("/api/test".equals(uri)) {
            responseContent = handleTestApi(request);
        } else {
            responseContent = "404 - Not Found";
            sendResponse(ctx, responseContent, HttpResponseStatus.NOT_FOUND);
            return;
        }
        sendResponse(ctx, responseContent, HttpResponseStatus.OK);
    }
    private void handleContent(ChannelHandlerContext ctx, HttpContent content) {
        // 读取POST请求的内容
        ByteBuf buf = content.content();
        if (buf.isReadable()) {
            String requestBody = buf.toString(CharsetUtil.UTF_8);
            System.out.println("Request body: " + requestBody);
            // 在这里处理请求体
            if (content instanceof LastHttpContent) {
                System.out.println("Request content received completely");
            }
        }
    }
    private void sendResponse(ChannelHandlerContext ctx, String content, HttpResponseStatus status) {
        // 创建响应内容
        ByteBuf responseBuf = Unpooled.copiedBuffer(content, CharsetUtil.UTF_8);
        // 创建FullHttpResponse
        FullHttpResponse response = new DefaultFullHttpResponse(
                HttpVersion.HTTP_1_1, 
                status, 
                responseBuf);
        // 设置响应头
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, responseBuf.readableBytes());
        // 添加CORS支持(可选)
        response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
        // 发送响应并关闭连接
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }
    private String generateHomePage() {
        StringBuilder html = new StringBuilder();
        html.append("<!DOCTYPE html>")
            .append("<html>")
            .append("<head>")
            .append("<title>Netty HTTP Server</title>")
            .append("<style>")
            .append("body { font-family: Arial; text-align: center; padding-top: 50px; }")
            .append("h1 { color: #333; }")
            .append("</style>")
            .append("</head>")
            .append("<body>")
            .append("<h1>Welcome to Netty HTTP Server</h1>")
            .append("<p>This is a simple HTTP server implemented with Netty</p>")
            .append("<p>Time: " + new java.util.Date() + "</p>")
            .append("<p>Test APIs:</p>")
            .append("<ul>")
            .append("<li><a href='/api/user'>API Test</a></li>")
            .append("<li><a href='/api/test?param=value'>Parameter Test</a></li>")
            .append("</ul>")
            .append("</body>")
            .append("</html>");
        return html.toString();
    }
    private String handleUserApi() {
        Map<String, Object> user = new HashMap<>();
        user.put("id", 1);
        user.put("name", "张三");
        user.put("age", 25);
        user.put("email", "zhangsan@example.com");
        return "JSON Response: " + user.toString();
    }
    private String handleTestApi(HttpRequest request) {
        // 解析查询参数
        QueryStringDecoder decoder = new QueryStringDecoder(request.uri());
        Map<String, java.util.List<String>> params = decoder.parameters();
        StringBuilder result = new StringBuilder();
        result.append("Query Parameters:\n");
        for (Map.Entry<String, java.util.List<String>> entry : params.entrySet()) {
            result.append(entry.getKey()).append("=").append(entry.getValue()).append("\n");
        }
        return result.toString();
    }
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.err.println("Error: " + cause.getMessage());
        ctx.close();
    }
}

完整示例:支持RESTful API的服务器

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
public class RestfulApiServer extends SimpleChannelInboundHandler<FullHttpRequest> {
    private final Gson gson = new Gson();
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        String uri = request.uri();
        HttpMethod method = request.method();
        // 路由处理
        if ("/api/hello".equals(uri) && method == HttpMethod.GET) {
            handleHello(ctx);
        } else if ("/api/posts".equals(uri) && method == HttpMethod.GET) {
            handleGetPosts(ctx);
        } else if ("/api/posts".equals(uri) && method == HttpMethod.POST) {
            // 读取POST请求体的JSON数据
            String content = request.content().toString(CharsetUtil.UTF_8);
            handleCreatePost(ctx, content);
        } else if (uri.startsWith("/api/posts/") && method == HttpMethod.GET) {
            String id = uri.substring("/api/posts/".length());
            handleGetPostById(ctx, id);
        } else {
            sendJsonResponse(ctx, "{\"error\":\"Not Found\"}", HttpResponseStatus.NOT_FOUND);
        }
    }
    private void handleHello(ChannelHandlerContext ctx) {
        JsonObject response = new JsonObject();
        response.addProperty("message", "Hello from Netty REST API");
        sendJsonResponse(ctx, response.toString(), HttpResponseStatus.OK);
    }
    private void handleGetPosts(ChannelHandlerContext ctx) {
        // 模拟帖子数据
        String mockData = "[{\"id\":1, \"title\":\"Post 1\"}, {\"id\":2, \"title\":\"Post 2\"}]";
        sendJsonResponse(ctx, mockData, HttpResponseStatus.OK);
    }
    private void handleCreatePost(ChannelHandlerContext ctx, String content) {
        // 解析JSON并创建帖子
        JsonObject request = gson.fromJson(content, JsonObject.class);
        String title = request.get("title").getAsString();
        JsonObject response = new JsonObject();
        response.addProperty("success", true);
        response.addProperty("title", title);
        response.addProperty("message", "Post created successfully");
        sendJsonResponse(ctx, response.toString(), HttpResponseStatus.CREATED);
    }
    private void handleGetPostById(ChannelHandlerContext ctx, String id) {
        JsonObject response = new JsonObject();
        response.addProperty("id", id);
        response.addProperty("title", "Post " + id);
        sendJsonResponse(ctx, response.toString(), HttpResponseStatus.OK);
    }
    private void sendJsonResponse(ChannelHandlerContext ctx, String json, HttpResponseStatus status) {
        ByteBuf content = Unpooled.copiedBuffer(json, CharsetUtil.UTF_8);
        FullHttpResponse response = new DefaultFullHttpResponse(
                HttpVersion.HTTP_1_1,
                status,
                content);
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8");
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
        response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

测试方法

命令行测试

# GET请求
curl -X GET http://localhost:8080/
# 带参数的GET请求
curl -X GET "http://localhost:8080/api/test?param1=value1&param2=value2"
# POST请求
curl -X POST http://localhost:8080/api/posts \
  -H "Content-Type: application/json" \
  -d '{"title":"My First Post"}'

浏览器访问

打开浏览器访问 http://localhost:8080/ 查看HTML页面

高级特性扩展

// 添加HTTPS支持
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.SelfSignedCertificate;
// 在ServerInitializer中添加SSL支持
public class SecureServerInitializer extends ChannelInitializer<SocketChannel> {
    private final SslContext sslContext;
    public SecureServerInitializer(SslContext sslContext) {
        this.sslContext = sslContext;
    }
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        // 添加SSL处理器
        pipeline.addLast("ssl", sslContext.newHandler(ch.alloc()));
        // 添加HTTP处理器
        pipeline.addLast("httpCodec", new HttpServerCodec());
        pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
        pipeline.addLast("handler", new HttpServerHandler());
    }
}

测试工具类

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class TestClient {
    public static void main(String[] args) throws Exception {
        // 测试GET请求
        URL url = new URL("http://localhost:8080/api/user");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        int responseCode = conn.getResponseCode();
        System.out.println("Response Code: " + responseCode);
        BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String inputLine;
        StringBuffer response = new StringBuffer();
        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }
        in.close();
        System.out.println("Response Body: " + response.toString());
    }
}
  1. 核心组件

    • HttpServerCodec:处理HTTP编解码
    • HttpObjectAggregator:聚合HTTP消息
    • SimpleChannelInboundHandler:处理具体请求
  2. 线程模型

    • Boss线程:接受连接
    • Worker线程:处理I/O
  3. 异步处理

    • 使用ChannelFuture进行异步操作
    • 通过addListener监听完成状态
  4. 性能优化

    • 使用零拷贝特性
    • 减少内存复制
    • 支持长连接和连接池

这个案例提供了一个完整的Netty HTTP服务器实现,可以直接运行并测试,您可以根据自己的需求修改处理器逻辑来扩展功能。

抱歉,评论功能暂时关闭!