您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

基于netty手写Tomcat

时间:2020-09-27 10:31:01  来源:  作者:

netty 简介

Netty一个基于NIO的客户、服务器端的编程框架

1.环境准备

maven依赖

  <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.42.Final</version>
  </dependency>
12345

RequestMethodEnum 请求方式

public enum RequestMethodEnum {
  GET("GET"),
  POST("POST");
  public String code;
  RequestMethodEnum(String code) {
    this.code=code;
  }}12345678

ParentServlet 父类servlet

public abstract class ParentServlet {
  public void service(ParentRequest request, ParentResponse response) throws Exception {
    //service 方法决定调用doGet、doPost;
    if (RequestMethodEnum.GET.code.equalsIgnoreCase(request.getMethod())) {
      doGet(request, response);
    } else {
      doPost(request, response);
    }
  }
  protected abstract void doPost(ParentRequest request, ParentResponse response) throws Exception;
  protected abstract void doGet(ParentRequest request, ParentResponse response) throws Exception;
}
12345678910111213141516
  1. FirstServlet
public class FirstServlet extends ParentServlet {
  @Override
  protected void doPost(ParentRequest request, ParentResponse response) throws Exception {
    response.write("This is the first");
  }  @Override
  protected void doGet(ParentRequest request, ParentResponse response) throws Exception {
    this.doPost(request,response);
  }}1234567891011
  1. SecondServlet
public class SecondServlet extends ParentServlet {
  @Override
  protected void doPost(ParentRequest request, ParentResponse response) throws Exception {
    response.write("this is the second");
  }  @Override
  protected void doGet(ParentRequest request, ParentResponse response) throws Exception {
    this.doPost(request,response);
  }}1234567891011
  1. ParentRequest
public class ParentRequest {
  private String method;
  private String url;
  public String getUrl() {
    return url;
  }  public String getMethod() {
    return method;
  }}1234567891011121314
  1. ParentResponse
public class ParentResponse {
  private OutputStream out;
  public ParentResponse (OutputStream out) {
    this.out = out;
  }  public void write(String s) throws Exception{
    //输出也要遵循HTTP
    //状态码为200
    StringBuilder sb = new StringBuilder();
    sb.Append("HTTP/1.1 200 OK n")
      .append("Content-Type: text/html;n")
      .append("rn")
      .append(s);
    out.write(sb.toString().getBytes());
  }
}
1234567891011121314151617
  1. web.properties
servlet.first.url=/first
servlet.first.className=com.aiden.servlet.FirstServletservlet.second.url=/secondservlet.second.className=com.aiden.servlet.SecondServlet1234

2.基于传统I/O手写Tomcat

  1. 修改ParentRequest
public class ParentRequest {
  private String method;
  private String url;
  public ParentRequest(InputStream in) {
    try {
      String content = "";
      byte[] buff = new byte[1024];
      int len = 0;
      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];
      System.out.println(method);
      this.url = arr[1].split("\?")[0];
    } catch (IOException e) {
      e.printStackTrace();    }  }  public String getUrl() {
    return url;
  }  public String getMethod() {
    return method;
  }}12345678910111213141516171819202122232425262728293031
  1. 编写tomcatStart类
