Netty实现一个小应用服务器 +消息收发推送系统

本文详细介绍了如何使用Netty实现Servlet容器、仿支付功能和多人聊天室。通过配置Servlet映射、HTTP协议的编解码、服务端启动以及处理业务逻辑,演示了Netty在构建高性能IO服务中的应用。此外,还涵盖了WebSocket协议的使用和自定义协议设计,以及静态资源的处理。
摘要由CSDN通过智能技术生成


项目内容

  • 构建一个Servlet容器(仿Tomcat)
  • 写一个仿支付的页面,其可以在接收到支付成功通知后自动跳转
  • 多人聊天室

建议至少有使用过Netty的经验、了解HTTP协议、了解Servlet用法的读者阅读,否则可能会有一点吃力
文章中的示例demo已上传至本人Github,戳此访问?
其中html、js文件均为网络web上的源码copy而来,如有侵犯版权,请告知作者

1. Servlet容器

1.1 介绍

在tomcat中,启动tomcat应用首先会读取web.xml文件,去读取servlet配置,在最早的时候我们就是在web.xml中配置servlet映射关系,然后在具体的servlet类中编写我们的业务逻辑,但是这样做相当冗余,表现在多一个uri请求我就要加一个servlet类,管理起来十分繁琐且复杂,所以这时候MVC思想就来了,使用仅一个DispatcherServlet接受所有请求,我们就不需要在web.xml配置Servlet映射了,正好Spring中有一个MVC模块,我们只需要将具体业务逻辑变成一个个method,变成Bean交给Spring管理,此时Spring就会让这个DispatcherServlet去根据uri执行它管理的Bean —>执行对应的method。

有点扯远了,但我想说的是,将每个uri映射到某个Servlet上这个过程十分繁琐,但项目中为了还原tomcat的原始度,构建最底层最古老的写法,包括对http协议的构建,io的处理,servlet的映射和业务逻辑处理,关注更多的也更值得我们学习的是如何实现服务端的io高性能处理,如何仿tomcat去接受请求,仿造一个Servlet容器的网络请求做法,和如何构建http协议,所以暂且不要关注它的可用性。

1.2 协议

既然仿造的是tomcat,那么协议一定是HTTP协议,这里客户端指的是浏览器,当在浏览器输入一个url后,将会向服务端发起一个HttpRequest,然后服务端需要根据request的不同,构造不同的response返回出去。

1.3 Servlet映射关系初始化

在这里插入图片描述
这里我没有照搬tomcat的web.xml配置,而是使用properties放置配置,怎么方便怎么来,大致意思有了就行

servlet.chatServlet.url=/
servlet.chatServlet.className=com.push.server.servlet.ChatIndexServlet

servlet.RestfulServlet.url=/payOrder
servlet.RestfulServlet.className=com.push.server.servlet.RestfulServlet

servlet.WaitPayServlet.url=/pay
servlet.WaitPayServlet.className=com.push.server.servlet.WaitPayServlet

这里是我demo中的一个配置,你需要按照以上规则去配置url与Servlet的映射关系,这样,我在浏览器访问/pay就会交给WaitPayServlet这个Servlet去处理请求了

具体初始化Servlet的代码如下:

public class ServletUtils {
   

  private static Properties webProperties = new Properties();

  // 存放url与Servlet关系的Map
  private static Map<String, IServlet> servletMapping = new HashMap<String, IServlet>();

  public static final String WEB_INF = "/WEB-INF";

  // 初始化url与Servlet关系
  public static synchronized void init() throws IOException {
   

    if (AbstractServlet.successInit) {
   
      log.info("已经初始化过一次了,不要重复初始化servlet");
    }
    InputStream in = null;
    try {
   
      // 获取 resources/web.properties 下的文件流
      log.info("开启文件流");
      in = ServletUtils.class.getResourceAsStream("/web.properties");

      log.info("读取文件流");
      // 读取properties的内容
      webProperties.load(in);

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

        log.info("读取内容");
        String key = k.toString();
        // 按照我们的规则去解析它
        if (key.endsWith(".url")) {
   
          String servletName = key.replaceAll("\\.url$", "");
          String url = webProperties.getProperty(key);
          String className = webProperties.getProperty(servletName + ".className");
          // 单实例,多线程
          IServlet obj = (IServlet) Class.forName(className).newInstance();
          servletMapping.put(url, obj);
        }

      }
      if (servletMapping.size() == 0) {
   
        log.info("没有读取到servlet映射配置");
        throw new RuntimeException("没有读取到servlet映射配置");
      } else {
   
        AbstractServlet.successInit = true;
      }

    } catch (Exception e) {
   
      log.info("读取servlet配置文件失败: [{}]", e.getMessage());
      throw new RuntimeException("读取servlet配置文件失败");
    } finally {
   
      if (in != null) {
   
        in.close();
      }
    }
  }

主要做的就是读取指定的文件名,然后按照我们的规则去解析映射关系,并且使用反射去创建一个Servlet实例,并保存在Map中,以便后续使用。值得一提的是,这里和tomcat的思想一致,Servlet是单实例,并多线程访问的。

之后我们只需要根据url拿到对应的Servlet去执行service方法即可:

public static IServlet findUriMapping(String uri) {
   
  return servletMapping.get(uri);
}

1.4 HTTP协议的编解码

  • 解码器作用:在网络io的过程中,数据的传输一般都是使用字节来传输,同样,在浏览器对服务端发起一个http请求的时候也是发送一串字节,此时我们需要根据字节解析出可以看得懂的HttpRequest对象,这时候我们就需要一个解码器。

  • 编码器作用:当我们根据请求构造相应的响应对象HttpResponse时,同样需要将其变成字节,利用网络传输出去给浏览器,这时候我们就需要一个编码器。

编解码过程相对繁琐,但Netty帮我们实现了大部分的公有协议,例如HTTP协议、WebSocket协议等等,所以编解码这块不需要我们关心,如果想自己实现一个轻量的协议,Netty提供了一些基类,所以实现起来也是很方便的,在后面的聊天室服务器的实现中我就自定义了一个通信规则,感兴趣的读者可以在下面了解自定义协议的实现。

1.5 服务端的启动

private static final int DEFAULT_PORT = 8888;

public static void start(int port) {
   

  EventLoopGroup bossEventLoop = new NioEventLoopGroup(1);
  EventLoopGroup workerEventLoop = new NioEventLoopGroup();

  try {
   
    // 初始化servlet的映射关系
    ServletUtils.init();

    ServerBootstrap bootstrap = new ServerBootstrap();

    bootstrap.group(bossEventLoop, workerEventLoop)
      .channel(NioServerSocketChannel.class)
      .childHandler(new ChannelInitializer() {
   

        @Override
        protected void initChannel
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值