网站首页 > 技术文章 正文
近期尝试使用tomcat和Nginx进行配合做负载均衡和静态与动态资源分配的Demo,期间遇到很多有意思的地方和知识短板,特此记录
一:什么是Nginx?
Nginx也是一款服务器,我们常用它做如:反向代理、负载均衡、动态与静态资源的分离的工作 反向代理:相对应的是正向代理,如果你使用过代理服务器的话就明白,我们访问某一个网站并非直接访问目标网站,而是告诉代理服务器我需要访问什么目标网站,由代理服务器发出请求给目标网站,将目标网站访问结果再转发给你,此时,你是请求代理方。而反向代理是此时代理服务器做服务器的代理,我们的访问请求并非直接访问到目标服务器上,而是访问代理服务器,由代理服务器决定什么样的请求以什么样的方式访问正式服务器 负载均衡:目前大多数的网站都会采用负载均衡手段来针对目前用户的指数级增长来减少对单点服务器的负载压力,比如目前我们拥有3台真实服务器,我们需要根据相应策略决定什么样的用户请求分配到哪个真实服务器,比如按照轮询的方式,用户请求挨个到达代理服务器,此时代理服务器按照第一个请求转发至第一台真实服务器,第二个请求转发到第二个服务器上,依次类推,这样可以防止大量的用户请求全部访问到同一台物理机上,单点物理机的性能始终有限的,当然这可能对服务器数据访问时候造成事务性的失效,在Web方面可能造成Session访问的问题,这不在本文讨论方面内 动态静态资源分离:最方便列举就是Java的JSP和静态资源如:.js/.css/.html/.png方面的资源分离,之前开发web方面的程序时候我们习惯将html、css等资源文件也放置于Tomcat之中,用户访问后tomcat需要将请求的这些静态资源文件一并返回给用户,再者如果有多台同业务逻辑的tomcat服务器的话,同样的资源还需要在每个服务器上放一份,同时也增加了tomcat服务器的网络IO,十分不合算的,如果我们只讲JSP之类的请求交给tomcat,而代理服务器上存放静态资源,当用户的请求非动态资源的时候,我们完全可以将代理服务器的静态资源直接返回给用户,而不去增大Tomcat的压力,tomcat只需要负责逻辑处理和动态资源的加载就可以了 基于上述的Nginx优势,决定搭建一下Nginx+Tomcat的组合来进行测试,包括参数传递,post、get传递参数是否有影响,还有Nginx的工作模式master和worker的工作方式进行一些浅薄的总结 1 2 3 4 5 6 7 8 9 10
一、Nginx的安装
安装部分这里就不在细说了,网上的教程很多,我们直接从配置文件开始吧 1 2
二、配置文件
#user nobody; #这里是核心worker数,一般设置为与cpu核心数相同的数目,避免进程切换造成的上下文切换耗费资源,cpu信息可以从/proc/cpuinfo中查看 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 { #use epoll model使用epoll模型,采用异步非阻塞模型加快处理速度 use epoll; worker_connections 1024; } 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; #设定通过nginx上传文件的大小 client_max_body_size 300m; #使用sendfile函数在两个文件描述符之间直接传递数据(完全在内核中操作,传送),从而避免了内核缓冲区数据和用户缓冲区数据之间的拷贝,操作效率很高,被称之为零拷贝。 sendfile on; #tcp_nopush on; #keepalive_timeout 0; #连接活跃时间 keepalive_timeout 65; #使用压缩数据减少IO量,但是在不支持数据解压浏览器可能产生乱码 #gzip on; #静态服务器组 #设定静态资源服务器访问接口 upstream static.zh-jieli.com { server localhost:808 weight=1; } #动态服务器组 upstream zh-jieli.com { #设置Hash轮询规则 #ip_hash; #weight: server ip:port weight=10 #默认 轮询 #fair:按照后端服务器的响应时间来分配 #url_hash:按照url规则进行分配,使得固定的请求分配到固定的服务器上 server localhost:8080; server localhost:8081; } server{ listen 808; server_name static; location / { } location ~ .*\.(js|css|ico|png|jpg|eot|svg|ttf|woff) { #所有静态文件直接读取硬盘内容:读取的静态资源存放位置 root /apache-tomcat-8.5.24/webapps/ROOT ; #资源是否进行缓存与缓存时间 expires 30d; #缓存30天 } } server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { root html; index index1.html index.htm; } location ~ .*\.(js|css|ico|png|jpg|eot|svg|ttf|woff) { #proxy_cache cache_one; proxy_cache_valid 200 304 302 5d; proxy_cache_valid any 5d; proxy_cache_key '$host:$server_port$request_uri'; add_header X-Cache '$upstream_cache_status from $host'; proxy_pass http://static.zh-jieli.com; # 所有静态文件直接读取硬盘 root /apache-tomcat-8.5.24/webapps/ROOT; expires 30d; #缓存30天 } #其他页面反向代理到tomcat容器 location ^~ /tomcat { index index; # proxy_pass http://localhost:8080/; #设定代理服务器组 proxy_pass http://zh-jieli.com/; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 整个nginx工作时当http请求到来时,由nginx针对nginx.conf配置好的规则,对location进行正则匹配,匹配到相应的正则,进行location内部的处理 关于Nginx的location配置附上一篇博客,很nice http://seanlook.com/2015/05/17/nginx-location-rewrite/ 里面很详细列出了各种要求的location匹配规则,值得注意的一点是: location匹配遵循最长原则,即满足了之前的匹配规则后,除了遇见^会终止向下继续匹配,其他情况会依次向下搜索,知道找到合适的location匹配规则然后进行处理 Nginx在模块功能上分三个模块: Handlers(处理器模块)。此类模块直接处理请求,并进行输出内容和修改headers信息等操作。Handlers处理器模块一般只能有一个。 Filters (过滤器模块)。此类模块主要对其他处理器模块输出的内容进行修改操作,最后由Nginx输出。 Proxies (代理类模块)。此类模块是Nginx的HTTP Upstream之类的模块,这些模块主要与后端一些服务比如FastCGI等进行交互,实现服务代理和负载均衡等功能。 因为是测试,所以我的两个tomcat都安装到了同一台本地机器上 生产环境中根据需要进行配置相应的IP就好了 本地写好相应的测试代码+log4j将信息日志打到相应的位置用来观察参数是否传递过来 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
TestOne.java
package com.nginx.controllers; import com.nginx.utils.Log4jUtils; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @Controller @RequestMapping("/test") public class TestOne { @RequestMapping("/getname") public void test(@RequestParam(value = "name") String name) { //用来记录获取的参数name,可以通过查看日志进行确认 Log4jUtils.getLogger().info("my name is" + name); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Log4jUtils.java
package com.nginx.utils; import org.apache.log4j.Logger; public class Log4jUtils { private static final Logger logger = Logger.getLogger(Logger.class); public static Logger getLogger() { return logger; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 这里简单做了两个的demo程序,做相应的测试,将项目打成war包后上传至Linux服务器,移动到tomcat/webapps中,tomcat进行热部署 先测试当前的tomcat是否能正常运行,由于没有做区别页面,就直接看日志来判断了 1 2 3
如图:以轮询方式进行访问8080和8081监听的tomcat 上述方式是get请求进行的测试,我们来试试post 1 2 3 4 <!DOCTYPE html> <html> <body> <form action="http://123.207.85.242/tomcat/nginx/test/getname" method="post"> <input type="text" name="name"> <input type="submit" value="提交"> </form> </body> </html> 1 2 3 4 5 6 7 8 9 测试结果依旧可以正常接收参数,这里就不贴图了 1 2
二、静态动态资源分离
现在试试直接访问 1 2 http://ip/tomcat 1 我们会直接跳转到tomcat的主页面,如果检索tomcat.png的话 1 2 http://ip/tomcat/tomcat.png 1 可以直接检索到tomcat.png,它是来自tomcat服务器的资源,如果需要检索nginx的静态资源,可以使用 1 2 http://ip/tomcat.png 1 这样我们的资源就来自于nginx.conf文件中配置的关于静态资源的访问路径了,为了证明此事资源来自于nginx,我尝试将8080端口的tomcat/webapps/ROOT/tomcat.png更名为tomcat1.png,然后尝试访问http://ip/tomcat1.png访问成功,这时候如果刷新http://ip/tomcat/访问tomcat主页的时候会发现图标会在出现和不出现之间轮转,原因就是这里请求了tomcat服务器本身去拿资源,tomcat:8080的图片名被更改,如果代理访问的服务器正好是tomcat:8080的话,它是找不到tomcat.png的,当然轮转到tomcat:8081时候,图片还是会出现滴 1 2
三、Nginx工作模式
如图nginx启动后会有两个进程 如果你将nginx.conf的 1 2 3 worker_processes 1;更改为worker_processes 2; 1 那么出现的就是 1 2
那我们来说说master和worker进程分别的是干什么的,从pid来看,matser的父进程是init进程,而worker的父进程都是master,这里就很有意思了,那nginx这两个进程都到底是干嘛的嘞??? 感谢baidu与google的强大,对于疑问的解释很清楚 原地址:http://blog.csdn.net/hguisu/article/details/8930668 感兴趣的同学可以看一看,这里只简单总结一下 1 2 3 4 5 6 7
master进程
主要用来管理worker进程,包含:接收来自外界的信号,向各worker进程发送信号,监控worker进程的运行状态,当worker进程退出后(异常情况下),会自动重新启动新的worker进程。 master的平滑重启很有意思,master只负责接收外界信号,那么当我们更改了nginx.conf文件需要重新启动nginx时候怎么办????只需要通过kill向master进程发送信号就行了。比如kill -HUP pid,这时候master并不会干掉自己所有的worker然后自杀,而是重新加载nginx.conf后,重新启动一批worker,在这之后的请求全部由新的worker进行处理,而老的worker也并不是立即终止,而是在运行完当前的请求后,被终止掉,接下来的请求就全部交给新的worker了,当然这个方式后来被改成了 1 2 3 nginx -s reload 1 这个命令重新加载配置和上面的略有不一样,其会新启动一个nginx然后向Master发送信号,当然接下来就和上面的过程一致了 1 2
worker进程
而基本的网络事件,则是放在worker进程中来处理了。多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。一个请求,只可能在一个worker进程中处理,一个worker进程,不可能处理其它进程的请求。worker进程的个数是可以设置的,一般我们会设置与机器cpu核数一致,这里面的原因与nginx的进程模型以及事件处理模型是分不开的。 那么worker是怎么做的呢,worker从master fork出来后,由master进行监听端口,当有相应的连接来之后,由worker争抢accept_mutex互斥锁,同一时刻此请求只能由一个worker争抢成功,并进行连接的读取、解析、处理然后断开连接 着整体是Nginx的工作的大致原理,Nginx依旧有很多细节和设计值得去深究学习,本篇博客先总结到此,后续有了更深入的了解,也会
- 上一篇: 看完你就明白的锁系列之锁的状态 锁sub
- 下一篇: 并行编程语言:Go:Go语言并行编程最佳实践
猜你喜欢
- 2024-10-12 漫画 | Linux 并发和竞态问题究竟是什么?
- 2024-10-12 【驱动】串口驱动分析(三)-serial driver
- 2024-10-12 synchronized锁 synchronized锁的是类还是对象
- 2024-10-12 Golang 程序遇到性能问题该怎么办?
- 2024-10-12 线程间通信——互斥锁 线程间互斥方式
- 2024-10-12 【Linux系统编程】互斥锁 linux 互斥锁优先级反转
- 2024-10-12 linux c/c++开发:多线程并发锁:互斥锁、自旋锁、原子操作、CAS
- 2024-10-12 每行代码都带注释,带你看懂Go互斥锁的源码
- 2024-10-12 一文搞懂pprof 一文搞懂伤寒论六经辨证
- 2024-10-12 并发原理系列八:信号量、互斥锁、自旋锁
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)