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服务,HttpServerCodec
和HttpObjectAggregator
提供了对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的返回值
- 获取请求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<String, Action> actionMap = new ConcurrentHashMap<>();
actionMap.put("/hello", new HelloAction()); // 而在HttpServerHandler调用就更简单了,不用反射
actionMap.get(uri).doAction(request);
自定义注解方式
// 表示是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
- 先说说为什么依赖SpringBoot,因为代码抄的是Spring-MVC的,而且SpringBoot提供的容器API真的很方便。
- 调度器:作为一个业务框架,需要业务逻辑对底层的依赖,也就是要满足大部分业务处理不会用到Netty的API,也就是Netty专注于接收和响应HTTP请求
- HandlerMapping:存储了请求路径与HandlerMethod的关系,可以通过uri定位到对应的Controller的方法
- HandlerMethod:存储了Controller和业务处理方法的信息
- ArgumentResolver:负责将请求参数转换成业务处理方法所需的参数(PS:Spring的DataBinder是个好东西)
- ReturnValueHandler:负责返回值的转换,比如将Map转成JSON
实现
实现有太多东西讲了,文章讲不完。我抄了一个简化版的Spring-MVC,就在文章开头的项目地址,感兴趣的自行去看吧。
Netty-如何写一个Http服务器的更多相关文章
- 用C写一个web服务器(二) I/O多路复用之epoll
.container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...
- 使用node.js 文档里的方法写一个web服务器
刚刚看了node.js文档里的一个小例子,就是用 node.js 写一个web服务器的小例子 上代码 (*^▽^*) //helloworld.js// 使用node.js写一个服务器 const h ...
- 使用Node.js原生API写一个web服务器
Node.js是JavaScript基础上发展起来的语言,所以前端开发者应该天生就会一点.一般我们会用它来做CLI工具或者Web服务器,做Web服务器也有很多成熟的框架,比如Express和Koa.但 ...
- 用C写一个web服务器(一) 基础功能
.container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...
- 用C写一个web服务器(四) CGI协议
* { margin: 0; padding: 0 } body { font: 13.34px helvetica, arial, freesans, clean, sans-serif; colo ...
- 网络编程—【自己动手】用C语言写一个基于服务器和客户端(TCP)!
如果想要自己写一个服务器和客户端,我们需要掌握一定的网络编程技术,个人认为,网络编程中最关键的就是这个东西--socket(套接字). socket(套接字):简单来讲,socket就是用于描述IP地 ...
- 用java写一个web服务器
一.超文本传输协议 Web服务器和浏览器通过HTTP协议在Internet上发送和接收消息.HTTP协议是一种请求-应答式的协议——客户端发送一个请求,服务器返回该请求的应答.HTTP协议使用可靠的T ...
- Tomcat源码分析 (一)----- 手写一个web服务器
作为后端开发人员,在实际的工作中我们会非常高频地使用到web服务器.而tomcat作为web服务器领域中举足轻重的一个web框架,又是不能不学习和了解的. tomcat其实是一个web框架,那么其内部 ...
- 手写一个Web服务器,极简版Tomcat
网络传输是通过遵守HTTP协议的数据格式来传输的. HTTP协议是由标准化组织W3C(World Wide Web Consortium,万维网联盟)和IETF(Internet Engineerin ...
- 用C写一个web服务器(三) Linux下用GCC进行项目编译
.container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...
随机推荐
- ajax、axios、fetch
XMLHttpRequest: XHR中文解释为: 可扩展超文本传输请求:XML可扩展标记语言,Http超文本传输协议,Request请求: XHR对象用于与服务器交换数据,所有现代游览器都支持XHR ...
- nginx1.1 nginx介绍和反向代理
1.什么是nginx nginx是一个高性能的http和反向代理的web服务器,所占内存小,高并发 nginx默认端口:80端口 命令存放目录:cd /usr/local/nginx/sbin 配置文 ...
- Linux Troubleshooting 超实用系列 - Disk Analysis
笔者历史文章: https://github.com/CarlJi/words 关于磁盘的使用,实际生产中以下问题会较为常见: No space left on device - 空间不足 Disk ...
- 零基础学Java第五节(面向对象一)
本篇文章是<零基础学Java>专栏的第五篇文章,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! 本文章首发于公众号[编程攻略] 类与对象 在哲学体系中,可以分为主 ...
- SSH管理多密钥
生成密钥对 ssh-keygen -t rsa -b 4096 -C "your_email@example.com" # 默认情况下在~/.ssh目录下生成id_rsa和id_r ...
- 108_Power Pivot购物篮分析分组GENERATE之笛卡尔积、排列、组合
博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 1.背景 昨天在看论坛帖子时候(帖子),看到一个关于SKU组合的问题,有很多M大佬都给出了处理方案,于是想用dax也写一个 ...
- 手把手教你使用 Spring Boot 3 开发上线一个前后端分离的生产级系统(一) - 介绍
项目简介 novel 是一套基于时下最新 Java 技术栈 Spring Boot 3 + Vue 3 开发的前后端分离的学习型小说项目,配备详细的项目教程手把手教你从零开始开发上线一个生产级别的 J ...
- JavaDoc——JavaSE基础
JavaDoc 文档注释内容的含义 @author // 作者 @version // 版本 @since // 最早支持的Java版本 @param // 接收的参数 @return // 返回值 ...
- 省HVV初体验(edu)
浙江省HVV初体验 此次参加的HVV是edu分会场,总的来说是对HVV有了一个初步的认识,了解实战和靶场练习之间存在的巨大鸿沟. 经历了这次HVV,对于渗透测试有了更深一步的理解.渗透测试的本质就是信 ...
- 【clickhouse专栏】数据库、数据仓库之间的区别与联系
从本篇文章开始,笔者打算写一个系列的<clickhouse专栏>,其全称是Click Stream,Data WareHouse,简称ClickHouse.从其全称中的"Data ...