高并发技术

时间:2022-07-22
本文章向大家介绍高并发技术,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

第一章 预备知识

一 理解大数据

我国是人口大国同时也是数据大国, 由数据的量(数以亿计)变产生了质变 , 我们步入了大数据时代. 而大数据也带来的高并发的问题. 解决高并发问题是大数据时代的永恒主题.

我们假设已经解决高并发的问题, 我们可以通过对数以亿计的数据做日志分析 , 从中分析用户行为 ,分析在哪个渠道的用户最具购买力 , 哪个渠道最容易接纳我们的产品. 进而对用户行为进行建模 ,分析用户喜好 ,根据这些喜好可以推荐给用户一些个性化服务. 即: 高并发>日志>分析行为>画像>推荐>服务 这便是大数据时代下企业发展之路 ,因此 ,解决高并发问题便是关键.

通过相应技术, 解决高并发问题 ,为企业节省更多资金 ,有益企业良性发展. 这便是大数据技术发展的不竭动力之源.

二 网工基础知识

OSI七层参考模型

OSI是Open System Interconnection的缩写,意为开放式系统互联。国际标准化组织(ISO)制定了OSI模型,该模型定义了不同计算机互联的标准,是设计和描述计算机网络通信的基本框架。OSI七层模型如下:

所在层

所在层名称

第七层

应用层 ( nginx 软件 )

第六层

表示层

第五层

会话层

第四层

传输层 /运输层( lvs 内核 )

第三层

网络层

第二层

链路层 / 数据链路层

第一层

物理层

应用层

网络应用层是通信用户之间的窗口,为用户提供网络管理、文件传输、事务处理等服务。 这一层设计的主要问题是分布数据库、分布计算技术、网络操作系统和分布操作系统、 远程文件传输、电子邮件、终端电话及远程作业登录与控制等。 应用层为操作系统或网络应用程序提供访问网络服务的接口

应用层协议的代表包括: Telnet(远程登录协议)、 FTP(文件传输协议)、 HTTP(超文本传输协议)、 SNMP(简单网络管理协议)、 DNS(域名解析协议)等。

表示层

表示层向上对应用层提供服务,向下接收来自会话层的服务。 表示层要完成某些特定的功能,主要有不同数据编码格式的转换,提供数据压缩、解压缩服务, 对数据进行加密、解密。例如图像格式的显示,就是由位于表示层的协议来支持。 表示层为应用层提供服务包括语法选择、语法转换等。语法选择是提供一种初始语法和以后修改这种选择的手段。 语法转换涉及代码转换和字符集的转换、数据格式的修改以及对数据结构操作的适配。

会话层

提供包括 访问验证 和 会话管理 在内的 建立和维护 应用之间通信的 机制(如服务器验证用户登录)。 会话层提供的服务可使应用建立和维持会话,并能使会话获得同步。 会话层使用校验点可使通信会话在通信失效时从校验点继续恢复通信。 会话层同样要担负应用进程服务要求,而运输层不能完成的那部分工作,给运输层功能差距以弥补。 主要的功能是对话管理,数据流同步和重新同步。

传输层

传输层建立在网络层和会话层之间,实质上它是网络体系结构中高低层之间衔接的一个接口层。 用一个寻址机制来标识一个特定的应用程序(端口号)。 传输层不仅是一个单独的结构层,它还是整个分层体系协议的核心,没有传输层整个分层协议就没有意义。 传输层的数据单元是由数据组织成的数据段(segment)这个层负责获取全部信息, 传输层最重要的两个协议:TCP , UDP协议

传输层主要负责的行为 三次握手>>(传输数据)>>四次分手

小技巧 利用 netstat -natp可查看虚拟机建立的tcp连接

网络层

网络层也称通信子网层,是高层协议之间的界面层,用于控制通信子网的操作,是通信子网与资源子网的接口。 如果你在谈论一个IP地址,那么你是在处理第3层的问题,这是“数据包”问题,而不是第2层的“帧”。 IP是第3层问题的一部分,此外还有一些路由协议和地址解析协议(ARP)。 有关路由的一切事情都在第3层处理。地址解析和路由是3层的重要目的 ,是路由器所属层。 网络层还可以实现拥塞控制、网际互连、信息包顺序控制及网络记账等功能。

网络层协议的代表包括 IP(网际协议:目前应用最广的网络互联协议, 消除物理网络差异性 ,使网络互连成为可能) OSPF(开放式最短路径优先, 属于内部网关协议)等。

小技巧 通过 route -n 查看虚拟机内核的IP路由表信息

链路层

在物理层提供比特流服务的基础上,将比特信息封装成数据帧Frame, 起到在物理层上建立、撤销、标识逻辑链接和链路复用以及差错校验等功能。 数据链路层在不可靠的物理介质上提供可靠的传输 ,是交换机所在的层。 该层的作用包括:物理地址寻址、数据的成帧、流量控制、数据的检错、重发等。 在这一层,数据的单位称为帧(frame)。

数据链路层协议的代表包括: ARP协议(地址解析协议:根据IP地址获取物理地址的一个TCP/IP协议)、 SDLC、HDLC、PPP、STP、帧中继等。

小技巧 通过arp -a 查询虚拟机ARP缓存中IP地址和MAC地址的对应关系

物理层

物理层是OSI分层结构体系中最重要、最基础的一层, 它建立在传输媒介基础上,起建立、维护和取消物理连接作用,实现设备之间的物理接口。 物理层只接收和发送一串比特(bit)流,不考虑信息的意义和信息结构。

功能分层

