Home Tags Posts tagged with "发版"

发版

0 51

如果你问我,实习期间最紧张最害怕的时刻是什么?

那一定是上线发版的时候。

 

年少轻狂三连击,服务全挂我挨批

公司的服务采用集群部署,发版时需要部署多台机器。当我第一次发版前,导师曾再三叮嘱我,项目发慢点,一台一台的发。那时我年少轻狂,觉得呆呆等着很无聊,于是一个三连击,仅有的三台机器同时发布,结果服务调用异常的报错铺天盖地而来

服务器部署服务的流程

服务部署在机器上,那么该机器就成了服务器,所有的用户请求都会发送到服务器上被处理。而当前互联网环境下,服务基本都是分布式/集群部署,也就是有多台服务器。各互联网企业在分布式/集群部署的环境下发布版本时,一定不会像我一样三连击,因为服务器部署服务的流程如下:

旧服务正在运行—>停掉旧服务—>部署新服务—>新服务启动

试想,三台服务器A,B,C工作的好好的,我先点击发布A机器,此时A机器先向注册中心发送消息——”我先走了哈,别把请求路由到我这里了”,然后Dubbo会将A机器从负载均衡策略中排除,如果A机器还有未处理的请求,它会先进行处理,之后服务彻底停止。(详细可参考Dubbo-优雅停机)停止后,A机器会部署新服务,也就是你发布的新版本,但是服务不是瞬间启动的它需要启动服务提供者,服务提供者在启动时还要向注册中心注册自己提供的服务,同时订阅自己需要的服务,等完成这些步骤后,A机器上的新服务才算是正式启动,才可以接收用户的请求(详细可参考Dubbo的简要执行流程)。显而易见,【停掉旧服务—>新服务启动】之间是需要耗费不少时间的,大概几分钟。

三连击为什么会导致服务挂掉呢?

原因就是 【停掉旧服务—>新服务启动】的这几分钟。A机器的服务还在启动中,此时A服务器不可用,而我又点击发布B机器,B机器的服务也进入了【停掉旧服务—>新服务启动】这个阶段,B服务器不可用,C机器同理。三连击导致A,B,C三台机器全部不可用,用户请求一过来就会报服务调用异常的错误

正确的流程应该是发布一台,观察一台的日志。A机器发布后,观察A机器的日志,确保A机器上的服务已经启动且有流量进入,这样的话不仅可以保证A机器可用,如果你的新版本出现了问题还可以立刻发现,此时就算需要回滚也只会影响A机器上的请求,B,C机器的服务是不受影响的,当然,最好的情况是任意一台机器都没有事故。

之所以服务器要进行分布式/集群部署,正是为了避免服务不可用。试想,如果淘宝618时用户无法下单,短短的几分钟可能会造成数以亿计的损失。所以一定要牢记,服务不可用是非常严重的事故。因此,涉及到集群环境下的服务部署,我们必须要考虑可用性及可靠性。

一台一台的发,我有一千台机器岂不是要发好几天?

非也,此处的一台一台发,强调的是一个百分比。我们知道,客户端请求会通过Dubbo的负载均衡策略选择路由到哪一台具体的服务器,如果你有A,B,C三台机器提供服务,一台一台的发可以保证任意时刻有2/3的机器可用,只要2/3的机器可以顶住当前的客户端流量,那就没有问题。(当然,如果2/3的机器顶不住,那你必须要加机器,不然的话流量会击垮B,C两台机器,服务彻底不可用)如果你有一千台机器,你完全可以一批10台或者20台的去发,尽管有10台或20台的机器不可用,但仍剩有99%或98%的机器可用,只要剩下的99%的机器可以顶得住当前流量,那就一点问题都没有。

一句话,多少台机器一起发,取决于发布时剩下可用的机器能否顶得住当前客户端流量。顶得住,没有任何问题;大大顶得住,甚至可以加大同时发布的机器数量,比如1000台只需要500台就足够顶住流量的话,直接50台50台的发布;顶不住的话,对不起,你还是老老实实的减少同时发布的机器数量吧~。

Dubbo-优雅停机

背景

