25--- HTTPD(apache)
来源:互联网 发布:怎样知道淘宝店铺网址 编辑:程序博客网 时间:2024/06/05 06:19
===================
协议分层 & socket
在开始 HTTPD 部分之前先简单回顾一下 TCP/IP 协议栈的分层,为什么需要分层呢?是因为要完成网络通信,需要分为好几步,就好比要把大象装冰箱要分几步,每一层其实就是一些 rutine,完成通信中的一步或几步动作,上层 ruitine 调用下层的 rutine 完成工作,这也就是为什么大家常说下层为上层服务。再举一个例子。比如要做馒头,就要种麦子,收麦子,磨面粉,和面,发酵,蒸馒头。怎么能够一步到位呢?蒸馒头尚且如此,通信系统中的两点间通信更是如此。只不过有些动作我们不需要自己做,而是操作系统帮我们做好了,就像我们只要调用 socket 说我需要面粉,socket 就会安排人去种麦子,收麦子,磨面粉,而我们只要负责和面,发酵和蒸馒头就好。再次强调,所谓分层,就是完成一件事要分几步,一个层完成其中的一步或几步,仅此而已。(补充一句,计算机系统中常常会把大家都用到的功能剥离出来形成一层或几层,然后留出接口供其他功能调用;另一个常见到的层的概念是用于屏蔽底层的不同,比如 VFS 这一层就屏蔽了各个文件系统的差异,并向上层提供接口。)
那么什么是 socket 呢?在讲 socket 之前我们先想一个问题。
操作系统的作用是什么?是把硬件资源抽象给使用者使用,这个使用者可以理解为普通用户和程序员。怎么理解把资源抽象然后提供给用户使用呢?举个例子,当我们用 cp 这个命令的时候,操作系统时怎么复制文件的?实现过程简单来讲是这样,先调用一个 read() 的 systemcall(也就是系统调用),把数据读出来,然后调用另一个名为 write() 的 systemcall,把读出来的数据写入磁盘,这样就完成了复制动作。 可以把 read() 和 write() 这两个系统调用理解为操作系统提供给我们的资源或资源操作接口。当程序员编写程序供普通用户的时候,如果没有操作系统,程序员只能从最基本的驱动写起,自己动手与硬件打交道,这显然是麻烦的。操作系统就是把大家都用到的操作,比如磁盘I/O,比如网络操作等写到内核里面,并留出接口(系统调用)给程序员使用(通常以函数的形式给出),然后程序员调用此接口告知内核应该做什么。这样一来,只要操作系统内核来完成某些动作就好了,应用开发的程序员就不需要开发此类底层程序了,想想世界上有成千上万的程序员,操作系统的这种模块共享功能节约了多少时间啊。再次借用上面的例子,操作系统负责种麦子,收麦子,磨面粉,这些活儿它全承包了,然后把面粉卖给做面条的,做烧饼的,做面包的。此外还有些人在面粉中加入了颜色,然后拿来卖给手工艺人捏面人(当然也可以卖给做面条的,做包子的等),这种掺了颜色的面粉我们可以理解为库,是对 systemcall 的一种封装,通常来讲功能上会更加复杂,比如 printf() 就封装了 puts() 的调用,加了一些格式输出的功能。
其实网络操作也是如此,先讲一下网卡接收到数据时候的处理。当网卡接收到数据,一般网卡都会把数据放在自己的寄存器中,显然这个寄存器的大小是有限的,一般来讲,这时候就会有中断产生告知主程序(也就是操作系统)数据到了,请来取数据。然后操作系统(准确说是 rutine 中的驱动模块)从网卡中读数据到内存中,然后交给二层协议栈 rutine (当然也是内核的一部分)处理,二层 rutine 做完各项检查根据二层中标记的三层协议类型让相应的三层 rutine 来处理信息,三层以同样的方式检查并交给四层例程处理之。四层的例程(常见的UDP,TCP,SCTP等)检查端口号,然后对应的进程处理。而四层的 rutine 在内核空间,其代码是以特权级别运行的,而应用程序,如监听80的HTTPD,运行在用户空间。那两者怎么通信呢?这就要用到 socket。常用的 socket API 有如下几个(此处没有给全,详情见“Linux C编程”系列中socket的部分),简单了解下。
-----
int socket(int domain, int type, int protocol);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int listen(int sockfd, int backlog);
作为客户端,socket() + connect() = 创建 socket 并将其连接到远端 server
作为服务端,socket() + bind() + listen() = 创建 socket 并指定IP及端口,同时启动监听,等待来自于客户端的连接请求
-----
网卡接收数据是这样处理的,那如果需要往外发数据呢?也是如此,先调用 socket() 生成一个 socket,然后调用 connect() 把对端的(IP:Port)写到 socket,其实就是告诉内核程序中的 TCP/IP 协议栈,我要和该地址通信,你们准备好。等内核准备好,发送端就可以调用发送数据的系统调用发送数据了。上面讲到的接收数据过程也需要建立 socket,由于本机可能会有多个IP,这就需要通过 bind() 来指定(IP:Port),所以我们认为一个(IP:Port)对儿就确定了一个 socket,然后就可以调用 listen() 开始监听这个(IP:Port)对儿。
至此我们应该理解,socket API 其实就是和内核的网络部分打交道的一个接口。创建 socket 就是在内存中创建一个数据结构,该结构可以认为是本端通信的端点。
=================
HTTPD(apache)
-----CentOS 6.0 上 httpd-2.2.15-----
HTTPD 是 core + modules 的软件,很多功能是以 DSO(Dynamic Shared Objects)的形式给出。可以通过 httpd -M(或httpd.worker -M / httpd.event -M)查看你有哪些 DSO。
配置文件
/etc/httpd/conf/httpd.conf
/etc/httpd/conf.d/*.conf <--- 在httpd.conf主配置文件中有 Include conf.d/*.conf
修改完配置文件后,使用service httpd configtest 或 httpd -t 检查下语法
服务脚本:
/etc/rc.d/init.d/httpd
脚本的配置文件 /etc/sysconfig/httpd
主程序文件:
/usr/sbin/httpd
/usr/sbin/httpd.event
/usr/sbin/httpd.worker
日志文件目录:
/var/log/httpd
access_log 访问日志
error_log 错误日志
-----
主配置文件 /etc/httpd/conf/httpd.conf 主要分为如下 3 个部分:
### Section 1: Global Environment
### Section 2: 'Main' server configuration <---
### Section 3: Virtual Hosts
修改配置文件后需要 reload 或 restart 服务以生效。
配置文件的格式:
directive value
常用 directive(指令)说明:
-----Global Environment-----
ServerRoot "/etc/httpd" <--- 日志等服务器信息的存储相对于此目录(其实log,module,run等目录都是软链接)
-----
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 15
说明:一个 page 上的资源往往不止一个,每个资源都需要发送 request 给服务器,KeepAlive 如果为 off 则每请求一个资源都需要建立/拆除 TCP 连接,如果页面资源较多(如多图)就会导致不停地建立和拆除TCP连接(每次返回一个response后,服务器拆连接),访问资源效率低下。这种连接被称之为短连接。我们一般讲 KeepAlive 设置为 on,以建立长连接,但是长连接有其副作用,即如果 TCP 连接建立后没有和服务器的交互(没有 request/response),那么就会占用服务器资源(一直有个进程在等待服务请求),这样也不好,尤其是并发访问较多时。所以这里通过KeepAliveTimeout 来控制两个 request 间的最大时长,如果第N个request后一直没有下一个 request,则 15 秒后断开 TCP 连接。此外MaxKeepAliveRequests 设置了每个连接的最大 request 数,建议这个值调的大一点。其实设置 MaxKeepAliveRequests 可以认为是对其他用户的一种公平,尤其是并发请求量比较大的时候。
可以通过 Telnet 到 httpd 的端口上,以手动发送 HTTP 报文的方式来验证长连接和短连接。
telnet 192.168.10.131 80
GET /index.html HTTP/1.1
Host: 192.168.10.131 <--- 这个 Host 首部的值会被 HTTPD 得到后选择 VirtualHost,下面会讲到 VirtualHost
两次回车; 如果是短连接的话可以看到服务器发送了 Connection 首部,其值为 close,即 Connection : close,并关闭了 TCP 连接。如果为长连接,则看到服务器没有关闭连接,我们可以继续在命令行中发送信息给服务器。
-----
# prefork MPM
<IfModule prefork.c>
StartServers 8
MinSpareServers 5
MaxSpareServers 20
ServerLimit 256
MaxClients 256
MaxRequestsPerChild 4000
</IfModule>
# worker MPM
<IfModule worker.c>
StartServers 4
MaxClients 300
MinSpareThreads 25
MaxSpareThreads 75
ThreadsPerChild 25
MaxRequestsPerChild 0
</IfModule>
HTTPD 的 MPM(Multi Processing Modules) 支持prefork(多进程处理并发),worker(多进程&多线程处理并发),event(基于事件机制处理并发),尽管我们说 HTTPD 是 core + DSO 的组织形式,但是 2.2 版本中这些功能是被单独编译进 HTTPD 的,而非以 .so 的动态库来提供。rpm 包为我们提供了编译好的3个程序,分别是 httpd,httpd.worker 和 httpd.event ,我们可以通过 httpd -l,httpd.worker -l 和 httpd.event -l 来查看编译时候使用的 c 源文件(然后对比 httpd -M , httpd.worker -M , httpd.event -M)。遗憾的是在 2.2 版本中 event 机制还在测试阶段,到 2.4 版本才打到用于生产的稳定性。
可以在 /etc/sysconfig/httpd 中修改 HTTPD= 的值来更换MPM,但是生产中 HTTPD 常见的 MPM 还是 prefork 形式。
-----
Listen [IP:]# // 监听的 IP 和 Port,IP 省略表示监听本机所有 IP
LoadModule 模块名 模块路径 // 控制载入哪些模块
-----Main Server----- <--- 此 section 的部分 directive 也作为 Virtual Hosts 的默认值,即若使能 Virtual Hosts 但有没有给某directive 赋值,则默认用本section的值。
ServerName www.freeland.com:80 <--- 服务器的FQDN
DocumentRoot "/var/www/html" <--- 服务器提供资源的根位置
Alias /manual/ "/usr/share/httpd/manual/" <--- 目录别名,此后访问http:/hostname/manual 就是访问服务器上/usr/share/httpd/manual 这个目录(文件系统路径哦)
DirectoryIndex index.html index.txt <---若请求某目录,则默认页面由此设置,index.html 位于被请求的目录下
ErrorLog logs/error_log <--- 错误日志,相对位置是相对于 Global Environment 中的 ServerRoot,但要知道 $ServerRoot/log 目录是软链接,真正的日志在 /var/httpd/log 目录下
LogLevel { warn | debug | info | notice | error | crit | alert | emerg } <--- ErrorLog 的等级,默认 warn
CustomLog logs/access_log combined <--- Custom日志,格式为 CustomLog PATH { common | referer | agent | combined }
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined <--- 日志格式
LogFormat "%h %l %u %t \"%r\" %>s %b" common
%h <--- 远程主机的IP(HostnameLookups=off 时,这个HostnameLookups 默认就是off)
%l <--- 较少使用,常见值为 -
%u <--- HTTP协议验证时键入的用户名(下文会讲解定义安全域)
%t <--- 收到 request 的时间
%r <--- request 的第一行
%>s <--- 最终状态码 %s为其实状态码
%b <--- response 的字节数
%{Referer}i <--- 获取 request 头部中 Referer 的值,该值标示从哪里跳转到该页面
%{User-agent}i <--- 获取用户agent,也就是浏览器的类型
-----访问控制
<Directory "/var/www/my_example"> <--- 基于文件系统绝对路径的访问控制,即访问 "/var/www/my_example" 时按如下 directive 处理
Options [Indexes] [FollowSymLinks] [MultiViews] <--- Indexes 表示GET 该目录时是否返回目录下文件信息(处于安全方面考虑,不建议使用);FollowSymLinks 表示 GET 的资源为软链接时,是否允许 response 被链接文件。
AllowOverride None
Order allow,deny
Allow from all <--- 常常是对 IP 的限制,如 Allow from 172.16 就是对 172.16.x.x 的访问允许
</Directory>
-----
<Location /hehe> <--- 基于URL路径的访问控制,即访问 http://host/hehe 时按如下 directive 处理
SetHandler server-status <--- 调用 server-status
Order deny,allow
Deny from all
Allow from 192.168.10.1
</Location>
-----定义安全域(此处以Directory为例,亦可用 Location对某URL做控制)
Step-01 修改配置文件,将指定目录设置为访问控制
<Directory "/var/www/html/test/admin"> <--- 注意这里的目录是绝对路径,是文件系统上的绝对路径!!!
Options None
AllowOverride None
AuthType Basic
AuthName "Please give password."
AuthUserFile "/etc/httpd/conf.d/.httpdusers"
AuthGroupFile "/etc/httpd/conf.d/.httpdgrps" <-- 手动创建,用户应该已经创建。 内容为 webgrp:tom jack
Require valid-user // 或 Require user tom ,其中 tom 必须已经在AuthUserFile中创建
# Require group webgrp
</Directory>
Step-02 生成一个文本文件存储账号
htpasswd [option] PASSWDFILE username
-c 自动创建passwdfile,第一次添加用户时候使用,添加用户到某文件 <-- 即上面的AuthUserFile
-m md5加密用户密码
-s sha1加密用户密码
-D 删除用户
htpasswd -c -m /etc/httpd/conf.d/.httpdusers tom
htpasswd -m /etc/httpd/conf.d/.httpdusers jack
当访问某页面时候需要键入用户名密码方可继续访问。http://tom:tom@192.168.10.131/admin/admin.html 可以直接把用户名和密码放入URL传给server
-----
Alias
-----Virtual Hosts-----
虚拟主机,一个主机定义多个站点 。3 种实现方案:基于ip,基于port,基于hostname ;简单描述如下:
基于IP:为每个虚拟主机准备至少一个IP地址 <-- 少用
基于Port: 为每个虚拟主机单独分配port <-- 少用
基于Hostname:为每个虚拟主机准备至少一个专用的主机名(FQDN)
Note: 一般虚拟主机不与中心主机(Main Server)混用,启用虚拟主机后,当资源请求到达时,httpd 会先去虚拟主机匹配。
-----基于IP
<VirtualHost IP1:80>
ServerName web1.free.com
DocumentRoot /www/docs1
ErrorLog logs/web1-error_log
CustomLog logs/web1-access_log common
</VirtualHost>
<VirtualHost IP2:80>
ServerName web2.free.com
DocumentRoot /www/docs2
ErrorLog logs/web2-error_log
CustomLog logs/web2-access_log common
</VirtualHost>
-----基于Port
<VirtualHost IP1:80>
ServerName web1.free.com
DocumentRoot /www/docs1
ErrorLog logs/web1-error_log
CustomLog logs/web1-access_log common
</VirtualHost>
<VirtualHost IP1:8080>
ServerName web3.free.com
DocumentRoot /www/docs3
ServerAdmin
ErrorLog logs/web3-error_log
CustomLog logs/web3-access_log common
ServerAlias
</VirtualHost>
-----基于Hostname
NameVirtualHost IP:80 <--- HTTP 2.2 上若要使用基于FQDN的虚拟主机, NameVirtuslHost 这个 directive 一定要启用(默认别注释)
<VirtualHost IP1:80>
ServerName web111.free.com
DocumentRoot /www/docs1
ErrorLog logs/web111-error_log
CustomLog logs/web111-access_log common
</VirtualHost>
<VirtualHost IP1:80>
ServerName web222.free.com
DocumentRoot /www/docs3
ErrorLog logs/web222-error_log
CustomLog logs/web222-access_log common
</VirtualHost>
<VirtualHost IP1:80>
ServerName web333.free.com
DocumentRoot /www/docs3
ErrorLog logs/web333-error_log
CustomLog logs/web333-access_log common
</VirtualHost>
浏览器中需要键入ServerName 就可以分别访问
============= 内置的status 页面 通过http://ip/server-status访问
<Location /server-status>
SetHandler server-status
Order deny,allow
Deny from all
Allow from .example.com
</Location>
-----HTTPS的配置
实现 HTTPS 就是在 VirtualHost 下配置一些 HTTPS 相关的 directive ,如:使能 ssl 引擎等。
httpd -M | grep ssl 检查 ssl_module 模块是否安装 ,若未安装则通过 yum 安装之,yum -y install mod_ssl
rpm -ql mod_ssl <-- 主要关注 /etc/httpd/conf.d/ssl.conf 和 /usr/lib64/httpd/modules/mod_ssl.so
先分析下 /etc/httpd/conf.d/ssl.conf <--- conf.d下*.conf 都被 /etc/httpd/conf/httpd.conf 所包含(通过 include)
LoadModule ssl_module modules/mod_ssl.so // 加载 ssl 模块
Listen 10086 // 设定监听端口
<VirtualHost 192.168.10.131:10086> <--- 该虚拟机绑到(IP:Port)对儿上
DocumentRoot "/var/www/https"
ServerName www.freeland.com <--- 指定 FQDN
ErrorLog logs/ssl_error_log
TransferLog logs/ssl_access_log
LogLevel warn
SSLEngine on // 对该 VirtualHost 使能 ssl 引擎
SSLProtocol all -SSLv2 // 减号代表disable
SSLCipherSuite DEFAULT:!EXP:!SSLv2:!DES:!IDEA:!SEED:+3DES
SSLCertificateFile /root/www.freeland.com.crt // 服务器公钥证书(文件系统上的绝对路径)
SSLCertificateKeyFile /root/myhttp.key // 服务器私钥(文件系统上的绝对路径)
</VirtualHost>
///// 公钥证书可以通过 openssl 生成,参考博文《22---加密解密原理及openssl》,注意要关闭 SELinux ,否则会提示我们的公钥证书有问题。/////
注意:以上操作后若启动 httpd 失败,请查阅日志 /var/log/httpd下 的 error_log 日志。 有一次我把 ErrorLoglogs/web1_error_log 错写为 ErrorLog log/web1_error_log ,结果启动失败,又被 Starting httpd: httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName所误导,所以排错了一个多小时才搞定,惭愧啊!
-----CentOS 7.x 上 httpd-2.4.6-----
主要说下 2.4 和 2.2 的几个不同:
(1). 配置文件
/etc/httpd/conf/htpd.conf
/etc/httpd/conf.d/*.conf
/etc/httpd/conf.modules.d/*.conf <--- 多了模块相关的配置目录,模块相关的配置放在该目录
/etc/httpd/conf.d/*.conf
/etc/httpd/conf.modules.d/*.conf <--- 多了模块相关的配置目录,模块相关的配置放在该目录
(2). prefork, worker, event 不再被静态编译进core中,而是以 module 的形式提供,选用哪一个 module 可以在 conf.modules.d 下的 00-mpm.conf 中指定。
(3). KeepAlive & Timeout 可以做到毫秒级别
(4). 对资源的访问更加严格(不再使用 Order,Allow,Deny)-----控制特定IP访问
Require ip IPADDRESS;
Require not ip IPADDRESS; <---其中IPADDRESS可以为IP,Network/Mask,Network/Length,Net(如172.16)
Require all granted
Require all deny
注意:httpd2.4 对于某路径的访问需要显式授权(子路径除外,如果父路径被允许访问,则子路径默认继承之,即亦可被访问)
<Directory "xxx"> <--- DocumentRoot未被显式授权则禁止访问
Options None
AllowOverride None
Require all granted
</Directory>
也就是分两种情况:
1.如果某要控制的Directory是一个(虚拟)主机的DocumentRoot,则默认是all denied,需要显式授权才能访问
2.如果某要控制的Directory是一个DocumentRoot下子目录(或子子目录,子子子目录。。。),则默认被访问权限继承父目录
<RequireAny> // 这个标签和下面的<RequireAll>要留意
Require ip 192.168.10.1
Require all denied
</RequireAny>
<RequireAll>
Require not ip 192.168.10.1
Require all granted
</RequireAll>
Require ip IPADDRESS;
Require not ip IPADDRESS; <---其中IPADDRESS可以为IP,Network/Mask,Network/Length,Net(如172.16)
Require all granted
Require all deny
注意:httpd2.4 对于某路径的访问需要显式授权(子路径除外,如果父路径被允许访问,则子路径默认继承之,即亦可被访问)
<Directory "xxx"> <--- DocumentRoot未被显式授权则禁止访问
Options None
AllowOverride None
Require all granted
</Directory>
也就是分两种情况:
1.如果某要控制的Directory是一个(虚拟)主机的DocumentRoot,则默认是all denied,需要显式授权才能访问
2.如果某要控制的Directory是一个DocumentRoot下子目录(或子子目录,子子子目录。。。),则默认被访问权限继承父目录
<RequireAny> // 这个标签和下面的<RequireAll>要留意
Require ip 192.168.10.1
Require all denied
</RequireAny>
<RequireAll>
Require not ip 192.168.10.1
Require all granted
</RequireAll>
(5). 对于虚拟主机的设置,不再需要 NameVirtualHost [IP:]PORT
阅读全文
0 0
- 25--- HTTPD(apache)
- 安装Apache(httpd)
- Redhat安装httpd(apache)
- apache httpd
- Apache配置(httpd.conf文件)
- apache tomcat(httpd)安装-windows&liunx
- Apache httpd.conf
- Apache----httpd.conf
- Apache httpd安装配置
- apache httpd详解
- Apache配置文件httpd.conf
- apache httpd.conf详解
- apache中httpd.conf
- Apache httpd 远程拒绝服务
- apache httpd学习
- 安装apache-httpd
- Apache httpd.conf
- httpd-apache服务器配置
- Ajax系列面试题总结02
- LINUX摄像驱动二:虚拟驱动VIVI测试及彻底分析
- 智联招聘爬虫
- sublime text3 常用快捷键
- (165)深度表达式
- 25--- HTTPD(apache)
- 怎样在myeclipse控制台输入汉字(防止乱码)
- C++类的特性之多态
- 8086/8088中断系统小探究
- Python3基础进阶(二)
- 单例模式
- (二)c++和JavaScript实现插入排序
- 动态规划测试test20170513
- 写了一半的微信订阅号