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)

大家都在看

  • Java — 面向对象

    简介:类是对事物的一种描述,对象则为具体存在的事物。 类的定义: public class 类名 { // 成员变量 访问修饰符 数据类型 变量名; … // 成员方法 访问修…

    Linux 2023年6月8日
    096
  • windows-cmd-help结果集

    有关某个命令的详细信息,请键入 HELP 命令名ASSOC 显示或修改文件扩展名关联。ATTRIB 显示或更改文件属性。BREAK 设置或清除扩展式 CTRL+C 检查。BCDED…

    Linux 2023年6月7日
    088
  • windows系统命令行cmd查看显卡驱动版本号CUDA

    好看请赞,养成习惯:) 本文来自博客园,作者:靠谱杨, 转载请注明原文链接:https://www.cnblogs.com/rainbow-1/p/16656547.html 关于…

    Linux 2023年6月14日
    0126
  • 为spring cloud config实现刷新动态掉的坑

    正常搭建配置中心,网上教程多,这里不讨论,只记坑也是为了后来者少花时间在这里,由于是当时研究了好久才写的文章,所以只能提供问题的原因,当然会给出印证的思路,闲话不多说进入正题! 版…

    Linux 2023年6月7日
    0100
  • kafka能做什么?kafka集群配置 (卡夫卡 大数据)

    什么是Kafka 官网介绍: 几个概念: 详细介绍 : 操作kafka: kafka集群 消息测试 问题检测 什么是Kafka 官网介绍: ApacheKafka®是一个分布式流媒…

    Linux 2023年6月7日
    0133
  • redis导致的错误错误

    ==========双预防系统启动成功========== 14:42:39.821 [http-nio-9217-exec-1] INFO o.a.c.c.C.[.[.[/] -…

    Linux 2023年5月28日
    0164
  • 前端之jQuery快速入门

    一、jQuery 一款轻量级的JS框架。jQuery的核心JS文件才几十kb,不会影响页面加载速度。 丰富的DOM选择器,jQuery的选择器用起来很方便,比如要找到某个DOM对象…

    Linux 2023年6月14日
    097
  • 数字证书工作原理 图文并茂

    SSL证书和HTTPS的关系 HTTPS在HTTP的基础上加入了SSL加密协议,HTTP是明文传输,HTTPS是加密传输。 A quick definition:HTTPS sta…

    Linux 2023年6月6日
    0121
  • redis在php中的基本使用

    //使用autoload加载相关库,这边重点就是为了require $file; spl_autoload_register(function($class) { $file = …

    Linux 2023年5月28日
    097
  • 小公司比较吃亏的两道微服务面试题

    其实选择工作的时候,很多技术牛人都会选择一些小而美的公司,技术全面,能够以一个更全面的视角看整个公司的运作,人和人之间的相处也很简单。但是,有两道微服务的面试题,小公司的朋友们会比…

    Linux 2023年6月14日
    0109
  • CentOS 文本编辑器

    Linux 终端的文本编辑器中,较著名的有:Nano、Vim、Emacs。其它文本编辑器还有 Gedit、Sublime,Atom 等等。 1.1、基础命令 nano:打开 nan…

    Linux 2023年6月8日
    0115
  • python虚拟环境介绍与安装(不借助anaconda)

    1 虚拟环境介绍 (1) 虚拟环境能对不同的状况进行环境隔离,程序A的环境变动不会影响程序B的开发 (2)比较便携,因为虚拟环境中都有各自的python包,U盘复制环境,省去其他人…

    Linux 2023年6月7日
    0113
  • linux系统(centos)redis分片集群安装部署

    redis分片集群安装与部署 只需更改配置文件 部署架构:6个节点,3主3从。数据集分为3片,分别再A,B,C节点上。A1和B1和C1分别为A,B,C的副本。 散列槽: ​ Red…

    Linux 2023年6月8日
    099
  • Centos7安装Docker

    一、docker运行流程 举个例子你想使用MySQL镜像,那么执行docker pull 下载镜像的时候 首先它会在本地仓库进行运行,如果本地仓库有你想要的MySQL镜像 那么它会…

    Linux 2023年6月6日
    090
  • rsync

    Rsync-远程同步 简介 rsync是linux系统下的数据镜像备份工具。使用快速增量备份工具Remote Sync可以远程同步,支持本地复制,或者与其他SSH、rsync主机同…

    Linux 2023年6月13日
    076
  • rocksdb列族笔记

    1、简介 列族(Column Families)是rocksdb3.0提出的一个机制,用于对同一个数据库的记录(键值对)进行逻辑划分。默认情况下所有的记录都会存储在一个默认列族里(…

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