层与层依赖

  1. 能够申请到端口号
  2. 路由表有下一跳条目
  3. ARP能请求到下一跳MAC
  4. 三次握手
  5. 传输数据
  6. 四次分手

总结

整个互联网建立在下一跳的模式下 IP是逻辑上的两个端点 MAC是物理上连接的两个节点

端点间TCP传输过程中 确认机制 状态机制 不可分割

解析数据包需要成本 交换机:二层,只关心MAC地址 学习机制 路由器:三层,只关心IP和路由表 LVS服务器:四层,只关心PORT,状态 nginx:七层,关心socket对应关系

第二章 LVS技术

一 LVS介绍

LVS是Linux Virtual Server的简写,即Linux虚拟服务器,是一个虚拟的服务器集群系统

相关概念

  1. ipvs : 嵌入到linux的内核
  2. ipvsadm:管理应用程序
  3. VIP: 虚拟服务器地址
  4. DIP: 转发的网络地址 和RIP通信:ARP协议,获取Real Server的RIP:MAC地址 转发Client的数据包到RIP上(隐藏的VIP)
  5. RIP: 后端真实主机(后端服务器)
  6. CIP: 客户端IP地址

工作模式

1. LVS -D_NAT 地址转换

NAT(Network Address Translation,网络地址转换): 当在专用网内部的一些主机本来已经分配到了本地IP地址(即仅在本专用网内使用的专用地址), 但现在又想和因特网上的主机通信时,可使用NAT方法 这种方法需要在专用网连接到因特网的路由器上安装NAT软件。 装有NAT软件的路由器叫做NAT路由器,它至少有一个有效的外部全球IP地址 所有使用本地地址的主机在和外界通信时,都要在NAT路由器上将本地地址转换成全球IP地址,才能连接网络。 私有ip不能出现再公网上,所以需要net转换, nat交换机将源id端口转换为net交换机分配到的公网ip,并记录下对于的内网主机地址, 收到请求后根据目标地址转换为内网地址

2. LVS -DR MAC欺骗 直接路由

这种方式不会修改源ip地址和目标ip地址,会在第二层数据链路层时修改目标MAC地址, 将其修改为server到的MAC地址。成为MAC欺骗。 又因为MAC基于下一跳的机制,所以server和负载均衡器必须位于同一个局域网。 server端使用隐藏的vip发送socket,这样源ip对应请求的目标ip,同时降低负载均衡器的IO.

3. LVS -TUN 隧道

使用ip隧道技术,将请求封装成ipTUN,server收到ipTUN后解包并处理请求。 响应机制使用隐藏的VIP

二 LVS调度算法

静态调度算法

简称

全称

rr

轮询调度(Round-Robin Scheduling)

wrr

加权轮询调度(Weighted Round-Robin Scheduling)

dh

目标地址散列调度(Destination Hashing Scheduling)

sh

源地址散列调度(Source Hashing Scheduling)

动态调度算法

简称

全称

lc

最小连接调度(Least-Connection Scheduling)

wlc

加权最小连接调度(Weighted Least-Connection Scheduling)

sed

最短的期望的延迟调度(Shortest Expected Delay’)

nq

最少队列调度(Never Queue )

lblc

基于局部性的最少链接(Locality-Based Least Connections Scheduling)

lblcr

带复制的基于局部性最少链接(Locality-Based Least Connections with Replication Scheduling)

默认方法:wlc 点击了解LVS三种工作模式与10种调度算法详细

LVS命令

监控多个端口号

# ipvs内核模块
yum install ipvsadm -y

# 管理集群服务
#添加:
-A -t|u|f service-address [-s scheduler]
	-t: TCP协议的集群 
	-u: UDP协议的集群
	service-address:     IP:PORT
	-f: FWM: 防火墙标记 
	service-address: Mark Number
#修改:-E
#删除:
-D -t|u|f service-address

eg: 
ipvsadm -A -t 192.168.9.100:80 -s rr

管理服务集群中的Real Serever(RS)

#添加:
-a -t|u|f service-address -r server-address [-g|i|m] [-w weight]
	-t|u|f service-address:事先定义好的某集群服务
	-r server-address: 某RS的地址,在NAT模型中,可使用IP:PORT实现端口映射;
		[-g|i|m]: LVS类型	
		-g: DR
		-i: TUN
		-m: NAT
		[-w weight]: 定义服务器权重
#修改:-e
#删除:
-d -t|u|f service-address -r server-address
#eg:
 ipvsadm -a -t 172.16.100.1:80 -r 192.168.10.8 –g
 ipvsadm -a -t 172.16.100.1:80 -r 192.168.10.9 -g

#查看
-L|l
-n: 数字格式显示主机地址和端口
	--stats:统计数据
	--rate: 速率
	--timeout: 显示tcp、tcpfin和udp的会话超时时长
-:c 显示当前的ipvs连接状况

#删除所有集群服务
-C:清空ipvs规则
-S: 保存规则
-R : 载入此前的规则
#eg:
 ipvsadm -S > /path/to/somefile
 ipvsadm -R < /path/form/somefile

三. LVS-DR实现

LVS-DR实验拓扑图

实现步骤

