你知道强行关机的后果有多严重吗!有一天,我正兴高采烈地写一篇技术文章,电脑却突然蓝屏了!哦,完了,写了好几千字,忘记保存了!我盲目猜测,很多同学都有这样的经历。可能因为一些意外事故,他们的电脑被迫关机,失去了现在的工作。同样,对于企业来说,所有的网站、应用、数据、服务都挂在服务器上。一旦发生意外,比如断线或遇到自然灾害,服务器将被强制关闭,使得机器上运行的所有程序都被强行中断,后果不堪设想!有的同学就笑了,这不就是程序被强行打断了吗,我们自己偶尔用任务管理器或者kill-9命令杀掉一个进程,重启程序快点就好了,有什么大不了的?确实,我曾经强行杀掉进程下线升级服务,简单干净。但是直到后来因为强杀进程,导致了一次线上事故,造成了经济损失和加班,这让我很生气!我意识到我太粗暴了,我的想法太简单了。事实上,如果一个程序被强行中断,除了无法提供服务之外,还有很多严重的后果!1、请求丢失对于Web服务器,比如JavaWeb开发中的主流Tomcat。当收到请求时,将启动一个线程来处理该请求。而如果请求数量过多,线程无法处理,则将请求放入等待队列,排队等待空闲线程。假设web服务进程突然中断,内存队列中所有等待执行的请求都会丢失。等了半天,空了!2、业务一旦中断,流程一旦中断,所有正在进行的业务都会中断,从而导致很多意想不到的后果。例如有一个检查数据的任务,需要检查所有数据库中状态为0的数据是否正确,代码流程如下://开始检查,将数据状态从0设置为1开始检查();//检查doCheck();//结束Check,设置正确的数据状态为2endCheck();假设数据状态刚刚设置为1,则表示正在检查中。那么程序就中断了,会导致以后这段数据的状态一直为1,再也不会去查了。同样,如果检查已经完成,数据正确,数据状态应该设置为2,但此时程序中断,也会导致数据状态与预期不符。以上只是一个简单的例子,但在实际业务场景中,业务中断可能会直接影响收入,尤其是涉及交易的支付转账业务。你真的不想变酷吗?3、事务中断数据库事务是指对数据库进行的一系列不可分割的操作,这些操作是一致的,每次执行都要使数据库从一种一致状态变为另一种一致状态。例如在转账业务中,用户A要给用户B转1元,用户A扣1元,用户B增加1元。但是,如果用户A已经扣了1块钱,应用程序或者数据库系统突然挂了,导致交易还没有完成就中断了,用户B的总额并没有因此而改变。此时数据库处于不一致状态。同理,即使在程序中设计了回滚,回滚过程也有可能被打断!除了数据不一致,事务中断也可能造成行锁和表锁,影响这部分数据的可用性。4、文件损坏假设程序正在写入一个文件,还没完成就被打断,可能会导致文件不完整甚至损坏。这让我想起了小时候,电脑配置不高,有时玩游戏会卡住,然后强行杀掉进程,导致游戏文件损坏,只好重装下载游戏。5.任务丢失我们在写业务代码的时候,经常会把耗时的任务异步化,提交到线程池后立即返回成功。线程池会依次从任务队列中读取并执行任务。一旦程序中断,线程池中的任务就会丢失,就好像从来没有提交过一样。这种感觉就像是你答应了人家做某事,人家对你很放心,结果你放鸽子跑了。6.数据丢失有时,我们会将数据暂时存放在内存中,然后周期性、定期或批量地持久化到数据库或本地磁盘中。比如Redis数据库的RDB机制,会定时在本地备份内存中的数据,从而减少大量数据并发写入时的负载,提高性能。但是就像上面说的任务丢失一样,一旦程序中断,可能会造成很多未持久化的数据丢失,比如缓存,批量提交数据等。7.消息丢失在分布式系统中,节点经常交互协作通过消息,程序中断在不同情况下可能导致消息丢失。1.消息未发送。假设在一个支付服务中,用户的账户余额被扣款,数据库被更新。接下来,必须将响应消息返回给客户端。但是当消息在发送队列中等待发送时,由于进程被强制退出导致消息没有发送,导致回复消息丢失。客户端长时间收不到消息后,可能会发起重试,导致重复更新。2.消息未确认。比如某段业务代码从消息队列中取出一条消息进行消费处理。代码流程如下://获取下一条消息Messagemsg=getNextMsg();//处理消息intres=handleMsg(msg);//处理成功?if(res==0){//确认消息ack();}else{//拒绝确认消息nack();}不管消息处理成功与否,都要给消息队列一个回复!如果处理成功,告诉他这条消息已经被我处理过了,请给我下一条消息;即使处理失败,你也必须告诉消息队列,请重新发送这条消息给我。一旦程序中断,没有人会知道这条消息的处理结果,这可能会导致消息队列阻塞或无限重发(根据具体的消息队列来决定)。8、强行中断占用资源的程序,可能会导致很多资源被占用,没有释放。例如:空间占用:如果分配的内存没有被回收,临时文件没有被删除等。端口占用:这个端口不能被其他应用程序使用。很多同学在本地调试的时候应该也会遇到3000和8080端口因为强制退出没有释放的问题。连接占用:比如和远程服务建立Http连接,由于连接没有释放,一个连接号就浪费了,就像买了电影票却不去。9、服务不下线在微服务场景下,服务通常是由一个中心化的注册中心来发现和管理的。比如在Eureka注册中心,服务生产者向注册中心注册服务,服务消费者从注册中心获取服务地址,然后远程调用:一旦某个服务进程还没有立即通知注册中心下线了,就会中断,结果服务消费者还是可以获取到服务的路由,所以调用失败。另外,如果服务下线了,如果没有通知上游(服务调用者),也可能导致上游不断调用,严重时会出现雪崩效应,整个服务链路都将崩溃。被打断!特别是在分布式场景下,强制进程中断对集群的影响(比如数据一致性)非常大。FLP不可能定理中描述:在异步通信场景下,即使只有一个进程失败,也没有算法可以保证非失败进程的一致性。其实比起这些问题,更可怕的是,如果没有完善的数据监控和检测机制,你连强行关机后有没有问题都不知道?出现了什么问题?什么数据丢失了?哪些数据不一致?哪些任务需要报酬?看不见的危险最可怕!因此,预防胜于治疗。一方面,我们要养成良好的习惯,无论是自己的电脑还是服务器,千万不要主动强行关机;另一方面,在程序设计中也要做好意外停机的预防措施。不要等到失去了才后悔。
