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);
         }
      }
   }
}
目录
相关文章
|
8天前
|
数据采集 Java 数据挖掘
Java IO异常处理:在Web爬虫开发中的实践
Java IO异常处理:在Web爬虫开发中的实践
|
6天前
|
关系型数据库 Java MySQL
"解锁Java Web传奇之旅:从JDK1.8到Tomcat,再到MariaDB,一场跨越数据库的冒险安装盛宴,挑战你的技术极限!"
【9月更文挑战第6天】在Linux环境下安装JDK 1.8、Tomcat和MariaDB是搭建Java Web应用的关键步骤。本文详细介绍了使用apt-get安装OpenJDK 1.8、下载并配置Tomcat,以及安装和安全设置MariaDB(MySQL的开源分支)的方法。通过这些步骤,您可以快速构建一个稳定、高效的开发和部署环境,并验证各组件是否正确安装和运行。这为您的Java Web应用提供了一个坚实的基础。
17 0
|
11天前
|
API C# 开发框架
WPF与Web服务集成大揭秘:手把手教你调用RESTful API,客户端与服务器端优劣对比全解析!
【8月更文挑战第31天】在现代软件开发中,WPF 和 Web 服务各具特色。WPF 以其出色的界面展示能力受到欢迎,而 Web 服务则凭借跨平台和易维护性在互联网应用中占有一席之地。本文探讨了 WPF 如何通过 HttpClient 类调用 RESTful API,并展示了基于 ASP.NET Core 的 Web 服务如何实现同样的功能。通过对比分析,揭示了两者各自的优缺点:WPF 客户端直接处理数据,减轻服务器负担,但需处理网络异常;Web 服务则能利用服务器端功能如缓存和权限验证,但可能增加服务器负载。希望本文能帮助开发者根据具体需求选择合适的技术方案。
41 0
|
11天前
|
Rust 安全 开发者
惊爆!Xamarin 携手机器学习,开启智能应用新纪元,个性化体验与跨平台优势完美融合大揭秘!
【8月更文挑战第31天】随着互联网的发展,Web应用对性能和安全性要求不断提高。Rust凭借卓越的性能、内存安全及丰富生态,成为构建高性能Web服务器的理想选择。本文通过一个简单示例,展示如何使用Rust和Actix-web框架搭建基本Web服务器,从创建项目到运行服务器全程指导,帮助读者领略Rust在Web后端开发中的强大能力。通过实践,读者可以体验到Rust在性能和安全性方面的优势,以及其在Web开发领域的巨大潜力。
22 0
|
11天前
|
Java Maven Android开发
解锁Web开发新技能:从零开始的Struts 2之旅——让你的Java编程之路更加宽广,首个应用实例带你飞!
【8月更文挑战第31天】对于初学者,掌握 Struts 2 框架不仅能提升 Web 开发能力,还能深入了解 MVC 架构。Struts 2 是一个基于 Servlet 的 Java 框架,提供表单验证、文件上传、国际化等功能,便于快速构建易维护的 Web 应用。本文通过示例演示如何从零开始搭建环境并创建一个简单的 Struts 2 项目,包括配置 `struts.xml`、编写 Action 类及视图文件,并配置 web.xml。通过这些步骤,你将学会基本的开发流程,为进一步学习高级功能打下基础。
23 0
|
11天前
|
前端开发 Java UED
JSF遇上Material Design:一场视觉革命,如何让传统Java Web应用焕发新生?
【8月更文挑战第31天】在当前的Web开发领域,用户体验和界面美观性至关重要。Google推出的Material Design凭借其独特的动画、鲜艳的颜色和简洁的布局广受好评。将其应用于JavaServer Faces(JSF)项目,能显著提升应用的现代感和用户交互体验。本文介绍如何通过PrimeFaces等组件库在JSF应用中实现Material Design风格,包括添加依赖、使用组件及响应式布局等步骤,为用户提供美观且功能丰富的界面。
19 0
|
11天前
|
Java 数据库 API
JSF与JPA的史诗级联盟:如何编织数据持久化的华丽织锦,重塑Web应用的荣耀
【8月更文挑战第31天】JavaServer Faces (JSF) 和 Java Persistence API (JPA) 分别是构建Java Web应用的用户界面组件框架和持久化标准。结合使用JSF与JPA,能够打造强大的数据驱动Web应用。首先,通过定义实体类(如`User`)和配置`persistence.xml`来设置JPA环境。然后,在JSF中利用Managed Bean(如`UserBean`)管理业务逻辑,通过`EntityManager`执行数据持久化操作。
21 0
|
11天前
|
Java 前端开发 Apache
Apache Wicket与Spring MVC等Java Web框架大PK,究竟谁才是你的最佳拍档?点击揭秘!
【8月更文挑战第31天】在Java Web开发领域,众多框架各具特色。Apache Wicket以组件化开发和易用性脱颖而出,提高了代码的可维护性和可读性。相比之下,Spring MVC拥有强大的生态系统,但学习曲线较陡;JSF与Java EE紧密集成,但在性能和灵活性上略逊一筹;Struts2虽成熟,但在RESTful API支持上不足。选择框架时还需考虑社区支持和文档完善程度。希望本文能帮助开发者找到最适合自己的框架。
23 0
|
11天前
|
Java Spring 开发者
Java Web开发新潮流:Vaadin与Spring Boot强强联手,打造高效便捷的应用体验!
【8月更文挑战第31天】《Vaadin与Spring Boot集成:最佳实践指南》介绍了如何结合Vaadin和Spring Boot的优势进行高效Java Web开发。文章首先概述了集成的基本步骤,包括引入依赖和配置自动功能,然后通过示例展示了如何创建和使用Vaadin组件。相较于传统框架,这种集成方式简化了配置、提升了开发效率并便于部署。尽管可能存在性能和学习曲线方面的挑战,但合理的框架组合能显著提升应用开发的质量和速度。
23 0