JAVA简易WEB服务器(五)

简介:

在之前的几篇博客中,我们的服务器已经具备雏形了,我们还需要继续对其进行优化,在《JAVA简易WEB服务器(三)》中,我们启动服务器的方法如下:

 /**
  * 启动服务器
  */
 public synchronized void start()
 {
    try
    {
       serverSocket = new ServerSocket(port);
       LOG.info("server init success.");
    }
    catch (IOException e)
    {
       LOG.log(Level.SEVERE, e.getMessage(), e);
    }
    new Thread()
    {
       public void run()
       {
          while (!isStop())
          {
             Socket socket;
             try
             {
                socket = serverSocket.accept();
                handleRequest(socket);
             }
             catch (IOException e)
             {
                LOG.log(Level.SEVERE, e.getMessage(), e);
             }
          }
       };
    }.start();
 }

我们是在一个线程中启动我们的服务器,避免阻塞主线程,但是这样,依然存在的问题是,我们的服务器一次只能处理一个请求,这样肯定是不合理的,所以在这一篇博客中,我们将使用多线程对服务端进行优化。

进一步的优化方式:使用多线程,我们可以在接收到请求后开辟新线程进行处理,代码如下:

/**
  * 启动服务器
  */
 public synchronized void start2()
 {
    try
    {
       serverSocket = new ServerSocket(port);
       LOG.info("server init success.");
    }
    catch (IOException e)
    {
       LOG.log(Level.SEVERE, e.getMessage(), e);
    }
    new Thread()
    {
       public void run()
       {
          while (!isStop())
          {
              try
             {
                final Socket socket = serverSocket.accept();
                new Thread()
                {
                   public void run()
                   {
                      try
                      {
                         handleRequest(socket);
                      }
                      catch (IOException e)
                      {
                        LOG.log(Level.SEVERE, e.getMessage(), e);
                     }
                  };
               }.start();
            }
            catch (IOException e)
            {
               LOG.log(Level.SEVERE, e.getMessage(), e);
            }
         }
      };
   }.start();
}

这里,一旦TCP连接建立之后,将会创建一个新的线程来处理新的请求,在新的线程中执行handleRequest方法。
通过创建新的线程,程序可以继续接受新的TCP连接,且这些请求可以并行的处理。
在每个请求一个线程实现中,创建一个线程(和后续的销毁)开销是非常昂贵的,因为JVM和操作系统都需要分配资源。另外,上面的实现还有一个问题,即创建的线程数是不可控的,这将可能导致系统资源被迅速耗尽。
所以我们应该继续寻找解决方案:线程池
使用线程池可以更好的管理我们创建的线程,JDK为我们提供了几种默认的线程池实现,当线程池满后,后续的请求我们是用丢弃的策略,所以在这里面们使用自定义的线程池,完整的HQHttpServer代码如下:

package com.gujin.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;

import com.gujin.server.utils.HQClose;

/**
 * 服务端
 * 
 * @author jianggujin
 * 
 */
public abstract class HQHttpServer implements HQHttpServerLog
{
   /** 端口号 **/
   private int port = 80;
   /** 服务套接字 **/
   private ServerSocket serverSocket = null;
   private int poolSize = 4;
   private int capacity = 16;

   /**
    * 默认构造方法
    */
   public HQHttpServer()
   {
   }

   /**
    * 构造方法
    * 
    * @param port
    */
   public HQHttpServer(int port)
   {
      this.port = port;
   }

   public int getPoolSize()
   {
      return poolSize;
   }

   public void setPoolSize(int poolSize)
   {
      this.poolSize = poolSize;
   }

   public int getCapacity()
   {
      return capacity;
   }

   public void setCapacity(int capacity)
   {
      this.capacity = capacity;
   }

   /**
    * 异步启动服务器
    */
   public void startAsyn()
   {
      new Thread()
      {
         public void run()
         {
            start();
         };
      }.start();
   }

   /**
    * 启动服务器
    * 
    * @throws IOException
    */
   public synchronized void start() throws IOException
   {
      if (isStop())
      {
         serverSocket = new ServerSocket(port);
         LOG.info("server init success.");
         ExecutorService executorService = newBoundedFixedPool(poolSize,
               poolSize);
         while (!isStop())
         {
            Socket socket = serverSocket.accept();
            LOG.info("accept client...");
            executorService.submit(new HQSocketRunnable(socket));
         }
      }
   }

   /**
    * 处理请求
    * 
    * @param socket
    * @throws IOException
    */
   protected void handleRequest(Socket socket) throws IOException
   {
      HQRequest request = new HQRequest(socket);
      request.execute();
      HQResponse response = new HQResponse(socket);
      try
      {
         response.setCookieHandler(request.getCookieHandler().clone());
      }
      catch (CloneNotSupportedException e)
      {
         LOG.log(Level.SEVERE, e.getMessage(), e);
      }
      handleRequest(request, response);
      response.response();
      HQClose.safeClose(socket);
   }

