Nginx默认采用多进程工做方式,Nginx启动后,会运行一个master进程和多个worker进程。其中master充当整个进程组与用户的交互接口,同时对进程进行监护,管理worker进程来实现重启服务、平滑升级、更换日志文件、配置文件实时生效等功能。worker用来处理基本的网络事件,worker之间是平等的,他们共同竞争来处理来自客户端的请求。生产环境通常采用的是多进程+多路复用的形式。这里能够从服务器上能够很直观的看到:html
进程模型:前端
1. 在建立master进程时,先创建须要监听的socket(listenfd),而后从master进程中fork()出多个worker进程,如此一来每一个worker进程均可以监听用户请求的socket。通常来讲,当一个链接进来后,全部Worker都会收到通知,可是只有一个进程能够接受这个链接请求,其它的都失败,这是所谓的惊群现象。nginx提供了一个accept_mutex(互斥锁),有了这把锁以后,同一时刻,就只会有一个进程在accpet链接,这样就不会有惊群问题了。java
2. 先打开accept_mutex选项,只有得到了accept_mutex的进程才会去添加accept事件。nginx使用一个叫ngx_accept_disabled的变量来控制是否去竞争accept_mutex锁。ngx_accept_disabled = nginx单进程的全部链接总数 / 8 -空闲链接数量,当ngx_accept_disabled大于0时,不会去尝试获取accept_mutex锁,ngx_accept_disable越大,让出的机会就越多,这样其它进程获取锁的机会也就越大。不去accept,每一个worker进程的链接数就控制下来了,其它进程的链接池就会获得利用,这样,nginx就控制了多进程间链接的平衡。mysql
3.每一个worker进程都有一个独立的链接池,链接池的大小是worker_connections。这里的链接池里面保存的其实不是真实的链接,它只是一个worker_connections大小的一个ngx_connection_t结构的数组。而且,nginx会经过一个链表free_connections来保存全部的空闲ngx_connection_t,每次获取一个链接时,就从空闲链接链表中获取一个,用完后,再放回空闲链接链表里面。一个nginx能创建的最大链接数,应该是worker_connections * worker_processes。固然,这里说的是最大链接数,对于HTTP请求本地资源来讲,可以支持的最大并发数量是worker_connections * worker_processes,而若是是HTTP做为反向代理来讲,最大并发数量应该是worker_connections * worker_processes/2。由于做为反向代理服务器,每一个并发会创建与客户端的链接和与后端服务的链接,会占用两个链接。linux
相关的配置:nginx
worker_processes 1; // 工做进程数,建议设成CPU总核心数。 events { // 多路复用IO模型机制,epoll . select ....根据操做系统不一样来选择。linux 默认epoll use epoll; //io 模型 worker_connections 1024; // 每一个woker进程的最大链接数,数值越大,并发量容许越大 } http{ sendfile on;//开启零拷贝 }
Nginx 做为反向代理服务器,全部的流量都会通过 Nginx,因此 Nginx 自己的可靠性是咱们首先要考虑的问题。c++
keepalived:redis
Keepalived 是 Linux 下一个轻量级别的高可用解决方案,Keepalived 软件起初是专为 LVS 负载均衡软件设计的,LVS 是 Linux Virtual Server 的缩写,也就是 Linux 虚拟服务器,在 linux2.4 内核之后,已经彻底内置了 LVS 的各个功能模块。它是工做在四层的负载均衡,相似于 Haproxy, 主要用于实现对服务器集群的负载均衡。用来管理并监控 LVS 集群系统中各个服务节点的状态,后来又加入了能够实现高可用的 VRRP 功能。所以,Keepalived 除了可以管理 LVS 软件外,还能够做为其余服务(例如:Nginx、Haproxy、MySQL 等)的高可用解决方案软件。算法
Keepalived 软件主要是经过 VRRP 协议实现高可用功能的。VRRP 是 Virtual Router RedundancyProtocol(虚拟路由器冗余协议)的缩写,VRRP 出现的目的就是为了解决静态路由单点故障问题的,它可以保证当个别节点宕机时,整个网络能够不间断地运行;(简单来讲,vrrp 就是把两台或多态路由器设备虚拟成一个设备,实现主备高可用)。因此,Keepalived 一方面具备配置管理 LVS 的功能,同时还具备对 LVS 下面节点进行健康检查的功能,另外一方面也可实现系统网络服务的高可用功能sql
关于四层负载,咱们知道 osi 网络层次模型的 7 层模模型(应用层、表示层、会话层、传输层、网络层、数据链路层、物理层);四层负载就是基于传输层,也就是ip+端口的负载;而七层负载就是须要基于 URL 等应用层的信息来作负载,同时还有二层负载(基于 MAC)、三层负载(IP);常见的四层负载有:LVS、F5; 七层负载有:Nginx、HAproxy; 在软件层面,Nginx/LVS/HAProxy 是使用得比较普遍的三种负载均衡软件。
keepalived 安装:
1.tar -zxvf keepalived-2.0.10.tar.gz
2. ./configure --prefix=/data/program/keepalived --sysconf=/etc 我这边使用默认安装路径,执行./configure --sysconf=/etc 会安装到 /usr/local 下
3.若是缺乏依赖库 安装 yum -y install pcre-devel zlib-devel openssl openssl-devel,yum install gcc gcc-c++
4.make && make install
5.进入安装后的路径 cd /usr/local, 建立软链接: ln -s sbin/keepalived /sbin
6.复制运行命令 cp /mysoft/keepalived-2.0.10/keepalived/etc/init.d/keepalived /etc/init.d/
7.添加到系统服务 chkconfig --add keepalived
8.启用该服务 systemctl enable keepalived.service
9.启动 service keepalived start 并查看状态,安装成功。在配置完conf文件后启动会有以下信息
keepalived 的配置 vim /etc/keepalived/keepalived.conf,因为keepalive只做为vrrp协议的实现,负载代理的工做是交给后面的nginx或者haproxy来实现的。因此部署到一个节点上便可。
MASTER:(192.168.254.139)与nginx在同一个机器上
! Configuration File for keepalived
global_defs {
router_id MASTER_DEVEL #运行 keepalived 服务器的标识,在一个网络内应该是惟一的
}
vrrp_instance VI_1 { #vrrp 实例定义部分
state MASTER #设置 lvs 的状态,MASTER 和 BACKUP 两种,必须大写
interface ens33 #设置对外服务的接口
virtual_router_id 51 #设置虚拟路由标示,这个标示是一个数字,同一个 vrrp 实例使用惟一标示
priority 150 #定义优先级,数字越大优先级越高,在一个 vrrp——instance 下,master 的优先级必须大于 backup
advert_int 1 #设定 master 与 backup 负载均衡器之间同步检查的时间间隔,单位是秒
authentication { #设置验证类型和密码
auth_type PASS
auth_pass 1111 #验证密码,同一个 vrrp_instance 下 MASTER 和 BACKUP 密码必须相同}
virtual_ipaddress { #设置虚拟 ip 地址,能够设置多个,每行一个
192.168.254.111
}
}
virtual_server 192.168.254.111 80 { #设置虚拟服务器,须要指定虚拟 ip 和服务端口
delay_loop 6 #健康检查时间间隔
lb_algo rr #负载均衡调度算法
lb_kind NAT #负载均衡转发规则
persistence_timeout 50 #设置会话保持时间
protocol TCP #指定转发协议类型,有 TCP 和 UDP 两种
real_server 192.168.254.139 80 { #配置服务器节点 1,须要指定 real server 的真实 IP 地址和端口weight 1 #设置权重,数字越大权重越高
TCP_CHECK { #realserver 的状态监测设置部分单位秒
connect_timeout 3 #超时时间
delay_before_retry 3 #重试间隔
connect_port 80 #监测端口
}
}
}
BACKUP:(192.168.254.137)与nginx在同一个机器上
! Configuration File for keepalived
global_defs {
router_id BACKUP_DEVEL #运行 keepalived 服务器的标识,在一个网络内应该是惟一的
}
vrrp_instance VI_1 { #vrrp 实例定义部分
state BACKUP #设置 lvs 的状态,MASTER 和 BACKUP 两种,必须大写
interface ens33 #设置对外服务的接口
virtual_router_id 51 #设置虚拟路由标示,这个标示是一个数字,同一个 vrrp 实例使用惟一标示
priority 100 #定义优先级,数字越大优先级越高,在一个 vrrp——instance 下,master 的优先级必须大于 backup
advert_int 1 #设定 master 与 backup 负载均衡器之间同步检查的时间间隔,单位是秒
authentication { #设置验证类型和密码
auth_type PASS
auth_pass 1111 #验证密码,同一个 vrrp_instance 下 MASTER 和 BACKUP 密码必须相同}
virtual_ipaddress { #设置虚拟 ip 地址,能够设置多个,每行一个
192.168.254.111
}
}
virtual_server 192.168.254.111 80 { #设置虚拟服务器,须要指定虚拟 ip 和服务端口
delay_loop 6 #健康检查时间间隔
lb_algo rr #负载均衡调度算法
lb_kind NAT #负载均衡转发规则
persistence_timeout 50 #设置会话保持时间
protocol TCP #指定转发协议类型,有 TCP 和 UDP 两种
real_server 192.168.254.137 80 { #配置服务器节点 1,须要指定 real server 的真实 IP 地址和端口weight 1 #设置权重,数字越大权重越高
TCP_CHECK { #realserver 的状态监测设置部分单位秒
connect_timeout 3 #超时时间
delay_before_retry 3 #重试间隔
connect_port 80 #监测端口
}
}
}
经过脚本实现动态切换(与nginx心跳的绑定):
经过脚本的方式来检测 nginx 服务是否存活,一旦nginx挂了,那么能够经过这个机制把 keepalived 关闭,把机会让给哪些还存活的节点。详细配置以下:
首先编写shell 脚本:我这边是在keepalived.conf 所在目录下执行:vim vim nginx_service_check.sh,输入如下信息并保存。
#!bin/sh #! /bin/sh 是指此脚本使用/bin/sh 来执行
A=`ps -C nginx --no-header |wc -l`
if [ $A -eq 0 ]
then
echo 'nginx server is died'
service keepalived stop
fi
能够经过sh nginx_service_check.sh 来验证该脚本是否正确。在nginx 没有启动的时候要是能输出以下信息就说明是OK的:
为给文件添加权限 : chmod +x nginx_service_check.sh 。
而后修改 keepalived.conf 文件:加入如下信息
global_defs {
router_id MASTER_DEVEL
enable_script_security
}
vrrp_script check_nginx_service {
script "/etc/keepalived/nginx_service_check.sh"
interval 3
weight -10
user root
}
#还有在vrrp_instance VI_1 这个模块中添加以下信息
vrrp_instance VI_1 {
track_script{
check_nginx_service
}
}
而后启动集群,把配置了该监听脚本的节点的nginx服务停掉,会发现此刻经过浏览器访问会切到另一个节点上。说明配置完成,或者在nginx服务没有启动的时候去启动 keepalived 服务,会发现该服务不管如何都是没法启动的,启动后查看状态一直会是 dead 的状态。高可用方案到此结束。
OpenResty 是一个经过 Lua 扩展 Nginx 实现的可伸缩的 Web 平台,内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建可以处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
安装:
1. 下载安装包 https://openresty.org/cn/download.html
2. 安装软件包 tar -zxvf openresty-1.13.6.2.tar.gz。cd openrestry-1.13.6.2。./configure [默认会安装在/usr/local/openresty 目录] --prefix= 指定路径。make && make install
3. 可能存在的错误,第三方依赖库没有安装的状况下会报错 yum install readline-devel / pcre-devel /openssl-devel
安装过程和 Nginx 是同样的,由于他是基于 Nginx 作的扩展。开始第一个程序,HelloWorld cd /usr/local/openresty/nginx/conf 编辑 nginx 配置文件 nginx.conf
location / { default_type text/html; content_by_lua_block { ngx.say("helloworld"); } }
在 sbin 目录下执行.nginx 命令就能够运行,访问能够看到 helloworld。
创建工做空间:
为了避免影响默认的安装目录,咱们能够建立一个独立的空间来练习,先到在安装目录下建立 demo 目录,安装目录为/usr/local/openresty .mkdir demo。而后在 demo 目录下建立三个子目录,一个是 logs 、一个是 conf,一个是 lua。
进接下去演示一个实现 API网关功能的简单demo,而后提供一个算法去计算传入参数a,b的和。入conf 建立配置文件 vim nginx.conf :
worker_processes 1; error_log logs/error.log; events { worker_connections 1024; } http { lua_package_path '$prefix/lua/?.lua'; //这里的¥prefix在启动的时候指定 lua_code_cache off; // lua脚本不须要从新加载 server { listen 80; // 正则匹配访问路径 location ~ ^/api/([-_a-zA-Z0-9]+) { // 请求过滤一下 access_by_lua_file lua/check.lua; content_by_lua_file lua/$1.lua; } } }
当来到的请求符合 ^/api/([-_a-zA-Z0-9/] 时 会在NGX_HTTP_CONTENT_PHASE HTTP请求内容阶段 交给 lua/$1.lua来处理。好比:
/api/addition 交给 lua/addition.lua 处理。
/api/lua/substraction 交给 lua/substraction .lua 处理。
接下去建立 三个 lua 脚本:params.lua:
local _M ={} // 定义一个模块 //定义一个方法 //该方法用于判断参数是否为数字 function _M.is_number(...) local args={...} local num; for i,v in ipairs(arg) do num=tonumber(v); if nil ==num then return false; end end return true; end //将该模块返回出去 return _M;
写一个用于起到网关过滤的检查脚本check.lua:
//导入模块
local param=require("params");
//获取uri的参数 local args=ngx.req.get_uri_args();
//判断a,b是否为空且是否为数字 if not args.a or not args.b or not param.is_number(args.a,args.b) then ngx.exit(ngx.HTTP_BAD_REQUEST); return; end
算法脚本 add.lua:
local args =ngx.req.get_uri_args(); ngx.say(args.a+args.b);
进入nginx的sbin目录执行:./nginx -p /usr/local/openresty/demo 【-p 主要是指明 nginx 启动时的配置目录】,此时会提示一个警告信息,无需理会,有强迫症把对应配置关了就还了:nginx: [alert] lua_code_cache is off; this will hurt performance in /usr/local/openresty/demo/conf/nginx.conf:12。经过访问http://192.168.254.137/api/add?a=1&b=3 能显示最后的值:
库文件使用:经过上面的案例,咱们基本上对 openresty 有了一个更深的认识,其中咱们用到了自定义的 lua 模块。实际上 openresty 提供了很丰富的模块。让咱们在实现某些场景的时候更加方便。能够在 /openresty/lualib 目录下看到;好比在 resty 目录下能够看到 redis.lua、mysql.lua 这样的操做 redis 和操做数据库的模块。更多的库能够去百度,或者查找相关书籍。
从一个房间到另外一个房间,必须必需要通过一扇门,一样,从一个网络向另外一个网络发送信息,必须通过一道“关口”,这道关口就是网关。顾名思义,网关(Gateway)就是一个网络链接到另外一个网络的“关口”。那什么是 api 网关呢?
在微服务流行起来以前,api 网关就一直存在,最主要的应用场景就是开放平台,也就是 open api; 这种场景你们接触的必定比较多,好比阿里的开放平台;当微服务流行起来之后,api 网关就成了上层应用集成的标配组件.
为何须要网关?
对微服务组件地址进行统一抽象,API 网关意味着你要把 API 网关放到你的微服务的最前端,而且要让 API 网关变成由应用所发起的每一个请求的入口。这样就能够简化客户端实现和微服务应用程序之间的沟通方式.
当服务愈来愈多之后,咱们须要考虑一个问题,就是对某些服务进行安全校验以及用户身份校验。甚至包括对流量进行控制。 咱们会对须要作流控、须要作身份认证的服务单独提供认证功能,可是服务愈来愈多之后,会发现不少组件的校验是重复的。这些东西很明显不是每一个微服务组件须要去关心的事情。微服务组件只须要负责接收请求以及返回响应便可。能够把身份认证、流控都放在 API 网关层进行控制。
在单一架构中,随着代码量和业务量不断扩大,版本迭代会逐步变成一个很困难的事情,哪怕是一点小的修改,都必需要对整个应用从新部署。 可是在微服务中,各个模块是是一个独立运行的组件,版本迭代会很方便,影响面很小。同时,为服务化的组件节点,对于咱们去实现灰度发布(金丝雀发布:将一部分流量引导到新的版本)来讲,也会变的很简单;因此经过 API 网关,能够对指定调用的微服务版本,经过版原本隔离。以下图所示
OpenResty 实现 API 网关限流及登陆受权
OpenResty 为何能作网关?
前面咱们了解到了网关的做用,经过网关,能够对 api 访问的前置操做进行统一的管理,好比鉴权、限流、负载均衡、日志收集、请求分片等。因此 API 网关的核心是全部客户端对接后端服务以前,都须要统一接入网关,经过网关层将全部非业务功能进行处理。OpenResty 为何能实现网关呢? OpenResty 有一个很是重要的因素是,对于每个请求,Openresty 会把请求分为不一样阶段,从而可让第三方模块经过挂载行为来实现不一样阶段的自定义行为。而这样的机制可以让咱们很是方便的设计 api 网关。
Nginx 自己在处理一个用户请求时,会按照不一样的阶段进行处理,总共会分为 11个阶段。而 openresty 的执行指令,就是在这 11 个步骤中挂载 lua 执行脚本实现扩展,咱们分别看看每一个指令的做用
init_by_lua : 当 Nginx master 进程加载 nginx 配置文件时会运行这段 lua 脚本,通常用来注册全局变量或者预加载 lua 模块。
init_woker_by_lua: 每一个 Nginx worker 进程启动时会执行的 lua 脚本,能够用来作健康检查。
set_by_lua:设置一个变量。
rewrite_by_lua:在 rewrite 阶段执行,为每一个请求执行指定的 lua 脚本。
access_by_lua:为每一个请求在访问阶段调用 lua 脚本。
content_by_lua:前面演示过,经过 lua 脚本生成 content 输出给 http 响应。
balancer_by_lua:实现动态负载均衡,若是不是走 contentbylua,则走 proxy_pass,再经过 upstream 进行转发。
header_filter_by_lua: 经过 lua 来设置 headers 或者 cookie。
body_filter_by_lua:对响应数据进行过滤。
log_by_lua : 在 log 阶段执行的脚本,通常用来作数据统计,将请求数据传输到后端进行分析。
灰度发布的实现:
1.跟前面同样建立一个新的工做空间 mkdir gray。而后在 demo 目录下建立三个子目录,一个是 logs 、一个是 conf,一个是 lua。
2.编写 Nginx 的配置文件 nginx.conf
worker_processes 1; error_log logs/error.log; events{ worker_connections 1024; } http{ lua_package_path "$prefix/lualib/?.lua;;"; lua_package_cpath "$prefix/lualib/?.so;;"; //生产环境 upstream prod {//tomcat地址 server 192.168.254.137:8080; } // 预生产环境 upstream pre {//tomcat地址 server 192.168.254.139:8080; } server { listen 80; server_name localhost; //当访问该路径就会进入lua脚本 location / { content_by_lua_file lua/gray.lua; } // 定义变量在lua中会使用 location @prod { proxy_pass http://prod; } location @pre { proxy_pass http://pre; } } }
3.编写 gray.lua 文件
local redis=require "resty.redis"; local red=redis:new(); red:set_timeout(1000); local ok,err=red:connect("192.168.254.138",6379); if not ok then ngx.say("failed to connect redis",err); return; end ok, err = red:auth("wuzhenzhao"); local_ip=ngx.var.remote_addr; local ip_lists=red:get("gray"); if string.find(ip_lists,local_ip) == nil then ngx.exec("@prod"); else ngx.exec("@pre"); end local ok,err=red:close();
4.
1.进入sbin目录 执行命令启动 nginx: ./nginx -p /usr/local/openresty/gray
2. 启动 redis,并设置 set gray 192.168.254.1,因为我这边是访问虚拟机,因此我本地ip去访问就是这个。
3. 经过浏览器运行: http://192.168.254.137/查看运行结果
修改 redis gray 的值, 到 redis 中 set gray 192.168.254.2. 再次运行结果,便可看到访问结果已经发生了变化.