vip :虚拟ip RS:real服务器

  1. 准备3台虚拟机 node1作为lvs服务器 node2,node3作为Real Server
  2. 先配置3台虚拟机的网络 eth0,配置在一个网段 DIP,RIP在一个网段
  3. 配置lvs的VIP(node1主机) # 临时配置 ,重启后配置消失 # 设置vip 需要根据自己所在网段配置, 例如我的网段 192.168.179.0,因此我随便设置了一个192.168.179.100 ifconfig eth0:2 192.168.179.100/24 # 设置转发级别, /proc不允许使用vim进行修改 ,因为使用vi命令会在目录下创建交换文件(临时配置) echo "1" > /proc/sys/net/ipv4/ip_forward
  4. 调整RS的响应。通告级别(每一台RS都配, node2, node3) # node2 echo 1 > /proc/sys/net/ipv4/conf/eth0/arp_ignore echo 2 > /proc/sys/net/ipv4/conf/eth0/arp_announce # node3 echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
  5. 配置RS的VIP(每一台RS都配, node2, node3) ifconfig lo:8 192.168.179.100 netmask 255.255.255.255
  1. 启动RS上的httpd(node2, node3) # 下载httpd服务 yum install -y httpd # 进入相关目录 cd /var/www/html # 添加主页内容 vi index.html ------------------index.html------------ from ooxxip(各自ip) ------------------index.html------------ # 启动服务 service httpd start # 关闭防火墙(node1,node2,node3主机) service iptables stop 客户端验证: VIP:80不能显示(node1) 访问的是node的vip: 192.168.179.100 ,而不是真实 ip:192.168.179.140 RIP:80 能显示(node2,node3)
  1. LVS——ipvsadm(node1) # 下载对lvs命令的支持 yum install -y ipvsadm # 设置监控的包 ipvsadm -A -t 192.168.179.100:80 -s rr # 添加real server(noed2, node3),-g直接路由, ipvsadm -a -t 192.168.179.100:80 -r 192.168.179.141 -g ipvsadm -a -t 192.168.179.100:80 -r 192.168.179.142 -g # 查看配置是否成功(图1) ipvsadm -ln # 浏览器刷新: 访问vip(node1),然后F5刷新,测试配置的轮询算法是否生效,并多次重复刷新 访问的是node的vip: 192.168.179.100 而不是真实 ip:192.168.179.140 # 查看tcp协议连接的有哪些进程 netstat -natp # 验证lVS偷窥/负载记录(图2) ipvsadm -lnc 图1

图2

LVS简单总结 负载均衡 四层(传输层) 不会握手——高速转发 丰富的调度算法

缺点: 后端:镜像服务器 ,没有健康检查机制 自身:单点故障(如果lvs出现故障,会导致服务不可用; 如果RS出现故障会出现数据倾斜)

没有解决的问题: 后端服务器如果臃肿,由计算和io瓶颈,lvs是无能为力的 这里就引出下面了Keepalived技术

第三章 Keepalived

keepalived是集群管理中保证集群高可用的服务软件

一 高可用 High Available

  1. 需要心跳机制探测后端RS是否提供服务。 a) 探测down,需要从lvs中删除该RS b) 探测发送从down到up,需要从lvs中再次添加RS。
  2. Lvs DR,需要主备(HA) Keepalived 原理: VRRP协议(虚拟路由冗余协议) - Virtual Router Redundancy Protocol IP漂移

Keepalived下的lvs架构图

添加了用于备用的lvs服务器 , keepalived通过心跳检测的形式检测lvs和rs , 如果主lvs出现问题则 ,立即为备份主机分配vip ,并令其生效

二 模拟实验配置

node1 主lvs node4 备lvs node2,node3 real server(RS) 每个节点node对应一台物理主机 进行实验时,防火墙必须关闭

# 1.清除上个实验lvs的配置(node1)
ipvsadm -C

# 2.查看相关配置是否清除完毕
ipvsadm -in

# 3. 关闭上个实验node1中vip的设置
ifconfig 
ifconfig eth0:2 down

# 4.打开node4, 可以不用安装ipvsadm -lnc ,安装 Keepalived ,keepalived(ipvsadm,自身高可用)
# 配置RS的步骤还和上次实验一致
yum install -y  keepalived ipvsadm 
yum install -y ipvsadm   # 这里安装 ipvsadm不是用来配置而是用来查看内核相关接口情况

# 5.在node1中安装Keepalived 
yum install -y  keepalived

# 6. 修改配置文件(建议备份一下)
cd /etc/keepalived
# 帮助手册
man keepalived.conf
cp keepalived.conf  keepalived.conf.bak
vim keepalived.conf    
# 修改内容在下一个代码块


# 7. 远程复制(从 node1复制到node4 ,图1)
scp ./keepalived.conf root@192.168.179.143:`pwd`
# 修改node4的角色状态并降低权重
vrrp_instance VI_1 {
    state BACKUP        #MASTER 主和从 
    priority 50      #优先级 主的优先级要高
}


# 8. 启动Keepalived(node1,node4)
service keepalived start
# 查看日志
tail /var/log/messages 


# 9. 测试
# a.查看通过keepalived配置的lvs是否生效(如图2)
ipvsadm -ln   # 对主备都进行查看
# 访问 node1的vip,刷新浏览器,看轮询算法是否生效(图3)

# b.模拟主lvs宕机 ,将node1网卡关闭 ,使node1与外界失联, 访问浏览器,查看轮询算法是否有异常(无异常:模拟用户对异常的无感知)
ifconfig eth0 down
# 模拟运维修复主lvs异常 ,在虚拟机中将网卡启动 ,查看虚拟接口 eth0:3是否从备机自动分配到了主机
ifconfig eth0 up

# c.关闭启动一个real server(node2/node3) ,查看keepalived是否将宕掉的real剔除(keepalived不仅对准备做健康检查还会对realserver做健康检查)
service httpd stop  # 测试完毕后重启服务
ipvsadm -ln        # 对主备都进行查看

