2021SC@SDUSC 山大智云 2.对seafobj的分析:1

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类服务。

至此,我们已经分析完了这三个文件的基本架构,会在后续对该项目由更深理解之后对本文进行更新

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值