Netty-如何写一个Http服务器

前言

动机

最近在学习Netty框架,发现Netty是支持Http协议的。加上以前看过Spring-MVC的源码,就想着二者能不能结合一下,整一个简易的web框架(PS:其实不是整,是抄)

效果

项目地址:terabithia

0.3版本使用效果如下,其实就是Spring-MVC的Controller的写法

@RestController
@RequestMapping(value = "/hello")
public class HelloController {

    /**
     * request url:/hello/testGet?strParam=test&intParam=1&floatParam=2&doubleParam=3
     */
    @RequestMapping(value = "/testGet", method = {RequestMethod.GET})
    public Object testGet(ParamWrapperRequest request, String strParam, Integer intParam, Float floatParam, Double doubleParam) throws Exception{
        System.out.println(request.getParameterNames());
        // 获取参数
        System.out.println("strParam:" + strParam);
        System.out.println("intParam:" + intParam);
        System.out.println("floatParam:" + floatParam);
        System.out.println("doubleParam:" + doubleParam);

        System.out.println("testGet");
        return request.getParameterMap();
    }
}

前置条件

以下列出各个版本需要掌握的知识:

  • 0.1版本:Netty
  • 0.2版本:Netty
  • 0.3版本:Netty + Spring-MVC

0.1版本

0.1版本就是Netty原生API对Http协议的支持

如何实现

HttpServer初始化Netty服务, HttpServerCodecHttpObjectAggregator提供了对Http的支持, HttpServerHandler处理业务逻辑。

public class HttpServer {

    private static final int PORT = 8080;

    public static void main(String[] args) {
        final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        final EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            final ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    // 临时存放已完成三次握手的请求的队列
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline()
                                    .addLast(new HttpServerCodec())
                                    //把多个消息转换为一个单一的FullHttpRequest或是FullHttpResponse
                                    .addLast(new HttpObjectAggregator(65536))
                                    .addLast(new HttpServerHandler());
                        }
                    });

            final Channel ch = b.bind(PORT).sync().channel();
            ch.closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}

public class HttpServerHandler extends SimpleChannelInboundHandler {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
        // 业务逻辑处理。。。
        String result = "hello world";

        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.OK);
        response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
        response.content().writeBytes(result.getBytes(StandardCharsets.UTF_8));
        response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
        response.headers().set(CONNECTION, CLOSE);

        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }
}

局限性

无法专注于业务处理,HttpServerHandler需要处理Netty的API和业务逻辑

0.2版本

0.1版本在同一个Handler中处理业务和网络是不合适的,想办法将HttpServerHandler解耦, 划分Handler和Controller。Handler处理Netty API,Controller处理业务逻辑。

如何实现

此版本不讲代码,讲思路(PS:主要是我懒得写)

Netty-如何写一个Http服务器
  • HttpServerHandler处理Netty API,转发请求到Controller以及处理Controller的返回值
  • 获取请求uri,根据uri找到相对应的Controller以及方法
  • 调用Controller的方法
  • 处理Controller的返回值,封装成HttpResponse返回
  • Controller处理业务逻辑,怎么在Spring-MVC用Controller,在这里就怎么用 Controller的主要问题是如何根据uri找到相对应的Controller以及方法,下面给出两种思路:
  • 自定义接口方式,并有Map存放uri与Controller的关系
// 表示是Controller的接口
public interface Action {
    Object doAction(FullHttpRequest request);
}
// 业务Controller
public class HelloAction implements Action{
    @Override
    public Object doAction(FullHttpRequest request) {
        // 业务逻辑处理
        return null;
    }
}
// 使用Map存放uri与Controller的关系
Map actionMap = new ConcurrentHashMap<>();
actionMap.put("/hello", new HelloAction());

// 而在HttpServerHandler调用就更简单了,不用反射
actionMap.get(uri).doAction(request);
  1. 自定义注解方式
// 表示是Controller的注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {

    String value() default "";

}

// 业务Controller
@RequestMapping("/hello")
public class HelloController {
    @RequestMapping("/index")
    public FullHttpResponse index(FullHttpRequest request) {
        return null;
    }
}

/*
 * 而在HttpServerHandler调用
 */
Class clazz = obj.getClass();
// 获取类上的注解
RequestMapping mapping = clazz.getAnnotation(RequestMapping.class);
if (mapping != null) {
    String mappingUri = mapping.value();
    // 获取controller的所有方法
    for (Method actionMethod : clazz.getMethods()) {
        // 获取方法上的注解
        RequestMapping subMapping = actionMethod.getAnnotation(RequestMapping.class);
        if (subMapping != null) {
            String subMappingUri = subMapping.value();
            // 如果uri符合注解规则,则反射调用方法
            if (uri.equalsIgnoreCase(mappingUri + subMappingUri)) {
                return (FullHttpResponse) actionMethod.invoke(obj, request);
            }
        }
    }
}

局限性

没有一般web框架的过滤器,参数处理,返回值处理等。业务代码仍然深度依赖Netty API

0.3版本

结构

依赖:Netty + SpringBoot

处理流程图如下:

Netty-如何写一个Http服务器

思路