对于任何一个线上应用,如何在服务更新部署过程中保证客户端无感知是开发者必须要解决的问题,即从应用停止到重启恢复服务这个阶段不能影响正常的业务请求。理想条件下,在没有请求的时候再进行更新是最安全可靠的,然而互联网应用必须要保证可用性,因此在技术层面上优化应用更新流程来保证服务在更新时无损是必要的。

传统的解决方式是通过将应用更新流程划分为手工摘流量、停应用、更新重启三个步骤,由人工操作实现客户端无对更新感知。这种方式简单而有效,但是限制较多:不仅需要使用借助网关的支持来摘流量,还需要在停应用前人工判断来保证在途请求已经处理完毕。这种需要人工介入的方式运维复杂度较高,只能适用规模较小的应用,无法在大规模系统上使用。

因此,如果在容器/框架级别提供某种自动化机制,来自动进行摘流量并确保处理完以到达的请求,不仅能保证业务不受更新影响,还可以极大地提升更新应用时的运维效率。

这个机制也就是优雅停机,目前Tomcat/Undertow/Dubbo等容器/框架都有提供相关实现。下面给出正式一些的定义:优雅停机是指在停止应用时,执行的一系列保证应用正常关闭的操作。这些操作往往包括等待已有请求执行完成、关闭线程、关闭连接和释放资源等,优雅停机可以避免非正常关闭程序可能造成数据异常或丢失,应用异常等问题。优雅停机本质上是JVM即将关闭前执行的一些额外的处理代码。

适用场景

  • JVM主动关闭(System.exit(int)
  • JVM由于资源问题退出(OOM);
  • 应用程序接受到SIGTERMSIGINT信号。

配置方式

服务的优雅停机

在Dubbo中,优雅停机是默认开启的,停机等待时间为10000毫秒。可以通过配置dubbo.service.shutdown.wait来修改等待时间。

例如将等待时间设置为20秒可通过增加以下配置实现:

dubbo.service.shutdown.wait=20000

容器的优雅停机

当使用org.apache.dubbo.container.Main这种容器方式来使用 Dubbo 时,也可以通过配置dubbo.shutdown.hooktrue来开启优雅停机。

通过QOS优雅上下线

基于ShutdownHook方式的优雅停机无法确保所有关闭流程一定执行完,所以 Dubbo 推出了多段关闭的方式来保证服务完全无损。

多段关闭即将停止应用分为多个步骤,通过运维自动化脚本或手工操作的方式来保证脚本每一阶段都能执行完毕。

在关闭应用前,首先通过 QOS 的offline指令下线所有服务,然后等待一定时间确保已经到达请求全部处理完毕,由于服务已经在注册中心下线,当前应用不会有新的请求。这时再执行真正的关闭(SIGTERM 或 SIGINT)流程,就能保证服务无损。

QOS可通过 telnet 或 HTTP 方式使用,具体方式请见Dubbo-QOS命令使用说明

流程

Provider在接收到停机指令后

  • 从注册中心上注销所有服务;
  • 从配置中心取消监听动态配置;
  • 向所有连接的客户端发送只读事件,停止接收新请求;
  • 等待一段时间以处理已到达的请求,然后关闭请求处理线程池;
  • 断开所有客户端连接。

Consumer在接收到停机指令后

  • 拒绝新到请求,直接返回调用异常;
  • 等待当前已发送请求执行完毕,如果响应超时则强制关闭连接。

当使用容器方式运行 Dubbo 时,在容器准备退出前,可进行一系列的资源释放和清理工。

例如使用 SpringContainer时,Dubbo 的ShutdownHook线程会执行ApplicationContextstopclose方法,保证 Bean的生命周期完整。

实现原理

Dubbo 优雅停机 | Apache Dubbo

 

Dubbo的简要启动流程

1. 服务器启动,运行服务提供者。

2. 服务提供者在启动时,向注册中心(zookeeper)注册自己提供的服务。

3. 服务消费者在启动时,向注册中心订阅自己所需的服务。

4. 注册中心返回服务提供者地址列表给消费者,(若有变更,注册中心将基于长连接推送变更数据给消费者)

5. 服务的消费者,从地址列表中,基于负载均衡,选一台提供者的服务器进行调用,若是失败,在从 地址列表中,选择另一台调用.

6. 期间Dubbo的监控中心,会记录定时消费者和提供者,的调用次数和时间

Dubbo的简要执行流程 – 简书 (jianshu.com)