# Distributed Reservation System **Repository Path**: hexiiii/distributed-reservation-system ## Basic Information - **Project Name**: Distributed Reservation System - **Description**: 一个分布式旅游预订系统,实现了事务管理器 Transaction Manager ,流程控制器 Workflow Controller , 并采用了两阶段提交保证分布式系统的 ACID 特性 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-12-27 - **Last Updated**: 2024-02-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Distributed Reservation System #### 介绍 一个分布式旅游预订系统,实现了事务管理器 Transaction Manager ,流程控制器 Workflow Controller , 并采用了两阶段提交保证分布式系统的 ACID 特性 #### 启动方法 TM->RM(Cars,Flights,Hotels,Reservations)->WC #### client编写规范: start->process->commit 需要throws或catch相应异常 需要保证每个事务都进行commit! #### 一、 项目整体架构图 ![输入图片说明](out/DDB1.jpg) ### 二、Transaction Manager 的作用和实现 #### 2.1 TM 的作用 TM 的作用有以下几个方面:实现多事务的并行功能,保证事务的ACID特性,配合 RM 实现两阶段提交,并针对异常情况进行正确的处理。本课程项目实现的分布式旅游预订系统通过两阶段提交保证了事务的ACID性质,TM 充当协调者的角色,它会接收来自 RM 的 enlist 消息得知哪个 RM 参与了哪个事务,并使用 prepare() 和 commit() 方法通知 RM 进行提交,或者在发生错误时适时合理地中止事务。为了能够实现事务的回滚和恢复,TM需要记录原本的信息保证在错误解决时恢复原本可以正确执行的操作。 #### 2.2 TM 的实现 ##### (1)Transaction 类 TM 管理所有的事务,设计了一个 transaction 类用于记录事务的详细内容。transaction 类中包括私有成员变量 xid 用于记录事务号、事务状态 status 包括 initiate, prepare, commit, abort 以及一个 `hashmap>`,hashmap 中记录的是本事务涉及的 RM 和 RM 操作的对象,这里的 Object 是 Flights, Cars, Hotels 和 Reservations 的父类,具体记录的是该事务操作前对象的内容,可用于事务撤销时的回滚,使用这种结构的原因是一个事务可能涉及多个 RM,一个 RM 可能操作了多个对象。 transaction 类中有四个方法 enlist(), prepare(), commit(), abort()。该类的 `enlist(xid, rm, o)` 方法是在 RM 调用了它的 enlist() 方法后被调用的,用于将 RM 和未修改的对象存入 hashmap 中,`prepare(xid)`方法将事务的状态修改为 prepare,`commit(xid)` 方法将事务的状态从 prepare 修改为 commit,`abort(xid)` 方法用于将事务的状态修改为 abort。在修改状态前都会确认传入的 xid 参数与本类的成员变量 xid 是相符的。 ##### (2)TransactionManager 接口 TransactionManager 接口中有几个抽象方法:Start(), commit(), abort()由WC调用,分别用来启动、提交、中断一个事务。启动一个事务时,记录当前事务的信息,commit或abort成功时,删除当前的事务信息。 ##### (3)TransactionManagerImpl 类 TransactionManagerImpl 类具体实现了 TM,它有一个成员变量 `HashTable`类型的 txn_list 用于记录事务号 xid 和对应的事务内容,另有一个成员变量 logger 用于记录操作日志,并有以下几个方法: - `start(xid)` :WC 调用 start(), TM 为事务 xid 在 txn_list 中创建一个条目 - `enlist(xid, rm, o)`:RM 调用RM的 enlist() 方法通知 TM 哪个 RM 参与了哪个事务和这个 RM 操作了哪个对象, 即 TM 获取对 RM 的引用, 这里的对象 o 是 RM 修改内存表数据对象前的状态,用于回滚 - 两阶段提交 `commit(xid)` 确认内存中所有被修改的都已经写回磁盘 -- 基于 Hashtable 的影子页面:首先检查事务号是否正确,是否存在于 txn_list 中 (1)第一阶段是 Prepare :向操作该事务的所有 RM 发送 prepare 消息,等待响应 (2)第二阶段是 Commit 或者 Abort: - Commit: - 将事务的 status 改为 commit - 写日志 commit log - 向操作该事务的 RM 发送 commit 消息, RM 将 table 写入新文件,记录最新的版本号 (影子页面写到磁盘) - 删除 txn_list 中对应的条目 - Abort: 调用abort() 方法将 RM 中的 table 回滚到修改前的状态 - `abort(xid)`:用于事务的撤销,首先获取 txn_list 中 key=xid 对应的 value,这个 value 是一个 transaction 类的对象 tx, 然后遍历 tx 的 `hashmap>`, 对每个 obj 逐一调用 RM 的 abort 方法进行回滚 ### 三、资源管理器 资源管理器,主要在应用端提供数据操作功能,包括查询、更新(插入、删除)数据,提供对资源访问的封装,完成对数据的实际访问和数据持久化等功能。在实际应用中,系统各项数据表一般存储于不同主机,每项数据均由各自独立的资源管理器提供管理服务。 ![输入图片说明](out/DDB5.jpg) ResourceManagerImpl 是资源管理器的实现类,其他类是根据各不同功能应用端的实现类 流程控制器 WC(WorkFlow Controller)通过调用资源管理器 RM 的各操作类接口实现不同应用端的数据流程操作。其操作主要分为三个部分: 第一,启动事务,获取事务 ID。第二,处理相应的业务逻辑;第三部分,事务提交。这里假设客户端操作均遵循此三个步骤,这里不考虑客户端发生错误操作的情况,异常处理集中于系统本身。 **接口定义** 不同应用端接口操作定义如下图所示,逻辑业务处理方面共包括5类函数,addXXX(), deleteXXX(),queryXXX(),reserveXXX(),unreserveXXX(),分别处理数据添加,删除、查询、预订和取消预订操作。由于预订和取消预订操作涉及到多个表的处理,所以单独作为一类。 ![输入图片说明](out/DDB6.jpg) **业务逻辑** 流程管理器 WC 提供了供客户端访问的接口,使得系统的其它部分(事务管理器 TM、资源管理器 RM)和实际数据表对客户端透明。主要接口包括实现应用端的各项流程接口,如 addFlight() 、deleteFlight() 、addRooms() 、addCars() 、newCoustomer() 、queryFlight() 、 deleteRooms()、deleteRooms()、queryCustomer()…等。用户可以通过相关接口实现目标功能。 addXXX():以 addCars()实现为例。数据业务逻辑流程如下: a)查询 Cars 中是否已经存在对应编号的车次; b)若存在,则操作转化为更改当前查询车次的价格,且将其他车次数量和空余车次数更新; c)若不存在,则按照输入信息增加车次信息。 ### 四、异常分析与处理 在设计 TM 时考虑了 dieAfterEnlist, dieBeforePrepare, dieAfterPrepare, dieBeforeCommit, dieAfterCommit 五种异常情况,其中对于 dieAfterEnlist, dieBeforePrepare, dieAfterPrepare 三种异常需要撤销事务,删除修改,回滚操作,然后重启 RM,对于 dieBeforeCommit 需要再次提交事务,然后重启 RM,对于dieAfterCommit ,由于事务已经提交,只需要重启 RM ### 五、测试 测试代码位于 test/test1.java (1)测试 TM,RM,WC 的连接 ![输入图片说明](out/DDB2.jpg) (2) 测试具体的操作 ```java int xid = wc.start(); wc.addFlight(xid, "NO1", 1, 700); wc.addCars(xid, "Shanghai", 100, 1000); wc.addRooms(xid, "Shanghai", 100, 500); wc.newCustomer(xid, "ding"); // wc.deleteCustomer(xid, "ding"); wc.reserveCar(xid, "ding", "Shanghai"); wc.reserveFlight(xid, "ding", "NO1"); int numAvail = wc.queryCustomerBill(xid, "ding"); ``` ![输入图片说明](out/DDB4.jpg) (3) 测试两阶段提交 ![输入图片说明](out/DDB3.jpg)