ProjectDarkStar服务器端开发文档(四)Tasks、Managers、Hello Timer!
Posted on 2009-11-24 16:41 Bill Yuan 阅读(657) 评论(0) 编辑 收藏 举报第三课:任务(Tasks),管理器(Managers)和Hello Timer!
1.任务(Tasks)
In Lessons 1 and 2, the system automatically invoked the initialize method for us in response to an event (in this case, the initial run of a newly installed PDS application). This meant executing code within the PDS environment.
在第一、二两课中,系统自动为我们调用initialize方法来回应一个事件(在这里,一个新安装的PDS应用开始运行)。这意味着在PDS环境下执行代码。
All code run in the PDS environment must be part of a task. From the point of view of the application coder, a task is a piece of monothreaded, transactional, event-driven code. This means that the task runs as if it were the only task executing at that moment, and all actions done by the task to change data occur in an all-or-nothing manner.
所有在PDS环境下执行的代码都必须是一个任务(task)中的一部分。从程序编写者的角度来看,一个任务就是一块单线程的、事务处理、事件驱动的代码。这就意味着,任务的运行就好像在这一刻只有它在运行,任务做的所有有关修改数据的操作都以“全做或全不做”的方式发生。
The realities of task execution
任务执行的真实情况
Each individual task executes in a monothreaded manner. However, if we executed them serially, waiting for each one to finish before the next one started, it would not be possible to get the kind of scaling the PDS provides.
每个任务都以单线程的方式执行。然而,如果我们顺序的执行它们,下一个任务的开始要等到前面每个任务完成,那就不可能得到PDS提供的好处了。
Instead, the PDS executes many of these monothreaded tasks simultaneously and watches for contention on the individual Managed Objects. If two tasks contend for control over a Managed Object, one task will be held up and will wait for the other to finish before it can proceed.
其实,PDS在同时执行许多单线程任务,并且注意在单个管理对象上产生的冲突。如果两个任务想要支配同一个管理对象,一个任务将被挂起,等到另一个完成操作之后再运行。
Many tasks can read the state of the same Managed Object at the same time without causing contention. If any of them wants to write to it, however, that can cause contention with tasks that read from or write to the same Managed Object.
许多任务可以同时读相同管理对象的状态而不会产生冲突。然而,如果它们中的任何一个任务想去写它,就会和那些读或写同一个管理对象的任务产生冲突。
To achieve optimal performance, it is important to design your data structures and game logic with as little potential object-contention as possible. Be especially wary of places where multiple tasks that are likely to occur simultaneously might have to write to the same Managed Object.
为了达到最佳性能,在设计你的数据结构和游戏逻辑时,尽量不要有潜在的对象冲突时很重要的。尤其要小心的地方是,很多任务可能在同时必须要写相同的管理对象。
All tasks registered with the PDS scheduler implement the interface Task, which has one method on it — run.(6) A task may be submitted to the scheduler to be executed either as soon as possible, or after a minimum delay time. A task can be one-shot or repeating. If it is a repeating task, it is also submitted with a period of repeat.
所有任务以PDS的调度程序来注册,都要实现Task接口,Task接口有一个方法——run。(6) 一个任务可能通过调度程序被提交去立即执行或是经过一段最小时间后执行。一个任务可以只执行一次或是反复执行。如果是个反复执行的任务,它提交时还要提交反复执行的时间段。
A repeating task is the same thing as a “timer” or “heartbeat” in traditional game systems; it lets you effectively generate an event to be handled every specified number of milliseconds.
一个反复执行的程序和传统游戏系统中的“计时器”或“心跳程序”是一回事;它让你有效的每隔特定毫秒数产生一个事件被处理。
2.管理器(Managers)
All communication between your server application’s game logic and the world outside of it is accomplished through managers. As described above, there are three standard managers:
● Task Manager
● Data Manager
● Channel Manager
服务器应用的游戏逻辑与外部世界之间的所有通信都是通过管理器(managers)来完成的。就像上面描述的,有三个标准的管理器:
● 任务管理器(Task Manager)
● 数据管理器(Data Manager)
● 通道管理器(Channel Manager)
There can also be installation-specific managers; these can be written by the author of the server application and deployed with the application into a PDS back end. The following static calls on the AppContext class are used by server application code to get a reference to a manager to talk to:
● getTaskManager()
● getDataManager()
● getChannelManager()
● getManager(managerClass)
也可以有特殊安装的管理器,它们可以由服务器应用的作者来编写,用PDS后端的应用来开发。下面的AppContext类的静态调用方法被服务器端应用代码用来得到一个管理器的引用:
● getTaskManager()
● getDataManager()
● getChannelManager()
● getManager(managerClass)
The first three are covered in this tutorial. The last is a generic call to get an installation-specific manager; it will be covered in the forthcoming Project Darkstar Server Extension Manual.
前三个方法在本文中涉及。最后一个是来调用一个特殊安装的管理器;它将在即将到来的Project Darkstar Server Extension Manual文档中进行讲解。
IMPORTANT: A Manager Reference is valid only for the life of the task within which the get manager call was invoked. Therefore, you should not try to cache manager references for use outside of that one invocation chain; get them from the AppContext instead.
重点:一个管理引用(Manager Reference)只在那个调用了获取管理器方法的任务的生命里是有效的。所以,你不要试图将管理对象缓存起来,在那个调用之外使用;而是要从AppContext得到它们。
The Task Manager is the part of the PDS that contains the scheduler, and thus what we use to schedule tasks. In order to be scheduled with the Task Manager as a task to be run, an object must be serializable and must implement the Task interface.7 The example below turns our AppListener into a task and starts it logging “Hello Timer” messages after a five-second delay at a half-second repeat period.
任务管理器是PDS系统的一部分,它包含调度器,我们可以用它来调度任务。为了作为一个任务让任务管理器来调度运行,一个对象必须序列化并且必须实现Task接口。(7) 下面的例子将我们的AppListener变成一个任务,并且开始运行5秒后,每隔半秒反复打印记录“Hello Timer”。
IMPORTANT: While the PDS stack makes a best effort to run tasks on schedule, it may back off execution of repeating tasks under heavy load. In that case, the task will skip execution of this period and reschedule to the next period. Additionally, contention for Managed Objects may cause a timed task to delay its execution.
重点:当PDS在全力进行调度运行任务时,在重负载下,它可能暂缓或放弃循环任务的执行。那样的话,那个任务将会跳过这段周期的执行,重新调度到下个周期。例外,管理对象的冲突可能导致一个定时任务的延迟执行。
The requested repeat frequency of a timed task is similar to a target frame rate in a game client, where frames may be dropped if they are taking too long to compute. If your application logic is tied to elapsed time or absolute number of “beats” in a given period of time, you’ll need to check the elapsed time and handle skipped periods in your run logic.
一个定时任务的请求的重复频率类似于一个游戏客户端中一个目标框架比率,这个客户端中,如果框架需要花太多时间来计算,那么它们可能会被清除掉。如果你的程序逻辑受困于运行时间或是给定的时间片里的若干“beats”,那么你将需要检查在run逻辑里运行时间和处理跳过的周期。(这段翻译的不好….)
3.编写HelloTimer
The HelloTimer application below uses the TaskManager to schedule a repeating task. The task will run after a delay of 5000ms at a frequency of once every 500ms.
HelloTimer应用使用TaskManager来调度一个循环任务。这个任务会在延迟5000毫秒后以500毫秒一次的频率运行。
HelloTimer
* Copyright 2007-2009 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package com.sun.sgs.tutorial.server.lesson3;
import java.io.Serializable;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.AppListener;
import com.sun.sgs.app.ClientSession;
import com.sun.sgs.app.ClientSessionListener;
import com.sun.sgs.app.Task;
import com.sun.sgs.app.TaskManager;
/**
* A simple timed-task example for the Project Darkstar Server. It uses the
* {@link TaskManager} to schedule itself as a periodic task that logs the
* current timestamp on each execution.
*/
public class HelloTimer implements AppListener, // to get called during
// application startup.
Serializable, // since all AppListeners are ManagedObjects.
Task // to schedule future calls to our run() method.
{
/** The version of the serialized form of this class. */
private static final long serialVersionUID = 1L;
/** The {@link Logger} for this class. */
private static final Logger logger = Logger.getLogger(HelloTimer.class
.getName());
/** The delay before the first run of the task. */
public static final int DELAY_MS = 5000;
/** The time to wait before repeating the task. */
public static final int PERIOD_MS = 500;
// implement AppListener
/**
* {@inheritDoc}
*
*
* Schedules the {@code run()} method to be called periodically. Since SGS
* tasks are persistent, the scheduling only needs to be done the first time
* the application is started. When the server is killed and restarted, the
* scheduled timer task will continue ticking.
*
*
* Runs the task {@value #DELAY_MS} ms from now, repeating every
* {@value #PERIOD_MS} ms.
*/
public void initialize(Properties props) {
TaskManager taskManager = AppContext.getTaskManager();
taskManager.schedulePeriodicTask(this, DELAY_MS, PERIOD_MS);
}
/**
* {@inheritDoc}
*
*
* Prevents client logins by returning {@code null}.
*/
public ClientSessionListener loggedIn(ClientSession session) {
return null;
}
// implement Task
/**
* {@inheritDoc}
*
*
* Logs the current timestamp whenever this {@code Task} gets run.
*/
public void run() throws Exception {
logger.log(Level.INFO,
"HelloTimer task: running at timestamp {0,number,#}", System
.currentTimeMillis());
}
}
Now that we have a repeating event, we have our first application that will do something when stopped and restarted. Task registration is persistent, which is to say, it survives a crash and reboot. Try stopping the server and restarting it again to see this in action.
现在,我们有了一个重复产生的事件,有了第一个在启动和重启的时候会做些操作的应用。任务的注册时持久的,就是说,它能在程序崩溃和重启中生存。试试停止服务然后重新启动它来看看这个持久性。
The periodic task is information stored in the Object Store along with your managed objects, so if you clear the data directory and return it to its pristine, uninitialized state, the periodic tasks will also get cleared.
周期性的任务信息和你的管理对象一起存储在对象存储器中,所以,如果你清除了数据目录,然后让它回到原来的、未初始化的状态,周期性任务也会被清除。
This behavior allows you to write your code as if the server were always up, with the caveat that you do have to check elapsed time in your periodic task’s run method if a delay between that and the last time it was run has significance to your logic.
这个行为允许你编写、修改代码时,就像服务一直在运行一样,需要警告你的是,如果实际运行的时间和它上次运行时间之间的延迟对你的逻辑有重要的意义,你必须检查在你周期性任务的run方法中实际运算的时间。
How you keep track of the last time run was called is the subject of the next lesson.
怎样来留意上次运行被调用是下节课的主题。
注释:
(6) 虽然像AppListener这样的事件处理接口不实现Task接口,但是这些监听器都是通过PDS的内置Task来调用。
(7) 你可能想知道为什么Task和ManagedObject接口为什么不继承Serializable。答案必须用到接口上的Serialization版本控制。为了防备serialVersionUID的不匹配,任何继承了Serializable接口的类或接口都要申明一个私有的serialVersionUID。但是因为接口只能申明公共变量,所以,最好是避免制造序列化的接口,而是留给类(或抽象类)来实现Serializable接口。