协程,线程,进程
进程
进程打开一个浏览器就是,就是启动了一个浏览器进程。一个运行的程序(代码)就是一个进程,没有运行的代码叫程序。进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所有进程间数据不共享,开销大。进程是执行任务的基本单元,也是操作系统执行任务的基本单元。进程中包含了程序指令和相关资源的集合。进程互不影响,可以同时执行,如果你是多核cpu
创建进程: 一般通过进程池
线程
在一个进程中,至少有一个线程,这个线程就是当前进程的主线程,
线程是进程中执行任务的基本单元,线程不能独立存在,依赖进程存在而多个线程共享内存(数据共享,共享全局变量),从而极大地提高了程序的运行效率。
多线程执行任务会出现数据混乱的问题甚至是死锁。不能同时执行,只能切换执行。
一般通过线程池
import concurrent.futures
def root():
with concurrent.futures.ThreadPoolExecutor() as executor:
# 执行urls的长度的线程
response = executor.map(fetch_data, urls)
return list(response)
def fetch_data(url):
# 通过url获取数据的代码
return data
协程
通过 async await 创建一个协程,和线程一样不能同时执行只能切换执行
async def root():
response = await asyncio.gather(
fetch_data(),
process_data()
)
return response
async def fetch_data():
async with aiohttp.ClientSession() as session:
response = await session.get("https://api.example.com")
return await response.json()
async def process_data():
# 处理数据的代码
return processed_data
三者对比
全局解释器锁(GIL):
多线程: 在Python中,由于全局解释器锁(GIL)的存在,即使在多线程环境下,CPython解释器在任何给定时间只允许一个线程执行Python字节码,只能执行一个。因此,对于计算密集型任务,多线程可能不会带来性能提升,因为线程们在执行计算时会相互阻塞。但对于IO密集型任务,由于IO操作(如文件读写、网络请求等)不受GIL影响,多线程可以在一个线程等待IO时执行其他线程,从而提高效率。
多进程: 每个进程有自己的内存空间和解释器,因此不受GIL的限制。这意味着在多核处理器上,多个进程可以真正并行执行计算密集型任务。每个进程都独立运行,所以它们不会像线程那样共享内存或数据。
资源消耗和通信开销:
多线程: 线程通常比进程消耗更少的资源。线程共享相同的内存空间,因此在线程间共享数据相对容易且开销较小。
多进程: 进程之间不共享内存,每个进程都有自己的内存空间。这意味着进程之间的通信(比如使用队列或管道)更复杂且开销更大。同时,创建进程的开销也比创建线程大。
适用场景:
多线程: 适合于IO密集型任务,如文件操作、网络通信等。
多进程: 更适合于计算密集型任务,尤其是在多核处理器上。
稳定性:
多线程: 一个线程崩溃可能会影响整个程序的稳定性。
多进程: 一个进程崩溃不会直接影响其他进程
Python中的WebSocket编程
我暂时还用到,可以考虑借鉴下面这篇
https://zhuanlan.zhihu.com/p/630520426
Chrome 浏览器中查看 webSocket 连接信息
https://www.cnblogs.com/zhuyeshen/p/12007217.html
通过 Chrome浏览器 查看http请求报文
https://www.cnblogs.com/loberty/p/11990576.html
https://blog.csdn.net/ytangdigl/article/details/73135466#
日志
主要了解一些必要的知识,以及部署后端接口的工程上要用到的知识,不会讲太细太全,一些知识需要自己去问gpt。
logging
日志基本设置输出,输出格式,输出目标文件
import logging
#设置输出到那个文件,什么级别以上的日志会被记录logging.ERROR)以上的
logging.basicConfig(filename='log.txt',
format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s-%(funcName)s',
level=logging.ERROR)
#下面的按顺序越来越高
logging.debug('debug,用来打印一些调试信息,级别最低')
logging.info('info,用来打印一些正常的操作信息')
logging.warning('waring,用来用来打印警告信息')
logging.error('error,一般用来打印一些错误信息')
logging.critical('critical,用来打印一些致命的错误信息,等级最高')
只有一个文件可能会这样用
日志记录器(Logger)
logger = logging.getLogger(name)
#name通常是使用__name__变量,以确保日志记录器在整个应用中的唯一性
这样输出到日志中会带有你的当前的模块,不同的模块在日志中就会显示出来
也可以设置日志基本设置,这个的优先级会比较高,如果没有设置就会继承他
logger = logging.getLogger()
logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
默认记录器: 如果没有指定名称,getLogger()函数返回的是root记录器。这在你只需要一个简单的日志记录系统时非常方便。
继承行为: 在logging的层次结构中,如果一个具体的记录器(由getLogger(name)返回)没有明确设置日志级别或处理器,它将继承root记录器的配置。
日志基本设置 也是用root记录器
处理器(Handlers):
用于确定日志的目的地,如文件、控制台。
常见的处理器包括StreamHandler(控制台输出),FileHandler(写入文件),RotatingFileHandler(日志滚动),
FileHandler
handler = logging.FileHandler(‘myapp.log’, ‘a’),这里’a’表示以追加模式打开文件
设置日志级别(可选):
你可以为FileHandler设置一个特定的日志级别,以过滤掉低于此级别的日志消息。
例如:handler.setLevel(logging.INFO)。
创建并设置Formatter(可选):
Formatter决定了日志消息的最终输出格式。你可以创建一个Formatter对象,并将其添加到FileHandler。
例如:formatter = logging.Formatter(‘%(asctime)s - %(name)s - %(levelname)s - %(message)s’),
然后使用handler.setFormatter(formatter)。
将创建的FileHandler添加到一个Logger对象
logger.addHandler(handler)。一般会考虑添加到root里面
设置自动按年月日保存
import os
import logging
from datetime import datetime
class CustomDailyFileHandler(logging.FileHandler):
# 初始化函数
def __init__(self, log_directory, mode='a', encoding=None, delay=False):
# 基本配置
self.log_directory = log_directory # 日志文件的根目录
self.mode = mode # 文件打开模式,默认为追加模式
self.encoding = encoding # 文件编码
self.delay = delay # 是否延迟打开文件
self.current_date = None # 当前日志文件的日期
self.update_file_path() # 更新日志文件路径
# 调用父类的初始化方法
super().__init__(self.file_path, self.mode, self.encoding, self.delay)
# 更新日志文件路径
def update_file_path(self):
current_date = datetime.now()
# 创建年、月、日目录
year_dir = os.path.join(self.log_directory, str(current_date.year))
month_dir = os.path.join(year_dir, str(current_date.month))
day_dir = os.path.join(month_dir, str(current_date.day))
# 如果目录不存在,则创建
os.makedirs(day_dir, exist_ok=True)
# 设置日志文件路径
self.file_path = os.path.join(day_dir, f"log_{current_date.strftime('%Y-%m-%d')}.log")
self.current_date = current_date.strftime('%Y-%m-%d')
# 重写 emit 方法,用于日志记录
def emit(self, record):
# 如果日期变更,更新文件路径
if datetime.now().strftime('%Y-%m-%d') != self.current_date:
self.update_file_path()
# 关闭当前文件流,并重新打开新的日志文件
self.stream.close()
self.stream = open(self.file_path, self.mode, encoding=self.encoding)
# 调用父类的 emit 方法记录日志
super().emit(record)
def setup_logging():
log_directory = 'log'
logger = logging.getLogger()
logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler = CustomDailyFileHandler(log_directory)
handler.setFormatter(formatter)
logger.addHandler(handler)
如果你是用gunicorn去记录日志的话,
import logging
logger = logging.getLogger(“gunicorn.error”) 通过这个就可以写到gunicorn的日志文件里面了