Nginx和Tomcat配合实现Java Web服务热部署

背景基于Springboot应用以war包的形式运行在Tomcat容器中,当更新war包时会有一段时间服务返回404,这对于线上服务是不可接受的 。4层的负载均衡可以自动将80端口关闭的节点下线,但由于内网服务器位于堡垒机后方,根据公司规定不能自行配置SSH服务,所以无法执行远程脚本 。所以只能通过别的方式实现 。
实验素材

Nginx和Tomcat配合实现Java Web服务热部署

文章插图
 
  1. Nginx 作为web server和7层负载均衡
  2. tomcat * 2 作为应用后端
  3. gitlab-ce 代码版本控制
  4. jenkins 发布平台
基本原理基本的原理就是让Nginx后方有3个Tomcat容器,其中1个是active,1个是standby,正常情况下不会访问到standby的容器,但可以通过额外的手段保证standby的容器是可以提供服务的,在发布前先更新所有的standby节点,验证没问题之后更新active的容器,来保证服务不会中断 。
实际操作创建springboot项目参考Springboot使用内置和独立tomcat以及其他思考 。
编写同一个接口的不同版本// tag v1@RestControllerpublic class HelloController {@GetMApping("/hello")public String hello() {return "V1";}}// tag v2@RestControllerpublic class HelloController {@GetMapping("/hello")public String hello() {return "V2";}}打包mvn clean package -Dmaven.test.skip=true创建两个tomcat容器Docker run -itd --name tomcat-active -v /tmp/tomcat/active:/usr/local/tomcat/webapps -p 32771:8080 tomcatdocker run -itd --name tomcat-standby -v /tmp/tomcat/standby:/usr/local/tomcat/webapps -p 32772:8080 tomcat将war包拷贝到容器中可能是docker toolbox的问题,无法挂载目录,所以只好把war包手动拷贝进去 。
docker cp ~/workspace/spring-demo/target/spring-demo-0.0.1-SNAPSHOT.war tomcat-active:/usr/local/tomcat/webapps/docker cp ~/workspace/spring-demo/target/spring-demo-0.0.1-SNAPSHOT.war tomcat-standby:/usr/local/tomcat/webapps/访问两个容器中的服务稍等片刻两个容器中的服务会自动部署,就可以分别通过相应的端口访问了,简单压测一下QPS可以达到2000+且没有报错 。
$ wrk -c 20 -d 10 -t 4 http://192.168.99.100:32771/spring-demo-0.0.1-SNAPSHOT/helloRunning 10s test @ http://192.168.99.100:32771/spring-demo-0.0.1-SNAPSHOT/hello4 threads and 20 connectionsThread StatsAvgStdevMax+/- StdevLatency10.20ms8.70ms 122.66ms81.20%Req/Sec554.18167.661.04k63.25%22088 requests in 10.02s, 2.43MB readRequests/sec:2203.76Transfer/sec:247.89KB$ wrk -c 20 -d 10 -t 4 http://192.168.99.100:32772/spring-demo-0.0.1-SNAPSHOT/helloRunning 10s test @ http://192.168.99.100:32772/spring-demo-0.0.1-SNAPSHOT/hello4 threads and 20 connectionsThread StatsAvgStdevMax+/- StdevLatency11.30ms14.24ms 186.52ms92.95%Req/Sec557.54207.911.24k67.17%22025 requests in 10.03s, 2.42MB readRequests/sec:2196.36Transfer/sec:247.05KB配置Nginxupstream ha {server 192.168.99.100:32771;server 192.168.99.100:32772 backup;}server {listen80;server_name_;location / {proxy_next_upstream http_502 http_504 http_404 error timeout invalid_header;proxy_passhttp://ha/spring-demo-0.0.1-SNAPSHOT/;}}注意:默认情况下只会转发GET/HEAD/PUT/DELETE/OPTIONS这种幂等的请求,而不会转发POST请求,如果需要对POST请求也做转发,就需要加上non_idempotent配置,整体配置如下
upstream ha {server 192.168.99.100:32771;server 192.168.99.100:32772 backup;}server {listen80;server_name_;location / {proxy_next_upstream http_502 http_504 http_404 error timeout invalid_header non_idempotent;proxy_passhttp://ha/spring-demo-0.0.1-SNAPSHOT/;}}注意proxy_next_upstream http_502 http_504 http_404 error timeout invalid_header;这行,这里就是表示把访问当前的upstream返回了这些状态码的请求转发到upstream中的下一台机器,在我们现在的应用场景下,当war包发布时,正在更新war包的tomcat会返回404,也就是对应http_404,如果不配置这行,是不会做转发的 。
但这样简单的配置还会有一个问题,那就是Nginx不会把出问题的后端从upstream中摘除,也就是说请求还会访问到这个正在更新中的realserver,只是Nginx会再把请求转发到下一台好的realserver上,这样会增加一些耗时 。目前有三种方式可以实现对Nginx负载均衡的后端节点服务器进行健康检查,具体参考Nginx负载均衡
通过Nginx压测基本测试
  1. 两个tomcat节点均正常的情况下压测
$ wrk -c 20 -d 10 -t 4 http://192.168.99.100:32778/helloRunning 10s test @ http://192.168.99.100:32778/hello4 threads and 20 connectionsThread StatsAvgStdevMax+/- StdevLatency57.36ms32.06ms 335.36ms71.29%Req/Sec89.2948.20390.0085.25%3577 requests in 10.05s, 562.30KB readRequests/sec:355.77Transfer/sec:55.93KB


推荐阅读