# d.模拟keepalived软件异常退出(在node1中执行)
ps -ef | grep keep   # 定位keepalived的所有端口号
kill  -9 端口号   # 所有都结束
#这样做的结果是备机node4也会拥有接口eth0:3, 而导致主备都会拥有同一个vip的情况 
#我们都知道在互联网中ip地址必须唯一,所以这样做是非常危险的,同时这也是keepalived的一个bug

步骤5修改的内容代码

# 主要是对端口号,virtual_ipaddress,persistence_timeout,lb_kind DR,心跳检查返回的状态的膝盖
# 这里没出现的建议删除

global_defs {
   notification_email {
     root@localhost  #发送提醒邮件的目标地址可有多个
     goldbin@126.com
  }
   notification_email_from test@localhost              #发送邮件的from地址,可以随意写,邮件地址不存在都无所谓
   smtp_server 127.0.0.1             #邮件服务的地址,一般写本地
   smtp_connect_timeout 30
   router_id LVS_DEVEL
}


vrrp_instance VI_1 {
    state MASTER        #MASTER 主和从 
    interface eth0        #VIP需要绑定的网卡名称
    virtual_router_id 51
    priority 100       #优先级 主的优先级要高
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {  
        192.168.179.100/24 dev eth0 label eth0:3         #设置VIP
    }
}


virtual_server 192.168.179.100 80 {       #设置虚拟lvs服务,VIP PORT
    delay_loop 6
    lb_algo rr   #调度算法wrr
    lb_kind DR  #lvs的模式
    nat_mask 255.255.255.0
   # persistence_timeout 50 同一个IP地址在50秒内lvs转发给同一个后端服务器
    persistence_timeout 0
    protocol TCP
  
    real_server 192.168.179.141 80 {       #设置真实服务器的心跳机制 RID PORT
        weight 1      #权重
        HTTP_GET {      #心跳检测的方式
            url {
              path /      #心跳检查的地址
              status_code 200      #心跳检查返回的状态
            }
            connect_timeout 3       #超时时间
            nb_get_retry 3      #重复检查3次
            delay_before_retry 3      #每隔1秒钟再次检查
        }
    }
    real_server 192.168.179.142 80 {      #第二个真实服务器设置
      weight 1      #权重
        HTTP_GET {      #心跳检测的方式
            url {
              path /      #心跳检查的地址
              status_code 200      #心跳检查返回的状态
            }
            connect_timeout 2       #超时时间
            nb_get_retry 3      #重复检查3次
            delay_before_retry 3      #每隔1秒钟再次检查
        }
   }

}

图1

图2

图3

第四章 Nginx和 Tengine

一 介绍

Nginx

Nginx (“engine x”) 是一个高性能的 HTTP 和 反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、 示例配置文件和低系统资源的消耗而闻名 .官方测试nginx能够支撑5万并发链接, 并且cpu、内存等资源消耗却非常低,运行非常稳定. 其特点是占有内存少,并发能力强, 事实上nginx的并发能力确实在同类型的网页服务器中表现较好, 中国大陆使用nginx网站用户有:新浪、网易、腾讯等。

Tengine

Tengine 是nginx的加强版,封装版 , 淘宝开源 ,官网http://tengine.taobao.org/ 动态模块加载(DSO)支持。加入一个模块不再需要重新编译整个Tengine; 支持SO_REUSEPORT选项,建连性能提升为官方nginx的三倍; 支持SPDY v3协议,自动检测同一端口的SPDY请求和HTTP请求; 流式上传到HTTP后端服务器或FastCGI服务器,大量减少机器的I/O压力; 更加强大的负载均衡能力,包括一致性hash模块、会话保持模块,还可以对后端的服务器进行主动健康检查,根据服务器状态自动上线下线,以及动态解析upstream中出现的域名; 输入过滤器机制支持。通过使用这种机制Web应用防火墙的编写更为方便; 支持设置proxy、memcached、fastcgi、scgi、uwsgi在后端失败时的重试次数 动态脚本语言Lua支持。扩展功能非常高效简单; 支持管道(pipe)和syslog(本地和远端)形式的日志以及日志抽样; 支持按指定关键字(域名,url等)收集Tengine运行状态; 组合多个CSS、JavaScript文件的访问请求变成一个请求; 自动去除空白字符和注释从而减小页面的体积

常用高并发模型设计

利用lvs来管理Nginx ,每台Nginx可支撑五万链接 ,因此通过这样的架构可以轻松实现对百万链接支撑的请求

二 Nginx和apache(httpd)的优缺点

  1. nginx相对于apache的优点: 轻量级,同样起web 服务,比apache 占用更少的内存及资源 抗并发,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗 高性能, 高度模块化的设计,编写模块相对简单 社区活跃,各种高性能模块出品迅速
  2. apache 相对于nginx 的优点: rewrite ,比nginx 的rewrite 强大 模块多,基本想到的都可以找到 少bug ,nginx 的bug 相对较多
  3. Nginx 配置简洁, Apache 复杂
  4. ,最核心的区别在于apache是同步多进程模型,一个连接对应一个进程;nginx是异步的,多个连接(万级别)可以对应一个进程

三 安装Tengine并制作Nginx脚本

Tengine集成了nginx ,所以我们只需要安装Tengine即可

# 1、安装依赖 gcc openssl-devel pcre-devel zlib-devel
yum install gcc openssl-devel pcre-devel zlib-devel -y

