基于Nginx dyups模块的站点动态上下线

2017-09-11 16:39:43

简介

今天主要讨论一下,对于分布式服务,站点如何平滑的上下线问题。 

分布式服务

在分布式服务下,我们会用nginx做负载均衡, 业务站点访问某服务站点的时候, 统一走nginx, 然后nginx根据一定的轮询策略,将请求路由到后端一台指定的服务器上。 

 

这样的架构是没有问题的, 但是我们这里考虑几个问题, 

1. 网站上下线问题:我们网站平时更新站点的时候是直接覆盖文件,然后重启, 那这样会造成一些请求中断,如果是非核心逻辑那还好, 如果是核心逻辑,那请求中断,会影响一些数据一致性,比如资金, 交易,订单等。  

 2. 动态加减机器,比如某个站点访问量大,要新增机器,那就需要修改nginx的配置,然后reload, 这样会中断连接。 虽然reload很快,但是还是会有一瞬间的请求中断。 

 

对于第一个问题,我们可以在请求量少的时候去更新, 但是这种在一些服务稳定的公司可用, 对于互联网企业,可能2-3天就一个版本, 而且需要立刻上线, 如果每次都要等到凌晨4点去更新, 可能整个的开发节奏都被带慢了。 

对于第二个问题, 对于可以预见的流量,比如大促来临,可以提前3天放在请求量少的时候更新。 

 

最近几年,随着SOA的普及和微服务的出现,特别是dubbo的出现,服务治理的概念被提出来。 服务治理是一个很宏大的概念,包括服务注册,服务自动发现,服务路由,服务依赖,集群容错,服务降级,服务监测,服务审批等,当然不是每个服务中心都必须实现这些东西, 公司可以根据自己的实际需求来定制实现。 

基于Nginx dyups模块的动态上下线

基于以上这些情况, 我计划实现一个工具,这个工具首先解决站点上下线和动态扩容问题,也就是说在不需要重启nginx的情况下,并且在保证请求不丢失的情况下来更新站点。 同时带有部分服务治理功能。 

 

 

服务上线

1. 在一个新服务上线的时候,一般会提前申请几台机器, 运维会在nginx上新增server,并新增server对应的upstream ,正常情况下upstream应该配置是后端服务器的IP,但是这里不配置(如果允许,甚至这一步都可以省略)。 

2. 服务部署好并启动,在启动的时候,向注册中心注册自身的服务信息,包括IP和端口。 

3. 注册中心收到请求后,会对服务进行健康检测,确保提供的服务没有问题,则将服务状态标示为预上线状态。 

4. 在后台管理中心,就可以将预上线的服务设置为上线,服务管理中心会调用nginx的上线接口,将服务IP新增或者更新到upstream中,服务就可以提供访问。   

 

服务更新

假如我们现在有一个服务需要更新,则执行以下步骤:

1. 在后台管理中心,将一个服务设为下线,此时服务中心会调用nginx的下线接口,将指定服务器的IP设置为下线。 

2. 在等待1分钟后,确保没有新连接连过来,则可以开始更新服务站点。

3. 更新完毕后,再手动设为上线,此时服务中心会调用nginx的上线接口,将指定服务器的IP设置为上线。当然对于成熟的服务,这些都可以自动化,有些公司会有一些自动化发布工具, 与自动化发布工具集成,可以一键下线,更新并上线。 

 

服务运行期间

在服务运行过程中,会有一个健康检测的服务对所有提供服务的站点进行健康检测,一旦检测到有问题,就执行下线逻辑。 直到问题被解决,最后执行上线流程。 

 

动态加减机器

在服务运行过程中,可能因为某些原因,服务请求飙高(前提是这些请求都是合法的),超过了当前集群的承载能力,当系统检测到这些情况后,可以动态扩充机器,比如现在流行的docker,在启动容器的时候,同时启动应用,应用在启动的时候,将自身信息注册给注册中心,注册中心再将这些信息同步到nginx,应用就可以提供访问,整体上就可以实现弹性计算。 

 

