0 背景
openstack组件动态批量加载组件时会用到stevedore,stevedore可以批量加载插件,看了下源码总结了下使用方法和原理
1 原理
stevedore加载插件一般返回一个对象,对象初始化过程进行插件加载,目前用到的类有:ExtensionManager、DriverManager,namedExtensionManager
加载原理为实例初始化时传入参数,实例初始化会按照参数找到参数对应插件,具体实现是类方法list_entry_points(self),该方法将初始化入参对应的entrypoint加载。加载的主要功能由pkg_resources.iter_entry_points(self,namespace)实现
1.1 pkg_resources.iter_entry_points流程
该方法为实例方法,实例的类为pkg_resources.WorkingSet
1.1 初始化
初始化中加载了sys.path目录下所有以.egg-info, .dist-info结尾的模块,每个模块为一个实例,例如加载的/usr/lib/python/site-packages/kafka.egg-info对应的实例以字典形式储存在实例属性self.by_key中,可这样获取 self.by_key['/usr/lib/python/site-packages/kafka.egg-info']
1.2 iter_entry_points
该方法实际遍历实例WorkingSet的实例属性self.by_key,调用self.by_key中存储的对象的get_entry_map(group)方法获取传入endpoint对应的插件
2 思考
2.1 实际环境中,例如传入的endpoint为ceilometer.notification,返回的插件的定义在ceilometer-9.0.7.egg-info中,ceilometer与ceilometer-9.0.7.eeg-info的对应关系是怎样建立的?
对模块名解析,按-分割模块名,如ceilometer-9.0.7.egg-info对应的就是ceilometer
3 使用
ExtensionManager加载一个namespace下所有插件
DriverManager提供namespace和name,加载namespace下名为name的插件
namedextensionmanager,提供namespace和names,加载namespace下载names中的插件
stevedore.driver.DriverManager(namespace, plugin_name)
入口,调用了_named:NamedExtensionManager的初始化方法
NamedExtensionManager的初始化方法。先调用_load_plugins方法获取extension,用获取到的extension调用_init_plugins方法将extension放到实例属性中,若extensions为空,_init_plugins方法会报错
_load_plugins
该方法定义在extension:ExtensionManager中
list_entry_points
有一个entrypoint缓存,缓存已加载的extension。没缓存时,调_cache.py中的get_group_all方法加载extension
_cache:Cache
_cache:Cache.get_group_all方法中,
path使用sys.path
self._internal用来缓存path
_hash_settings_for_path这个方法返回一个长度为2的元组,元组第二个元素是sys.path下所有entry_points.txt文件的绝对路径和最近修改时间的秒数;元组第一个元素是元组第二个元素生成的一个哈希,用来缓存查到的entry_points。此时元组第二个元素还只是entrypoint的路径字符串,不是对应的driver对象。
_hash_settings_for_path执行完,会生成cache目录,在cache目录下生成_hash_settings_for_path方法返回元组第一个元素的哈希名的同名文件,文件存放元组第二个元素,也就是查到的entry_points。cache目录默认是/root/.cache/python-entrypoints下。如果cache目录下已存在同名文件,则直接读取文件,否则生成新的文件并缓存entrypoints
_init_plugins
思考 怎么解释新模块找不到driver
stevedore.driver.DriverManager加载模块时,调importlib.metadata.entry_points()方法没有返回新开发模块对应的项目,导致找不到driver
解决方法 entry_points文件没写对 不能直接将仓库的setup.cfg拷贝到环境上,环境上的文件和仓库的文件有区别。文件写对的话,一个entry_points文件就够了