python:优雅的退出程序或重启服务

在微服务中,使用任务队列有助于松耦合的设计,但有时,我们需要重启服务,但不能打断队列中正在进行的任务。 正确的做法是handle sigterm信号,具体代码如下:

import sys
import argparse
import logging
import signal
import asyncio

class GracefulKiller:
  kill_now = False
  def __init__(self):
    signal.signal(signal.SIGINT, self.exit_gracefully)
    signal.signal(signal.SIGTERM, self.exit_gracefully)

  def exit_gracefully(self,signum, frame):
    self.kill_now = True

async def loop_task():
    killer = GracefulKiller()
    while 1:
        print("ha") # 任务主体
        if killer.kill_now:
            break
        await asyncio.sleep(2)

loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(loop_task())
finally:
    loop.run_until_complete(loop.shutdown_asyncgens())  # see: https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.shutdown_asyncgens
    loop.close()

利用异步

python3.5 之后,有了比较完善的asyncio库和协程机制。对IO密集型任务,可以使用异步调用在单线程中实现“并发”。极大的增加任务吞吐。

想要让IO 任务并发,只需要使用支持asyncio的库(比如aiohttp),简单的loop.create_task就行。有时,需要限制后台任务的数量,在重启服务的时候,需要等待所有后台并发任务的完成。

此时消费者可以使用信号量进行控制。

import argparse
import logging
import signal
import asyncio

class GracefulKiller:
  kill_now = False
  def __init__(self):
    signal.signal(signal.SIGINT, self.exit_gracefully)
    signal.signal(signal.SIGTERM, self.exit_gracefully)

  def exit_gracefully(self,signum, frame):
    self.kill_now = True

def pop_queue(task_queue):
    try:
        task_args = task_queue.pop(0)
        print("dequeue args {}".format(task_args))
    except:
        task_args = None
    return task_args

async def wait_tasks_done(semaphore, value):
    while semaphore._value != value:
        print("wait all task done!")
        await asyncio.sleep(1)

# 处理单个异步任务
async def run_task(task_args, semaphore):
    print("run_task {}".format(task_args))
    await asyncio.sleep(3)  # 模拟耗时
    print("run_task {} done".format(task_args))
    semaphore.release()

async def loop_task():
    killer = GracefulKiller()
    loop = asyncio.get_event_loop()
    task_queue = [x for x in range(0, 10)] # 模拟一个任务队列

    task_limit = 2  # 同时允许任务数量
    semaphore = asyncio.Semaphore(value=task_limit, loop=loop)  
    while 1:
        task_args = pop_queue(task_queue)
        if task_args != None:
            await semaphore.acquire()
            loop.create_task(run_task(task_args, semaphore))

        if killer.kill_now:
            await wait_tasks_done(semaphore, task_limit)
            break

loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(loop_task())
finally:
    loop.run_until_complete(loop.shutdown_asyncgens())
    loop.close()

输出:

dequeue args 0
dequeue args 1
dequeue args 2
run_task 0
run_task 1
run_task 0 done
run_task 1 done
wait all task done!
run_task 2
wait all task done!
wait all task done!
run_task 2 done

上面的程序,无论何时重启,都将等待所有后台的任务完成。妈妈再也不用担心我重启服务被用户投诉了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏晓月寒·

MongoDB实现问卷/考试设计

MongoDB是一个面向文档存储的数据库。在MongoDB中,一条记录叫做document(文档),由类似于JSON结构的键值对组成。

6020
来自专栏微信公众号:Java团长

业务复杂=if else?刚来的大神竟然用策略+工厂彻底干掉了他们!

首先可读性,不言而喻,过多的if-else代码和嵌套,会使阅读代码的人很难理解到底是什么意思。尤其是那些没有注释的代码。

9610
来自专栏TechBox

LLVM简介

官方描述如下: The LLVM Project is a collection of modular and reusable compiler and t...

8600
来自专栏FunTester

Selenium编写自动化用例的8种技巧

在开始自动化时,您可能会遇到各种可能包含在自动化代码中的方法,技术,框架和工具。有时,与提供更好的灵活性或解决问题的更好方法相比,这种多功能性导致代码更加复杂。...

6910
来自专栏搜云库技术团队

实战 SpringCloud 微服务“秒杀”架构(含代码)

分析,在做秒杀系统的设计之初,一直在思考如何去设计这个秒杀系统,使之在现有的技术基础和认知范围内,能够做到最好;同时也能充分的利用公司现有的中间件来完成系统的实...

11910
来自专栏wordpress建站吧

wordpress优缺点盘点

wordpress优缺点很多,不管是说他好的还是不好的,但终究不能阻挡他的强大,毕竟全世界范围使用最多的最广泛的就是他,全世界超过30%的网站在用他里建站;

5510
来自专栏营旗的小记录

SpringBoot基础(二、原理分析)

接着上一篇SpringBoot基础(一、快速入门)的介绍,我们来简单的谈谈SpringBoot的原理分析。

6510
来自专栏HACK学习

实战渗透 | 向吃鸡外挂站开炮

发现大多数都是用的一套aspx的程序,可惜没有源码不能白盒审计,黑盒也找不到什么洞

24930
来自专栏营旗的小记录

SpringBoot基础(四、整合RabbitMQ)

先安装Erlang语言(一路确定即可),再安装RabbitMQ服务(一路确定即可,注意WIN10是否中文名字)。

5310
来自专栏微信公众号:Java团长

聊聊前后端分离接口规范

随着互联网的高速发展,前端页面的展示、交互体验越来越灵活、炫丽,响应体验也要求越来越高,后端服务的高并发、高可用、高性能、高扩展等特性的要求也愈加苛刻,从而导致...

7920

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励