9. 客户端/服务器
Now that we have seen how transactions work in db4o conceptually, we are prepared to tackle concurrently executing transactions.
现在我们已经明白db4o中事务的概念了,本章讲处理并行执行的事务。
现在按照以往熟悉的方法准备数据。
//
setFirstCar
Pilot pilot
=
new
Pilot(
"
Rubens Barrichello
"
,
99
);
Car car
=
new
Car(
"
BMW
"
);
car.Pilot
=
pilot;
db.Set(car);
//
setSecondCar
Pilot pilot
=
new
Pilot(
"
Michael Schumacher
"
,
100
);
Car car
=
new
Car(
"
Ferrari
"
);
car.Pilot
=
pilot;
db.Set(car);
9.1. 嵌入式服务器
- 从API角度看,分布式事务和单一VM上的事务没有什么本质区别。在单机上使用事务,我们需要打开db4o服务器,指示它从0端口运行,从而其他的网络程序不可以占用。
//
accessLocalServer
IObjectServer server
=
Db4oFactory.OpenServer(Util.YapFileName,
0
);
try

{
IObjectContainer client = server.OpenClient();
// Do something with this client, or open more clients
client.Close();
}
finally

{
server.Close();
}
- 再次委托打开和关闭数据库的服务器作为运行环境,从而捕捉客户端交互。
//
queryLocalServer
IObjectContainer client
=
server.OpenClient();
ListResult(client.Get(
new
Car(
null
)));
client.Close();
- 这个事务的级别在db4o里面被成为“read committed”。但是,每个客户端维护它自己的已知对象缓存的引用。为了让其他客户端的变化快速执行,我们需要在服务器端直接引用已知的对象。我们将这个任务代理为一个专门的ListResult()方法的版本。
public
static
void
ListRefreshedResult(IObjectContainer container, IObjectSet items,
int
depth)

{
Console.WriteLine(items.Count);
foreach (object item in items)

{
container.Ext().Refresh(item, depth);
Console.WriteLine(item);
}
}
//
demonstrateLocalReadCommitted
IObjectContainer client1
=
server.OpenClient();
IObjectContainer client2
=
server.OpenClient();
Pilot pilot
=
new
Pilot(
"
David Coulthard
"
,
98
);
IObjectSet result
=
client1.Get(
new
Car(
"
BMW
"
));
Car car
=
(Car)result.Next();
car.Pilot
=
pilot;
client1.Set(car);
ListResult(client1.Get(
new
Car(
null
)));
ListResult(client2.Get(
new
Car(
null
)));
client1.Commit();
ListResult(client1.Get(
typeof
(Car)));
ListRefreshedResult(client2, client2.Get(
typeof
(Car)),
2
);
client1.Close();
client2.Close();
会滚也可以这样工作,就像你预料的一样:
//
demonstrateLocalRollback
IObjectContainer client1
=
server.OpenClient();
IObjectContainer client2
=
server.OpenClient();
IObjectSet result
=
client1.Get(
new
Car(
"
BMW
"
));
Car car
=
(Car)result.Next();
car.Pilot
=
new
Pilot(
"
Someone else
"
,
0
);
client1.Set(car);
ListResult(client1.Get(
new
Car(
null
)));
ListResult(client2.Get(
new
Car(
null
)));
client1.Rollback();
client1.Ext().Refresh(car,
2
);
ListResult(client1.Get(
new
Car(
null
)));
ListResult(client2.Get(
new
Car(
null
)));
client1.Close();
client2.Close();
9.2. 网络
From here it's only a small step towards operating db4o over a TCP/IP network. We just specify a port number greater than zero and set up one or more accounts for our client(s).
到了这里,距离通过TCP/IP协议操作db4o没有多少步骤了。我们需要专门指定一个大于0的端口号码,为客户端设置一个或者更多的帐户。
//
accessRemoteServer
IObjectServer server
=
Db4oFactory.OpenServer(Util.YapFileName, ServerPort);
server.GrantAccess(ServerUser, ServerPassword);
try

{
IObjectContainer client = Db4oFactory.OpenClient("localhost", ServerPort, ServerUser, ServerPassword);
// Do something with this client, or open more clients
client.Close();
}
finally