为什么不实现服务动态发现?

   这里可以看到图中已经有一个服务注册中心。 既然有了服务注册中心了, 那可以让业务站点连接服务注册中心来获取真实的服务IP,然后绕过nginx来连接服务,这里之所以没有这样做,是因为:

    1.  实现服务动态发现,这个需要和RPC框架配合,而且需要做服务的软负载,失败重连,限流等,整个项目设计就上升了一个复杂度, 考虑到有些项目还未使用RPC,并且不想对原有的项目有过多的侵入, 所以这里不做实现。 但是并不意味没有这些功能,服务的负载, 失败重连, 限流,其实这些功能在nginx中同样也有,可以直接使用,所以没有必要重新再开发。 

    2.  实现服务动态发现,获取到真实的服务IP,然后直连,这些一般是在流量特别大,nginx上出现短板的时候使用,但实际情况,一般很少会耗尽nginx的性能,即使有,也可以通过ngxin水平扩展来实现,所以这里依然使用nginx作为负载均衡。 

 

这里讲一下这个项目的关键点:

1. 服务的注册和健康检测这个没有技术难点,这里不做解释。

2. 关于操作nginx上下线,这里的确是一个难点,因为nginx本身并没有提供这些上下线API,需要openresty并配合一些第三方扩展来实现。 这里主要用到了两个扩展模块:ngx_http_dyups_module  lua-upstream-nginx-module

  ngx_http_dyups_module(https://github.com/yzprofile/ngx_http_dyups_module)提供了粗粒度的upstream管理方法,可以对整个upstream进行新增,删除。 

  lua-upstream-nginx-module(https://github.com/openresty/lua-upstream-nginx-module) ,则提供了细粒度的管理方式,可以对某一个服务IP进行管理,其中提供的set_peer_down方法,可以对upstream中的某个ip进行上下线。

3. 也可以使用ngx_dynamic_upstream(https://github.com/cubicdaiya/ngx_dynamic_upstream)

这些插件有一个共同点,那就是在不需要重启nginx的基础上, 动态修改nginx的配置。 


  • 2019-05-09 11:46:30

    Glide使用高级技巧(解决Glide生成缓存Key问题)

    虽说Glide将缓存功能高度封装之后,使得用法变得非常简单,但同时也带来了一些问题。 比如之前有一位群里的朋友就跟我说过,他们项目的图片资源都是存放在七牛云上面的,而七牛云为了对图片资源进行保护,会在图片url地址的基础之上再加上一个token参数。也就是说,一张图片的url地址可能会是如下格式:

  • 2019-05-13 14:34:42

    linux系统中清理MySql的日志文件,打印日志文件

    默认情况下mysql会一直保留mysql-bin文件,这样到一定时候,磁盘可能会被撑满,这时候是否可以删除这些文件呢,是否可以安全删除,是个问题。 首先要说明一下,这些文件都是mysql的日志文件,如果不做主从复制的话,基本上是没用的,虽然没用,但是不建议使用rm命令删除,这样有可能会不安全,正确的方法是通过mysql的命令去删除。

  • 2019-05-14 16:47:27

    数据库整理碎片

    最后还是用的ALTER TABLE来修改的,不知道为什么有时候管用,有时候不管用。

  • 2019-05-17 16:27:26

    在vue项目里面使用引入公共方法

    今天早上来到公司,没事看了一下别人的博客,然后试了一下,发现的确是可以的,在此记录一下,方便自己日后查阅。 首先新建一个文件夹:commonFunction ,然后在里面建立 一个文件common.js

  • 2019-05-18 12:37:39

    Android夜间模式的实现方案

    对于一款阅读类的软件,夜间模式是不可缺少的。最初看到这个需求时候觉得无从下手,没有一点头绪。后来通过查阅资料发现Android官方在Support Library 23.2.0中已经加入了夜间主题。也就是只需要通过更换主题便可实现日间模式和夜间模式的切换。下面截取项目实现的夜间模式效果图:

  • 2019-05-18 12:38:41

    android 快速实现夜间模式

    最近项目中遇到了一个问题,夜间模式在8.0以上的手机中不起作用,查看了一下原因,是夜间模式实现方法的问题。分两种情况介绍一下

  • 2019-05-18 12:40:35

    Android夜间模式的几种实现

    通过增加一层遮光罩来实现。效果不是很理想,但是好用,毕竟很多手机都有自己的夜间模式了

  • 2019-05-19 02:25:15

    php使用TCPDF生成PDF文件教程

    orientation属性用来设置文档打印格式是“Portrait”还是“Landscape”。 Landscape为横式打印,Portrait为纵向打印