notes notes
首页
  • 主题初衷与诞生
  • 介绍
  • 快速上手
  • 目录结构
  • 相关文章

    • 使目录栏支持h2~h6标题
    • 如何让你的笔记更有表现力
    • 批量操作front matter工具
    • 部署
    • 关于写文章和H1标题
    • 关于博客搭建与管理
  • PostgreSQL 全文搜索
  • 依赖注入
  • 开发常见问题
  • 逻辑删除
  • TCP 粘包和拆包
  • netty 粘包和拆包
  • 在k8s集群中搭建一个harbor私有仓库
  • 优雅停机
  • helm 命令学习
  • yield 关键字
  • 不定参数
TODO
  • REST API
  • 使用 IntelliJ IDEA 反编译 jar
  • Spring Boot 删除前后空白字符
  • 设计模式

    • 装饰器模式
Docker
Tool Box (opens new window)
GitHub (opens new window)
首页
  • 主题初衷与诞生
  • 介绍
  • 快速上手
  • 目录结构
  • 相关文章

    • 使目录栏支持h2~h6标题
    • 如何让你的笔记更有表现力
    • 批量操作front matter工具
    • 部署
    • 关于写文章和H1标题
    • 关于博客搭建与管理
  • PostgreSQL 全文搜索
  • 依赖注入
  • 开发常见问题
  • 逻辑删除
  • TCP 粘包和拆包
  • netty 粘包和拆包
  • 在k8s集群中搭建一个harbor私有仓库
  • 优雅停机
  • helm 命令学习
  • yield 关键字
  • 不定参数
TODO
  • REST API
  • 使用 IntelliJ IDEA 反编译 jar
  • Spring Boot 删除前后空白字符
  • 设计模式

    • 装饰器模式
Docker
Tool Box (opens new window)
GitHub (opens new window)
  • 知识积累

    • PostgreSQL 全文搜索
    • @AutoWired VS @Resource VS @Inject
    • 开发常见问题
    • 逻辑删除
    • TCP 粘包和拆包
    • netty 粘包和拆包
    • 在k8s集群中搭建一个harbor私有仓库
    • 优雅停机
    • helm 命令学习

优雅停机

优雅停机(Graceful Shutdown)是一种良好的开发实践,有助于确保服务的可用性、数据完整性和用户体验。 通过适当处理关闭信号、请求处理和资源释放,可以实现平滑的服务停机过程,减少潜在的问题和不可预测的行为。

具体说,实现优雅停机可以带来如下好处:

  • 数据完整性:通过优雅停机,可以确保正在处理的请求能够完成,以避免数据丢失或事务未完成。
  • 用户体验:通过允许正在处理的请求顺利完成,用户不会中断正在进行的操作,提供更好的用户体验。
  • 资源管理:在优雅停机期间,可以正确地释放和关闭与服务相关的资源,例如数据库连接、文件句柄等,以避免资源泄漏或浪费。
  • 避免服务中断:优雅停机可以避免服务突然中断,并允许正在进行的请求在关闭之前得到响应。这对于对服务可用性要求很高的应用程序尤为重要。
  • 协调其他服务:优雅停机还可以提供通知或广播给其他相关服务,以便它们能够适应服务的停机状态,并采取相应的措施,例如重新路由请求、切换到备用服务等。

然而,优雅停机并没有统一的实现方案,需要用户根据自己的应用程序和需求经行调整和优化。

一般来说,在应用收到停机信号( SIGTERM 或 SIGINT)应分两步实现优雅停机:

  1. 停止接收新请求
  2. 在一定时间内处理未完成请求,释放连接、文件句柄等,如果超时后仍有在处理的任务,需要在保证数据完整性和安全性的前提下强制停机

以 tornado 为例演示:

需注意的是,在网上不少文档中都提到在71行处用 server.stop() 停止接受新请求,但经测试发现,该方法并不是停止接受新请求。
测试场景:用浏览器或者 Postman 调用一个接口,然后执行 server.stop(),然后再调用该接口,大概率仍有响应
原因:server.stop() 只是
停止监听端口
,停止接受新
TCP连接**。如果复用之前已经创建的连接创建 http 请求,则仍可正常使用。
结论:server.stop() 并不适合用于Graceful shutdown,这可以从 tornado 开发者的回答 (opens new window) 中得到印证。

import time
import threading
import signal, asyncio
from functools import partial

shutting_down = False
_request_counter = 0
_request_counter_lock = threading.Lock()

def stop_accepting_new_request():
    global shutting_down
    shutting_down = True

class RequestCounterHandler(object):
    def prepare(self) -> None:
        if shutting_down:
            self.send_error()
        with _request_counter_lock:
            global _request_counter
            _request_counter += 1

    def on_finish(self):
        with _request_counter_lock:
            global _request_counter
            _request_counter -= 1

class CustomFallbackHandler(RequestCounterHandler, tornado.web.FallbackHandler):
    def prepare(self) -> None:
        RequestCounterHandler.prepare(self) 
        tornado.web.FallbackHandler.prepare(self) 

GRACEFUL_SHUTDOWN_TIMEOUT = 30

if __name__ == '__main__':

    import tornado.ioloop
    import tornado.web

    loop = tornado.ioloop.IOLoop.current()

    def shutdown_handler(server, signum, frame):
        io_loop = tornado.ioloop.IOLoop.current()

        def unfinished_tasks():
            return [1]
        
        def stop_job_forcely(task):
            print(f'task {task} has been finished forcely.')

        def stop_backend_tasks(server, deadline):
            now = time.time()

            tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task() and not t.done()]
            if now < deadline and len(tasks) + len(unfinished_tasks()) + _request_counter > 0:
                print(f"Awaiting pending backend tasks to finish, contains {len(tasks)} asyncio tasks, {_request_counter} pending requests and {len(unfinished_tasks())} tasks.")
                io_loop.add_timeout(now + 1, stop_backend_tasks, server, deadline)
            else:
                # still has unfinished tasks, cancel them forcely.
                _tasks = unfinished_tasks()
                if _tasks:
                    print('Still has unfinished tasks after timeout and will terminate them forcely.')
                    for _task in unfinished_tasks():
                        stop_job_forcely(_task)
                io_loop.stop()
                print('Graceful shutdown complete.')

        def shutdown():
            print('Start graceful shutdown.')
            try:
                # stop accepting new requests
                stop_accepting_new_request()

                stop_backend_tasks(server, time.time() + GRACEFUL_SHUTDOWN_TIMEOUT)
            except Exception as e:
                print(e)

        print(f'Caught signal: {signum}')
        global shutting_down
        if not shutting_down:
            shutting_down = True
            io_loop.add_callback_from_signal(shutdown)
    
    
    tornado_app = tornado.web.Application([
        (r'.*', CustomFallbackHandler)
    ], websocket_max_message_size=1024)
    
    server = tornado_app.listen(port=8080)

    signal.signal(signal.SIGTERM, partial(shutdown_handler, server))
    signal.signal(signal.SIGINT, partial(shutdown_handler, server))

    loop.start()
上次更新: 2023/12/08, 06:34:37
在k8s集群中搭建一个harbor私有仓库
helm 命令学习

← 在k8s集群中搭建一个harbor私有仓库 helm 命令学习→

Theme by Vdoing | Copyright © 2023-2023
Powered by vuepress-theme-vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式