# 2.上传Tengine,解压,复制
# 要注意我们所复制后的目录以及复制后的文件名
tar tengine-2.1.0.tar.gz -rf
cp tengine-2.1.0 /usr/local/ -rf

# 3. 在复制后的tengine-2.1.0目录下运行
./configure 
  --prefix=/usr/local/tengine-2.1.0


# 4 .编译安装
make && make install

# 5. 上传脚本使nginx可以像服务一样启动 ,内容在下一个代码块
cd /etc/init.d/
chmod +x nginx 

# 6、添加该文件到系统服务中去
	chkconfig --add nginx
	查看是否添加成功
	chkconfig --list nginx

# 7. 运行nginx服务
service nginx  start

# ps:如果出现没有相关文件的错误, 请使用touch目录创建
# 在浏览器输入配置Tengine的ip即可访问,如下图

脚本内容

注意这里配置的路径要与我们make install所在的目录一致 nginx="/usr/local/tengine-2.1.0/sbin/nginx" NGINX_CONF_FILE="/usr/local/tengine-2.1.0/conf/nginx.conf"

#!/bin/sh
#
# nginx - this script starts and stops the nginx daemon
#
# chkconfig:   - 85 15 
# description:  Nginx is an HTTP(S) server, HTTP(S) reverse 
#               proxy and IMAP/POP3 proxy server
# processname: nginx
# config:      /etc/nginx/nginx.conf
# config:      /etc/sysconfig/nginx
# pidfile:     /var/run/nginx.pid
 
# Source function library.
. /etc/rc.d/init.d/functions
 
# Source networking configuration.
. /etc/sysconfig/network
 
# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0
 
nginx="/usr/local/tengine-2.1.0/sbin/nginx"
prog=$(basename $nginx)
 
NGINX_CONF_FILE="/usr/local/tengine-2.1.0/conf/nginx.conf"
 
[ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx
 
lockfile=/var/lock/subsys/nginx
 
make_dirs() {
   # make required directories
   user=`nginx -V 2>&1 | grep "configure arguments:" | sed 's/[^*]*--user=([^ ]*).*/1/g' -`
   options=`$nginx -V 2>&1 | grep 'configure arguments:'`
   for opt in $options; do
       if [ `echo $opt | grep '.*-temp-path'` ]; then
           value=`echo $opt | cut -d "=" -f 2`
           if [ ! -d "$value" ]; then
               # echo "creating" $value
               mkdir -p $value && chown -R $user $value
           fi
       fi
   done
}
 
start() {
    [ -x $nginx ] || exit 5
    [ -f $NGINX_CONF_FILE ] || exit 6
    make_dirs
    echo -n $"Starting $prog: "
    daemon $nginx -c $NGINX_CONF_FILE
    retval=$?
    echo
    [ $retval -eq 0 ] && touch $lockfile
    return $retval
}
 
stop() {
    echo -n $"Stopping $prog: "
    killproc $prog -QUIT
    retval=$?
    echo
    [ $retval -eq 0 ] && rm -f $lockfile
    return $retval
}
 
restart() {
    configtest || return $?
    stop
    sleep 1
    start
}
 
reload() {
    configtest || return $?
    echo -n $"Reloading $prog: "
    killproc $nginx -HUP
    RETVAL=$?
    echo
}
 
force_reload() {
    restart
}
 
configtest() {
  $nginx -t -c $NGINX_CONF_FILE
}
 
rh_status() {
    status $prog
}
 
rh_status_q() {
    rh_status >/dev/null 2>&1
}
 
case "$1" in
    start)
        rh_status_q && exit 0
        $1
        ;;
    stop)
        rh_status_q || exit 0
        $1
        ;;
    restart|configtest)
        $1
        ;;
    reload)
        rh_status_q || exit 7
        $1
        ;;
    force-reload)
        force_reload
        ;;
    status)
        rh_status
        ;;
    condrestart|try-restart)
        rh_status_q || exit 0
            ;;
    *)
        echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"
        exit 2
esac

Tengine配置成功图

四 配置文件nginx.conf

位于Nginx编译安装后指定的目录 cd /usr/local/tengine-2.1.0/conf/nginx.conf

配置文件解读

#1.定义Nginx运行的用户和用户组
user root root;

#nginx进程数,建议设置为等于CPU总核心数。
worker_processes 1;

#全局错误日志定义类型,[ debug | info | notice | warn | error | crit ]
error_log /var/log/nginx/error.log info;

#进程文件
pid /var/run/nginx.pid;

#一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(系统的值ulimit -n)与nginx进程数相除,但是nginx分配请求并不均匀,所以建议与ulimit -n的值保持一致。
worker_rlimit_nofile 65535;
#工作模式与连接数上限
events {
    #参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型是Linux 2.6以上版本内核中的高性能网络I/O模型,如果跑在FreeBSD上面,就用kqueue模型。
    use epoll;
    #单个进程最大连接数(总的最大连接数=连接数*进程数)
    worker_connections 1024;
}