下文说的Handler就是Controller

  1. 先说说为什么依赖SpringBoot,因为代码抄的是Spring-MVC的,而且SpringBoot提供的容器API真的很方便。
  2. 调度器:作为一个业务框架,需要业务逻辑对底层的依赖,也就是要满足大部分业务处理不会用到Netty的API,也就是Netty专注于接收和响应HTTP请求
  3. HandlerMapping:存储了请求路径与HandlerMethod的关系,可以通过uri定位到对应的Controller的方法
  4. HandlerMethod:存储了Controller和业务处理方法的信息
  5. ArgumentResolver:负责将请求参数转换成业务处理方法所需的参数(PS:Spring的DataBinder是个好东西)
  6. ReturnValueHandler:负责返回值的转换,比如将Map转成JSON

实现

实现有太多东西讲了,文章讲不完。我抄了一个简化版的Spring-MVC,就在文章开头的项目地址,感兴趣的自行去看吧。

Original: https://www.cnblogs.com/konghuanxi/p/16396875.html
Author: 王谷雨
Title: Netty-如何写一个Http服务器

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/583258/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 每日好书推荐:《Kali Linux渗透测试的艺术》PDF高清版

    Original: https://www.cnblogs.com/bnn86/p/15344056.htmlAuthor: 测试楠楠君Title: 每日好书推荐:《Kali Li…

    Linux 2023年5月27日
    0115
  • 调度器简介

    内核中用来安排进程执行的模块称为调度器(scheduler),它可以切换进程状态(process state)。例如执行、可中断睡眠、不可中断睡眠、退出、暂停等。 调度器是CPU中…

    Linux 2023年6月7日
    079
  • uniapp使用阿里云矢量图标库,h5端显示正常,真机app不显示问题解决

    1、在阿里云矢量图标库网站管理界面如上图,首先下载至本地1的位置,然后在2的位置复制代码 2、在下图中static目录下放入下载的iconfont.css文件,并且修改里面的链接,…

    Linux 2023年6月7日
    0109
  • 设计模式——–原型模式

    原型模式定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 原型模式的核心是一个clone方法,通过该方法进行对象的拷贝。 代码实例 原型模式的优点:性能更加优…

    Linux 2023年6月7日
    079
  • 表中添加唯一字段报错解决方案

    添加唯一字段的迁移 应用向具有现有行的表添加唯一不可为空字段的”普通”迁移将引发错误,因为用于填充现有行的值仅生成一次,从而破坏了唯一约束。 因此,应采取以…

    Linux 2023年6月14日
    096
  • Linux 基于flock命令实现多进程并发读写文件控制

    需求描述 实际项目中,需要在Linux下通过 shell脚本并发读写同一个文件,但是希望同一时刻,只有一个进程可以在读、写目标文件。 解决方案 使用 flock命令。 flock …

    Linux 2023年5月27日
    0107
  • 四大高阶函数、匿名函数、递归

    四大高阶函数: map、reduce、filter、sorted 1.map函数: 根据提供的函数对指定序列做映射 使用可迭代对象(指定的序列)中的每个元素调用函数,将返回值作为新…

    Linux 2023年6月8日
    0116
  • prometheus监控redis集群

    【1】利用 redis_exporter 监控 redis 集群 (1.0)redis_exporter 以前都是用傻办法,一个实例一个采集器; redis_exporter 支持…

    Linux 2023年5月28日
    0108
  • Mysql数据库语言学习的路线

    对于我们数据库的学习,不管是测试人员还是开发人员以及我们的DBA来说重点都是SQL;但是我们的SQL可以分多少类型,学习重点又是在哪里呢,本文仅仅针对测试人员来展开说明: SQL:…

    Linux 2023年6月14日
    090
  • Python导入cx_Oracle报错

    系统环境:RHEL5.4 python2.5(手动编译安装,系统带有2.4版本) 在使用python脚本访问数据库时,需要导入cx_Oracle模块 $>>>im…

    Linux 2023年6月14日
    083
  • TCP 和 UDP 协议简介

    一、TCP TCP(Transmission Control Protocol),传输控制协议,对”传输、发送、通信”进行”控制”的…

    Linux 2023年6月16日
    0171
  • pod(二):创建包含多个容器的pod(sidecar)

    服务器版本 docker软件版本 CPU架构 CentOS Linux release 7.4.1708 (Core) Docker version 20.10.12 x86_64…

    Linux 2023年6月7日
    0106
  • 画图3D Paint 3D工作区黑屏

    最近不知道画图3D抽什么风,黑屏了。 后来研究很久,发现这货竟然是用独立显卡,集显带不起来。 解决方案是在Nvidia控制面板给他分配独立显卡,不要使用集显,不要使用集显,不要使用…

    Linux 2023年6月13日
    0121
  • Vim 编辑器|批量注释与批量取消注释

    添加注释 ctrl + v 进入块选泽模式。 上下键选中需要注释的行。 按大写 I 进入插入模式,输入注释符。 按两次 ESC 退出,即完成添加注释。 取消注释 ctrl + v …

    Linux 2023年5月27日
    0112
  • CPU 是如何与内存交互的

    这篇文章主要整理了一下计算机中的内存结构,以及 CPU 是如何读写内存中的数据的,如何维护 CPU 缓存中的数据一致性。什么是虚拟内存,以及它存在的必要性。如有不对请多多指教。 概…

    Linux 2023年6月8日
    099
  • debian与windows时间不同步的简单治疗方法

    试过几种方法, 但就这个方法好使点。hwclock -w –localtime Original: https://www.cnblogs.com/leotiger/p…

    Linux 2023年6月13日
    076
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球