{
server.Close();
}
.
这个客户端的连接需要提供一个主机、端口、用户名和密码。
//
queryRemoteServer
IObjectContainer client
=
Db4oFactory.OpenClient(
"
localhost
"
, port, user, password);
ListResult(client.Get(
new
Car(
null
)));
client.Close();
//
demonstrateRemoteReadCommitted
IObjectContainer client1
=
Db4oFactory.OpenClient(
"
localhost
"
, port, user, password);
IObjectContainer client2
=
Db4oFactory.OpenClient(
"
localhost
"
, port, user, password);
Pilot pilot
=
new
Pilot(
"
Jenson Button
"
,
97
);
IObjectSet result
=
client1.Get(
new
Car(
null
));
Car car
=
(Car)result.Next();
car.Pilot
=
pilot;
client1.Set(car);
ListResult(client1.Get(
new
Car(
null
)));
ListResult(client2.Get(
new
Car(
null
)));
client1.Commit();
ListResult(client1.Get(
new
Car(
null
)));
ListResult(client2.Get(
new
Car(
null
)));
client1.Close();
client2.Close();
//
demonstrateRemoteRollback
IObjectContainer client1
=
Db4oFactory.OpenClient(
"
localhost
"
, port, user, password);
IObjectContainer client2
=
Db4oFactory.OpenClient(
"
localhost
"
, port, user, password);
IObjectSet result
=
client1.Get(
new
Car(
null
));
Car car
=
(Car)result.Next();
car.Pilot
=
new
Pilot(
"
Someone else
"
,
0
);
client1.Set(car);
ListResult(client1.Get(
new
Car(
null
)));
ListResult(client2.Get(
new
Car(
null
)));
client1.Rollback();
client1.Ext().Refresh(car,
2
);
ListResult(client1.Get(
new
Car(
null
)));
ListResult(client2.Get(
new
Car(
null
)));
client1.Close();
client2.Close();
9.3. 消息传输
- 有时候客户端需要请求服务器做一些事情,就要发送一个特殊的消息给服务器。服务器需要接受消息、发送消息到涉及的对象。
- 可以通过SETMESSAGERECIPIENT()方法,找到发送消息的对象。
public
void
RunServer()

{
lock(this)

{
IObjectServer db4oServer = Db4oFactory.OpenServer(FILE, PORT);
db4oServer.GrantAccess(USER, PASS);
// Using the messaging functionality to redirect all
// messages to this.processMessage
db4oServer.Ext().Configure().ClientServer().SetMessageRecipient(this);
try

{
if (! stop)

{
// wait forever for Notify() from Close()
Monitor.Wait(this);
}
}
catch (Exception e)

{
Console.WriteLine(e.ToString());
}
db4oServer.Close();
}
}
消息已经接收到了,并且被PROCESSMESSAGE() 方法传递。
db4o允许一个客户端以无格式的类的对象的形式发送一个任意的消息或者信号给服务器。服务器收到后会发回一个收到消息,包含从客户发来的对象。服务器可以任意定义消息的格式。
9.4. 整合--一个简单但是完整的db4oserver
- 让我们将以上的信息整合,从而写出一个带有专门客户端的简单服务器,客户端可以通知服务器关闭。
- 首先,服务器和客户端都需要一些共享的设置信息。我们提供一个接口:
namespace
Db4objects.Db4o.Tutorial.F1.Chapter5

{

/**//// <summary>
/// Configuration used for StartServer and StopServer.
/// </summary>
public class ServerConfiguration

{

/**//// <summary>
/// the host to be used.
/// If you want to run the client server examples on two computers,
/// enter the computer name of the one that you want to use as server.
/// </summary>
public const string HOST = "localhost";

/**//// <summary>
/// the database file to be used by the server.
/// </summary>
public const string FILE = "formula1.yap";

/**//// <summary>
/// the port to be used by the server.
/// </summary>
public const int PORT = 4488;

/**//// <summary>
/// the user name for access control.
/// </summary>
public const string USER = "db4o";

/**//// <summary>
/// the pasword for access control.
/// </summary>
public const string PASS = "db4o";
}
}
创建server:
锘縰sing System;
using
System.Threading;
using
Db4objects.Db4o;
using
Db4objects.Db4o.Messaging;
namespace
Db4objects.Db4o.Tutorial.F1.Chapter5

{

/**//// <summary>
/// starts a db4o server with the settings from ServerConfiguration.
/// This is a typical setup for a long running server.
/// The Server may be stopped from a remote location by running
/// StopServer. The StartServer instance is used as a MessageRecipient
/// and reacts to receiving an instance of a StopServer object.
/// Note that all user classes need to be present on the server side
/// and that all possible Db4oFactory.Configure() calls to alter the db4o
/// configuration need to be executed on the client and on the server.
/// </summary>
public class StartServer : ServerConfiguration, IMessageRecipient

{

/**//// <summary>
/// setting the value to true denotes that the server should be closed
/// </summary>
private bool stop = false;

/**//// <summary>
/// starts a db4o server using the configuration from
/// ServerConfiguration.
/// </summary>
public static void Main(string[] arguments)

{
new StartServer().RunServer();
}

/**//// <summary>
/// opens the IObjectServer, and waits forever until Close() is called
/// or a StopServer message is being received.
/// </summary>
public void RunServer()

{
lock(this)

{
IObjectServer db4oServer = Db4oFactory.OpenServer(FILE, PORT);
db4oServer.GrantAccess(USER, PASS);
// Using the messaging functionality to redirect all
// messages to this.processMessage
db4oServer.Ext().Configure().ClientServer().SetMessageRecipient(this);
try

{
if (! stop)

{
// wait forever for Notify() from Close()
Monitor.Wait(this);
}
}
catch (Exception e)

{
Console.WriteLine(e.ToString());
}
db4oServer.Close();
}
}

/**//// <summary>
/// messaging callback
/// see com.db4o.messaging.MessageRecipient#ProcessMessage()
/// </summary>
public void ProcessMessage(IObjectContainer con, object message)

{
if (message is StopServer)

{
Close();
}
}

/**//// <summary>
/// closes this server.
/// </summary>
public void Close()

{
lock(this)

{
stop = true;
Monitor.PulseAll(this);
}
}
}
}