上节课我们自己动手制作了一个RPC框架,本节课我们挑战一个稍有难度的一点的任务,手动制作一个Web框架。
首先我们看看这个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"); // 纯文本网页
}).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, re) -> {
ctx.redirect("/hello"); // 302跳转
}).child("/user", () -> { // URL嵌套
return new Router((ctx, req) -> {
ctx.html("Hello, World");
}).handler("/hello.json", (ctx, req) -> {
ctx.json(new String[] { "Hello", "World" });
}).handler("/hello", (ctx, req) -> {
var res = new HashMap<String, Object>();
res.put("req", req);
ctx.render("playground.ftl", res);
}).handler("/world", (ctx, re) -> {
ctx.redirect("/hello");
}).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;
});
}).resource("/pub", "/static");
var rd = new KidsRequestDispatcher("/kids", router);
rd.templateRoot("/tpl"); // 模版所在的classpath目录
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/user
http://localhost:8080/kids/user/hello
http://localhost:8080/kids/user/hello.json
http://localhost:8080/kids/user/world
http://localhost:8080/kids/pub/bootstrap.min.css
堆栈深度
非Java程序员总是抱怨Java的框架过于复杂,特别爱拿Java恐怖的调用栈说事。比如下面这张图广为流传。
所以这里我要亮出httpkids的调用栈,我们来看看它到底有多深
项目代码
https://github.com/pyloque/httpkids
https://github.com/pyloque/rpckids
大爆炸
关注公众号「码洞」,让我们来一起聊聊这个框架。