#2. event下的一些配置及其意义
 #单个后台worker process进程的最大并发链接数    
    #worker_connections  1024;
    # 并发总数是 worker_processes 和 worker_connections 的乘积
    # 即 max_clients = worker_processes * worker_connections
    # 在设置了反向代理的情况下,max_clients = worker_processes * worker_connections / 4  为什么
    # 为什么上面反向代理要除以4,应该说是一个经验值
    # 根据以上条件,正常情况下的Nginx Server可以应付的最大连接数为:4 * 8000 = 32000
    # worker_connections 值的设置跟物理内存大小有关
    # 因为并发受IO约束,max_clients的值须小于系统可以打开的最大文件数
	# 而系统可以打开的最大文件数和内存大小成正比,一般1GB内存的机器上可以打开的文件数大约是10万左右
    # 我们来看看360M内存的VPS可以打开的文件句柄数是多少:
    # $ cat /proc/sys/fs/file-max
    # 输出 34336
    # 32000 < 34336,即并发连接总数小于系统可以打开的文件句柄总数,这样就在操作系统可以承受的范围之内
    # 所以,worker_connections 的值需根据 worker_processes 进程数目和系统可以打开的最大文件总数进行适当地进行设置
    # 使得并发总数小于操作系统可以打开的最大文件数目
    # 其实质也就是根据主机的物理CPU和内存进行配置
    # 当然,理论上的并发总数可能会和实际有所偏差,因为主机还有其他的工作进程需要消耗系统资源。
    # ulimit -SHn 65535

#3. 设定http服务器
http {
    include mime.types; #文件扩展名与文件类型映射表
    default_type application/octet-stream; #默认文件类型
    #charset utf-8; #默认编码
    server_names_hash_bucket_size 128; #服务器名字的hash表大小
    client_header_buffer_size 32k; #上传文件大小限制
    large_client_header_buffers 4 64k; #设定请求缓
    client_max_body_size 8m; #设定请求缓
    sendfile on; #开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件,对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。注意:如果图片显示不正常把这个改成off。
    autoindex on; #开启目录列表访问,合适下载服务器,默认关闭。
    tcp_nopush on; #防止网络阻塞
    tcp_nodelay on; #防止网络阻塞
    keepalive_timeout 120; #长连接超时时间,单位是秒
    
    #5. gzip模块设置
    gzip on; #开启gzip压缩输出
    gzip_min_length 1k; #最小压缩文件大小
    gzip_buffers 4 16k; #压缩缓冲区
    gzip_http_version 1.0; #压缩版本(默认1.1,前端如果是squid2.5请使用1.0)
    gzip_comp_level 2; #压缩等级
    gzip_types text/plain application/x-javascript text/css application/xml;
    #压缩类型,默认就已经包含text/html,所以下面就不用再写了,写上去也不会有问题,但是会有一个warn。
    gzip_vary on;
    #limit_zone crawler $binary_remote_addr 10m; #开启限制IP连接数的时候需要使用
    
    #6. 虚拟主机的配置
    #nginx支持三种类型的虚拟主机配置,
    #1、基于ip的虚拟主机, (一块主机绑定多个ip地址)
    #2、基于域名的虚拟主机(servername)
    #3、基于端口的虚拟主机(listen如果不写ip端口模式)
    server {
        #监听端口
        listen 80;
        #域名可以有多个,用空格隔开
        server_name www.ha97.com ha97.com;

    #7. location 映射(ngx_http_core_module)
    	#location [ = | ~ | ~* | ^~ ] uri { ... }
    	#location URI {}:
    	#	对当前路径及子路径下的所有对象都生效;
    	#location = URI {}: 注意URL最好为具体路径。
        #   精确匹配指定的路径,不包括子路径,因此,只对当前资源生效;
    	#location ~ URI {}:
    	#location ~* URI {}:
    	#	模式匹配URI,此处的URI可使用正则表达式,~区分字符大小写,~*不区分字符大小写;
    	#location ^~ URI {}:
    	#	不使用正则表达式
    	#优先级:= > ^~ > ~|~* >  /|/dir/
        #=前缀的指令严格匹配这个查询。如果找到,停止搜索。
        # 特别注意: 所有剩下的常规字符串,最长的匹配。如果这个匹配使用^〜前缀,搜索停止。 
        #正则表达式,在配置文件中定义的顺序。
        #如果第3条规则产生匹配的话,结果被使用。否则,如同从第2条规则被使用
        #先普通
            #顺序无关
            #最大前缀
            #匹配规则简单
            #打断:
                #^~
                #完全匹配
        #再正则
            #不完全匹配
            #正则特殊性:一条URI可以和多条location匹配上
            #有顺序的
            #先匹配,先应用,即时退出匹配
        #IP访问控制
        #location  {
        	  # deny  IP /IP段   禁止访问
        	   #deny  192.168.1.109;
        	   #allow 192.168.1.0/24;192.168.0.0/16;192.0.0.0/8   允许访问
        	#}
        #规则:按照顺序依次检测,直到匹配到第一条规则
       # 用户认证访问
        #模块ngx_http_auth_basic_module 允许使用“HTTP基本认证”协议验证用户名和密码来限制对资源的访问。
        #	location / {
        #		auth_basic           "closed site";
        #		auth_basic_user_file /var/users;
        #    }
        #Apache发行包中的htpasswd命令来创建user_file 文件
        
        #要通过yum –y install httpd
        #htpasswd -c -m /var/users username
        #注:需要安装httpd才可以使用上面命令
        

        location ~ .*.(php|php5)?$
            index index.html index.htm index.jsp;
            root /data/www/ha97;
        {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.jsp;
        include fastcgi.conf;
    }
}    

技术延伸

通过对Tengine中Nginx的配置文件修改达到下面的效果

利用Nginx配置虚拟Server

# 1.修改配置文件
# 修改配置文件中的server, 修改了server_name
# 创建了一个server,配置server_name ,listen 以及location

 	server {
        listen 80;
        server_name www.chy.com;
        location / {
            root   /mnt;
            autoindex on;    #这里配置了自动索引
        }

         }
    server {
        listen       80;
        server_name  www.timepause.com;


        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }



# 2. 重新加载nginx ( 需要将Nginx封装成一个服务,本章第三节支持 )
service nginx reload

# 3. 在本地hosts文件中配置地址映射,将上面配置的两个域名映射到有nginx的虚拟机中
# hosts 文件位于 C:WindowsSystem32driversetc下
192.168.179.140  www.chy.com www.timepause.com

# 4.测试访问
www.timepause.com   如图1
www.chy.com              如图2

# 5. 如果虚拟机添加了光盘文件.可以挂载,以列表的形式进行显示
mount /dev/cdrom /mnt/
# 再次访问  www.chy.com (这样可以做yum的本地安装库, 如图3)
#思路 :找一台物理机+nginx可以作为公司的本地安装源

图1

图2

如3

利用Nginx对服务器做反向代理

在配置文件中的server配置中的location中添加如下配置

location / {
           root   html;
            index  index.html index.htm;
        }
         location /chy.doc {
             proxy_pass http://192.168.179.141/;
         }
        location ~* .go$  {
            proxy_pass http://192.168.179.142;
        }

通过上面配置并重载服务, 即可实现对指定uri的静态访问拦截 ,与动态拦截(图1) 静态拦截如图2,图3. 可以拦截server_name后面为 /chy.doc的请求, 但会在url中将 chy.doc 去掉, 因此我们实际访问的都是Index.html页面 如访问 server_name/chy.doc/index.html 通过Nginx处理后实际访问的是 192.168.179.141/index.html 访问 server_bane/chy.doc 通过Nginx处理后实际访问的是 192.168.179.141/ ,这里默认的也是访问index.html

正则表达式动态拦截, 可以拦截 .go 结尾的uri, 并且以 ip/uri 的路径将其转发(不会去掉该uri ,图4), 如访问 server_name/index.go 通过Nginx处理后实际访问的是 192.168.179.142/index.go 但是要求我们在 192.168.179.192这台主机需要有 index.go页面处理

图1

图2

图3

图4

利用Nginx做反向代理负载均衡

修改Tengine 中的nginx中的配置文件nginx.config

# 1. 修改超时时间,如果不为0 ,会等到超时时间结束才会被负载到另一个服务器, 
keepalive_timeout  0;

# 2. 创建 upstream配置, 名称任意 ,配置用于负载的服务器, 一般位于server配置的上方
upstream testbalance {
        server 192.168.179.141;
        server 192.168.179.142;
    }

# 3. 配置反向代理
# 重载服务后, 我们只需要访问 server_name+用于反向代理的localtion.即http://www.timepause.com/test
#就会被随机负载到 .141和 .142的两台服务器上了( 图1,图2 )
  location /test {                    							  # location后的拦截路径名任意,但是需要有"/"
             proxy_pass http://testbalance/;            # 这里最后也要有 "/;"
         }
         
----------------------------------------------------------
    server {
        listen       80;
        server_name  www.timepause.com;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
           root   html;
            index  index.html index.htm;
        }
         location /test {
             proxy_pass http://testbalance/;
         }
}
----------------------------------------------------------

