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

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

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

  1. /**
  2. * 处理HTTP请求的一个类
  3. * @author xin.qiliuhai 1045931706@qq.com
  4. * @date 2018/4/29 19:15
  5. */
  6. public class HttpHandler implements Runnable {
  7. private Socket socket;
  8. public HttpHandler(Socket socket){
  9. this.socket=socket;
  10. }
  11. @Override
  12. public void run() {
  13. BufferedReader br=null;
  14. BufferedWriter bw=null;
  15. try{
  16. br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  17. bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
  18. String s;
  19. int contentLength = 0;
  20. //4.输出请求体中的内容,并且将Content-Length的值赋值给contentLength,确定post请求体的大小
  21. while ((s = br.readLine()) != null && !s.isEmpty()) {
  22. System.out.println(s);
  23. if (s.indexOf("Content-Length") != -1) {
  24. contentLength = Integer.parseInt(s.substring(s.indexOf("Content-Length") + 16));
  25. }
  26. }
  27. //5.如果有请求体,通过read方法读取请求体中的内容
  28. if (contentLength != 0) {
  29. char[] buf = null;
  30. if (contentLength != 0) {
  31. buf = new char[contentLength];
  32. br.read(buf, 0, contentLength);
  33. System.out.println("The data user posted: " + new String(buf));
  34. }
  35. }
  36. //6 设置响应体内容
  37. bw.write("HTTP/1.1 200 OK\n");
  38. bw.write("Content-Type: text/html; charset=UTF-8\n\n");
  39. bw.write("<html>\n" +
  40. "<head>\n" +
  41. " <title>first page</title>\n" +
  42. "</head>\n" +
  43. "<body>\n" +
  44. " <h1>Hello World!" + "</h1>\n" +
  45. "</body>\n" +
  46. "</html>\n");
  47. //7.冲刷到浏览器
  48. bw.flush();
  49. bw.close();
  50. br.close();
  51.  
  52. //阻塞十秒钟 相当于在执行请求时堵塞了
  53. Thread.sleep(10000);
  54. }catch (Exception ex){
  55. ex.printStackTrace();
  56. }
  57. }
  58. }

单线程调用

  1. /**
  2. * HTTP报文
  3. * @author xin.qiliuhai 1045931706@qq.com
  4. * @date 2018/4/29 9:09
  5. */
  6. public class HttpDemo {
  7. public static void main(String[] args) throws Exception {
  8. ServerSocket ss = null;
  9. Socket socket = null;
  10. try {
  11. //1.创建socket连接
  12. ss = new ServerSocket(8080);
  13. //循环等待
  14. while (true) {
  15. //2.堵塞,直到有新的连接进来
  16. socket = ss.accept();
  17. //3处理相应的请求,这个方法会堵塞
  18. new HttpHandler(socket).run();
  19. }
  20. } catch (IOException e) {
  21. e.printStackTrace();
  22. } finally {
  23. //关闭资源
  24. ss.close();
  25. }
  26. }
  27. }

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

多线程版本1

  1. /**
  2. * @author xin.qiliuhai 1045931706@qq.com
  3. * @date 2018/4/29 20:03
  4. */
  5. public class HttpSimpleThread {
  6. public static void main(String[] args)throws Exception{
  7. //创建一个与CPU一样的的
  8. ServerSocket ss = null;
  9. Socket socket = null;
  10. try {
  11. while(true){
  12. //1.创建socket连接
  13. ss = new ServerSocket(8081);
  14. //循环等待
  15. while (true) {
  16. socket=ss.accept();
  17. //一个请求对应一个线程,请求直接交给线程进行处理
  18. new Thread(new HttpHandler(socket)).start();
  19. ss.close();
  20. }
  21. }
  22. } catch (IOException e) {
  23. e.printStackTrace();
  24. } finally {
  25. //关闭资源
  26. ss.close();
  27. }
  28. }
  29. }

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

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

多线程版本2

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

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

