你真的了解Tomcat吗之简单搭建一个Tomcat

76 篇文章 1 订阅

一、Tomcat主要的作用:

  1. 接受来自客户端的请求并根据请求流中的信息生成相应的Request对象和用于返回的Response对象;(如请求所使用的协议,HTTP?AJP?;请求资源的URL?等信息放到Request对象中)
  2. 调用容器中与URL相对应的处理程序处理该请求;
  3. 通过Response对象将服务器处理的结果返回至客户端。

 

 

 

二、架构发展过程:

2.1 原始的刀耕火种时代:

使用Socket与ServerSocket来构建客户端与服务器通信的桥梁,那个时候的架构是这样的:

 

 

Server端负责既负责与客户端连接又负责处理请求并返回处理结果;

 

下面是一个简单的Demo,麻雀虽小,五脏俱全,简单实现了Tomcat的主要功能:

2.1.1 这是通信客户端,主要功能是向服务端发起一个HTTP请求:

"GET /index.html HTTP/1.1 Host: localhost:8080 Connection:Close" 意为请求服务器ContextPath下相对路径为 "/index.html" 的这个资源:

 
/** * @CreatedBy:成江灿 * @Date:2019/12/22/15:24 * @Description:Socket通信客户端 */ public class Client { public static void main(String[] args) { Socket socket = new Socket("127.0.0.1", 8080){ OutputStream os = socket.getOutputStream(); PrintWriter out = new PrintWriter(os, true); /*向服务器发送一个HTTP1.1的请求*/ out.println("GET /index.html HTTP/1.1"); out.println("Host: localhost:8080"); out.println("Connection Close"); out.println(); /*读取服务器端响应的信息,这里用了带缓冲的Reader*/ BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); StringBuffer stringBuffer = new StringBuffer(8096); //一直等待服务器端的响应(NIO通过事件机制和Selector解决了这个问题) boolean loop = true; while(loop){ if(in.ready()){ int i = 0; //输入流读完后返回的值为-1 while(i != -1){ i = in.read(); stringBuffer.append((char)i); } loop = false; } Thread.currentThread().sleep(50L); } } } } 

2.1.2 这是简单实现的服务器

服务器端的主要作用是根据请求流中的信息生成相应的Request对象,然后根据Request对象拿到URL信息并通过Response将对应URL的资源写回给客户端; ps:因为传统的JavaIO会卡在accept()方法下,为防止线程负荷增高,所以这里使用了线程池。

 
/** * @Description:Socket通信服务器端 */ public class Server { public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot"; /*用于标识服务器是否正在运行*/ private static boolean shutdown = false; private static BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(10); private static ExecutorService executorService = new ThreadPoolExecutor(2, 2, 100L, TimeUnit.MINUTES, workQueue); public static void main(String[] args) { ServerSocket serverSocket81 = null; final ServerSocket serverSocket80 = new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1")); Runnable runnableTask80 = new Runnable() { @Override public void run() { while(!shutdown){ Socket socket = null; InputStream inputStream = null; OutputStream outputStream = null; /*serverSocket建立连接后使用socket传值*/ socket = serverSocket80.accept(); inputStream = socket.getInputStream(); /*根据传入的请求流构建Request对象*/ Request request = new Request(inputStream); request.parse(); /*创建Response对象用于返回*/ Response response = new Response(outputStream); response.setRequest(request); response.sendStaticResource(); /*关闭Socket*/ socket.close(); } } }; executorService.execute(runnableTask80); } } 

2.1.3 这是简单实现的Request

主要负责从请求流中获取URL;

 
/** * @CreatedBy:成江灿 * @Date:2019/12/22/16:17 * @Description:将Socket中传入的请求流转为请求对象 */ public class Request { private InputStream inputStream; private String uri; public Request(InputStream inputStream){ this.inputStream = inputStream; } public void parse(){ /*从输入流中获取请求信息*/ StringBuffer request = new StringBuffer(2048); int i; byte[] buffer = new byte[2048]; try{ /*将输入流写入到缓冲区*/ i = inputStream.read(buffer); } catch (IOException e){ e.printStackTrace(); i = -1; } for(int j=0; j<i; j++){ request.append((char)buffer[j]); } uri = parseUri(request.toString()); } private String parseUri(String requestString){ int index1, index2; index1 = requestString.indexOf(' '); if (index1 != -1) { /*取出HTTP请求的第一行中涉及到URL的第二部分*/ index2 = requestString.indexOf(' ', index1 + 1); if (index2 > index1) return requestString.substring(index1 + 1, index2); } return null; } public String getUri(){ return uri; } } 

2.1.4 这是简单实现的返回对象

 
/** * @CreatedBy:成江灿 * @Date:2019/12/22/16:28 * @Description:主要作用是将请求的文件返回 */ public class Response { private static final int BUFFER_SIZE = 1024; Request request; OutputStream output; public Response(OutputStream output) { this.output = output; } public void setRequest(Request request) { this.request = request; } public void sendStaticResource() throws IOException { byte[] bytes = new byte[BUFFER_SIZE]; FileInputStream fis = null; try { File file = new File(System.getProperty("user.dir") + File.separator + "webroot", request.getUri()); if (file.exists()) { fis = new FileInputStream(file); int ch = fis.read(bytes, 0, BUFFER_SIZE); while (ch!=-1) { output.write(bytes, 0, ch); ch = fis.read(bytes, 0, BUFFER_SIZE); } } else { // file not found String errorMessage = "HTTP/1.1 404 File Not Found\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 23\r\n" + "\r\n" + "<h1>File Not Found</h1>"; output.write(errorMessage.getBytes()); } } catch (Exception e) { // thrown if cannot instantiate a File object System.out.println(e.toString() ); } finally { if (fis!=null) fis.close(); } } } 

2.2 各种协议并发的年代:

可以注意到,上面编写的服务端只能接受HTTP协议的请求并相应结果,而且上述程序是高度耦合的,即连接的建立与请求的处理是耦合在服务器端的,所以一旦协议发生改变,那么连接的规则也会发生改变,那么服务器的代码也要发生改变,也许你会觉得重新复制一遍代码新建一个服务器端程序不就好了吗?但你是否想过也许他们的请求处理程序可以是一样的吗,即处理程序它只是通过Request对象来获取信息的,所以我们是不是只需要改变一下处理请求流的规则便可以了呢?

所以我们需要将请求的连接与请求的处理在服务器端解耦合!! 新版的架构图如下:

 

 

其中:(1)Connector负责协议负责各种协议连接的建立和Request和Response对象的构建;

 

(2)Engine则负责通过Request和Response对象对请求进行处理;

(3)Service则用于管理多个Connector与其相对应的Engine,因为基于不同的协议的请求可能会共用同一套处理程序的嘛!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值