基于Netty自己动手实现Web框架

上节课我们自己动手制作了一个RPC框架,本节课我们挑战一个稍有难度的一点的任务,手动制作一个Web框架。 我不太愿意叫什么MVC框架,因为我在制作这个小项目的时候可没想过它要怎么怎么的MVC,一切皆以易于使用为目标。

首先我们看看这个Web框架使用起来如何简单

Hello World

import httpkids.server.KidsRequestDispatcher;
import httpkids.server.Router;
import httpkids.server.internal.HttpServer;

public class HelloWorld {

    public static void main(String[] args) {
        var rd = new KidsRequestDispatcher("/kids", new Router((ctx, req) -> {
            ctx.html("Hello, World");
        }));
        new HttpServer("localhost", 8080, 2, 16, rd).start();
    }

}

http://localhost:8080/kids
复制代码

KidsRequestDispatcher是请求派发器,用于将收到的HTTP请求对象扔给响应的RequestHandler进行处理。 Router用于构建路由,它负责的是将URL规则和RequestHandler挂接起来,形成一个复杂的映射表。

Router为了简化实现细节,所以没有支持复杂的URL规则,例如像RESTFUL这种将参数写在URL里面的这种形式是不支持的。

HttpServer是Web服务器的核心对象,构建HttpServer除了IP端口之外,还需要提供3个关键参数,分别是IO线程数、业务线程数和请求派发器对象。IO线程用于处理套件字读写,由Netty内部管理。业务线程专门用于处理HTTP请求,由httpkids框架来管理。

一个全面的例子

import java.util.HashMap;

import httpkids.server.KidsRequestDispatcher;
import httpkids.server.Router;
import httpkids.server.internal.HttpServer;

public class HelloWorld {

	public static void main(String[] args) {
		var router = new Router((ctx, req) -> {
			ctx.html("Hello, World");  // 纯文本html
		})
		.handler("/hello.json", (ctx, req) -> {
			ctx.json(new String[] { "Hello", "World" });  // JSON API
		})
		.handler("/hello", (ctx, req) -> {
			var res = new HashMap<String, Object>();
			res.put("req", req);
			ctx.render("playground.ftl", res); // 模版渲染
		})
		.handler("/world", (ctx, req) -> {
			ctx.redirect("/hello");  // 302跳转
		})
		.handler("/error", (ctx, req) -> {
			ctx.abort(500, "wtf");  // 异常
		})
		.resource("/pub", "/static")  // 静态资源
		.child("/user", () -> {  // 路由嵌套
			return new Router((ctx, req) -> {
				ctx.html("Hello, World");
			})
			.handler("/hello.json", (ctx, req) -> {
				ctx.json(new String[] { "Hello", "World" });
			})
			.filter((ctx, req, before) -> {  // 过滤器、拦截器
				if (before) {
					System.out.printf("before %s\n", req.path());
				} else {
					System.out.printf("after %s\n", req.path());
				}
				return true;
			});
		});

		var rd = new KidsRequestDispatcher("/kids", router); // 请求派发器
		rd.templateRoot("/tpl"); // 模版classpath根目录
		rd.exception(500, (ctx, e) -> { // 异常处理
			ctx.html("what the fuck it is", 500);
		})
		.exception((ctx, e) -> {  // 通用异常处理
			ctx.html("mother fucker!", e.getStatus().code());
		});

		var server = new HttpServer("localhost", 8080, 2, 16, rd);
		server.start();
		
		Runtime.getRuntime().addShutdownHook(new Thread() {

			public void run() {
				server.stop(); // 优雅停机
			}

		});		
	}

}

http://localhost:8080/kids
http://localhost:8080/kids/hello
http://localhost:8080/kids/hello.json
http://localhost:8080/kids/world
http://localhost:8080/kids/error
http://localhost:8080/kids/pub/bootstrap.min.css
http://localhost:8080/kids/user
http://localhost:8080/kids/user/hello
复制代码

堆栈深度

非Java程序员总是抱怨Java的框架过于复杂,特别爱拿Java恐怖的调用栈说事。比如下面这张图广为流传。

所以这里我要亮出httpkids的调用栈,我们来看看它到底有多深

项目代码

HttpKids Web Framework based on Netty for Kids of You

RpcKids RPC Framework based on Netty for Kids of You

大爆炸

关注公众号「 码洞」,让我们来一起学习一下这个框架的设计与实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值