Http多线程版本的更多相关文章

  1. /MD、/MT、/LD( 使用 多线程版本 运行时库的C runtime library)

    /MD./MT./LD(使用运行时库)(微软官网解释) Visual C++ 编译器选项 /MD./ML./MT./LD 区别 指定与你项目连接的运行期库 /MT多线程应用程序 /Mtd多线程应用程序 ...

  2. Python-爬取校花网视频(单线程和多线程版本)

    一.参考文章 python爬虫爬取校花网视频,单线程爬取 爬虫----爬取校花网视频,包含多线程版本 上述两篇文章都是对校花网视频的爬取,由于时间相隔很久了,校花网上的一些视频已经不存在了,因此上述文 ...

  3. Python-爬取妹子图(单线程和多线程版本)

    一.参考文章 Python爬虫之——爬取妹子图片 上述文章中的代码讲述的非常清楚,我的基本能思路也是这样,本篇文章中的代码仅仅做了一些异常处理和一些日志显示优化工作,写此文章主要是当做笔记,方便以后查 ...

  4. [易学易懂系列|rustlang语言|零基础|快速入门|(26)|实战3:Http服务器(多线程版本)]

    [易学易懂系列|rustlang语言|零基础|快速入门|(26)|实战3:Http服务器(多线程版本)] 项目实战 实战3:Http服务器 我们今天来进一步开发我们的Http服务器,用多线程实现. 我 ...

  5. Linux网络通信(TCP套接字编写,多进程多线程版本)

    预备知识 源IP地址和目的IP地址 IP地址在上一篇博客中也介绍过,它是用来标识网络中不同主机的地址.两台主机进行通信时,发送方需要知道自己往哪一台主机发送,这就需要知道接受方主机的的IP地址,也就是 ...

  6. Java学习笔记(3)----网络套接字服务器多线程版本

    本例给出一个客户端程序和一个服务器程序.客户端向服务器发送数据.服务器接收数据,并用它来生成一个结果,然后将这个结果返回给客户端.客户端在控制台上显示结果.在本例中,客户端发送的数据是圆的半径,服务器 ...

  7. c++ Socket客户端和服务端示例版本三(多线程版本)

    客户端 #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/soc ...

  8. python多线程和多进程对比

    1.多线程:开启一个进程test.py ,占用两个cpu  共占用45%左右(top -c ,按1)  多进程:开启两个进程test.py 用两个cpu  90%*2左右 test.py # codi ...

  9. python高性能代码之多线程优化

    以常见的端口扫描器为实例 端口扫描器的原理很简单,操作socket来判断连接状态确定主机端口的开放情况. import socket def scan(port): s = socket.socket ...

随机推荐

  1. python实现 多叉树 寻找最短路径

    完全原创,能力有限,欢迎参考,未经允许,请勿转载 ! 完全原创,能力有限,欢迎参考,未经允许,请勿转载 ! 完全原创,能力有限,欢迎参考,未经允许,请勿转载 ! 完全原创,能力有限,欢迎参考,未经允许 ...

  2. Qt自定义控件

    Qt创建自定义控件教程 一.新建Qt设计师控件 二.设置项目名称 三.选择kits 这里取消Debug选项,不需要这个选项都是编译为dll文件直接调用. 删除掉MyControl原有的.h和cpp文件 ...

  3. [论文阅读]VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION(VGGNet)

    VGGNet由牛津大学的视觉几何组(Visual Geometry Group)提出,是ILSVRC-2014中定位任务第一名和分类任务第二名.本文的主要贡献点就是使用小的卷积核(3x3)来增加网络的 ...

  4. scrollTop doesn't scroll on Chrome 61

    在chrome61 不支持滚动 解决方案: Use document.scrollingElement if supported, and fall back to the current code. ...

  5. java内部类、接口、集合框架、泛型、工具类、实现类

    .t1 { background-color: #ff8080; width: 1100px; height: 40px } 一.内部类 1.成员内部类. (1)成员内部类的实例化: 外部类名.内部类 ...

  6. [LeetCode] Closest Leaf in a Binary Tree 二叉树中最近的叶结点

    Given a binary tree where every node has a unique value, and a target key k, find the value of the n ...

  7. python的变量与赋值

    1.变量的命名规则 变量其实通过一个标记调用内存中的值,而变量名就是这个标记的名称,但是万一这个标记已经被提前占用或者解释器认为这个标记是不合法的,那么就会报错.下面总结了一下变量的命名规则: 1.不 ...

  8. [PA 2014]Iloczyn

    Description 斐波那契数列的定义为:k=0或1时,F[k]=k:k>1时,F[k]=F[k-1]+F[k-2].数列的开头几项为0,1,1,2,3,5,8,13,21,34,55,…你 ...

  9. 51nod 1673 树有几多愁

    lyk有一棵树,它想给这棵树重标号. 重标号后,这棵树的所有叶子节点的值为它到根的路径上的编号最小的点的编号. 这棵树的烦恼值为所有叶子节点的值的乘积. lyk想让这棵树的烦恼值最大,你只需输出最大烦 ...

  10. 【BZOJ2733】【HNOI2012】永无乡

    原题传送门 题意:给你N个带权点,一开始相互独立(每个点视为单独一个集合),有2种操作:1)合并2个集合:2)查询包含某元素集合内的权值第k小点编号. 解题思路:显然合并就是并查集,而查询则是平衡树实 ...