public class TomcatStart {
  private int port = 8080;
  private ServerSocket server;
  private Map<String, ParentServlet> servletMapping = new HashMap<String, ParentServlet>();
  private Properties webProperties = new Properties();
  private void init() {
    try {
      String WEB_INF = this.getClass().getResource("/").getPath();
      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");
          //单实例  多线程
          ParentServlet obj = (ParentServlet) Class.forName(className).newInstance();
          servletMapping.put(url, obj);
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  public void start() {
    //1.加载配置类,初始化servletMapping
    init();
    try {
      //2.绑定端口启动
      server = new ServerSocket(this.port);
      System.out.println("Tomcat 已启动,监听端口是:" + this.port);
      //3.等待用户请求,用一个死循环
      while (true) {
        Socket client = server.accept();
        //4.http 请求
        process(client);
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  private void process(Socket client) throws IOException {
    InputStream is = null;
    OutputStream os = null;
    try {
      is = client.getInputStream();
      os = client.getOutputStream();
      //5.Request(inputstream) Response (outputstream)
      ParentRequest request = new ParentRequest(is);
      ParentResponse response = new ParentResponse(os);
      //6.从协议内容中获取url 映射相应的servlet
      String url = request.getUrl();
      if (servletMapping.containsKey(url)) {
        //7.调用实例化对象的service方法
        servletMapping.get(url).service(request, response);
      } else {
        response.write("404 - Not Found");
      }
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      if (os != null) {
        os.flush();
        os.close();
      }
      if (is != null) {
        is.close();
      }
      client.close();
    }
  }
  public static void main(String[] args) {
    //启动
    new TomcatStart().start();
  }
}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182

3.基于netty手写Tomcat

  1. 修改ParentRequest
public class ParentRequest {
  private ChannelHandlerContext ctx;
  private HttpRequest req;
  public ParentRequest(ChannelHandlerContext ctx, HttpRequest req) {
    this.ctx = ctx;
    this.req = req;
  }  public String getUrl() {
    return req.uri();
  }  public String getMethod() {
    return req.method().name();
  }  public Map<String, List<String>> getParameters() {
    QueryStringDecoder decoder = new QueryStringDecoder(req.uri());
    return decoder.parameters();
  }  public String getParameter(String name) {
    Map<String, List<String>> params = getParameters();
    List<String> param = params.get(name);
    if (null == param) {
      return null;
    } else {
      return param.get(0);
    }  }}123456789101112131415161718192021222324252627282930313233
  1. 修改ParentResponse
public class ParentResponse {
  //SocketChannel的封装  private ChannelHandlerContext ctx;  private HttpRequest req;  public ParentResponse(ChannelHandlerContext ctx, HttpRequest req) {    this.ctx = ctx;    this.req = req;  }  public void write(String out) throws Exception {
    try {      if (out == null || out.length() == 0) {
        return;
      }      // 设置 http协议及请求头信息      FullHttpResponse response = new DefaultFullHttpResponse(        // 设置http版本为1.1
        HttpVersion.HTTP_1_1,        // 设置响应状态码        HttpResponseStatus.OK,        // 将输出值写出 编码为UTF-8
        Unpooled.wrappedBuffer(out.getBytes("UTF-8")));
      response.headers().set("Content-Type", "text/html;");
      ctx.write(response);
    } finally {      ctx.flush();
      ctx.close();
    }  }}12345678910111213141516171819202122232425262728293031323334
  1. 修改TomcatStart
public class TomcatStart {
  private int port = 8080;
  private Map<String, ParentServlet> servletMapping = new HashMap<String, ParentServlet>();
  private Properties webProperties = new Properties();
  private void init() {
    try {
      String WEB_INF = this.getClass().getResource("/").getPath();
      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");
          //单实例  多线程
          ParentServlet obj = (ParentServlet) Class.forName(className).newInstance();
          servletMapping.put(url, obj);
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  public void start() {
    //1.加载配置类,初始化servletMapping
    init();
    // Netty  NIO Reactor模型 Boss Worker
    //Boss 线程
    EventLoopGroup bossGroup = new NioEventLoopGroup();
    //Work线程
    EventLoopGroup workGroup = new NioEventLoopGroup();
    ServerBootstrap server = null;
    try {
      //创建对象
      server = new ServerBootstrap();
      //配置参数
      //链式编程
      server.group(bossGroup, workGroup)
        //主线程处理类,
        .channel(NIOServerSocketChannel.class)
        //子线程处理类
        .childHandler(new ChannelInitializer<SocketChannel>() {
          @Override
          protected void initChannel(SocketChannel client) throws Exception {
            //无锁化串行编程
            //netty对http的封装 对顺序有要求
            //httpResponseEncoder 编码器
            client.pipeline().addLast(new HttpResponseEncoder());
            //httprequestDecoder 解码器
            client.pipeline().addLast(new HttpRequestDecoder());
            //业务处理器
            client.pipeline().addLast(new TomcatHandler());
          }
        })
        //主线程 线程最大数量128
        .option(ChannelOption.SO_BACKLOG, 128)
        //子线程配置 保存长连接
        .childOption(ChannelOption.SO_KEEPALIVE, true);
      ChannelFuture f = server.bind(port).sync();
      System.out.println("Tomcat 已启动,监听端口是:" + this.port);
      f.channel().closeFuture().sync();
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      bossGroup.shutdownGracefully();
      workGroup.shutdownGracefully();
    }
  }
  public class TomcatHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
      if (msg instanceof HttpRequest) {
        System.out.println("hello request");
        HttpRequest req = (HttpRequest) msg;
        ParentRequest request = new ParentRequest(ctx, req);
        ParentResponse response = new ParentResponse(ctx, req);
        String url = request.getUrl();
        if (servletMapping.containsKey(url)) {
          //7.调用实例化对象的service方法
          servletMapping.get(url).service(request, response);
        } else {
          response.write("404 - Not Found");
        }
      }
    }
  }
  public static void main(String[] args) {
    //启动
    new TomcatStart().start();
  }
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798

4.访问

http://localhost:8080/first

基于netty手写Tomcat


Tags:netty   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
前言在实现TCP长连接功能中,客户端断线重连是一个很常见的问题,当我们使用netty实现断线重连时,是否考虑过如下几个问题: 如何监听到客户端和服务端连接断开 ? 如何实现断线后重...【详细内容】
2021-12-24  Tags: netty  点击:(12)  评论:(0)  加入收藏
这篇文章对于排查使用了 netty 引发的堆外内存泄露问题,有一定的通用性,希望对你有所启发 背景最近在做一个基于 websocket 的长连中间件,服务端使用实现了 socket.io 协议(基于...【详细内容】
2021-12-16  Tags: netty  点击:(22)  评论:(0)  加入收藏
简介在之前的文章中,我们提到了在netty的客户端通过使用Http2FrameCodec和Http2MultiplexHandler可以支持多路复用,也就是说在一个连接的channel基础上创建多个子channel,通过...【详细内容】
2021-12-14  Tags: netty  点击:(6)  评论:(0)  加入收藏
本系列为 Netty 学习笔记,本篇介绍总结Java NIO 网络编程。Netty 作为一个异步的、事件驱动的网络应用程序框架,也是基于NIO的客户、服务器端的编程框架。其对 Java NIO 底层...【详细内容】
2021-12-07  Tags: netty  点击:(16)  评论:(0)  加入收藏
想要阅读Netty源码的同学,建议从GitHub上把源码拉下来,方便写注释、Debug调试哦~点我去下载! 先来看一个简单的Echo服务端程序,监听本地的9999端口,有客户端接入时控制台输出一句...【详细内容】
2021-10-22  Tags: netty  点击:(43)  评论:(0)  加入收藏
相信很多人知道石中剑这个典故,在此典故中,天命注定的亚瑟很容易的就拔出了这把石中剑,但是由于资历不被其他人认可,所以他颇费了一番周折才成为了真正意义上的英格兰全境之王,亚...【详细内容】
2021-07-22  Tags: netty  点击:(102)  评论:(0)  加入收藏
家纯 阿里技术 一 什么是 Netty? 能做什么? Netty 是一个致力于创建高性能网络应用程序的成熟的 IO 框架。 相比较与直接使用底层的 Java IO API,你不需要先成为网络专家就...【详细内容】
2021-06-23  Tags: netty  点击:(136)  评论:(0)  加入收藏
客户端代码package com.huanfeng.test;import java.io.BufferedReader;import java.io.InputStreamReader;import java.net.URL;import java.net.URLConnection;import java...【详细内容】
2021-05-26  Tags: netty  点击:(145)  评论:(0)  加入收藏
前言:作为一个刚踏入职场的实习生,我很幸运参加了某个政府项目,并且在项目中负责一个核心模块功能的开发,而不是从头到尾对数据库的crud。虽然我一直心里抱怨我的工作范围根本...【详细内容】
2021-05-24  Tags: netty  点击:(230)  评论:(0)  加入收藏
要求1.群聊系统可以实现服务器端和客户端之间的数据简单通讯(非阻塞)2.通过系统可以实现多人群聊3.服务器端:可以监控用户上线,离线,并实现消息转发功能4.客户端:通过channel可...【详细内容】
2021-05-24  Tags: netty  点击:(147)  评论:(0)  加入收藏
▌简易百科推荐
近日只是为了想尽办法为 Flask 实现 Swagger UI 文档功能,基本上要让 Flask 配合 Flasgger, 所以写了篇 Flask 应用集成 Swagger UI 。然而不断的 Google 过程中偶然间发现了...【详细内容】
2021-12-23  Python阿杰    Tags:FastAPI   点击:(6)  评论:(0)  加入收藏
文章目录1、Quartz1.1 引入依赖<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version></dependency>...【详细内容】
2021-12-22  java老人头    Tags:框架   点击:(11)  评论:(0)  加入收藏
今天来梳理下 Spring 的整体脉络啦,为后面的文章做个铺垫~后面几篇文章应该会讲讲这些内容啦 Spring AOP 插件 (了好久都忘了 ) 分享下 4ye 在项目中利用 AOP + MybatisPlus 对...【详细内容】
2021-12-07  Java4ye    Tags:Spring   点击:(14)  评论:(0)  加入收藏
&emsp;前面通过入门案例介绍,我们发现在SpringSecurity中如果我们没有使用自定义的登录界面,那么SpringSecurity会给我们提供一个系统登录界面。但真实项目中我们一般都会使用...【详细内容】
2021-12-06  波哥带你学Java    Tags:SpringSecurity   点击:(18)  评论:(0)  加入收藏
React 简介 React 基本使用<div id="test"></div><script type="text/javascript" src="../js/react.development.js"></script><script type="text/javascript" src="../js...【详细内容】
2021-11-30  清闲的帆船先生    Tags:框架   点击:(19)  评论:(0)  加入收藏
流水线(Pipeline)是把一个重复的过程分解为若干个子过程,使每个子过程与其他子过程并行进行的技术。本文主要介绍了诞生于云原生时代的流水线框架 Argo。 什么是流水线?在计算机...【详细内容】
2021-11-30  叼着猫的鱼    Tags:框架   点击:(21)  评论:(0)  加入收藏
TKinterThinter 是标准的python包,你可以在linx,macos,windows上使用它,你不需要安装它,因为它是python自带的扩展包。 它采用TCL的控制接口,你可以非常方便地写出图形界面,如...【详细内容】
2021-11-30    梦回故里归来  Tags:框架   点击:(26)  评论:(0)  加入收藏
前言项目中的配置文件会有密码的存在,例如数据库的密码、邮箱的密码、FTP的密码等。配置的密码以明文的方式暴露,并不是一种安全的方式,特别是大型项目的生产环境中,因为配置文...【详细内容】
2021-11-17  充满元气的java爱好者  博客园  Tags:SpringBoot   点击:(25)  评论:(0)  加入收藏
一、搭建环境1、创建数据库表和表结构create table account(id INT identity(1,1) primary key,name varchar(20),[money] DECIMAL2、创建maven的工程SSM,在pom.xml文件引入...【详细内容】
2021-11-11  AT小白在线中  搜狐号  Tags:开发框架   点击:(29)  评论:(0)  加入收藏
SpringBoot开发的物联网通信平台系统项目功能模块 功能 说明 MQTT 1.SSL支持 2.集群化部署时暂不支持retain&will类型消 UDP ...【详细内容】
2021-11-05  小程序建站    Tags:SpringBoot   点击:(55)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条