zoukankan      html  css  js  c++  java
  • Http多线程版本

    上一篇文章讲了HTTP是如何通过TCP协议传输到服务器上,以及服务器接收到的报文信息
    请参考[HTTP与TCP的关系]

    这篇文章主要讲述的多线程处理Http请求,关于多线程的好处我就不再叙述了。由于我们的
    请求处理可能非常的耗时,导致服务器无法在规定的时间内出力请求,这样服务器的吞吐量比较低,
    为了达到高吞吐量,往往在请求处理时使用多线程技术,不会影响接受请求线程,这样一来即使处理
    请求的线程堵塞了也不会影响处理请求的线程,这个也是现在比较流行的Reactor模型。

    首先来看看处理请求的线程代码:

    /**
         * 处理HTTP请求的一个类
         * @author xin.qiliuhai 1045931706@qq.com
         * @date 2018/4/29 19:15
         */
        public class HttpHandler implements Runnable {
            private Socket socket;
            public HttpHandler(Socket socket){
                this.socket=socket;
            }
            @Override
            public void run() {
                BufferedReader br=null;
                BufferedWriter bw=null;
                try{
                     br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                     bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                    String s;
                    int contentLength = 0;
                    //4.输出请求体中的内容,并且将Content-Length的值赋值给contentLength,确定post请求体的大小
                    while ((s = br.readLine()) != null && !s.isEmpty()) {
                        System.out.println(s);
                        if (s.indexOf("Content-Length") != -1) {
                            contentLength = Integer.parseInt(s.substring(s.indexOf("Content-Length") + 16));
                        }
                    }
                    //5.如果有请求体,通过read方法读取请求体中的内容
                    if (contentLength != 0) {
                        char[] buf = null;
                        if (contentLength != 0) {
                            buf = new char[contentLength];
                            br.read(buf, 0, contentLength);
                            System.out.println("The data user posted: " + new String(buf));
                        }
                    }
                    //6 设置响应体内容
                    bw.write("HTTP/1.1 200 OK
    ");
                    bw.write("Content-Type: text/html; charset=UTF-8
    
    ");
                    bw.write("<html>
    " +
                            "<head>
    " +
                            "    <title>first page</title>
    " +
                            "</head>
    " +
                            "<body>
    " +
                            "    <h1>Hello World!" + "</h1>
    " +
                            "</body>
    " +
                            "</html>
    ");
                    //7.冲刷到浏览器
                    bw.flush();
                    bw.close();
                    br.close();
                    
                    //阻塞十秒钟 相当于在执行请求时堵塞了
                    Thread.sleep(10000);
                }catch (Exception ex){
                    ex.printStackTrace();
                }
            }
        }
    View Code

    单线程调用

    /**
         * HTTP报文
         * @author xin.qiliuhai 1045931706@qq.com
         * @date 2018/4/29 9:09
         */
        public class HttpDemo {
            public static void main(String[] args) throws Exception {
                ServerSocket ss = null;
                Socket socket = null;
                try {
                    //1.创建socket连接
                    ss = new ServerSocket(8080);
                    //循环等待
                    while (true) {
                        //2.堵塞,直到有新的连接进来
                        socket = ss.accept();
                        //3处理相应的请求,这个方法会堵塞
                        new HttpHandler(socket).run();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    //关闭资源
                    ss.close();
                }
            }
        }
    View Code

    大家可以尝试一下,第一次请求速度比较快,后面的请求必须至少等10秒才能进行,所以一旦请求处理
    线程堵塞了会严重影响后面的请求,这个也是netty中一直强调的不要再主线程中调用耗时比较长的方法。

    多线程版本1

    /**
     * @author xin.qiliuhai 1045931706@qq.com
     * @date 2018/4/29 20:03
     */
    public class HttpSimpleThread {
        public static void main(String[] args)throws Exception{
            //创建一个与CPU一样的的
            ServerSocket ss = null;
            Socket socket = null;
            try {
                while(true){
                    //1.创建socket连接
                    ss = new ServerSocket(8081);
                    //循环等待
                    while (true) {
                        socket=ss.accept();
                        //一个请求对应一个线程,请求直接交给线程进行处理
                        new Thread(new HttpHandler(socket)).start();
                        ss.close();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //关闭资源
                ss.close();
            }
        }
    }
    View Code

    每当来一个请求新建一个线程请求,但是我们知道一个电脑同时能处理的线程是十分有限的,创建与销毁线程

    是需要占用一部分时间的,而且涉及到核心态与用户态之间的转换,我们可以理解一个请求时间=tcp时间+线程
    创建时间+处理请求时间+销毁线程时间,那么有没有一种可能将线程创建与销毁时间做到忽略不计呢?答案是有的,
    我们可以将处理请求的线程放到线程池中运行,这样减少很大一部分时间上的开销。

    多线程版本2

    /**
         * @author xin.qiliuhai 1045931706@qq.com
         * @date 2018/4/29 19:13
         */
        public class HttpThreads {
            public static void main(String[] args)throws Exception{
                //创建一个与计算机线程数相同的不可变线程
                Executor executor= Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
                ServerSocket ss = null;
                Socket socket = null;
                BufferedReader br = null;
                BufferedWriter bw = null;
                try {
                    //1.创建socket连接
                    ss = new ServerSocket(8081);
                    //循环等待
                    while (true) {
                       socket=ss.accept();
                       //2.将线程放入到线程池中运行
                       executor.execute(new HttpHandler(socket));
                       //Thread.sleep(10000);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    //关闭资源
                    ss.close();
                 }
            }
        }
    View Code

    当然在这里面的还有很多工作需要进行,比如当线程池满了,对新来的线程进行如何操作,对HTTP请求的解码与编码怎么处理
    还有如果直到某个请求一定会占用比较长的时间怎么处理。这些将会在之后的文章进行讨论。

  • 相关阅读:
    控制台内容保存为文件
    SpringBoot
    JAVA基础
    jenkins的.gradle目录结构说明和清理
    macos 签名+公证app生成dmg后,安装使用过程中崩溃
    MacOS命令行打包+签名+公证+生成dmg文件
    jenkins构建调用tar报错:tar: Failed to set default locale
    jenkins构建报错:appdmg: command not found
    jenkins 构建xcode-select -s 切换xcode版本失败 (切换xcode路径无效)
    jenkins 执行shell编译go 代码报错:build cache is required, but could not be located: GOCACHE is not defined and neither $XDG_CACHE_HOME nor $HOME are defined
  • 原文地址:https://www.cnblogs.com/bufferflies/p/8971878.html
Copyright © 2011-2022 走看看