OpenStack公共组件oslo之九——oslo.db
来源:互联网 发布:淘宝商城9.9包邮 编辑:程序博客网 时间:2024/05/16 04:41
oslo.db库为OpenStack其他组件提供了针对不同后端数据库的数据库连接,并提供了各种数据库操作的辅助工具类和方法。OpenStack各组件使用SQLAlchemy框架实现对数据库的连接、查询等操作,因此oslo.db并不是一个完整的ORM库,也没有封装执行SQL语句;其只是对SQLAlchemy进行了封装,其与SQLAlchemy结合使用使得在OpenStack各组件中实现数据库连接、查询等操作变得更加简单。因此,本文不对SQLAlchemy的使用进行详细介绍,如果需要了解SQLAlchemy的使用,可以参考python对Mysql操作和使用ORM框架(SQLAlchemy)和SQLAlchemy技术文档。本文将结合oslo.db的使用详细分析其实现方式。
1. Session Handling
1.1 使用方法
Session handling指的是使用oslo_db.sqlalchemy.enginefacade模块管理数据库连接、会话和事务处理等,该模块一般以装饰器形式使用,也可以使用with语句进行调用。
Session handling可以为一个函数提供装饰器,也可以作为一个上下文管理器传递一个Session或Connection对象。这两种方式都需要用到一个上下文对象。这个对象可以是任何一个类实例。下面这个例子便是使用上下文管理器形式的Session handling的使用方式:
from oslo_db.sqlalchemy import enginefacade class MyContext(object): "User-defined context class." def some_reader_api_function(context): with enginefacade.reader.using(context) as session: return session.query(SomeClass).all() def some_writer_api_function(context, x, y): with enginefacade.writer.using(context) as session: session.add(SomeClass(x, y)) def run_some_database_calls(): context = MyContext() results = some_reader_api_function(context) some_writer_api_function(context, 5, 10)
在这个例子中,首先定义了一个MyContext作为上下文管理器,然后分别在some_reader_api_function(context)和some_writer_api_function(context, x, y)来进行一些数据库操作。在some_reader_api_function(context)中,通过enginefacade.reader使用定义的上下文管理器生成一个数据库连接Session对象,然后使用Session对象的query()方法进行查询偶作;而在some_writer_api_function(context, x, y)中,则通过enginefacade.writer使用定义的上下文管理器生成一个数据库连接Session对象,然后使用这个Session对象的add()方法进行插入操作。其中,使用了enginefacade模块中的reader和writer两个重要的变量,这两个变量分别表示一个reader和writer函数的全局调用入口,关于reader和writer会在之后进行详细分析。除了这种方式之外,还可以使用装饰器模式使用enginefacade模块中的装饰器方法,使用方法如下所示:
from oslo_db.sqlalchemy import enginefacade @enginefacade.transaction_context_provider class MyContext(object): "User-defined context class." @enginefacade.reader def some_reader_api_function(context): return context.session.query(SomeClass).all() @enginefacade.writer def some_writer_api_function(context, x, y): context.session.add(SomeClass(x, y)) def run_some_database_calls(): context = MyContext() results = some_reader_api_function(context) some_writer_api_function(context, 5, 10)首先,在定义MyContext类时,使用了@enginefacade.transaction_context_provider装饰器为其设置Session等属性,该装饰器可用于设置Session、Connection、Transaction等属性。在使用时,则可以直接使用@enginefacade.writer和@enginefacade.reader从MyContext对象中获取对应的session,并用其进行数据库操作。
@enginefacade.reader.connection def _refresh_from_db(context, cache): sel = sa.select([table.c.id, table.c.name]) res = context.connection.execute(sel).fetchall() cache.id_cache = {r[1]: r[0] for r in res} cache.str_cache = {r[0]: r[1] for r in res}当不需要一个具体的数据库Session对象时,可以直接使用@enginefacade.reader.connection获取一个临时的数据库连接进行数据库相关操作。使用方式是使用@engingefacade.reader.connection装饰器,为上下文对象context添加一个数据库连接对象connection,直接使用该对象进行数据库操作即可。
需要注意的是,无论是context.session还是context.connection,都需要在一个writer或reader声明的对象中。否则,使用时将抛出异常。
当然,装饰器也可以用在一个类或对象的方法中,使用方式如下:
class DatabaseAccessLayer(object): @classmethod @enginefacade.reader def some_reader_api_function(cls, context): return context.session.query(SomeClass).all() @enginefacade.writer def some_writer_api_function(self, context, x, y): context.session.add(SomeClass(x, y))此时,需要注意的是,enginefacade的装饰器必须在classmethod装饰器之前使用,否则会抛出TypeError异常。
1.2 实现原理
在1.1节中详细介绍了Session Handling,即enginefacade的几种使用场景和使用方法,本节将据此分析enginefacade中的几个重要的类和方法,以及其实现原理。
1.2.1 _TransactionFactory类
_TransactionFactory类是_TransactionContext对象的一个工厂类,_TransactionContext对象可以使用该类生成一个数据库Session或一个数据库连接Connection对象;而_TransactionFactory对象会通过读取配置文件中数据库的配置信息创建相应的Session和Connection对象以进行数据库的相关操作。
1.2.2 _TransactionContext类
_TransactionContext类的每一个实例化对象都代表了单个数据库事务,其中,定义了factory和global_factory两个_TransactionFactory对象属性,factory用来为每一个_TransactionContext对象创建数据库连接或Session,而global_factory会被一个全局的_context_manager在创建一个新的_TransactionContext对象时使用。在_TransactionContext类中还定义了session、connection、transaction来分别表示一个数据库session、连接、事务对象,用于进行具体的数据库操作。最后,_TransactionContext类还定义了一个mode属性来表示数据库操作的状态。在oslo_db的enginefacade中定义了3种数据库操作状态:
- _READER:这种状态表示事务是只读的,且其只能在同步更新的从数据库上使用,否则应该使用主数据库。
- _ASYNC_READER:这个状态表示事务是只读的,且其可以在异步更新的从数据库上使用。
- _WRITER:这种状态表示事务写入数据,并且应该直接指向主数据库。
1.2.3 _TransactionContextManager类
2 ModelBase
from oslo_db.sqlalchemy import modelsclass NovaBase(models.TimestampMixin, models.ModelBase): metadata = None def __copy__(self): """Implement a safe copy.copy(). SQLAlchemy-mapped objects travel with an object called an InstanceState, which is pegged to that object specifically and tracks everything about that object. It's critical within all attribute operations, including gets and deferred loading. This object definitely cannot be shared among two instances, and must be handled. The copy routine here makes use of session.merge() which already essentially implements a "copy" style of operation, which produces a new instance with a new InstanceState and copies all the data along mapped attributes without using any SQL. The mode we are using here has the caveat that the given object must be "clean", e.g. that it has no database-loaded state that has been updated and not flushed. This is a good thing, as creating a copy of an object including non-flushed, pending database state is probably not a good idea; neither represents what the actual row looks like, and only one should be flushed. """ session = orm.Session() copy = session.merge(self, load=False) session.expunge(copy) return copyNovaBase类便继承了oslo.db中的ModelBase和TimestampMixin类。ModelBase的使用就是这么简单。接下来,将通过ModelBase的实现分析该类的主要作用。与ModelBase相关的类与方法都定义在oslo_db.sqlalchemy.models模块下,该模块中主要定义了一下几个类:
- ModelBase类:该类作为所有数据库表抽象的基类,提供了save、get、update、keys、items等常用的数据库操作方法,程序员在为数据库表定义数据结构时,都需要继承该类,然后使用或覆写这些方法完成对一个数据库表的所有CURD操作。
- ModelIterator类:该类主要用于对数据库表中数据进行迭代查询的操作。
- TimestampMixin类:由于OpenStack多数数据库表中都定义了创建时间和更新时间字段,因此该类专门用来获取记录的创建时间和更新时间,如果为空则可以自动设置为当前时间。
- SoftDeleteMixin类:OpenStack多数数据库表都为定义了删除记录标志和删除记录时间,该类则是专门用来获取记录的删除记录标志和删除时间。
3 DB API backup support
from oslo_config import cfg from oslo_db import api as db_api _BACKEND_MAPPING = {'sqlalchemy': 'project.db.sqlalchemy.api'} IMPL = db_api.DBAPI.from_config(cfg.CONF, backend_mapping=_BACKEND_MAPPING) def get_engine(): return IMPL.get_engine() def get_session(): return IMPL.get_session() # DB-API method def do_something(somethind_id): return IMPL.do_something(somethind_id)oslo.db除了定义DBAPI统一管理数据库后端API,还定义了多个装饰器辅助进行数据库操作,如@wrap_db_retry可以捕获数据库操作过程中抛出的异常,然后重连数据库等。其使用方法如下:
@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)@pick_context_manager_writerdef service_update(context, service_id, values): service_ref = service_get(context, service_id) # Only servicegroup.drivers.db.DbDriver._report_state() updates # 'report_count', so if that value changes then store the timestamp # as the last time we got a state report. if 'report_count' in values: if values['report_count'] > service_ref.report_count: service_ref.last_seen_up = timeutils.utcnow() service_ref.update(values) return service_ref上述示例中表示如果更新service_group表操作抛出异常则重试该操作,最大重试次数为5次,设置retry_on_deadlock为True,表示如果操作中发生死锁也会进行重试操作。
4 DB migration extensions
oslo.db中还定义了与数据库相关的扩展操作,这些操作主要定义在oslo_db.sqlalchemy.migration模块中。其中,主要定义了db_version_control(engine, abs_path, version=None)方法进行版本控制,定义了db_sync(engine, abs_path, version=None, init_version=0, sanity_check=True)方法进行数据库的数据同步。- OpenStack公共组件oslo之九——oslo.db
- OpenStack公共组件oslo之二——oslo.utils
- OpenStack公共组件oslo之三——oslo.log
- OpenStack公共组件oslo之四——oslo.context
- OpenStack公共组件oslo之五——oslo.service
- OpenStack公共组件oslo之六——oslo.messaging
- OpenStack公共组件oslo之七——oslo.middleware
- OpenStack公共组件oslo之八——oslo.i18n
- OpenStack公共组件oslo之十——oslo.concurrency
- OpenStack公共组件oslo之十一——oslo.serialization
- OpenStack公共组件oslo之十二——oslo.policy
- OpenStack公共组件oslo之十三——oslo.cache
- OpenStack公共组件oslo之一——oslo.config
- OpenStack公共组件oslo之十四——pbr
- OpenStack公共组件oslo之十五——taskflow
- OpenStack公共组件oslo之十六——stevedore
- openstack组件oslo.message之RPCClient
- openstack组件oslo.message之RPCServer实现
- 使用 Visual Studio Team Services 和 IIS 创建持续集成管道
- 可见性与生存期
- NOIP复赛复习(十五)动态规划巩固与提高
- 如何同步TableStore数据到Elasticsearch
- Anaconda Navigator 闪退解决办法
- OpenStack公共组件oslo之九——oslo.db
- 轮播图
- [转载]理解OAuth 2.0
- 【Scikit-Learn 中文文档】广义线性模型
- C#获取本周/月第一天(后台)
- mybatis中大于等于小于等于的写法
- 动态规划之计算矩阵连乘积
- JavaSE_URLConnection/HttpURLConnection发送HTTP请求的方法(一)
- 开始写技术博客啦!