大家好,又见面了,我是你们的朋友全栈君。
pytho程序的热部署
知乎上面的回答
真正意义上的代码热部署应该是类似erlang那样的,将代码更新到节点后不停服务,不断连接的自动应用新代码。auto reload(代表django的autoreload)什么的还是会造成业务瞬间中断。我感觉是可以从wsgi容器级别上实现,比如更新代码后检测到文件变更,然后通知容器创建新的wsgi application的实例,之后所有新的请求都发送到新的wdgi application实例上。等旧wsgi application实例的最后一个请求返回后就将其回收掉。不过貌似没有看到类似的实现
爬虫程序的热部署的原理
主要使用了:reload ,importlib 两个模块
爬虫程序,尤其是多爬虫系统,比如自动同步系统(实时爬虫),需要经常修改爬虫规则(代码),如果使用重启的方式,对于实时爬虫来说,运维工作量大,而且还会造成服务中断。所以可以使用python的reload方法来实现热部署。
但是,由于对reloa的机制不是很了解,暂时还不清楚reload对程序的负面影响,尤其对于高并发程序的影响。
实现
实现很简单,程序使用多线程,热部署线程负责监听mq消息,收到消息,reload对应的模块。
代码
新建hotupdate包 修改__init__.py文件
代码语言:javascript复制# coding=utf-8
"""实现热更新功能"""
import importlib
import json
import sys
import threading
import logging
from hotupdate.hot_update import HotUpdateEventDriven, HotUpdateMessage
driven = HotUpdateEventDriven()
def _callback(ch, method, properties, body):
"""收到消息执行的回调函数"""
try:
msg = json.loads(body.decode('utf-8'))
message = HotUpdateMessage(model_path=msg['model_path'], is_append_path=msg['is_append_path'], physical_path=msg['physical_path'])
# 物理路径加入pythonPath中
if message.is_append_path:
sys.path.append(message.physical_path)
# 动态的引入该模块
import_module = importlib.import_module(message.model_path)
reload(import_module)
# 打印热更新信息
logging.warning('%s模块已经更新' % (message.model_path))
except Exception, e:
logging.warning('热部署失败,收到的消息%s,异常信息%s' % (body, e.message))
# 消息确认
driven.input_channel.basic_ack(delivery_tag=method.delivery_tag)
def _run():
"""线程的执行方法"""
driven.receive(_callback)
def auto_reload_module():
"""reload a module. 新开一个线程来进行reload操作 动态更新python模块代码,依赖事件驱动,就是需要有人或者程序发送消息通知程序 """
t = threading.Thread(target=_run, name='autoReloadThread')
t.start()
新增hot_update.py文件
代码语言:javascript复制# encoding=utf-8
# ---------------------------------------
# 语言:Python2.7
# 功能:事件驱动,触发热部署动作
# ---------------------------------------
import uuid
import pika
from queues import queueBase
from settings import MQ
class HotUpdateEventDriven(object):
"""监听mq消息,收到消息,自动reload"""
def __init__(self):
credentials = pika.PlainCredentials(MQ['userName'], MQ['password'])
parameters = pika.ConnectionParameters(MQ['host'], MQ['port'], '/', credentials=credentials)
self.connection = pika.BlockingConnection(parameters)
self.__init_input__()
def __init_input__(self):
''' 初始化入口通道 '''
self.input_channel = self.connection.channel()
input_exchange = 'python.ppmoney.hotupdate'
input_exchange_type = 'fanout'
input_routing_key = '#'
# 消息入口的队列
self.input_queue = 'python.hotupdate.%s' % (uuid.uuid1())
# 声明路由,如果存在就什么也不会做
self.input_channel.exchange_declare(exchange=input_exchange, exchange_type=input_exchange_type, durable=True)
# 声明队列
self.input_channel.queue_declare(queue=self.input_queue, auto_delete=True)
# 通过routing_key 将交换机与queue绑定起来
self.input_channel.queue_bind(exchange=input_exchange, queue=self.input_queue, routing_key=input_routing_key)
def receive(self, callback):
''' 入口通道:接受自动同步服务发送的任务消息 '''
# 入口通道订阅消费者
self.input_channel.basic_consume(consumer_callback=callback, queue=self.input_queue, consumer_tag='hotupdate_client')
self.input_channel.start_consuming()
class HotUpdateMessage(object):
"""收到的mq消息"""
def __init__(self, model_path, is_append_path, physical_path):
# 模块名称
self.model_path = model_path
# 是否需要添加到pythonPath中
self.is_append_path = is_append_path
# 如果需要添加到PythonPath中,需要提供模块的物理路径
self.physical_path = physical_path
使用起来很简单,只需要增加一行代码即可
代码语言:javascript复制# 开启热更新
hotupdate.auto_reload_module()
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/143670.html原文链接:https://javaforall.cn