1.Nginx简介
•Nginx(“engine x”)是一个高性能的HTTP和反向代理服务器,也是一IMAP/POP3/SMTP代理服务器。
•第一个公开版本0.1.0发布于2004年10月4日。
•其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名
•官方测试nginx能够支支撑5万并发链接,并且cpu、内存等资源消耗却非常低,运行非常稳定
2011年6月1日,nginx1.0.4发布。
•Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like协议下发行。由俄罗斯的程序设计师IgorSysoev所开发,其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:新浪、网易、腾讯等。
功能:
1.web服务器
2.webreverseproxy
3.smtpreverseproxy
官网:
2.Nginx和apache(PHP)的优缺点(面试题)
nginx相对于apache的优点:
轻量级,同样起web服务,比apache占用更少的内存及资源
抗并发,nginx处理请求是异步非阻塞的,而apache则是阻塞型的,在高并发下nginx
能保持低资源低消耗高性能
高度模块化的设计,编写模块相对简单
社区活跃,各种高性能模块出品迅速
apache相对于nginx的优点:
rewrite,比nginx的rewrite强大
模块超多,基本想到的都可以找到
少bug,nginx的bug相对较多
Nginx配置简洁,Apache复杂
最核心的区别在于apache是同步多进程模型,一个连接对应一个进程;
nginx是异步的,多个连接(万级别)可以对应一个进程
3.Tengine
Tengine 是nginx的加强版,封装版,淘宝开源 基于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文件的访问请求变成一个请求;
– 自动去除空白字符和注释从而减小页面的体积
4.面临的问题
• 如何解决高并发和负载均衡
• 如何解决高可用问题
• 如何负载均衡的session一致性问题(后期用redis解决)
5.传统的WEB架构高并发问题
• 单个tomcat支持最高并发
• 怎么解决高并发问题,解决单个服务器过载问题
• 前端和后端架构
• Tomcat: 一个Servlet和JSP容器
• 前端服务器处理静态页面
6.Tengine的编译安装
6.1 安装之前准备
上传安装文件到指定目录
解压安装文件
[root@21 opt]# tar -zxvf tengine-2.1.0.tar.gz
安装依赖: yum -y install gcc openssl-devel pcre-devel zlib-devel
6.2编译安装tengine
在tengine目录中执行以下命令:
–prefix=/opt/tengine-2.1.0/ \ 注意:这里需要改为你自己的tengin的安装路径
./configure \
--prefix=/opt/soft/tengine-2.1.0/ \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx/nginx.pid \
--lock-path=/var/lock/nginx.lock \
--with-http_ssl_module \
--with-http_flv_module \
--with-http_stub_status_module \
--with-http_gzip_static_module \
--http-client-body-temp-path=/var/tmp/nginx/client/ \
--http-proxy-temp-path=/var/tmp/nginx/proxy/ \
--http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ \
--http-uwsgi-temp-path=/var/tmp/nginx/uwsgi \
--http-scgi-temp-path=/var/tmp/nginx/scgi \
--with-pcre
执行安装命令:
make && make install
6.3为tengine准备启动文件
在/etc/init.d目录下,创建一个启动文件名字为 nginx,并粘贴以下脚本:
#!/bin/bash
#
# chkconfig: - 85 15
# description: nginx is a World Wide Web server. It is used to serve
# 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="/opt/soft/tengine-2.1.0/sbin/nginx"
prog=$(basename $nginx)
NGINX_CONF_FILE="/opt/soft/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: "
# -HUP是nginx平滑重启参数
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
给启动脚本可执行权限
[root@21 init.d]# chmod 755 nginx
创建该目录:
[root@21 init.d]# mkdir -p /var/tmp/nginx/client/
启动nginx
[root@bruce1 init.d]# systemctl start nginx
注意:重新启动后会报
nginx: [emerg] open() "/var/run/nginx/nginx.pid" failed (2: No such file or directory)
需要在nginx的配置文件中
打开pid:配置nginx的安装路径.
7.Tengine的配置文件详解
在tengine的安装文件中,有一个核心的配置文件
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
# load modules compiled as Dynamic Shared Object (DSO)
#
#dso {
# load ngx_http_fastcgi_module.so;
# load ngx_http_rewrite_module.so;
#}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
配置文件解析
–#定义Nginx运行的用户和用户组
–user www www;
–#nginx进程数,建议设置为等于CPU总核心数。
–worker_processes 8;
–#全局错误日志定义类型,[ 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 65535;
}
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
http下的一些配置及其意义
– #设定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; #长连接超时时间,单位是秒
gzip的一些配置及其意义
– #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连接数的时候需要使用
虚拟主机一些配置及其意义
– #虚拟主机的配置
server
{
#监听端口
listen 80;
#域名可以有多个,用空格隔开
server_name www.ha97.com ha97.com;
index index.html index.htm index.jsp;
root /data/www/ha97;
location ~ .*\.(php|php5)?$
{
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.jsp;
include fastcgi.conf;
}
8.反向代理
8.1 反向代理的概念
通常的代理服务器,只用于代理内部网络对Internet的连接请求,客户机必须指定
代理服务器,并将本来要直接发送到Web服务器上的http请求发送到代理服务器中由代理服务器向Internet上的web服务器发起请求,最终达到客户机上网的的。而反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
8.2 经典的反向代理结构
upstream mynginx{
server 192.168.221.22:8080;
server 192.168.221.23:8080;
}
server {
listen 80;
server_name www.nginx1.cn;
location / {
proxy_pass http://mynginx;
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
测试
8.4 反向代理的健康检查模块
upstream mynginx{
server 192.168.221.132:8080;
server 192.168.221.133:8080;
#这里配置
check interval=3000 rise=2 fall=5 timeout=1000 type=http;
check_http_send "HEAD / HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}
server {
listen 80;
server_name www.nginx1.cn;
location / {
proxy_pass http://mynginx;
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
#这里访问路径
location /ngx_status{
check_status;
}
}
测试
当其中的一个服务器宕机
9.Nginx的session一致性问题
http协议是无状态的,即你连续访问某个网页100次和访问1次对服务器来说是没有区别对待的,因为它记不住你。那么,在一些场合,确实需要服务器记住当前用户怎么办?比
如用户登录邮箱后,接下来要收邮件、写邮件,总不能每次操作都让用户输入用户名和密码吧,为了解决这个问题,session的方案就被提了出来,事实上它并不是什么新技术,
而且也不能脱离http协议以及任何现有的web技术。
session的常见实现形式是会话cookie(session cookie),即未设置过期时间的cookie,这个cookie的默认生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。
实现机制是当用户发起一个请求的时候,服务器会检查该请求中是否包含sessionid,如果未包含,则系统会创造一个名为JSESSIONID的输出 cookie返回给浏览器(只放入内存,并不
存在硬盘中),并将其以HashTable的形式写到服务器的内存里面;当已经包含sessionid是,服务端会检查找到与该session相匹配的信息,如果存在则直接使用该sessionid,若不存在则重新生成新的 session。这里需要注意的是session始终是有服务端创建的,并非浏览器自己生成的。但是浏览器的cookie被禁止后session就需要用get方法的URL重写的
机制或使用POST方法提交隐藏表单的形式来实现。
Session共享:
首先我们应该明白,为什么要实现共享,如果你的网站是存放在一个机器上,那么是不存在这个问题的,因为会话数据就在这台机器,但是如果你使用了负载均衡把请求分发到不同的机器呢?这个时候会话id在客户端是没有问题的,但是如果用户的两次请求到了两台不同的机器,而它的session数据可能存在其中一台机器,这个时候就会出现取不到session数据的情况,于是session的共享就成了一个问题。
10.Nginx的session一致性问题解决方案
1、session复制
tomcat 本身带有复制session的功能。(不用)
2、共享session
需要专门管理session的软件,memcached 缓存服务,可以和tomcat整合,帮助tomcat
共享管理session。
10.1 使用memcached解决session一致性问题
安装memcached
yum install -y memcached
启动memcached
service memcached start
测试memcached
退出 quit
向tomcat服务器lib中放入jar包,目的是让tomcat往缓存数据库中存取session,将以下jar包拷贝到lib目录下
修改server.xml里面修改Engine标签,添加jvmRoute属性,目的是查看sessionid里面带有tomcat的名字,就是这里配置的jvmRoute
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
配置tomcat的conf目录下的context.xml
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:192.168.17.9:11211"
sticky="false"
lockingMode="auto"
sessionBackupAsync="false"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
sessionBackupTimeout="1000" transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory" />
配置memcachedNodes属性,配置memcached数据库的ip和端口,默认11211,多个的话用空格隔开
目的?让tomcat服务器从memcached缓存里面拿session或者是放session
测试session是否一致
10.2使用redis解决session一致性问题(常用)
安装redis
启动时,制定配置文件
[root@22 redis]# ./bin/redis-server ./redis.conf
Redis默认端口:6379,通过当前服务进行查看
[root@22 redis]# ps -ef | grep -i redis
正确停止Redis的方式应该是向Redis发送SHUTDOWN命令,方法为(关闭默认的端口)
[root@22 redis]# ./bin/redis-cli shutdown
Ping,测试客户端与Redis的连接是否正常,如果连接正常,回收到pong
[root@22 redis]# ./bin/redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> ping
PONG
修改redis配置文件
安装redis缓存数据库,修改配置文件vi /etc/redis.conf将bind的127.0.0.1修改为本机地址,否则只能本机访问了
导入redislib中三个jar包到tomcat中
配置tomcat的conf目录下的context.xml
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
host="192.168.221.22"
port="6379"
database="0"
maxInactiveInterval="60" />
测试session是否一致
11.Tengine的会话保持
11.1 Tengine的会话保持模块简介
ngx_http_upstream_session_sticky_module
该模块是一个负载均衡模块,通过cookie实现客户端与后端服务器的会话保持, 在一定条件下可以保证同一个客户端访问的都是同一个后端服务器.
**mode****设置cookie的模式
Ø insert: 在回复中本模块通过Set-Cookie头直接插入相应名称的cookie。
Ø prefix: 不会生成新的cookie,但会在响应的cookie值前面加上特定的前缀,当浏览器带着这个有特定标识的cookie再次请求时,模块在传给后端服务前先删除加入的前缀,后端服务拿到的还是原来的cookie值,这些动作对后端透明。如:“Cookie: NAME=SRV~VALUE”。
Ø rewrite: 使用服务端标识覆盖后端设置的用于session sticky的cookie。如果后端服务在响应头中没有设置该cookie,则认为该请求不需要进行session sticky,使用这种模式,后端服务可以控制哪些请求需要sesstion sticky,哪些请求不需要。
参考文档:
http://tengine.taobao.org/document_cn/http_upstream_session_sticky_cn.html
配置示例:
配置
upstream mynginx{
!! session_sticky cookie=uid fallback=on path=/ mode=insert option=indirect;
server 192.168.221.132:8080;
server 192.168.221.133:8080;
check interval=3000 rise=2 fall=5 timeout=1000 type=http;
check_http_send "HEAD / HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}
server {
listen 80;
server_name www.nginx1.cn;
location / {
!! session_sticky_hide_cookie upstream=mynginx;
proxy_pass http://mynginx;
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location /ngx_status{
check_status;
}
}
#insert + indirect模式:
upstream test {
session_sticky cookie=uid domain=www.xxx.com fallback=on path=/ mode=insert option=indirect;
server 127.0.0.1:8080;
}
server {
location / {
#在insert + indirect模式或者prefix模式下需要配置session_sticky_hide_cookie
#这种模式不会将保持会话使用的cookie传给后端服务,让保持会话的cookie对后端透明
session_sticky_hide_cookie upstream=test;
proxy_pass http://test;
}
}
测试: