zoukankan      html  css  js  c++  java
  • 千万别强制停机!我嘴都气歪了!

    你知道强制停机的后果有多严重吗!

    有一天,我正在愉快地写技术文章,结果电脑啪地一下就蓝屏了!

    哦豁,完蛋,写了几千字,忘了保存!

    我盲猜很多同学都有这种体验,可能因为一些突发意外,导致自己的电脑强制停机了,丢失了自己当前的工作。

    同样,对于企业,所有的网站、应用、数据、服务都是挂在服务器上的,一旦意外发生,比如被挖断了电线、遭遇了自然灾害,会导致服务器被强制停机,使得机器上 所有进行中的程序被强制中断,后果不堪设想!

    有同学就笑了,不就是程序被强制中断么,我们自己偶尔也会用任务管理器或者 kill -9 命令杀个进程啊,抓紧重新启动程序不就好了,有啥大不了的?

    的确,我以前也是通过强杀进程来下线和升级服务的,干脆利落爽。但直到后来有一次,因为强杀进程导致了线上事故,造成了经济损失和加班,把我嘴都气歪了!我才意识到自己之前太粗暴、想法太简单了。

    其实,一个程序被强制中断,除了无法提供服务外,还有很多严重的后果!

    1. 请求丢失

    对于一个 web 服务器,比如 Java Web 开发中主流的 Tomcat。当接受到请求时,会开启一个线程来处理该请求。而如果请求数较多,线程处理不过来,就会将此请求放入等待队列中,排队等待空闲线程。

    等待队列

    假设 web 服务进程突然中断,会导致所有在内存队列中等待执行的请求丢失,等了半天,等了个空!

    2. 业务中断

    一旦进程中断,会导致 所有 正在执行的业务中断,会导致很多意想不到的后果。

    比如有一个检查数据的任务,要检查所有数据库中状态为 0 的数据是否正确,代码流程如下:

    // 开始检查,数据状态由 0 置为 1
    startCheck();
    // 检查
    doCheck();
    // 结束检查,将正确的数据状态置为 2
    endCheck();
    

    假设刚把数据的状态置为 1,表示正在检查中。然后程序就中断了,会导致以后这条数据的状态始终为 1,再也不会被检查。

    同理,如果已经检查完,并且数据正确,本来应该将数据状态置为 2,但这时程序中断,也会导致 数据的状态和预期不一致

    以上只是一个简单的例子,但实际的业务场景中,业务中断可能直接影响收益,尤其是涉及交易的支付转账业务,如果用户已经付款,却因为程序的中断,没有存储付款记录,那这个支付业务不是真要凉凉?

    3. 事务中断

    数据库事务是指对数据库的一系列 不可分割 的操作,具有一致性,每次执行必须使数据库从一个一致性状态变到另一个一致性状态。

    比如转账业务中,用户 A 要给用户 B 转账 1 元,用户 A 扣除 1 元,用户 B 就要增加 1 元。

    但如果用户 A 已扣除 1元后,应用程序或者数据库系统突然挂了,导致事务尚未完成就被迫中断,结果用户 B 的总金额并没有变化。这时数据库就处于不一致状态。同理,即使在程序中设计了回滚,回滚过程也可能会被中断!

    除了数据不一致外,事务中断还可能导致锁行、锁表,使得这部分 数据的可用性受到影响

    4. 文件损坏

    假设程序正在向一个文件进行写操作,还未完成,就被中断了,可能会导致文件的不完整、甚至损坏。

    这让我想起小时候,电脑配置不高,有时玩游戏会卡住,然后我就强制杀了进程,结果导致游戏文件损坏,只能重新下载游戏。

    文件损坏

    5. 任务丢失

    我们在编写业务代码时,经常会将比较耗时的任务异步化,将任务提交到线程池后立即返回成功。线程池会从任务队列中依次读取并执行任务。

    而一旦程序中断,线程池中的任务就会丢失,好像他从来没有被提交过一样。这种感觉就像你答应别人要做一件事,别人对你很放心,但你最后却放了鸽子跑路了。

    6. 数据丢失

    有时,我们会先将数据临时放在内存中,然后定期、定时、或者分批地持久化到数据库或本地磁盘中。

    比如 Redis 数据库的 RDB 机制,每隔一段时间,会将内存中的数据进行本地备份,从而降低大量数据并发写入时的负载,提升性能。

    但就像上面提到的任务丢失一样,一旦程序中断,可能会导致很多 未持久化的数据丢失,比如缓存、分批提交数据等。

    7. 消息丢失

    在分布式系统中,各个节点间经常通过消息来进行交互和协作,而程序的中断可能会在不同情况下导致消息丢失。

    1. 消息未发出

    假设某支付业务中,已经扣除了用户的账户余额,并更新了数据库,接下来要向客户端返回应答消息。

    但是消息正在发送队列中排队等待发送时,由于进程被强制退出导致消息未发出,从而导致应答消息丢失。客户端久久接收不到消息后,可能会发起重试,导致重复更新。

    消息未发出

    2. 消息未确认

    比如说某段业务代码从消息队列中取出了一个消息,进行消费处理,代码流程如下:

    // 获取下一个消息
    Message msg = getNextMsg();
    // 处理消息
    int res = handleMsg(msg);
    // 处理成功?
    if(res == 0) {
    	// 确认消息
      ack();
    } else {
      // 拒绝确认消息
      nack();
    }
    

    无论消息处理成功与否,都必须要给消息队列一个回复!如果处理成功,要告诉他这条消息已经被我处理完成啦,请给我下一条消息;即使处理失败,也要告诉消息队列,请给我重发本条消息。

    而一旦程序中断,这条消息的处理结果便无人知晓,可能导致消息队列的 阻塞或者无限重发(根据具体消息队列来决定)。

    8. 资源占用

    程序的强制中断可能会导致很多资源的占用未被释放。比如:

    1. 空间占用:如已分配的内存未回收,临时文件未被删除等。
    2. 端口占用:会导致这个端口无法被其他应用程序使用。很多同学在本地调试时,应该也会遇到因为强退导致的 3000、8080 端口未被释放的问题。
    3. 连接占用:比如和远程的服务建立了 Http 连接,由于连接未被释放,会浪费一个连接数,就像买了电影票却不去一样。

    9. 服务未下线

    在微服务场景下,服务通常由集中的注册中心进行统一的服务发现和管理。

    比如 Eureka 注册中心,服务生产者向注册中心注册服务,服务消费者从注册中心获取服务地址,然后远程调用:

    Eureka 注册中心

    而一旦某个服务进程还没有即时通知注册中心它要下线,就中断了,会导致服务消费者仍能获取到该服务的路由,从而调用失败。

    此外,服务下线时如果未向上游(该服务调用方)通知,还可能导致上游的持续调用,严重时会产生雪崩效应,整条服务链路中断!

    尤其是在分布式场景下,出现进程强制中断对集群的影响(比如数据一致性)非常大。正如 FLP不可能定理 的描述:在异步通信场景,即使只有一个进程失败,也没有任何算法能保证非失败进程达到一致性。


    其实,相比起这些问题,更可怕的是,如果没有完善的数据监控和检测机制,你甚至完全不知道在强制停机后有没有出现问题?出现了哪些问题?哪些数据丢失?哪些数据不一致?哪些任务需要补偿?看不见的危险才最可怕啊!

    因此,预防大于治疗。一方面要养成良好习惯,无论是对自己的电脑还是服务器,都千万不要再主动强制停机了;另一方面,也要在程序设计时,做好应对意外停机的防控措施。不要等到失去了,才追悔莫及。

  • 相关阅读:
    使用 yo 命令行向导给 SAP UI5 应用添加一个新的视图
    SAP Fiori Elements 应用的 manifest.json 文件运行时如何被解析的
    SAP UI5 标准应用的多语言支持
    微软 Excel 365 里如何设置下拉菜单和自动高亮成指定颜色
    SAP Fiori Elements 应用里的 Title 显示的内容是从哪里来的
    本地开发好的 SAP Fiori Elements 应用,如何部署到 ABAP 服务器上?
    如何在 Cypress 测试代码中屏蔽(Suppress)来自应用代码报出的错误消息
    教你一招:让集群慢节点无处可藏
    应用架构步入“无服务器”时代 Serverless技术迎来新发展
    MySQL数据库事务隔离性的实现
  • 原文地址:https://www.cnblogs.com/yupi/p/14490169.html
Copyright © 2011-2022 走看看