服务器端扩展
SmartFoxServer 2X扩展在SmartFoxServer Pro的许多方面得到了重新访问和改进。我们将注意力集中在Java扩展开发上,并且已经放弃了对脚本语言的支持。主要原因是:
性能:用Java编写的扩展程序比任何脚本语言(如Javascript或Python)都要好一些数量级。随着面向企业级应用程序的新型服务器架构,动态语言的使用将很快成为瓶颈。我们仍然认为,剥皮是快速原型创意的好方法,但我们不建议用于生产。为此,您仍然可以插入动态语言引擎(如Rhino和Jython)。
并发性:Java允许完全控制并发,这是服务器端代码的关键方面。特别是最新的Java 5/6并发集合和实用程序为开发人员提供了不能从动态语言完全访问的优秀并发工具。
集成:如果在Java中直接完成,与其他库的集成更为自然,并避免后续可能在开发中出现的意外后果。
Java扩展框架已经大大改进。服务器端API是为符合最新的Java 6标准而编写的,它们利用了所有最新的功能,如并发集合,泛型,枚举,注释等。API还符合Java最佳实践,提供了前所未有的定制。
简化了开发周期,以便从IDE中进行一键式部署,并且幕后完全管理诸如类路径修改或类加载事务等复杂操作。
一个扩展来统治一切
扩展架构中的第一个值得注意的变化是开发人员可以将单个扩展插件插入其房间或区域。这与SmartFoxServer Pro相反,可以附加多个扩展。这样做的理由是,新的扩展将允许您以更少的方式做更多的事情:
由于Zone代表在SmartFoxServer中运行的隔离应用程序,因此我们发现使用单扩展应用程序模型是提供可扩展性的最合乎逻辑的方法。
扩展必须被视为包含服务器应用程序所有逻辑的Java程序。使用简单的对象OOP实践,您可以像编写独立应用程序一样分开代码的关注。新的Extensions API还将通过一套新的工具来帮助您完成此项工作。
使用单个扩展允许将状态保存在中心位置,并与代码中的所有类共享。
使用单个扩展消除了尴尬的互操作性工具的需要,这些工具只会使代码的流程复杂化,并增加了类加载和并发问题。
»扩展概述
Java扩展部署在单个.jar文件中,该文件夹代表扩展名。下图显示了所有扩展程序发布的根目录的主扩展名/文件夹。我们的testExtension文件夹包含一个包含我们所有代码的jar文件。
还有一些事情要注意:
您可以在同一扩展文件夹下部署多个jar文件,例如依赖关系或其他库。它们都将被加载到相同的类加载器下。
有一个名为__lib__的专用文件夹,您可以在其中放置依赖关系。这样,您可以选择哪些库在多个扩展中共享,哪些库在特定扩展中是本地的。
这两种方法都可以有用,取决于您需要做什么:
在Extension文件夹下部署的库将被加载到Extension Class Loader中。这意味着您可以更改该特定库而不影响其他扩展。
在__lib__文件夹下共享的库将加载到父类加载器中。如果您更改任何这些依赖关系,它将影响使用它们的所有扩展。
»定制配置
每个扩展可以自动加载包含您的代码中可立即可用的自定义设置的.properties文件。默认情况下,Extension API将尝试加载一个名为config.properties的文件,但您可以指定任何其他文件名。当将相同的扩展程序附加到多个区域或房间时,这是特别有用的,但是您需要将不同的设置传递给每个区域。
这是关于如何在AdminTool中配置扩展的示例(请参阅“区域配置器”模块文档中的“区域扩展”选项卡或“房间扩展”选项卡以获取其他信息和重要说明):
Where:
名称是指扩展名,由文件夹名称表示;
类型表示正在使用的扩展的类型(建议使用Java);
主类是主要扩展类(或Python脚本文件名)的完全限定名称;
使用命名约定不是扩展设置:它仅激活/禁用主类字段上的过滤器,以仅显示名称以“扩展名”结尾的类;
属性文件是扩展文件夹中部署的.properties文件的可选名称,包含自定义扩展设置(默认为config.properties);
重新加载模式指示扩展是否被监视并自动重新加载(AUTO),手动重新加载(MANUAL)或不可重新加载(NONE)。
»类路径管理
好消息是,您不必像SmartFoxServer Pro那样触摸类路径。 SmartFoxServer 2X扫描依赖关系和扩展文件夹并加载所有的jar文件。
»扩展名重新加载
SmartFoxServer 2X提供扩展热重新部署,这在开发阶段非常有用。当此功能打开(参见上面的自定义配置段落)时,服务器将监视您的扩展名文件夹,并在修改jar文件时重新加载代码。
所有您需要做的是配置您的Java IDE直接在SmartFoxServer路径的Extension文件夹下构建或复制jar文件,您将拥有一键式部署系统。
»必需的依赖项
为了开始创建自己的扩展,您需要在最喜欢的IDE中为项目添加一些库:
lib / sfs2x.jar
lib / sfs2x-core.jar
日志记录工具不是强制性的,除非您需要特定的日志功能:
lib / log4j-1.2.15.jar
lib / slf4j-api-1.5.10.jar
lib / slf4j-log4j12.jar
»服务器端javadoc
您可以查看服务器端API javadoc。 API的主要入口点是com.smartfoxserver.v2.api.SFSApi类。请注意,使用未记录的方法和属性可能会导致系统故障。
»看看Extension API
有两个主要类别为您将创建的任何扩展提供基础:
BaseSFSExtension。提供SmartFoxServer Pro Extensions开发中已知的基本四种方法:init,destroy,handleClientRequest,handleServerEvent。我们已经包括这个课程,主要是为了兼容以前的扩展方法,但我们认为你会发现下一个更方便。
SFSExtension。这是您应该在Extension主类中继承的推荐的新基类。 SFSExtension提供了内置的服务,以便适当地分离请求和事件处理程序,在扩展和销毁这些扩展时释放自动侦听器。
»最简单的扩展可能
我们来看看我们可以写的最简单的扩展类:
public class MyFirstExtension extends SFSExtension
{
@Override
public void init()
{
trace("Hello, this is my first SFS2X Extension!");
}
}
这是创建完全功能扩展的最低限度:一种方法init。
当然,你可能已经在想,你将无法用单一的方法完成很多工作,所以让我们添加一个请求处理程序。 我们希望用户能够向我们发送两个号码,我们将添加它们并发送回来。 我们的YouTube频道也提供了一个完整的视频教程,介绍如何创建这个简单的扩展程序。
开发扩展程序时的推荐做法是,每个请求或事件处理程序都是一个单独的类,以便清楚地分离代码中的每一条逻辑。 我们提供了两个接口来创建请求处理程序或服务器事件处理程序。 在我们创建我们的添加请求处理程序之前,我们必须定义我们期望从客户端得到的参数:在这个简单的例子中,我们预期会有两个称为n1和n2的整数,我们将返回一个二进制数的整数。
现在我们可以开始编写处理程序:
public class AddReqHandler extends BaseClientRequestHandler
{
@Override
public void handleClientRequest(User sender, ISFSObject params)
{
// Get the client parameters获取客户端参数
int n1 = params.getInt("n1");
int n2 = params.getInt("n2");
// Create a response object创建响应对象
ISFSObject resObj = SFSObject.newInstance();
resObj.putInt("res", n1 + n2);
// Send it back发回它
send("add", resObj, sender);
}
}
handleClientRequest方法接收以下两个参数:
sender:表示发送请求的客户端的User对象;
参数:具有用户发送的所有参数的对象。 SFSObject是所有对象的肉和马铃薯在客户端和服务器之间交换。服务器和客户端API都提供相同的ISFSObject接口,以确保编码一致性(阅读本文档以获取更多信息)。
上述示例中的代码应该是不言自明的。我们首先获得两个预期的整数,为响应准备一个新的SFSObject,并添加结果n1 + n2。最后,我们在父扩展上调用send方法将响应发回给用户。以下参数传递给方法:
“add”:用于此请求的唯一命令名称(建议在同一请求/响应对中使用相同的名称);
resObj:包含发送到客户端的数据的SFSObject;
发件人:在此使用表示发送请求的客户端的用户对象,因为我们要将响应发送回请求者。
为了“连接”这个处理程序与主扩展,我们需要做的是在扩展的init方法中添加一行:
@Override
public void init()
{
trace("Hello, this is my first SFS2X Extension!");
// Add a new Request Handler添加一个新的请求处理程序
addRequestHandler("add", AddReqHandler.class)
}
addRequestHandler方法允许注册特定请求ID的处理程序。 类似地,可以使用addEventHandler方法添加任意数量的服务器事件处理程序。 这是一个如何监听USER_LOGIN服务器事件的示例:
public class LoginEventHandler extends BaseServerEventHandler
{
@Override
public void handleServerEvent(ISFSEvent event) throws SFSException
{
String name = (String) event.getParameter(SFSEventParam.LOGIN_NAME);
if (name.equals("Gonzo") || name.equals("Kermit"))
throw new SFSLoginException("Gonzo and Kermit are not allowed in this Zone!");
}
}
就像以前一样,现在我们现在回到主要的扩展类修改init方法:
@Override
public void init()
{
trace("Hello, this is my first SFS2X Extension!");
// Add a new Request Handler添加一个新的请求处理程序
addRequestHandler("add", AddReqHandler.class)
// Add a new SFSEvent Handler添加一个新的SFS事件处理程序
addEventHandler(SFSEventType.USER_LOGIN, LoginEventHandler.class);
}
现在我们已经创建了第一个基本的扩展,我们可以注意到几件事情:
与SFS Pro方法不同,我们不用无限长的if或switch块将主要的扩展代码混淆,将请求或事件分派到适当的处理程序;
在SFS2X扩展类中,我们订阅事件,而不是老系统,其中所有事件始终被触发到扩展,即使他们不需要它们;
即使它的破坏方法似乎缺少,实际上它不是:它存在于父SFSExtension类中,并不是强制覆盖它; 如果没有,则默认行为是调用该方法时,所有RequestHandler和EventHandler都将被释放。
如果您需要自定义destroy方法行为,您可以简单地覆盖它。 当然别忘了调用super.destroy来确保事件/请求处理程序是自动注销的。
@Override
public void destroy()
{
super.destroy()
/*
* More code here...
*/
}
总之,还值得一提的是一些有用的方法,它们由任何命令或服务器处理程序继承:
getParentExtensions:返回对扩展的主类的引用;
getApi:返回对主服务器端API对象的引用;
发送:向客户端发送分机消息/响应;
trace:具有各种签名的有用的日志记录方法(有关更多信息,请参阅javadoc)。
»高级扩展功能
现在我们已经介绍了新的Extesion 2.0架构的基础知识,我们可以深入研究更高级的功能,从而更好地控制代码。
»实例化注释
我们提供了几个有用的注释,可以用来指定你的处理程序类应该如何实例化。默认情况下,当您声明一个请求/事件处理程序类时,这将在每次调用时被实例化为新的。您可以使用类上的@Instantiation注释更改此行为:
@Instantiation(NEW_INSTANCE):在每次调用时创建一个新的实例
@Instantiation(SINGLE_INSTANCE):对所有调用使用相同的实例
»多处理程序和请求点语法
为了在复杂应用程序中正确组织请求名称,我们已经建立了类似于使用“点语法”的Java包命名的约定。假设您的扩展可以处理一些游戏和其他操作,如用户注册和配置文件编辑。您可以组织所有这些请求,如下所示:
register.submitForm
register.passwordLost
register.changeEmail
寄存器. ...
profile.changeAvatarType
profile.changeNick
profile.getAvatar
个人资料. ...
checkers.sendMove
checkers.getMyScore
checkers.leave游戏
跳棋. ...
Extension API提供了一个可以添加到您的处理程序类定义中的@MultiHandler注释。这将以从某个前缀开始的所有请求注册该类。让我们为一组请求实现一个多处理程序:
@MultiHandler
public class RegisterMultiHandler extends BaseClientRequestHandler
{
@Override
public void handleClientRequest(User sender, ISFSObject params)
{
// Obtain the request custom name 获取请求自定义名称
String command = params.getUtfString(SFSExtension.MULTIHANDLER_REQUEST_ID);
if (command.equals("submitForm"))
handleSubmitForm(sender, params);
else if (command.equals("changeEmail"))
handleChangeEmail(sender, params);
// ... etc ... 等等
}
private void handleSubmitForm(User sender, ISFSObject params)
{
// ... app logic here app逻辑在这里
}
private void handleChangeEmail(User sender, ISFSObject params)
{
// ... app logic here
}
}
在多处理程序中,上述示例中的“寄存器”中的具体请求ID是从传递给处理程序的参数对象获得的。 然后可以使用if或switch语句来根据请求id执行适当的代码。
现在我们在之前学到的扩展的init方法中注册该类。
@Override
public void init()
{
trace("Hello, this is my first multi handler test!");
// Add a new Request Handler添加一个新的请求处理程序
addRequestHandler("register", RegisterMultiHandler.class)
}
在这个例子中唯一真正的区别是处理程序类被标记为@MultiHandler。完成此操作后,扩展调度程序将以“register”前缀开头的任何请求ID调用处理程序。换句话说,它将处理寄存器。
注意
您还可以将@Instantiation注释与@MultiHandler注释混合使用。
总而言之,值得注意的是,您不限于请求名称中的单个“点”。您可以有多个嵌套级别,例如:games.spacewars.fireBullet或user.profile.avatar.getHairColor等。我们唯一的建议是保持这些请求名称相当短,因为它们将与请求/响应对象一起传输。
»扩展滤镜
此游览的最后一个高级功能是扩展程序过滤器。如果您熟悉Java Servlet API,这可能会响铃。 SmartFoxServer中的扩展过滤器受到servlet过滤器的启发,它们具有类似的用途:它们在链中执行,它们可以用于在到达扩展本身之前记录,过滤或处理特定请求或事件。
可插拔过滤器的优点是它们不会妨碍您的扩展代码,它们的执行顺序可以被更改,甚至可以在必要时停止执行流程。一个例子可能是一个自定义禁用过滤器,在将请求传递给您的登录处理程序之前,用户凭据将针对黑名单进行检查。
这是一个简单的扩展过滤器的例子:
public class CustomFilter extends SFSExtensionFilter
{
@Override
public void init(SFSExtension ext)
{
super.init(ext);
trace("Filter inited!");
}
@Override
public void destroy()
{
trace("Filter destroyed!");
}
@Override
public FilterAction handleClientRequest(String cmd, User sender, ISFSObject params)
{
// If something goes wrong you can stop the execution chain here!如果出现问题,可以在这里停止执行链
if (cmd.equals("BadRequest"))
return FilterAction.HALT;
else
return FilterAction.CONTINUE;
}
@Override
public FilterAction handleServerEvent(ISFSEvent event)
{
return FilterAction.CONTINUE;
}
}
过滤器可以在配置时或动态地在运行时轻松添加到任何扩展中:
@Override
public void init()
{
/*
* This is your Extension main class init()
*/
// Add filters
addFilter("customLoginFilter", new CustomLoginFilter());
addFilter("pubMessageFilter", new PubMessageFilter());
addFilter("privMessageFilter", new PrivMessageFilter());
}
当新的请求或事件发送到您的扩展时,它将首先按照添加过滤器的顺序遍历过滤器链。 在上面的示例中,它将是:customLoginFilter»pubMessageFilter»privMessageFilter»Extension。
翻译自http://docs2x.smartfoxserver.com/AdvancedTopics/server-side-extensions