netty web 容器_基于Netty完成Web服务器--上

今天我们计划使用Netty完成一个Web服务器。

Tomcat是基于J2EE规范的Web容器。包括了Servlet、Filter、Linster,这些在J2EE中都是抽象实现,具体的实现交给开发者来完成。本文以Servlet为例来详细展开。

本部分计划分上下来完成,上部分主要完成基于传统的IO的Tomcat服务器。

1.定义Servlet抽象类

在servlet中,最常用的方法就是doGet和doPost方法。我们定义了GPServlet这样的一个抽象类,包括了这两个方法,和service方法。

/*** GPServlet抽象类** @author 荀彧 2020/12/26*/

public abstract class GPServlet {

public void service(GPRequest request, GPResponse response) throws Exception{

// 如果请求方法为GET if ("GET".equalsIgnoreCase(request.getMethod())) {

doGet(request,response);

}else {

doPost(request,response);

}

}

public abstract void doGet(GPRequest request,GPResponse response) throws Exception;

public abstract void doPost(GPRequest request,GPResponse response) throws Exception;

}

从代码可以看到,doGet和doPost中有两个参数,GPRequest和GPResponse对象,这两个对象主要是对Socket的封装。

2. 实现GPRequest

其中主要包括了获取用户的请求方式,以及请求的url

/*** 是对input的封装。** @author 荀彧 2020/12/26*/

public class GPRequest {

private String method;

private String url;

public GPRequest(InputStream in) {

String content = "";

byte[] buff = new byte[1024];

int len = 0;

try {

if ((len = in.read(buff)) > 0) {

content = new String(buff, 0, len);

}

String line = content.split("\\n")[0];

String[] arr = line.split("\\s");

this.method = arr[0];

this.url = arr[1].split("\\s")[0];

} catch (IOException e) {

e.printStackTrace();

}

}

/*** 获取请求方式。** @return*/

public String getMethod() {

return this.method;

}

public String getUrl() {

return this.url;

}

}

3.实现GPResponse

/*** 是对output的封装** @author 荀彧 2020/12/26*/

public class GPResponse {

private OutputStream outputStream;

public GPResponse(OutputStream outputStream) {

this.outputStream = outputStream;

}

/*** 输出 ,需要遵守HTTP协议。* 按照HTTP规范输出字符串。* @param outMsg*/

public void write(String outMsg) throws Exception{

StringBuilder sb = new StringBuilder();

sb.append("HTTP/1.1 200 OK\n")

.append("Content-Type: text/html;\n")

.append("\r\n")

.append(outMsg);

outputStream.write(sb.toString().getBytes());

}

}

GPResponse主要实现了对用户的响应,主要为设置响应内容,和相应的状态码。

4.创建用户的业务代码

我们基于GPServlet来完成我们的两个业务逻辑。FirstSevlet 和SecondServlet。

FirstServlet如下:

/*** 第一个Servlet** @author 荀彧 2020/12/26*/

public class FirstServlet extends GPServlet {

public void doGet(GPRequest request, GPResponse response) throws Exception {

doPost(request,response);

}

public void doPost(GPRequest request, GPResponse response) throws Exception {

response.write("this is the first Servlet");

}

}

SecondServlet如下:

/*** 第二个Servlet** @author 荀彧 2020/12/26*/

public class SecondServlet extends GPServlet {

public void doGet(GPRequest request, GPResponse response) throws Exception {

doPost(request,response);

}

public void doPost(GPRequest request, GPResponse response) throws Exception {

response.write("this is the second Servlet");

}

}

我们继承了GPServlet这个抽象类,并重写了doGet和doPost方法。

5.完成web.properties配置

servlet.one.url=/firstServlet

servlet.one.className=com.learn.netty.tomcat.servlet.FirstServlet

servlet.second.url=/secondServlet

servlet.second.className=com.learn.netty.tomcat.servlet.SecondServlet

配置的url主要用于映射。className用于反射来寻找到相对应的servlet。

6.创建GPTomcat启动类

/*** TomCat的启动类** @author 荀彧 2020/12/26*/

public class GPTomcat {

/*** 默认端口号。*/

private int port = 8080;

private ServerSocket server;

private Map servletMap = new HashMap();

/*** 读取配置文件*/

private Properties webProperties = new Properties();

/*** 初始化函数*/

private void init() {

String WEB_INF = this.getClass().getResource("/").getPath();

try {

FileInputStream fis = new FileInputStream(WEB_INF + "web.properties");

webProperties.load(fis);

for (Object k: webProperties.keySet()) {

String key = k.toString();

if (key.endsWith(".url")) {

String servletName = key.replaceAll("\\.url$","");

String url = webProperties.getProperty(key);

String className = webProperties.getProperty(servletName + ".className");

GPServlet obj = (GPServlet) Class.forName(className).newInstance();

servletMap.put(url,obj);

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

/*** 服务就绪,准备启动*/

public void start() {

init();

try {

server = new ServerSocket(this.port);

System.out.println("TomCat已经启动。。。。端口号为:" + this.port);

// 等待用户请求 while (true) {

Socket client = server.accept();

// 处理http请求 process(client);

}

} catch (Exception e) {

e.printStackTrace();

}

}

/*** 处理的业务逻辑。* @param client* @throws Exception*/

private void process(Socket client) throws Exception {

InputStream inputStream = client.getInputStream();

OutputStream outputStream = client.getOutputStream();

// 转换为GPRequest GPRequest request = new GPRequest(inputStream);

GPResponse response = new GPResponse(outputStream);

// 从协议中获取url。 String url = request.getUrl();

if (servletMap.containsKey(url)) {

servletMap.get(url).service(request,response);

}else {

response.write("404-Not-Found");

}

outputStream.flush();

outputStream.close();

inputStream.close();

client.close();

}

public static void main(String[] args) {

new GPTomcat().start();

}

}

Web容器的实现,主要分为三个阶段。初始化阶段。对应于代码中的init部分。主要是解析web.properties

服务就绪阶段。对应于代码中的start()方法。

接受请求阶段。对应于代码中的process()方法。

每次客户端请求到来的时候,会从Map中寻找到对应的Servlet对象,并实例化GPRequest 和GPResponse对象。最后给用户反馈。

测试

我们启动之后。

此时我们用浏览器发送一个Get请求。得到了正确的响应。

小结

我们利用传统IO,完成了一个简易的web服务器,主要也了解到了进行网络通信的过程。下一节我们会进行基于Netty的改进。

传统IO的问题:线程资源受限:线程是操作系统中非常宝贵的资源,同一时刻有大量的线程处于阻塞状态是非常严重的资源浪费,操作系统耗不起

线程切换效率低下:单机 CPU 核数固定,线程爆炸之后操作系统频繁进行线程切换,应用性能急剧下降。

除了以上两个问题,IO 编程中,我们看到数据读写是以字节流为单位。

代码:uestc_wb/netty-tomcat​gitee.com

切换分支:simple-web

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值