分布式数据库管理系统






4.50/5 (4投票s)
一个分布式数据库管理系统,包含一个主服务器 LookupServer,多个 DatabaseServers 和 Clients,使用 java.nio 进行通信。
引言
本文档描述了一个分布式数据库管理系统的实现。该系统包含主服务器 LookupServer,多个 DatabaseServers 和 Clients。通过提供的类,可以创建包含任意数量的 DatabaseServers 和 Clients 的应用程序。
该应用程序使用 java.nio
中的套接字进行通信。
- LookupServer<->DatabaseServers
- DatabaseServers<->DatabaseServers
- Clients<->DatabaseServers
背景
理解此代码所需的根本概念是关于套接字。套接字是基于 IP 的网络上双向进程间通信的端点。套接字在进程、线程或应用程序与操作系统提供的 TCP/IP 协议栈之间提供了一个接口。套接字可以是阻塞的,也可以是非阻塞的。阻塞套接字顾名思义,在发送或接收消息时会阻塞。非阻塞套接字在希望接收消息时会检查消息是否可用;如果可用,它会读取消息,否则会继续做其他事情。
Using the Code
为了编译和运行代码,我使用了 ant。在 build.xml 文件中,我定义了编译所有源代码并生成 jar 文件的目标。为了运行某个测试的应用程序,我引入了一个 runTest1
目标。
我将解释项目中包含的主要类。
1. Client
Client
类定义了数据库中的一个事务。事务的所有操作都必须保持数据库的完整性。客户端所做的更改只有在该客户端提交数据之前才对该特定客户端可用。每个事务都将以提交或回滚命令结束。选择提交将保存事务所做的更改,使其可供其他客户端(事务)使用,而选择回滚将取消到目前为止所做的所有更改。
客户端的行为由以下模式定义。首先,它将连接到一个数据库服务器,该服务器将为其分配一个唯一的 TID(事务 ID)。收到 TID 后,客户端可以继续使用可用的对象数据库。因此,它将发送多个请求来识别某些对象的值或修改它们。当事务完成时,客户端将选择提交数据或回滚。
Client
类实现了 TransactionInterface
,该接口定义了以下函数:
public String begin(String DatabaseServerIP, int DatabaseServerPort)
public int getValue(String objName);
public void setValue(String objName, int value);
public void commit();
public void rollback();
begin
:客户端将连接到由参数定义的数据库服务器:DatabaseServerIP
和 DatabaseServerPort。getValue
:客户端将从 DBServer 请求某个对象的 A值。该对象不一定存储在该服务器上,由它负责查找对象的位置并向客户端提供其值。setValue
:客户端将向服务器声明其希望更改某个对象的值。在客户端选择提交新数据之前,其他 DBServers 和客户端将不会看到这些更改。commit
:客户端将向 DBServer 发送一个请求,以提交所有已处理的数据,然后断开与服务器的连接。数据库服务器负责将所做的更改通知所有其他数据库服务器。rollback
:客户端将向服务器声明其希望取消所有更改,然后断开与服务器的连接。
2. DatabaseServer
有多个数据库服务器,每个服务器存储一定数量的对象。对象由一个在该组数据库服务器中唯一的名称及其值定义。用于定义此类对象的类是 DatabaseObject
,我将在后面讨论。一组这样的数据库服务器将连接到一个查找服务器,该服务器将了解所有现有的数据服务器及其存储的对象。
DatabaseServer
类扩展了 Thread
并实现了 DatabaseServerInterface
,该接口定义了以下函数:
public void startServer(String myIP, int myPort, String LookupServerIP,
int LookupServerPort);
public void addObject(String objName, int initialValue);
startServer
:此函数将打开一个连接,在该连接上它将监听来自客户端或其他 DBServers 的请求。它还将存储查找服务器的信息(其 IP 和端口作为参数提供),以备将来进行信息请求。addObject
:DBServer 将存储有关某个对象的信息(其名称和值),并将向LookupServer
通告其存在。stopServer
:DBServer 将等待预定义的时间让当前任务完成,然后关闭所有连接。
每个数据库服务器都是一个线程,在启动后将在后台运行,等待请求。为了处理多个传入请求,数据库服务器使用线程池。每个收到的请求将被分配给池中的一个线程(如果可用);否则,它将等待一个可用的线程。分配给线程池中线程的任务由内部类 HandlerData
定义。根据接收到的消息类型,它将接受连接或读取、解释数据并发送答案。
3. LookupServer
查找服务器是中央服务器,负责管理其他服务器并向它们提供关于彼此的信息。也可以定义多个查找服务器,但每个服务器将负责其自己的数据库服务器集。
LookupServer
Class 扩展了 Thread
类并实现了 LookupServerInterface
,该接口定义了以下函数:
public void startServer(String myIP, int myPort);
public void stopServer();
startServer
:此函数将打开一个连接,服务器将在该连接上监听来自 DBServers 的请求。stopServer
:服务器将等待预定义的时间让当前任务完成,然后关闭所有连接。
与 DBServers 一样,查找服务器也可以处理多个请求。为此,它使用线程池。收到请求后,将从池中分配一个线程来完成处理该请求的任务。分配给此线程的任务由内部类 HandlerLookup
定义。根据接收到的消息类型,它将接受连接或读取、解释数据并发送答案(如果需要)。
4. DatabaseObject
DatabaseObject
类定义了一个存储在数据库服务器上的对象。它存储的重要信息包括对象名称及其值。
5. TestScenario1
TestScenario1
是一个演示已实现类基本功能的测试。它定义了一个查找服务器、两个数据库服务器和三个客户端,并检查数据库完整性和客户端操作的有效性。
兴趣点
当前项目提供了一个使用简单对象分布式数据库的系统,各部分之间的通信良好。但仍有改进空间。最重要的改进是 DBServer 应该告知查找服务器其退出信息,以便查找服务器可以从其自身的数据库中删除关于它的条目。另一个改进是定义单独实体(客户端、DBServers 和 LookupServer)之间的协议,将其包含在一个单独的类中,使其更具灵活性。
我应该在此处提及的一个重要问题涉及选择器(在使用非阻塞套接字时)的使用。在此项目中,多个线程对同一数据进行操作,同步至关重要。最重要的是在使用选择器时,主线程将阻塞在 select
方法上,因此在收到传入连接释放它之前,没有人可以向选择器添加新的套接字通道。即使那样,它也无法确定尝试向选择器添加新套接字通道的线程是否能在主线程再次阻塞在 select
方法之前有机会这样做。为了解决这个问题,我使用了对象 sinc
上的同步。当工作线程希望向选择器注册新通道时,它首先获得 sinc
对象的拥有权,然后“唤醒”选择器。注册通道后,它释放 sinc
对象。
synchronized(sinc)
{
this.selector.wakeup();
sChannel.register(this.selector, SelectionKey.OP_READ);
}
与此同时,主线程将以返回代码 0
退出 select
函数。测试返回的代码,如果为 0
,则尝试获取 sinc
的拥有权。在工作线程完成之前,主线程将无法进入同步部分,从而允许在没有干扰的情况下注册通道。
while (true)
{
try
{
// Wait for an event
a = selector.select();
if(a == 0)
{
synchronized(sinc)
{}
continue;
}
//...
}
//...
}
参考文献和感谢
本项目最初是作为布加勒斯特理工大学分布式编程语言课程的作业开发的。特别感谢教学助理提出了这项作业,因为它是我所做过的最复杂、最有趣的作业之一。
历史
- 2008年12月5日:首次发布
关于我
我目前是布加勒斯特理工大学的最后一年学生。我的主要兴趣在于分布式系统和并行体系结构,但我也很乐意参与其他具有挑战性的项目。业余时间,我会在一个关于 web 界面 的博客上写作。