前言

动机

最近在学习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<SocketChannel>() {
@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<FullHttpRequest> { @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:主要是我懒得写)

  • HttpServerHandler处理Netty API,转发请求到Controller以及处理Controller的返回值

    1. 获取请求uri,根据uri找到相对应的Controller以及方法
    2. 调用Controller的方法
    3. 处理Controller的返回值,封装成HttpResponse返回
  • Controller处理业务逻辑,怎么在Spring-MVC用Controller,在这里就怎么用

    Controller的主要问题是如何根据uri找到相对应的Controller以及方法,下面给出两种思路:

    1. 自定义接口方式,并有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<String, Action> actionMap = new ConcurrentHashMap<>();
      actionMap.put("/hello", new HelloAction()); // 而在HttpServerHandler调用就更简单了,不用反射
      actionMap.get(uri).doAction(request);
    2. 自定义注解方式

      // 表示是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

处理流程图如下:

思路

下文说的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,就在文章开头的项目地址,感兴趣的自行去看吧。

Netty-如何写一个Http服务器的更多相关文章

  1. 用C写一个web服务器(二) I/O多路复用之epoll

    .container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...

  2. 使用node.js 文档里的方法写一个web服务器

    刚刚看了node.js文档里的一个小例子,就是用 node.js 写一个web服务器的小例子 上代码 (*^▽^*) //helloworld.js// 使用node.js写一个服务器 const h ...

  3. 使用Node.js原生API写一个web服务器

    Node.js是JavaScript基础上发展起来的语言,所以前端开发者应该天生就会一点.一般我们会用它来做CLI工具或者Web服务器,做Web服务器也有很多成熟的框架,比如Express和Koa.但 ...

  4. 用C写一个web服务器(一) 基础功能

    .container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...

  5. 用C写一个web服务器(四) CGI协议

    * { margin: 0; padding: 0 } body { font: 13.34px helvetica, arial, freesans, clean, sans-serif; colo ...

  6. 网络编程—【自己动手】用C语言写一个基于服务器和客户端(TCP)!

    如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西--socket(套接字). socket(套接字):简单来讲,socket就是用于描述IP地 ...

  7. 用java写一个web服务器

    一.超文本传输协议 Web服务器和浏览器通过HTTP协议在Internet上发送和接收消息.HTTP协议是一种请求-应答式的协议——客户端发送一个请求,服务器返回该请求的应答.HTTP协议使用可靠的T ...

  8. Tomcat源码分析 (一)----- 手写一个web服务器

    作为后端开发人员,在实际的工作中我们会非常高频地使用到web服务器.而tomcat作为web服务器领域中举足轻重的一个web框架,又是不能不学习和了解的. tomcat其实是一个web框架,那么其内部 ...

  9. 手写一个Web服务器,极简版Tomcat

    网络传输是通过遵守HTTP协议的数据格式来传输的. HTTP协议是由标准化组织W3C(World Wide Web Consortium,万维网联盟)和IETF(Internet Engineerin ...

  10. 用C写一个web服务器(三) Linux下用GCC进行项目编译

    .container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...

随机推荐

  1. zipper题解

    -请奆佬们洁身自好,好好打代码从我做起 - 题目大意: 给三个字符串,判断C字符串是否由A B字符串顺序组成, 题意分析: 很容易想到的是,A的长度加上B的长度为C的长度 其实进一步想,这 提供了一个 ...

  2. Nginx的常用配置

    Nginx配置文件结构 设置worker进程的用户,指的linux中的用户,会涉及到nginx操作目录或文件的一些权限,默认为 nobodyuser root; worker进程工作数设置,一般来说C ...

  3. 1┃音视频直播系统之浏览器中通过WebRTC访问摄像头

    一.WebRTC的由来 对于前端开发小伙伴而言,如果用 JavaScript 做音视频处理 在以前是不可想象的,因为首先就要考虑浏览器的性能是否跟得上音视频的采集 但是 Google 作为国际顶尖科技 ...

  4. VS.NET启动显示ID为XXXX的进程当前未运行

    解决办法:在启动项目根目录下用文本编辑器打开Web项目下的{X}.csproj文件,然后查找 <WebProjectProperties>,将这一对标签之间的内容全部删除,然后再打开项目就 ...

  5. 203. Remove Linked List Elements - LeetCode

    Question 203. Remove Linked List Elements Solution 题目大意:从链表中删除给定的数 思路:遍历链表,如果该节点的值等于给的数就删除该节点,注意首节点 ...

  6. 821. Shortest Distance to a Character - LeetCode

    Question 821. Shortest Distance to a Character Solution 思路:遍历字符串S,遇到与字符C相等就分别向左/右计算其他字符与该字符的距离,如果其他字 ...

  7. CMU 15-445 数据库课程第四课文字版 - 存储2

    熟肉视频地址: CMU数据库管理系统课程[熟肉]4.数据库存储结构2(上) CMU数据库管理系统课程[熟肉]4.数据库存储结构2(下) 1. 面向日志的存储 上节课我们讲完了面向元组的存储,这节课从面 ...

  8. Java_Scanner的使用

    目录 Scanner对象 scanner.next()和scanner.nextln()的区别 scanner.hasNext()和scanner.hasNextln() Scanner拓展 视频课程 ...

  9. iOS全埋点解决方案-采集奔溃

    前言 ​ 采集应用程序奔溃信息,主要分为以下两种场景: ​ NSException 异常 ​ Unix 信号异常 一.NSException 异常 ​ NSException 异常是 Objectiv ...

  10. BUUCTF-BJDCTF2020]just_a_rar

    BJDCTF2020]just_a_rar 压缩包提示是四位数密码 爆破得知压缩包密码 16进制查看解压的图片后发现flag flag{Wadf_123}