   /**
    * 处理请求
    * 
    * @param request
    * @param response
    * @throws IOException
    */
   public abstract void handleRequest(HQRequest request, HQResponse response)
         throws IOException;

   /**
    * 是否停止
    * 
    * @return
    */
   public boolean isStop()
   {
      return serverSocket == null || serverSocket.isClosed();
   }

   /**
    * 停止服务器
    */
   public synchronized void stop()
   {
      if (!isStop())
      {
         HQClose.safeClose(serverSocket);
         serverSocket = null;
      }
   }

   public static ExecutorService newBoundedFixedPool(int nThreads, int capacity)
   {
      return new ThreadPoolExecutor(nThreads, nThreads, 0L,
            TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>(capacity),
            new ThreadPoolExecutor.DiscardPolicy());
   }

   private class HQSocketRunnable implements Runnable
   {
      private Socket socket = null;

      public HQSocketRunnable(Socket socket)
      {
         this.socket = socket;
      }

      @Override
      public void run()
      {
         try
         {
            handleRequest(socket);
         }
         catch (Exception e)
         {
            LOG.log(Level.SEVERE, e.getMessage(), e);
         }
      }
   }
}
目录
相关文章
|
12天前
|
Java Maven Spring
Java Web 应用中,资源文件的位置和加载方式
在Java Web应用中,资源文件如配置文件、静态文件等通常放置在特定目录下,如WEB-INF或classes。通过类加载器或Servlet上下文路径可实现资源的加载与访问。正确管理资源位置与加载方式对应用的稳定性和可维护性至关重要。
|
15天前
|
存储 安全 搜索推荐
理解Session和Cookie:Java Web开发中的用户状态管理
理解Session和Cookie:Java Web开发中的用户状态管理
40 4
|
14天前
|
Java 持续交付 项目管理
使用Maven进行项目管理:提高Java Web开发的效率
Maven 是一款强大的项目管理和构建自动化工具,广泛应用于Java社区。它通过依赖管理、构建生命周期管理、插件机制和多模块项目支持等功能,简化了项目的构建过程,提高了开发效率。本文将介绍Maven的核心功能及其在Java Web开发中的应用。
32 0
|
1月前
|
运维 Java Linux
【运维基础知识】Linux服务器下手写启停Java程序脚本start.sh stop.sh及详细说明
### 启动Java程序脚本 `start.sh` 此脚本用于启动一个Java程序,设置JVM字符集为GBK,最大堆内存为3000M,并将程序的日志输出到`output.log`文件中,同时在后台运行。 ### 停止Java程序脚本 `stop.sh` 此脚本用于停止指定名称的服务(如`QuoteServer`),通过查找并终止该服务的Java进程,输出操作结果以确认是否成功。
37 1
|
1月前
|
分布式计算 资源调度 Hadoop
大数据-01-基础环境搭建 超详细 Hadoop Java 环境变量 3节点云服务器 2C4G XML 集群配置 HDFS Yarn MapRedece
大数据-01-基础环境搭建 超详细 Hadoop Java 环境变量 3节点云服务器 2C4G XML 集群配置 HDFS Yarn MapRedece
77 4
|
1月前
|
Java Shell Maven
Flink-11 Flink Java 3分钟上手 打包Flink 提交任务至服务器执行 JobSubmit Maven打包Ja配置 maven-shade-plugin
Flink-11 Flink Java 3分钟上手 打包Flink 提交任务至服务器执行 JobSubmit Maven打包Ja配置 maven-shade-plugin
105 4
WK
|
20天前
|
安全 Java 编译器
C++和Java哪个更适合开发web网站
在Web开发领域,C++和Java各具优势。C++以其高性能、低级控制和跨平台性著称,适用于需要高吞吐量和低延迟的场景,如实时交易系统和在线游戏服务器。Java则凭借其跨平台性、丰富的生态系统和强大的安全性,广泛应用于企业级Web开发,如企业管理系统和电子商务平台。选择时需根据项目需求和技术储备综合考虑。
WK
35 0
|
1月前
|
前端开发 Java API
JAVA Web 服务及底层框架原理
【10月更文挑战第1天】Java Web 服务是基于 Java 编程语言用于开发分布式网络应用程序的一种技术。它通常运行在 Web 服务器上,并通过 HTTP 协议与客户端进行通信。
25 1
|
1月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
107 3
|
19天前
|
设计模式 前端开发 数据库
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第27天】本文介绍了Django框架在Python Web开发中的应用,涵盖了Django与Flask等框架的比较、项目结构、模型、视图、模板和URL配置等内容,并展示了实际代码示例,帮助读者快速掌握Django全栈开发的核心技术。
113 45