26.第20章 HTTP协议和WEB服务器APACHE
一.经典面试题:在浏览器地址栏输入一个 URL 后回车,背后发生了什么?
1.DNS
在浏览器中输入URL后,首先要进行DNS解析,DNS解析的顺序为:
- 浏览器缓存
- 本地hosts文件
- 系统缓存
- 路由器缓存
- DNS服务器迭代查询
2.发送请求
通过DNS得到目标的IP地址后,通过TCP协议向服务器发送请求即三次握手。
3.服务器永久重定向响应
大多数的网站会将用户访问的地址永久重定向,这主要与缓存和搜索排名有关。
- 搜索排名方面:例如www.test.com与test.com搜索引擎认为是两个网站,不会将排名合并。如果使用重定向将test.com定向到www.test.com搜索引擎就会认为是一个页面将排名信息合并。
- 缓存方面:如果使用不同的地址,在缓存中出现好几次,缓存友好性变差
4.跟踪重定向地址
根据返回新重定向地址,重新发送新的http请求
5.处理HTTP请求,返回响应
- 建立连接:服务器允许客户端建立连接
- 接受请求:从网络中读取HTTP报文交给Nginx或者Apache进行规则匹配
- 处理请求:根据方法,资源,首部和可选的主体部分对请求进行处理
- 访问资源:寻找存储对象,访问报文中指定的资源
- 构建响应报文:创建有正确首部的HTTP响应报文
6.处理HTTP响应
- 发送响应:将响应会送给客户端
- 记录日志:将与已经完成的事务记录在一个日志文件中
7.浏览器解析显示
浏览器得到页面后会进行展示,如果还包含其他外部资源如图片、视频等等则继续请求其他资源。
二.HTTP1.0和HTTP1.1的区别
- 缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略
- 带宽优化及网络连接的使用,HTTP1.0中,存在一些浪费带宽的现象,例如:客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),方便了开发者自由的选择以便于充分利用带宽和连接
- 错误通知的管理,在HTTP1.1中新增24个状态响应码,如409(Conflict)表示请求的资源与资源当前状态冲突;410(Gone)表示服务器上的某个资源被永久性的删除
- Host 头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 BadRequest)
- 长连接,HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,弥补了HTTP1.0每次请求都要创建连接的缺点
HTTP1.0和1.1的问题
- HTTP1.x在传输数据时,每次都需要重新建立连接,无疑增加了大量的延迟时间,特别是在移动端更为突出
- HTTP1.x在传输数据时,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份,无法保证数据的安全性
- HTTP1.x在使用时,header里携带的内容过大,增加了传输的成本,并且每次请求header基本不怎么变化,尤其在移动端增加用户流量
- 虽然HTTP1.x支持了keep-alive,来弥补多次创建连接产生的延迟,但是keep-alive使用多了同样会给服务端带来大量的性能压力,并且对于单个文件被不断请求的服务(例如图片存放网站),keep-alive可能会极大的影响性能,因为它在文件被请求之后还保持了不必要的连接很长时间
三.HTTP2.0和SPDY区别:
- HTTP2.0 支持明文 HTTP 传输,而 SPDY 强制使用 HTTPS
- HTTP2.0 消息头的压缩算法采用 HPACK,而非 SPDY 采用的 DEFLATE
四.MPM multi-processing module 工作模式
- prefork:多进程I/O模型,每个进程响应一个请求,CentOS 7 默认模型
一个主进程:生成和回收n个子进程,创建套接字,不响应请求
多个子进程:工作 work进程,每个子进程处理一个请求;系统初始时,预先生成多个空闲进程,等待请求
Prefork MPM: 预派生模式,有一个主控制进程,然后生成多个子进程,每个子进程有一个独立的线程响应用户请求,相对比较占用内存,但是比较稳定,可以设置最大和最小进程数,是最古老的一种模式,也是最稳定的模式,适用于访问量不是很大的场景
优点:稳定
缺点:慢,占用资源,不适用于高并发场景
- worker:复用的多进程I/O模型,多进程多线程,IIS使用此模型
一个主进程:生成m个子进程,每个子进程负责生个n个线程,每个线程响应一个请求,并发响应
请求:m*n
worker MPM:是一种多进程和多线程混合的模型,有一个控制进程,启动多个子进程,每个子进程里面包含固定的线程,使用线程程来处理请求,当线程不够使用的时候会再启动一个新的子进程,然后在进程里面再启动线程处理请求,由于其使用了线程处理请求,因此可以承受更高的并发。
优点:相比prefork 占用的内存较少,可以同时处理更多的请求
缺点:使用keep-alive的长连接方式,某个线程会一直被占据,即使没有传输数据,也需要一直等待到超时才会被释放。如果过多的线程,被这样占据,也会导致在高并发场景下的无服务线程可用。(该问题在prefork模式下,同样会发生)
- event:事件驱动模型(worker模型的变种),CentOS8 默认模型
一个主进程:生成m个子进程,每个子进程负责生个n个线程,每个线程响应一个请求,并发响应
请求:m*n,有专门的监控线程来管理这些keep-alive类型的线程,当有真实请求时,将请求传递给服务线程,执行完毕后,又允许释放。这样增强了高并发场景下的请求处理能力
uevent MPM:Apache中最新的模式,属于事件驱动模型(epoll),每个进程响应多个请求,在现在版本里的已经是稳定可用的模式。它和worker模式很像,最大的区别在于,它解决了keep-alive场景下,长期被占用的线程的资源浪费问题(某些线程因为被keep-alive,空挂在哪里等待,中间几乎没有请求过来,甚至等到超时)。event MPM中,会有一个专门的线程来管理这些keep-alive类型的线程,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放。这样增强了高并发场景下的请求处理能力
event只在有数据发送的时候才开始建立连接,连接请求才会触发工作线程,即使用了TCP的一个选项,叫做延迟接受连接TCP_DEFER_ACCEPT,加了这个选项后,若客户端只进行TCP连接,不发送请求,则不会触发Accept操作,也就不会触发工作线程去干活,进行了简单的防攻击(TCP连接)
优点:单线程响应多请求,占据更少的内存,高并发下表现更优秀,会有一个专门的线程来管理keep-alive类型的线程,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放
缺点:没有线程安全控制
httpd-2.4:event 稳定版,centos7 以后默认
httpd-2.2:event 测试版,centos6 默认
五.一键编译安装httpd2.4脚本
[root@centos7 ~]# cat install_httpd.sh
#!/bin/bash
#
#******************************************************************************
#Author: zhanghui
#QQ: 19661891
#Date: 2021-02-28
#FileName: install_httpd.sh
#URL: www.cnblogs.com/neteagles
#Description: install_httpd for centos 7/8 & ubuntu 18.04/20.04
#Copyright (C): 2021 All rights reserved
#******************************************************************************
SRC_DIR=/usr/local/src
COLOR="echo -e \\033[01;31m"
END='\033[0m'
CPUS=`lscpu |awk '/^CPU\(s\)/{print $2}'`
APR_URL=https://mirrors.cloud.tencent.com/apache/apr/
APR_FILE=apr-1.7.0.tar.gz
APR_UTIL_URL=https://mirrors.cloud.tencent.com/apache/apr/
APR_UTIL_FILE=apr-util-1.6.1.tar.gz
HTTPD_URL=https://mirrors.cloud.tencent.com/apache/httpd/
HTTPD_FILE=httpd-2.4.48.tar.gz
INSTALL_DIR=/apps/httpd
MPM=event
os(){
if grep -Eqi "CentOS" /etc/issue || grep -Eq "CentOS" /etc/*-release;then
rpm -q redhat-lsb-core &> /dev/null || { ${COLOR}"安装lsb_release工具"${END};yum -y install redhat-lsb-core &> /dev/null; }
fi
OS_ID=`lsb_release -is`
}
check_file(){
cd ${SRC_DIR}
if [ ${OS_ID} == "CentOS" ] &> /dev/null;then
rpm -q wget &> /dev/null || yum -y install wget &> /dev/null
fi
if [ ! -e ${APR_FILE} ];then
${COLOR}"缺少${APR_FILE}文件"${END}
${COLOR}"开始下载${APR_FILE}源码包"${END}
wget ${APR_URL}${APR_FILE} || { ${COLOR}"下载${APR_FILE}源码包下载失败"${END}; exit; }
else
${COLOR}"${APR_FILE}文件已准备好"${END}
fi
if [ ! -e ${APR_UTIL_FILE} ];then
${COLOR}"缺少${APR_UTIL_FILE}文件"${END}
${COLOR}"开始下载${APR_UTIL_FILE}源码包"${END}
wget ${APR_UTIL_URL}${APR_UTIL_FILE} || { ${COLOR}"下载${APR_UTIL_FILE}源码包下载失败"${END}; exit; }
else
${COLOR}"${APR_UTIL_FILE}文件已准备好"${END}
fi
if [ ! -e ${HTTPD_FILE} ];then
${COLOR}"缺少${HTTPD_FILE}文件"${END}
${COLOR}"开始下载${HTTPD_FILE}源码包"${END}
wget ${HTTPD_URL}${HTTPD_FILE} || { ${COLOR}"下载${HTTPD_FILE}源码包下载失败"${END}; exit; }
else
${COLOR}"${HTTPD_FILE}文件已准备好"${END}
fi
}
install_httpd(){
[ -d ${INSTALL_DIR} ] && { ${COLOR}"Httpd已存在,安装失败"${END};exit; }
${COLOR}"开始安装Httpd"${END}
${COLOR}"开始安装Httpd依赖包"${END}
if [ ${OS_ID} == "CentOS" ] &> /dev/null;then
yum -y install gcc make pcre-devel openssl-devel expat-devel wget bzip2 &> /dev/null
else
apt update&> /dev/null
apt -y install gcc make libapr1-dev libaprutil1-dev libpcre3 libpcre3-dev libssl-dev wget &> /dev/null
fi
tar xf ${APR_FILE} && tar xf ${APR_UTIL_FILE} && tar xf ${HTTPD_FILE}
APR_FILE_DIR=`echo ${APR_FILE}_| sed -nr 's/^(.*[0-9]).*/\1/p'`
APR_UTIL_FILE_DIR=`echo ${APR_UTIL_FILE}_| sed -nr 's/^(.*[0-9]).*/\1/p'`
HTTPD_FILE_DIR=`echo ${HTTPD_FILE}_| sed -nr 's/^(.*[0-9]).*/\1/p'`
mv ${APR_FILE_DIR} ${HTTPD_FILE_DIR}/srclib/apr
mv ${APR_UTIL_FILE_DIR} ${HTTPD_FILE_DIR}/srclib/apr-util
cd ${HTTPD_FILE_DIR}
./configure --prefix=${INSTALL_DIR} --enable-so --enable-ssl --enable-cgi --enable-rewrite --with-zlib --with-pcre --with-included-apr --enable-modules=most --enable-mpms-shared=all --with-mpm=${MPM}
make -j ${CPUS} && make install
[ $? -eq 0 ] && $COLOR"Httpd编译安装成功"$END || { $COLOR"Httpd编译安装失败,退出!"$END;exit; }
useradd -s /sbin/nologin -r apache
sed -i 's/daemon/apache/' ${INSTALL_DIR}/conf/httpd.conf
echo "PATH=${INSTALL_DIR}/bin:$PATH" > /etc/profile.d/http24.sh
. /etc/profile.d/http24.sh
cat > /lib/systemd/system/httpd.service <<-EOF
[Unit]
Description=The Apache HTTP Server
After=network.target remote-fs.target nss-lookup.target
Documentation=man:httpd(8)
Documentation=man:apachectl(8)
[Service]
Type=forking
ExecStart=${INSTALL_DIR}/bin/apachectl start
ExecReload=${INSTALL_DIR}/bin/apachectl graceful
ExecStop=${INSTALL_DIR}/bin/apachectl stop
KillSignal=SIGCONT
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now httpd &> /dev/null
systemctl is-active httpd &> /dev/null || { ${COLOR}"httpd 启动失败,退出!"${END} ; exit; }
${COLOR}"httpd安装完成"${END}
}
main(){
os
check_file
install_httpd
}
main
六.请解释一下以下response header各行的含义?
Date Tue,22Feb 2011 04:37:45 GMT #报文的创建时间
Server Apache #服务器程序软件名称
Last-Modified Thu,04 Nov 2010 04;12:24 GMT #最后一次修改的时间
Etag"9fc-10c-82-5d73fa00” #实体的扩展标签
Content-Length 130 #主体的长度
Cache-Control max-age-2592000 #控制缓存时长
Expires Thu,24 Mar 2011 04:37:45 GMT #实体的过期时间
Age 21190 #从最初创建开始,响应持续时长
X-Cache HIT from 86-74.ul.sinaimg.cn #表示从CDN访问
Via 1.0.86-74.ul.sinaimg.cn:80(squid/2.6.STABLE21) #:显示报文经过的中间节点(代理,网关)
Connection keep-alive #连接状态:持久连接
七.http协议常用的状态码
200: 成功,请求数据通过响应报文的entity-body部分发送;OK
301: 请求的URL指向的资源已经被删除;但在响应报文中通过首部Location指明了资源现在所处的新位置;Moved Permanently
302: 响应报文Location指明资源临时新位置 Moved Temporarily
304: 客户端发出了条件式请求,但服务器上的资源未曾发生改变,则通过响应此响应状态码通知客户端;Not Modified
307: 浏览器内部重定向
401: 需要输入账号和密码认证方能访问资源;Unauthorized
403: 请求被禁止;Forbidden
404: 服务器无法找到客户端请求的资源;Not Found
500: 服务器内部错误;Internal Server Error
502: 代理服务器从后端服务器收到了一条伪响应,如无法连接到网关;Bad Gateway
503: 服务不可用,临时服务器维护或过载,服务器无法处理请求
504: 网关超时