Code Ease Code Ease
  • 个人博客网站 (opens new window)
  • 好用的工具网站 (opens new window)
  • Java核心基础
  • 框架的艺术
  • 分布式与微服务
  • 开发经验大全
  • 设计模式
  • 版本新特性
数据库系列
大数据+AI
  • xxl-job
运维与Linux
  • 基于SpringBoot和BootStrap的论坛网址
  • 基于VuePress的个人博客网站
  • 基于SpringBoot开发的小功能
  • 做一个自己的IDEA插件
程序人生
关于我
  • 分类
  • 标签
  • 归档

神秘的鱼仔

你会累是因为你在走上坡路
  • 个人博客网站 (opens new window)
  • 好用的工具网站 (opens new window)
  • Java核心基础
  • 框架的艺术
  • 分布式与微服务
  • 开发经验大全
  • 设计模式
  • 版本新特性
数据库系列
大数据+AI
  • xxl-job
运维与Linux
  • 基于SpringBoot和BootStrap的论坛网址
  • 基于VuePress的个人博客网站
  • 基于SpringBoot开发的小功能
  • 做一个自己的IDEA插件
程序人生
关于我
  • 分类
  • 标签
  • 归档
服务器
  • Java核心基础

  • 框架的艺术

    • Spring

    • Mybatis

    • SpringBoot

    • MQ

    • Zookeeper

    • netty

      • 网络开发的最强大框架:Netty快速入门
      • 粘包和半包有了解过吗?netty是如何解决这个问题的
      • 如何使用Netty实现Http服务器
        • 引言
        • 服务端搭建
        • 效果演示
        • 源码中的实践
  • 分布式与微服务

  • 开发经验大全

  • 版本新特性

  • Java
  • 框架的艺术
  • netty
CodeEase
2025-01-23
目录

如何使用Netty实现Http服务器

作者:鱼仔
博客首页: codeease.top (opens new window)
公众号:Java鱼仔

# 引言

之前在看XxlJob源码的时候,发现他用Netty实现了一个Http服务器,用来做调度中心和执行器之间的通信。这篇文章我们就用Netty去实现一个Http服务器。同时加深对Netty的理解。

netty版本选择4.1.90.Final

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-codec-http</artifactId>
    <version>4.1.90.Final</version>
</dependency>
1
2
3
4
5

# 服务端搭建

整体的搭建流程和之前写TCP的例子是一样的,定义bossGroup和workerGroup,再通过ServerBootstrap组装group,channel和handler。在ChannelPipeline中,先添加了一个netty提供的处理HTTP的编码器HttpServerCodec,然后再添加一个自定义的编码处理器,当接收到请求的时候,就在这个自定义的编码处理器中进行逻辑处理。

package com.spring.ease.example.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;

/**
 * @author by: 鱼仔
 * @ClassName: FirstHttpServer
 * @Description:
 * @Date: 2024/11/8 15:32
 */
public class FirstHttpServer {

    public static void main(String[] args) {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            // netty提供的处理HTTP的编码器
                            pipeline.addLast("httpServerCodec", new HttpServerCodec());
                            // 增加一个自定义的编码处理器
                            pipeline.addLast("httpServerHandler",new MyHttpServerHandler());

                        }
                    });
            ChannelFuture channelFuture = serverBootstrap.bind(8080).sync();
            System.out.println("初始化完成,运行端口为:8080");
            channelFuture.channel().closeFuture().sync();

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }

    private static class MyHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {

        @Override
        protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject httpObject) throws Exception {
            // 如果传输的是Http请求,才进行处理
            if (httpObject instanceof HttpRequest) {

                HttpRequest request = (HttpRequest) httpObject;
                String uri = request.uri();
                System.out.println("uri = " + uri);

                // 跳过favicon.ico
                String faviconUrl = "/favicon.ico";
                if (faviconUrl.equals(uri)){
                    return;
                }

                //回复信息给浏览器 [http协议]

                ByteBuf content = Unpooled.copiedBuffer("hello, 我是服务器", CharsetUtil.UTF_8);

                //构造一个http的相应,即 httpresponse
                FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);

                response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
                response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());

                //将构建好 response返回
                channelHandlerContext.writeAndFlush(response);
            }
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

# 效果演示

运行项目之后,在控制台会输出一条消息:初始化完成,运行端口为:8080

接着在浏览器上访问:http://localhost:8080/test,收到代码中我们想让他输出的内容。

3-1.png

在控制台中,我们可以看到一共收到了两次请求,一次是我们正常的/test路径,另一次是/favicon.ico,后面的这一次请求是浏览器默认发起的行为,因此我在代码中跳过了它。

3-2.png

# 源码中的实践

xxl-job项目中,调度中心和任务执行器之间的交互,就是通过netty实现的http服务器,核心类是 EmbedServer

我现在贴出这个类中核心的代码:

3-3.png

具体的代码逻辑和我们上面自己写的是一样的,处理请求的逻辑在EmbedHttpServerHandler中

3-4.png

通过和我们类似的代码拿到uri之后,通过不同的 uri 后缀,触发执行器中对应的代码逻辑,从而实现了调度器能调度我们应用中的执行器的功能。

上次更新: 2025/04/29, 17:22:06
粘包和半包有了解过吗?netty是如何解决这个问题的
SpringCloud与微服务入门,使用idea搭建第一个微服务项目

← 粘包和半包有了解过吗?netty是如何解决这个问题的 SpringCloud与微服务入门,使用idea搭建第一个微服务项目→

最近更新
01
AI大模型部署指南
02-18
02
半个月了,DeepSeek为什么还是服务不可用
02-13
03
Python3.9及3.10安装文档
01-23
更多文章>
Theme by Vdoing | Copyright © 2023-2025 备案图标 浙公网安备33021202002405 | 浙ICP备2023040452号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式