图1

图2

利用Nginx 对指定域名(百度)进行代理

在 nginx.conf中的server配置中添加如下代码 ,修改后需要重载nginx服务

location /baidu {
             proxy_pass https://www.baidu.com/;
         }
# 重载nginx服务,访问 (图1)
service nginx reload 

注意 :

  1. 如果遇到图3问题需要配置默认网关route add default gw 网关地址要根据自己所在网段进行配置
  2. 我们需要注意的是避免域名直接的跳转 ,如果我们将反向代理写成了这个 http://www.baidu.com/ , 由于现在的网址使用的协议大多都是https(安全性考虑) ,通过Nginx代理后的目录就是( 图2 ), 导致Nginx不能对当前域名进行管理, 因此在今后的项目代码中尽量不要出现客户端的跳转 以后在企业开发时一定多多注意类似问题

图1

图2

图3

小技巧 :利用 !命令的前几个字符匹配命令并执行

举例 ,其他命令也同本例

# 利用 ifconfig 命令查看网络信息,图1
ifconfig 

# 利用 !if 可快速执行上一个,命令,图2
!if

#这个命令的原理是会调用 history 命令来查看我们的历史命令,
#通过对我们在!后书写的字符进行匹配 ,如果匹配到就进行执行该命令

图1

图2

nginx如何识别我们的域名

我们发送这样的域名请求时 ,虽然域名被本地hosts文件映射到了对应的虚拟机ip地址,因此我们实际访问的其实是对应的IP地址 但我们仍可以根据域名访问到对应的地址 , 这是因为在我们发送域名请求时, 域名信息也会被封装到请求头的Host中发送(图1) Nginx扫描请求头中的信息(域名/ip/端口) ,然后与Nginx的配置文件的server配置(图2)进行比对 ,如果符合就跳转到对应的页面

图2

利用Nginx的access.log监听用户的浏览信息

该日志文件位于Nginx目录下的: logs/access.log 利用 tail -f access.log 可以通过Nginx持续监听用户在被Nginx代理的网站的浏览记录 例如下图中我访问了chy.com 的 /EFI 目录 ,而日志中就会显示我的访问信息 个人感觉这个功能可能在数据收集中可能会有意想不到的作用

拓展: 指定 access.log 日志输出格式

