最近在张开涛所著的《亿级流量网站架构核心技术》,粗粗浏览了下,核心思想是围绕着Nginx在转。如果平时没有一些运维实际经验的话,可能整本书看下来效果不是很好。要想吸收更多的知识,光看是没用的,还是得亲自动手操作。书中有不少代码片段,我会尝试尽量把案例都实现,组成可以跑通的完整测试项目,以达到学习的目的。本系列学习笔记的内容大多来自这本书,如果涉及到版权问题,请及时联系本人。下面就进入正题了。
环境准备
目前需要的环境是Java运行环境和Nginx。
Nginx
Nginx的话直接安装Openresty最新版(目前版本1.11.2.5),具体参照官网安装步骤。本机环境为Mac OS X,直接采用官方推荐的方式:
安装完之后的目录在/usr/local/Cellar/openresty下,直接运行openresty命令即可:
如果还是想执行nginx命令的话,将脚本路径加入到的PATH变量。具体怎么加,按照自己的系统来,下面举个本机的例子:
Java
Java环境的准备就不多说了,这里主要是提一下建测试项目,依然采用目前比较便捷的SpringBoot搭建后端项目。项目地址取名hunger,本系列涉及到的所有项目都在这个目录下。
Nginx常见用法
感觉这本书面向的人群需要具有目前主流架构的简单了解,尤其是Nginx。本人也是一边查资料一遍学,过程就不写出来了,还是以实践为主。
负载均衡/反向代理
其意思就是请求落到Nginx,Nginx代理了后端真正的服务地址,同时又能使流量均衡地发送过去。是通过upstream配置来实现该功能的。下面我们要做如下工作才能完成测试:
配置Nginx,并且重启。我们就直接在hungeru目录下创建conf/nginx.conf文件。参照官网,文件如下内容如下:
12345678910111213141516worker_processes 1;error_log logs/error.log;events {worker_connections 1024;}http {server {listen 80;location / {default_type text/html;content_by_lua 'ngx.say("<p>hello, world</p>")';}}}尝试启动Nginx
12sudo nginx -c ~/IdeaProjects/hunger/conf/nginx.conf会报错,提示没有logs文件夹,需要在/usr/local/Cellar/openresty/1.11.2.5/nginx下手动添加logs文件夹,再启动就好了。启动成功之后,访问localhost会成功输出hello, world。content_by_lua配置项属于Openresty范畴,如果不了解的话,需要自行再去学习了。
新建Java项目,我们在hunger目录下快速创建2个项目,过程就不写出来了。贴出核心代码:
123456789"/", method = RequestMethod.GET)(path =public String handle() {return "from real server 1";}"/", method = RequestMethod.GET)(path =public String handle() {return "from real server 2";}模拟业务项目准备完毕之后,就可以来改Nginx配置了,主要是修改nginx.conf文件:
123456789101112http {upstream real_server {server localhost:8081;server localhost:8082;}server {listen 80;location / {proxy_pass http://real_server;}}}
上述配置完毕,将项目都启动,即可进行测试,稍微解释下上述配置文件。当有localhost:80的请求过来,nginx会反向代理到proxy_pass配置的upstream server,然后请求会平均地落到两台实际业务服务器上。测试结果如下:
负载均衡算法
负载均衡指的是如何将流量分配到upstream server,默认采用的是round-roobin,从上面的测也也可以看出来,请求是平均地落到real_server上。同时还支持其他几种算法:
weighte权重算法,通过给server配置weight可以实现基于权重的轮询,修改nginx.conf文件:
12345678910111213upstream real_server {server localhost:8081 weight=1;server localhost:8082 weight=2;}# 测试结果> curl localhostfrom real server 1%> curl localhostfrom real server 2%> curl localhostfrom real server 2%...上述配置的意思是每3个请求中的1个落到8081,另外2个落到8082。可以请求localhost:80来测试下。
ip_hash算法,根据客户端IP进行负载均衡,即相同的IP将负载均衡到同一个upstream,修改nginx.conf文件:
1234567891011upstream real_server {ip_hash;server localhost:8081;server localhost:8082;}# 经过测试,本机所有的请求都落在8081上,证明配置生效> curl localhostfrom real server 1%> curl localhostfrom real server 1%...hash key[consistent],对某个key进行哈希或者使用一致性哈希算法进行负载均衡。这里简单地根据请求uri进行负载均衡。修改java项目和nginx.conf来测试:
1234567891011121314151617181920212223// Java项目新增接口@RequestMapping(path = "/{uri}", method = RequestMethod.GET)public String handleUri(@PathVariable String uri) {return "from real server 1, param is " + uri;}@RequestMapping(path = "/{uri}", method = RequestMethod.GET)public String handleUri(@PathVariable String uri) {return "from real server 2, param is " + uri;}# 修改nginx配置upstream real_server {hash $uri;server localhost:8081;server localhost:8082;}# 测试结果> curl localhost/afrom real server 1, param is a> curl localshot/cfrom real server 2, param is c
失败重试
通过upstream server的max_fails和fail_timeout来实现该功能。意思是在fail_timeout时间内失败了max_fails次请求后,则认为该上游服务器不可用,然后将该服务地址踢除掉。fail_timeout时间后会再次将该服务器加入存活列表,进行重试。修改nginx.conf文件:
下面我们来测试下:
- 停掉real_server_2
请求localhost,并查看输出以及nginx error.log
1234curl localhostfrom real server 1%tail -f /usr/local/Cellar/openresty/1.11.2.5/nginx/logs/error.log...Connection refused...upstream: "http://127.0.0.1:8082/"...重启real_server_2后会再次连上
通过wireshark截取请求可以看出,确实在判断出real_servert_2不在线之后,fail_timeout时间内没有对它再请求,以此可以作为nginx把服务器地址踢掉的证据。另外,翻了一下书本,后面有一章专门讲超超时与重试机制,到那时候再详细看看。
健康检查
Nginx对上游服务器的健康检查默认采用的是惰性策略,书中的例子是通过集成nginx_upstream_check_module模块来进行主动健康检查。但是Openresty本身自带了一个叫lua-resty-upstream-healthcheck的模块,可以提供这样的功能。我们就直接使用该模块做测试,下面开始可能会比较多地涉及Openresty的相关内容,建议先看下开涛老师的一个系列博客入门。假定我们已经有一点基础了,就来试下吧:
在conf目录下新建一个名为lua的文件夹,以后所有的的lua文件都放到conf/lua目录下,然后在lua目录下新建healthcheck.lua
123456789101112131415161718192021222324-- 引入healthcheck模块local hc = require "resty.upstream.healthcheck"healthcheck = function()local ok, err = hc.spawn_checker {shm = "healthcheck",upstream = "real_server",type = "http",http_req = "GET / HTTP/1.0\r\nHost: real_server\r\n\r\n",interval = 2000,timeout = 5000,fall = 3,rise = 2,valid_statuses = {200, 302},concurrency = 1,}if not ok thenngx.log(ngx.ERR, "=====> failed to spawn health checker: ", err)returnendend-- 调用上面定义的functionhealthcheck()修改nginx.conf
1234567891011121314151617181920212223242526272829http {# 共享全局变量,在所有worker间共享lua_shared_dict healthcheck 1m;# 关闭cosocket错误日志lua_socket_log_errors off;# 用于启动一些定时任务,比如心跳检查,定时拉取服务器配置等等# 此处的任务数量 == Worker进程数init_worker_by_lua_file /Users/zhangjing/IdeaProjects/hunger/conf/lua/healthcheck.lua;upstream real_server {server localhost:8081;server localhost:8082;}server {...location /status {access_log off;allow 127.0.0.1;deny all;content_by_lua_block {local hc = require "resty.upstream.healthcheck"ngx.say("Nginx Worker PID: ", ngx.worker.pid())ngx.print(hc.status_page())}}}}测试,启动real_server_1、real_server_2、nginx
12345678910111213141516171819curl localhost/statusNginx Worker PID: 59109Upstream real_serverPrimary Peers127.0.0.1:8081 up[::1]:8081 up127.0.0.1:8082 up[::1]:8082 upBackup Peers停掉real_server_2curl localhost/statusNginx Worker PID: 59109Upstream real_serverPrimary Peers127.0.0.1:8081 up[::1]:8081 up127.0.0.1:8082 DOWN[::1]:8082 DOWNBackup Peers
其他配置项
- 备份上游服务器,通过backup来指定,当所有主服务器都不存活时,请求会发给备服务器
- 不可用上游服务器,通过down来指定,表示该上游服务器永久不可用
这两个配置项比较简单,就不测试了。另外,这一章节就学到这里吧。