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);
         }
      }
   }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值