通过对 nginx.conf 文件的修改 ,获取指定格式的日志格式文件, 方便我们进行日志分析

  • Nginx中文文档 http://tengine.taobao.org/nginx_docs/cn/docs/
  • 修改日志格式参数的参考(下面两个地方) 1.日志模块格式配置所在地址 http://tengine.taobao.org/nginx_docs/cn/docs/http/ngx_http_log_module.html 2.内嵌变量介绍 http://tengine.taobao.org/nginx_docs/cn/docs/http/ngx_http_core_module.html#variables 注意: 可以通过将这些字段复制, 然后通过ctrl+f 在上面两个页面中查找

具体步骤

  1. 创建 my_log 用于格式化日志输出格式(图1) # 注意: 这里的^A 是通过 ctrl+v 和 ctrl+A 实现的 , 方面我们以后进行日志的分割 'remote_addr^Amsechttp_host^Arequest_uri';
  2. 创建一个location ,用于捕获浏览器请求为 xx/log.gif 的请求 (图2) 注意: /opt/data/access.log 会在我们重载nginx 服务后自动创建 location =/log.gif { default_type image/gif; access_log /opt/data/access.log my_log; }
  3. 重载nginx服务 ,访问 # 重载 service nginx reload # 打开日志记录文件 ,阻塞式访问 ( 图4 ) tail -f /opt/data/access.log # 浏览器访问(图3 ) http://node2/log.gif http://node2/log.gif?name=002&age=15 #看一看到带参访问的参数也会被显示到日志文件 # 打开 ctrl+f5 快速清除缓存
  4. 额外补充: 我们可以看到直接访问nginx页面会出现 404请求资源不存在, 我们可以随便找一张图片修改其名称为 log.gif 放到nginx的html目录下( /usr/local/tengine-2.1.0/html ),然后再次访问即可看到图片啦( 图5 )~~~

图1

图2

图3

图4

图5

五 session一致性问题的解决

情景模拟

三台虚拟机node1,node2,node3 node2,3安装jdk与tomcat(目的是运行tomcat) node1 安装Nginx, memcached

# 1. 修改主页,便于识别,查看session,访问页面,如图1,图2
# node2中的tomcat的index页面, 清除所有内容,编写如下代码 ,用于分辨主机以及对应的session信息(  位于/webapps/ROOT/index.jsp )
-----------------------------------
from 192.168.179.141<br>
<%=session.getId()%>

# node3中的tomcat的index页面,清除所有内容,编写如下代码(  /webapps/ROOT/index.jsp )
-----------------------------------
from 192.168.179.142<br>
<%=session.getId()%>

# 2 利用Nginx代理上面两个服务器(给我们的感觉是代理了两个tomcat主页 ) ,修改nginx.conf (配置负载均衡upstream 以及location  )
----------------------------配置的内容-----------------
 upstream test.tomcat.session {
        server 192.168.179.141:8080;
        server 192.168.179.142:8080;
        }

   server {
        listen       80;
        server_name  www.timepause.com;
        
        location /tomcat {
          proxy_pass http://test.tomcat.session/;
        }

}

# 3. 重载nginx 服务,访问 浏览器 ,可以看到一直刷新可以分别访问到这两个tomcat主页(图3,图4 )
http://www.timepause.com/tomcat

# 4.观察session变化我们可以看到,每次刷新session都会改变,这里就说明出现了session不一致的情况,下面我们将解决这个问题

图1

图2

图3

图4

解决方案——安装memcached

安装memcached可以为我们解决seession的问题

# 1、安装memcached(node1)
  yum -y install memcached
  
# 2、启动memcached(node1)
	memcached -d -m 128m -p 11211 -l 192.168.179.140 -u root -P /tmp/
	-d:后台启动服务
	-m:缓存大小
	-p:端口
	-l:IP
	-P:服务器启动后的系统进程ID,存储的文件
	-u:服务器启动是以哪个用户名作为管理用户

	
# 3. 修改node2,node3中tomcat中conf  ( context.xml ), 注意memcachedNodes的配置
------------------------------------------------------------------------
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager" 
	memcachedNodes="n1:192.168.179.140:11211" 
    sticky="false" 
    lockingMode="auto"
    sessionBackupAsync="false"
	requestUriIgnorePattern=".*.(ico|png|gif|jpg|css|js)$"
    sessionBackupTimeout="1000" transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory" />



# 4、拷贝jar到tomcat的lib下 ,分享在博客末尾
[asm-3.2.jar]
[kryo-1.04.jar]
[kryo-serializers-0.11.jar]
[memcached-session-manager-1.7.0.jar]
[memcached-session-manager-tc7-1.8.1.jar]
[minlog-1.2.jar]
[msm-kryo-serializer-1.7.0.jar]
[reflectasm-1.01.jar]
[spymemcached-2.7.3.jar]


5. 重启tomcat ,访问测试
由图1,图2可以看到运行memcached 就消除了一致性问题 ,而且他们的时间也被一致化了

注意: 时间一致性 不仅是Nginx代理的tomcat需要时间一致, 集群中时间也必须一致 , 查看虚拟机当前时间命令 date , 通过date -s 设置时间 . 可以通过xshell的全部会话功能( 图3 )来同步时间

[root@node4 ~]# date -s "2011-11-11 11:11:11"
Fri Nov 11 11:11:11 CST 2011

图1

图2

图3

时间一致性的另一种解决方案(安装ntpdate服务)

  1. 在Xshell右下方选择全部会话功能, 安装ntp服务 yum -y install ntpdate
  2. 启动ntp服务(-u指定的是网络上的ntp服务器,可以自行搜索) ntpdate -u ntp.api.bz

链接:https://pan.baidu.com/s/11KOnUQcDOEfSkmYcZdU9jw 提取码:nc38