智慧 + 毅力 = 无所不能

正确性、健壮性、可靠性、效率、易用性、可读性、可复用性、兼容性、可移植性...

导航

ProjectDarkStar服务器端开发文档(八)

Posted on 2009-11-24 23:22  Bill Yuan  阅读(580)  评论(0编辑  收藏  举报

结尾部分

At this point you know all the basics of writing PDS applications. The applications you write using the PDS API on a single-node system will operate unmodified in exactly the same way on a large-scale multiple-node PDS production back end. In that environment they will scale out horizontally, handle failover, and be fault-tolerant.

       到这里,你了解了所有编写PDS应用的基础知识。你在单节点系统中用PDS的API编写的应用,不用修改,就可以以完全相同的方式在大型的,多节点的PDS产品上运行。在这种环境下,它们将是超水平的、可故障恢复的并且是可容错的。

There are, however, some best practices to follow to ensure optimal scalability for your application. Failing to follow these can seriously limit how many users your application will be able to support at once.

       然而,你可以参照一些最好的实践技巧来确保你应用的最理想的扩展性。如果不按这些来做,将会严重限制你的应用一次性可以支持的用户数量。

最好的实践经验

Do not design with bottleneck Managed Objects.

设计管理对象不要有瓶颈

You can think of each Managed Object as an independent worker who can only do one task at a time. When one task is modifying the state of a Managed Object, any other tasks that want to read or change its state must wait. A Managed Object may have many readers at once if no one is writing to it, but any writing turns it into a potential bottleneck. In general, a pattern of one writer and many readers is the best configuration, although it is not always possible.

       你可以把每个管理对象想象成一个独立的工作者,它一次只可以做一件任务。当一个任务要修改一个管理对象的状态时,其他任何想读或修改它的任务都必须等待。一个管理对象如果没有写操作时,一次可以有很多读取者,但是任何的写操作都会将它转变为一个潜在的瓶颈。通常,一个写和多个读的模式是最好的配置,虽然这不是一直可行。

In the worst case, multiple lockers of the same objects will cause potential deadlock situations that are computationally more expensive to resolve and can result in processing delays (additional latency).

       最坏的情况,对同一个对象进行大量的锁操作会导致潜在的死锁情况,那样为解决问题会消耗更多的资源来计算,可能最终导致处理的延时(额外的潜在的延时)。

Avoid contending for object access.

避免对象访问的冲突

Although the PDS will detect and resolve contentions for write access to objects between parallel events such that your code never actually stops processing, this deadlock avoidance can significantly load the CPU and slow response time to the users. In the worst case, contention can actually prevent parallel processing, seriously impacting the scalability of your code.

       虽然PDS会检测并解决并行事件之间对对象进行访问的冲突,以至于你的代码从来不会停止运行,但是,这种避免死锁的行为会导致CPU负载过高,拖慢对用户的响应时间。最坏的情况,冲突实际上可以阻止并发执行,严重影响你代码的扩展性。

A classic example of a deadlock is two combatants who have to modify both their own data and that of their enemies when resolving an attack. If all the data is on a combatant Managed Object, then both combat tasks need to lock both objects, typically in the opposite order, which is a formula for deadlock. One solution is to divide the combatants’ combat statistics into two groups, the ones they modify themselves and the ones modified by an attacker, and store them on separate Managed Objects. The combatant object would then retain Managed References to these objects, and only lock them as needed.

       一个典型的死锁的例子是,有两个争斗者,当他们要解决一次进攻时,都必须修改自己的数据和敌人的那份数据。如果所有的数据都在一个争斗者管理对象上,那么,两个竞争任务需要锁住各自的对象。死锁的惯例就是通常都处在相反的顺序。(翻的太生涩了,其实意思就是两个任务互相需要对方的资源来完成自己的任务,完成任务前都不能释放自己的资源,由此造成死锁。)一个解决方法是将争斗者的争斗统计数据分割为两组,一组它们自己修改,一组由攻击者来修改,并把它们保存在独立的管理对象中。争斗者对象会保留对这些对象的管理引用,只在需要的时候锁住它们。

Another practical solution is partial serialization of the problem. This is especially appropriate if you want the combatants attacks to be processed in a fixed order. Here you have a single “combat clock” task (generally a repeating task) that processes in sequence all the attacks from all the combatants in a given battle.

另一个实用的方法是将问题部分序列化。如果你想让争斗者的进攻在一个固定的顺序下进行,这就尤其合适了。这里你有一个独立的“争斗计时器”任务(通常是一个循环任务),它会依次处理一场战争中,来自所有战士的所有进攻。

Not all contention situations are this easy to spot and design around. The PDS gives you feedback at runtime as to what objects are deadlocking. Use this to tune your application.

       不是所有冲突的解决方法都能这样简单的点出并设计方案。PDS会在运行时给你反馈,哪个对象正处于死锁。使用这个特性来调节你的应用。

Give all serializable objects a fixed serialVersionUID.

   给所有可序列化的对象一个固定的serialVersionUID

The PDS uses Serialization internally. If you don’t give your Serializable classes a serialVersionUID, then any change to their public interface could invalidate the stored copies, leading to a need to delete the entire Object Store. Giving them a fixed serialVersionUID will allow you to make “compatible changes” without invalidating your existing store. (For what constitutes a compatible change, please see the JDK™ documents on Serialization.)

       PDS使用内部的序列化操作。如果你不给你序列化的类一个serialVersionUID,任何对它们公共接口的改变都可以使已经保存的副本无效,导致需要删除整个对象存储器。给它们一个固定的serialVersionUID可以允许你做“兼容性的改变”,而不会使你已存在的存储器无效。(至于什么构成了兼容性改变,请参看JDK™序列化文档。)

Avoid inner classes in Serializable objects.

   避免在序列化对象中使用内部类

Serialization of objects whose classes contain non-static inner classes gets complicated. It is best to avoid inner classes, including anonymous inner classes. If you must use them, the safest thing is to declare them as static inner classes. (12)

       那些包含非静态内部类的类的对象序列化会变得非常复杂。最好避免使用内部类,包括匿名内部类。如果你必须使用它们,最好将它们申明为静态的。(12)

Do not create non-final static fields on Managed Objects.

   不要在管理对象上创建non-final的静态域

The most important reason for this is that static fields exist only within the scope of a single VM, and a PDS back end floats Managed Objects between many different VMs.

       最重要的理由是:静态域仅存在于一个单独的虚拟机范围之内,一个PDS应用后端的管理对象游走在不同的虚拟机之间。

Do not use the synchronized keyword in Managed Objects.

   不要在管理对象上使用synchronized关键字

First, this is unnecessary in a PDS application. Synchronization is used to prevent contention over data between multiple parallel threads of control. The PDS programming model handles this transparently for you. Second, it won’t work, since synchronization is relative to a single VM and a full PDS back-end operates over many VM instances simultaneously. Finally, it causes interactions between tasks that can defeat the system’s deadlock-proofing feature and actually cause your code to lock up.

       首先,这在PDS应用里是没有必要的。同步化是被用在预防多线程访问数据时的冲突的。PDS的开发模式暗地里为你做了这些处理。第二,它不会工作,因为同步化和一个单独的虚拟机进行关联,而一个完整的PDS后端操作同时在多台虚拟机上执行。最后,它造成任务之间相互影响,会使检验死锁特征的操作失败,使你代码被锁住,不能运行。

Any task that locks up for too long will be forced by the PDS to yield its control of Managed Objects back to the system, but this is a last-ditch safety feature and will result in significant delays in code execution.

       任何锁住很长时间的任务会被PDS强制让出它对管理对象的控制,并交还给系统,但是这是最后一道保险,并且会导致代码执行的大量的延迟。

Do not make blocking I/O calls or stay in a loop for a long period.

   不要使用阻塞式I/O调用,或者处在一个周期很长的循环当中。

The system contains the assumption that tasks are short-lived. If a task lives too long, it will be forcibly terminated by the back end. The right way to do blocking I/O and the like is to create an extension manager, do the blocking calls in it, and submit a task to the Task Manager to handle the results when done. (Writing and installing custom managers will be covered in the Project Darkstar Server Extension Manual.)

       系统会有一个假设:任务都是短命的。如果一个任务生命太长,它会被后端应用强行终止。使用阻塞式I/O操作和其他类似操作的正确方式是创建一个扩展管理器,在里面做阻塞调用,提交一个任务到任务管理器去处理完成的结果。(编写并安装一个自定义的管理器将在Project Darkstar Server Extension Manual里描述。)

Do not catch java.lang.Exception.

不要捕捉java.lang.Exception

Instead, catch the explicit exceptions you are expecting. The PDS also uses exceptions to communicate exceptional states to the execution environment. Although the system does its best to do the right thing even if you hide these exceptions from it, it will operate more efficiently if you don’t.

       作为替代,捕捉你期望出现的明确的异常。PDS也会使用异常来与执行环境的异常状态进行交流。虽然系统会尽量做正确的事情,即使你隐藏了这些异常,但是这会造成更多的资源消耗。

Do not carry a non-transient Java reference on a Managed Object to another Managed Object.

   不要在一个管理对象上携带非瞬时的Java引用到另一个管理对象。

Instead, use a Managed Reference. Any object that is referred to by a Java reference chain that starts at the Managed Object is assumed to be part of the private state of that particular Managed Object. This means that, while you may set two Java references on two different Managed Objects to the same Java object during a task, they will each end up with their own copy of that object at the termination of the task.

       而是要用一个管理引用(Managerd Reference)。任何被一个Java引用关联的对象都会被假定为是这个引用它的管理对象的私有状态的一部分。这就意味着,在一个任务期间,你可能在两个不同的管理对象上各设置了一个Java引用,它们都指向相同的Java对象。在任务终止时,它们会得到各自对那个对象的副本。

Never try to save a Manager Instance on a non-transient field of a Managed Object.

   绝对不要尝试在一个管理对象的非瞬时域上保存一个管理器实例。

This is because Manager References are only valid for the life of the task that fetched them. Manager instances are not serializable objects. Any attempt to save a reference to one in your Managed Object will cause the Data Manager to throw a non-retriable exception and the entire task to be abandoned.

       这是因为管理引用只在那些取得它们的任务的生命周期中有效。管理器实例不是序列化的对象。任何企图保存一个管理器的引用都会导致数据管理器抛出一个non-retriable异常,整个任务都会被放弃。

Carefully manage the life cycle of your Managed Objects.

   小心管理你的管理对象的生命周期

Remember that the Object Store does no garbage collection for you. Managed Objects are “real objects” in the simulation sense. They don’t exist until explicitly created, exist in one and only one state at any given time, and persist until explicitly destroyed. These are very good properties from a simulation programming stance, but if you go wild creating Managed Objects and don’t destroy them when they are no longer useful, you can load down the Object Store with garbage and potentially impact performance.

       记住,对象存储器不会为你进行垃圾收集。管理对象在模拟环境下是“真实对象”。它们直到创建后才会存在,任何时间都只存在于一个状态下,并且是持久的,直到被消除。在模拟程序设计里有非常好的特性,但是如果你无休止的创建管理对象,并且在它们没用的时候不去销毁它们,你会使对象存储器负担过多的垃圾,从而潜在的影响性能。

Be aware that any PDS API call that accepts a Managed Object may create a Managed Reference to that object. If this is the first time the Data Manager has been made aware of the Managed Object, this will result in the Managed Object being added to the Object Store. It is still the developer’s responsibility to remove the Managed Object from the Object Store when it is no longer in use. For this reason, it is best to avoid passing Managed Objects that your application is not explicitly managing into the PDS APIs

       要知道,任何PDS的API的调用来接受一个管理对象都可能创建一个管理引用来引用这个对象。如果这是数据管理器第一次意识到这个管理对象,那么会使这个管理对象添加到对象存储器中。当管理对象不在使用时,从对象存储器中移除它,仍然是开发者的责任。基于这个原因,最好避免将那些你的应用还没有确实管理到的管理对象装入PDS的APIs。

一些指南中没有提到的

This tutorial is intended to introduce you to the fundamentals of coding a PDS application. Although we present all basic uses of the standard managers, these managers have additional functions and capabilities not covered here. Please see the Javadoc for those other functions.

       这篇指南打算为你介绍编写PDS应用的基础知识。虽然我们呈现了所有标准管理器的基本用法,但是这些管理器还有很多额外的功能没有在这里提及。请查看这些功能的Javadoc。

As discussed, the list of managers in a given PDS back end is extensible. This tutorial does not cover writing or using plug-in managers. For that, see the forthcoming document on PDS extensions.

       就像前面讨论过的,PDS后端里的一系列管理器都是可扩展的。这篇指南没有涉及编写和使用扩展的管理器。这些参看即将发出的PDS扩展文档。

Although we use PDS client programs in Lessons 5 and 6, this tutorial does not explain how those are written. For those explanations, please see the Project Darkstar Client Tutorial.

       虽然我们在第5、6课中使用到了客户端程序,但是这里并没有解释那些客户端程序是怎么编写的。这些都在文档Project Darkstar Client Tutorial中。

Finally, for the sake of clarity, this tutorial shows very simple examples. For more complex patterns of PDS usage, please see the community examples at http://www.projectdarkstar.com/.

       最后,为了简洁起见,这篇指南展现了非常简单的例子。更复杂的PDS用法的示例,请查看http://www.projectdarkstar.com/的社区例子。

注释:

(12) 通过代码的生成,一个静态的内部类和外部类没有区别,除了存在于它的外部类命名空间中的类名。