2021SC@SDUSC
首先我们先列出seafobj的目录:
backends
utils
__init__.py
blocks.py
commits.py
commit_differ.py
db.py
exceptions.py
fs.py
objstore_factory.py
打开__ init __.py:看到
from .commits import commit_mgr
from .fs import fs_mgr
from .blocks import block_mgr
from .commit_differ import CommitDiffer
所以我们先分析exception.py、objstore_factory、db.py
db.py
一共由三个方法
create_engine_from_conf(config)
init_db_session_class(config)
ping_connection(dbapi_connection, connection_record, connection_proxy)
根据调用关系,将以如下顺序进行分析
ping_connection
cursor = dbapi_connection.cursor()
try:
cursor.execute("SELECT 1")
cursor.close()
except:
logging.info('fail to ping database server, disposing all cached connections')
connection_proxy._pool.dispose() # pylint: disable=protected-access
# Raise DisconnectionError so the pool would create a new connection
raise DisconnectionError()
create_engine_from_conf(config)
need_connection_pool_fix = True
一开始会将该变量设为True,该变量会在如下条件变为False
if not config.has_section('database'):
......
need_connection_pool_fix = False
所以need_connection_pool_fix由config是否有database决定
接着看if分支
seafile_data_dir = os.environ['SEAFILE_CONF_DIR']
if seafile_data_dir:
path = os.path.join(seafile_data_dir, 'seafile.db')
else:
logging.warning('SEAFILE_CONF_DIR not set, can not load sqlite database.')
return None
db_url = "sqlite:///%s" % path
可以看到会先去寻找seafile_data_dir,如果该变量不存在,logging进行警告。并且返回None
接着看else分支
backend = config.get('database', 'type')
先得到config的类型
if backend == 'mysql':
elif backend == 'oracle':
else:
raise RuntimeError("Unknown database backend: %s" % backend)
可以看出这些语句主要是为了得到数据库的类型,看一下mysql下的语句
if config.has_option('database', 'host'):
host = config.get('database', 'host').lower()
else:
host = 'localhost'
if config.has_option('database', 'port'):
port = config.getint('database', 'port')
else:
port = 3306
username = config.get('database', 'user')
passwd = config.get('database', 'password')
dbname = config.get('database', 'db_name')
db_url = "mysql+pymysql://%s:%s@%s:%s/%s?charset=utf8" % (username, quote_plus(passwd), host, port, dbname)
这些是为了获取一些config信息。
oracle下的语句和mysql下的语句的区别只有port。
添加池回收
kwargs = dict(pool_recycle=300, echo=False, echo_pool=False)
engine = create_engine(db_url, **kwargs)
if need_connection_pool_fix and not has_event_listener(Pool, 'checkout', ping_connection):
# We use has_event_listener to double check in case we call create_engine
# multipe times in the same process.
add_event_listener(Pool, 'checkout', ping_connection)
return engine
init_db_session_class方法
根据配置文件配置mysql的Session类
try:
engine = create_engine_from_conf(config)
except configparser.NoOptionError as xxx_todo_changeme:
configparser.NoSectionError = xxx_todo_changeme
raise RuntimeError("invalid seafile config.")
调用了上面的create_engine_from_conf(config)方法
# reflect the tables
Base.prepare(engine, reflect=True)
Session = sessionmaker(bind=engine)
return Session
到此,db.py文件已经分析完毕,该文件的作用是根据config为数据库配置session文件
exceptions.py
该文件很明显声明了一系列关于seafile的exception
首先声明一个SeafObjException(Exception)类,基类为Exception类
class SeafObjException(Exception):
def __init__(self, msg):
Exception.__init__(self)
self.msg = str(msg)
def __str__(self):
return self.msg
之后所有的class,基类均为SeafObjException
class InvalidConfigError(SeafObjException):
#This Exception is rasied when error happens during parsing
解析时报错异常
class ObjectFormatError(SeafObjException):
#This Exception is rasied when error happened during parse object
解析对象时发生异常
class GetObjectError(SeafObjException):
#This exception is raised when we failed to read object from backend.
从后端读取对象异常
class SwiftAuthenticateError(SeafObjException):
#This exception is raised when failed to authenticate for swift.
swfit认证失败
class SeafCryptoException(SeafObjException):
#This exception is raised when crypto realted operation failed.
加密相关工作失败
objstore_factory.py
由于该引用语句,我们先去filesystem文件
from seafobj.backends.filesystem import SeafObjStoreFS
同时。filesystem文件中也有引用语句
from .base import AbstractObjStore
我们继续回溯到base文件
Base.py
可以看到base文件只有AbstactObjStore一个类
class AbstractObjStore(object):
'''Base class of seafile object backend'''
可以看到该类为seafile的后端基类
其中的方法一共由有
__init__
read_obj
read_obj_raw
get_name
list_objs
obj_exists
write_obj
stat
stat_raw
补充知识:NotImplementedError
Method or function hasn't been implemented yet.
下列方法均只抛出该异常,其余均在后面的子类实现
read_obj_raw
从后端读取对象的原始内容。
get_name
获取后端名称以便在日志中显示
list_objs
列出所有对象
obj_exists
给定repo_id,obj_id,查看是否存在该对象
write_obj
将数据写入目标后端
stat_raw
read_obj
调用read_obj_raw方法
然后又两个if语句
if self.crypto:
data = self.crypto.dec_data(data)
if self.compressed and version == 1:
data = zlib.decompress(data)
整个方法包裹在try\catch块里,会抛出GetObjectError()
stat
if self.crypto or self.compressed:
try:
data = self.read_obj(repo_id, verison, obj_id)
return len(data)
except:
raise
return self.stat_raw(repo_id, obj_id)
FileSystem.py
fileSystem中核心代码为class SeafObjStoreFS(AbstractObjStore),可以看出,这个对象继承了Base.py中的AbstractObjStore类,所以需要对其中的许多类方法进行定义
再看objstore_factory.py
该文件的结构
get_ceph_conf(cfg,section):
get_ceph_conf_from_json(cfg):
get_s3_conf(cfg,section):
get_s3_conf_from_json(cfg):
get_oss_conf(cfg,section):
get_swift_conf(cfg,section):
get_swift_conf_from_json(cfg):
class SeafileConfig(object)
class SeafObjStoreFactory(object):
get_repo_storage_id(repo_id):
先从SeafObjStoreFactory类看起
SeafObjStoreFactory
obj_section_map = {
'blocks': 'block_backend',
'fs': 'fs_object_backend',
'commits': 'commit_object_backend',
}
然后看init方法
def __init__(self, cfg=None):
self.seafile_cfg = cfg or SeafileConfig()
self.json_cfg = None
self.enable_storage_classes = False
self.obj_stores = {'commits': {}, 'fs': {}, 'blocks': {}}
先空过SeafileConfig类
cfg = self.seafile_cfg.get_config_parser()
if cfg.has_option ('storage', 'enable_storage_classes'):
查看SeafileConfig中的get_config_parser方法
def get_config_parser(self):
if self.cfg is None:
self.cfg = configparser.ConfigParser()
try:
self.cfg.read(self.seafile_conf)
except Exception as e:
raise InvalidConfigError(str(e))
return self.cfg
可以看到返回configparser对象
enable_storage_classes = cfg.get('storage', 'enable_storage_classes')
if enable_storage_classes.lower() == 'true':
从配置中得到该属性并查看属性
from seafobj.db import init_db_session_class
self.enable_storage_classes = True
self.session = init_db_session_class(cfg)
try:
json_file = cfg.get('storage', 'storage_classes_file')
f = open(json_file)
self.json_cfg = json.load(f)
except Exception:
logging.warning('Failed to load json file')
raise
继续看get_obj_stores()方法
def get_obj_stores(self, obj_type):
try:
if self.obj_stores[obj_type]:
return self.obj_stores[obj_type]
except KeyError:
raise RuntimeError('unknown obj_type ' + obj_type)
如果obj__type类型为错则报错
接下来的代码结构
可以看到是针对不同的类型给出不同的处理
在具体的处理中,方式均相似,只对fs的处理进行分析。
obj_dir = os.path.join(bend[obj_type]['dir'], 'storage', obj_type)
self.obj_stores[obj_type][storage_id] = SeafObjStoreFS(compressed, obj_dir, crypto)
可以看到就是新建对象并存入obj_stores对象中。
接着分析get_obj_store方法
返回一个SeafileObjStore的实现
代码结构如下,和上面的方法结构相似
可以看到其他方法均为SeafObjStoreFactory类服务。
至此,我们已经分析完了这三个文件的基本架构,会在后续对该项目由更深理解之后对本文进行更新