oslo.concurrency是一个为OpenStack其他项目提供用于管理线程的工具库,这样,OpenStack其他项目可以直接调用oslo.concurrency库利用其锁机制安全的运行多线程和多进程应用,也可以运行外部进程。本文总结了oslo.concurrency中常用的工具类或方法及其对应的使用方法。
1. lockutils
lockutils模块封装了oslo库的锁机制,其中,定义了读写锁、信号量以及同步装饰器方法等。本节分别介绍这些类和方法的实现与使用。
1.1 锁机制
lockutils中的锁机制实质上是直接使用了fasteners的实现,所以本节直接介绍fasteners库中的读写锁和共享内存,即ReaderWriteerLock和InterProcessLock类。
1.1.1 ReaderWriterLock类
ReaderWriterLock类实现了一个读写锁,读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。fasteners通过ReadWriterLock类实现了读锁和写锁,其在该类中定义了READER和WRITER标识分别表示申请的锁是读锁还是写锁。使用该类可以获得多个读锁,但只能存在一个写锁。在目前的版本中,该类不能实现从读写升级到写锁,且写锁在加锁时不能获取读锁;而以后可能会对这些问题进行优化。该类的主要方法如下:
- read_lock():为当前线程申请一个读锁,其只有在没有为其他线程分配写锁时才能获取成功;如果另一个线程以及获取了写锁,调用该方法会返回RuntimeError异常。
- write_lock():为当前线程申请一个写锁,其只有在没有为其他线程分配读锁或写锁时才能获取成功;一旦申请写锁成功,将会阻塞申请读锁的线程。
- is_reader():判断当前线程是否申请了读锁。
- is_writer():判断当前线程是否申请了写锁,或已申请但尚未获得写锁。
- owner():判断当前线程是否申请了锁;如果获得了锁是读锁还是写锁。
1.2.1 InterProcessLock类
1.2 信号量
1.3 同步装饰器
1.4 外部锁
2 processutils
processutils模块定义了一系列系统级的工具类和辅助函数。本小节将介绍processutils库的相关类或方法。
2.1 进程或线程异常类
processutils模块中定义了多个进程或线程异常类,如参数不合法异常InvalidArgumentError、不知名参数异常UnknownArgumentError、进程执行异常ProcessExecutionError、日志记录异常LogErrors等。
2.2 资源限制类
ProcessLimits类封装了一个进程对资源的限制,这些限制主要包括以下几个方面:
- address_space:进程地址空间限制。
- core_file_size:core文件大小限制。
- cpu_time:CPU执行当前进程时间限制。
- data_size:数据大小限制。
- file_size:文件大小限制。
- memory_locked:加锁的内存大小限制。
- number_files:打开的文件最大数量限制。
- number_processes:进程的最大数量限制。
- resident_set_size:最大驻留集(RSS)大小限制。
- stack_size:栈大小限制。
2.3 进程执行方法
- execute()方法:该方法通过启动一个子进程提取并执行一个命令。主要参数有:待执行的命令cmd;设置当前目录的cwd;发送到打开的进程process_input;为进程设置环境变量的env_variables;代表退出进程的int、bool或list值check_exit_code,默认为0,只有产生异常才会设置为其他值;重试延迟时间delay_on_retry,如果设置为True,表示马上进行重试操作;cmd重试次数attempts;run_as_root,该值如果设置为True,则为cmd命令加上root_helper指定的前缀;为命令指定的前缀root_helper;shell表示是否使用shell执行这个命令;执行命令记录日志的等级loglevel;监听错误日志log_errors,是一个LogErrors对象;binary,该值如果为True,则返回Unicode编码的stdout后stderr;prlimit表示一个ProcessLimits对象,用于限制执行该cmd的命令的资源用量。
- trycmd()方法:execute()的一个装饰器,使用这个装饰器可以更加容易的处理错误和异常。返回一个包含命令输出strdout或stderr字符串的元组。如果err不为空,则表示执行命令出现异常或错误。
- ssh_execute():通过ssh执行命令。
- get_worker_count():获取默认的worker数量,返回CPU的数量;如果无法确定则返回1.
3 watchdog
- logger:一个记录日志的对象。
- action:描述将执行的操作。
- level:表示记录日志的等级,默认为debug。
- after:发送消息之前的持续时间,默认为5s。
4 使用方法
from oslo_concurrency import lockutils
@synchronized('mylock')
def foo(self, *args):
...
@synchronized('mylock')
def bar(self, *args):
...
为一个方法添加@syschronized装饰器,可以保证统一时刻只有一个线程执行这个方法;但是,同时可以有两个方法共享这个锁,此时统一时刻要么只能执行foo方法,要么只能执行bar方法。
(in nova/utils.py)
from oslo_concurrency import lockutils
synchronized = lockutils.synchronized_with_prefix('nova-')
(in nova/foo.py)
from nova import utils
@utils.synchronized('mylock')
def bar(self, *args):
...
如果需要设置一个带有前缀的同步锁,可以使用如上的方式进行设置。
FORMAT = '%(asctime)-15s %(message)s'
logging.basicConfig(format=FORMAT)
LOG = logging.getLogger('mylogger')
with watchdog.watch(LOG, "subprocess call", logging.ERROR):
subprocess.call("sleep 10", shell=True)
print "done"
如果设置一个看门狗,则可以使用with语法调用watchdog.watch()方法。