通过前面hello小明案例的介绍,我们对对象消息编模式或框架有了初步认识。为了验证这种模式是否的可用,我还对数据库、网络应用等方面进行了测试,在tlobjdemo下有案例代码。编码质量比较低,当时完全是为了测试。dbdemo包是数据库方面的测试,http是网络方面的测试,包括客户端和服务器。
我认为对象消息编程框架一个优点是逻辑清晰,因为对象之间只调用一个方法putmsg,跟随这个方法,就能清晰看到消息的传输途径。还有一个特点是模块化,程序之间都是模块的组合。掌握了消息对象原理再加上这些特点使代码阅读变得容易、清晰。现在我们看看demo下http包下的serverstart。这个类是启动一个应用server。这个应用server通过json和客户端传递消息。
代码:
public class serverstart extends TLBaseModule {
public serverstart(String main, TLObjectFactory myfactory) {
super(main, myfactory);
}
@Override
protected void init() {
}
@Override
protected TLMsg checkMsgAction(Object fromWho, TLMsg msg) {
switch (msg.getAction()){
default :
}
return null;
}
public static void main(String[] args) {
String configdir = System.getProperty("user.dir") + "\\config\\person\\";
Myfactory myfactory = Myfactory.getInstance(configdir, "applicationServer_appConfig.xml");
myfactory.boot();
serverstart serverstart = new serverstart("main", (TLObjectFactory) myfactory);
MyAppCenter appCenter = (MyAppCenter) serverstart .getModule("appCenter");
}
}
这次入口类直接继承了TLBaseModule,主要是为了编程方便,可以直接使用putmsg。在入口main方法内,和hello小明类似,
先启动工厂实例,然后启动appCenter。稍微不同的是在启动工厂类时,直接指定了配置文件applicationServer_appConfig.xml"。然后执行工厂boot。这个main方法也很简单,看不出做什么,我们看配置文件,看主要部分:
<msgTable>
<msgid value="setup" >
<msg action="run" destination="managerServer" waitFlag="false" />
<msg action="run" destination="HSServer" waitFlag="false" />
<msg action="getModule" destination="moduleFactory" moduleName="myserviceRegistTask" waitFlag="false" />
</msgid>
</msgTable>
<initMsg>
<msg msgid="setup" />
</initMsg>
<factoryBoot>
<factoryBoot>
<msg action="getModule" moduleName="log" usePreReturnMsg="false" />
<msg action="getModule" moduleName="monitorConfig" usePreReturnMsg="false" />
<msg action="getModule" moduleName="cache" usePreReturnMsg="false" />
<msg action="getModule" moduleName="dbserver1" usePreReturnMsg="false" />
<msg action="getModule" moduleName="dbserver2" usePreReturnMsg="false" />
<msg action="getModule" moduleName="HSServer" usePreReturnMsg="false"/>
<msg action="getModule" moduleName="managerServer" usePreReturnMsg="false"/>
<msg action="getModule" moduleName="serviceModle" usePreReturnMsg="false"/>
</factoryBoot>
</factoryBoot>
在工厂boot表中,工厂顺序启动了八个模块。
log--日志模块
monitorConfig---配置文件监听模块,配置文件变化是动态配置模块
cache--缓存模块
dbserver1、dbserver2 --数据库链接模块
HSServer----这是我们要启动的应用server
managerServer---管理server,应用server启动后,通过管理server管理应用server的启动、停止等
serviceModle---一个自定义的服务模块,这里启动是为了放在主线程,可以共用。可以不用在这启动,用户调用服务时启动。
工厂boot完毕后,上面模块都由工厂创建完毕。appCenter启动,执行初始化消息“setup” 。在路由消息表<msgTable>中,setup消息对应三个消息,然后依次执行这三个消息。分别是管理server运行,应用server运行。myserviceRegistTask是一个服务注册模块,用于将应用server注册到服务中心,如不注册可以不用。该模块用一个异步方式启动,在新的一个线程里运行,周期给服务中心发生注册消息。至此,程序启动完毕,结果如下:
程序启动,应用server监听8081端口,管理server监听8082端口。观察上面启动,发现没有myserviceRegistTask的周期服务注册消息日志。这是因为在日志配置log_config.xml中屏蔽了该模块的日志:
<?xml version="1.0" encoding="UTF-8" ?>
<moduleConfig>
<params>
<log4jConfigFile value="config/person/log4j.properties" />
<level value="warn" />
<noLogModules value="httpClient;myserviceRegistTask;userw1;HttpJsonClientHandler1"/>
</params>
<logModules>
<module name="HSServer" levels="info;warn;debug" actions="" ></module>
</logModules>
</moduleConfig>
配置项noLogModules 中为不进行日志输出的模块,现在我们把屏蔽取消,同时日志level改为info:
我们看到对于日志配置的修改,日志模块重新加载了配置,myserviceRegistTask的周期注册信息出现了。监听配置文件修改就是工厂boot时启动的monitorConfig 模块。该模块在单独的线程周期监听配置文件,monitorConfig的配置文件如下:
<?xml version="1.0" encoding="UTF-8" ?>
<moduleConfig>
<params>
<delay value="5" />
<modules value="appCenter;HSServer;serviceMsgMap;log" />
</params>
</moduleConfig>
配置中,delay的值为5,意思是以5秒周期检测文件变化。项modules 定义监听那些模块的配置。
细心的同学会发现管理server 和应用server 都是一个类,不过是配置不同。
<module name="managerServer" proxyModule="HSServer" configfile="managerServer_config.xml"/>
在应用server运行任务已满或者停止的时候,我们的管理客户端无法直接连接到应用server,这时候其实我们是另起了一个应用server担当管理的责任,客户端通过连接管理server而控制应用server。管理server和应用server在一个应用内,所以可以通信。server只是和客户端一个网络接口,负责消息的收发,具体提供的服务功能要依据后端的模块。看应用server的配置HSServer_config.xml :
<?xml version="1.0" encoding="UTF-8" ?>
<moduleConfig>
<!-- 目录相对于filter配置目录下-->
<params>
<serviceName value="HSServer" />
<serviceType value="usermanager" />
<hostname value="127.0.0.1" />
<port value="8081" />
<maxTaskNumb value="30" />
<serviceHander value="serviceMsgMap" />
<serviceAuth value="myServiceAuth" />
</params>
<msg-mapping>
<msgid value="index" >
<!-- client的msg 对应的服务msg-->
<msg destination="serviceModle" action="index"/>
</msgid>
<msgid value="user" >
<!-- client的msg 对应的服务msg-->
<msg destination="serviceModle" action="user"/>
</msgid>
<msgid value="dbmodle" >
<!--msg destination="serviceModle" action="dbservice" newModule="true" / -->
<msg destination="serviceModle" action="dbservice" />
</msgid>
<msgid value="putdb" >
<msg destination="serviceModle" action="putdb" />
</msgid>
<msgid value="insertMap" >
<!--//异步并发操作 对于不需要返回结果
<msg destination="serviceModle" action="insertMap" waitFlag="false" />
-->
<msg destination="serviceModle" action="insertMap" />
</msgid>
</msg-mapping>
</moduleConfig>
应用server收到的消息转发到前面提到的自定义服务模块serviceModle上,由服务模块处理。
<msgid value="dbmodle" > <!--msg destination="serviceModle" action="dbservice" newModule="true" / --> <msg destination="serviceModle" action="dbservice" /> </msgid>
对于上面的映射,对于客户端消息ID为dbmodle的消息,转发至serviceModle , 处理行为dbservice 。
看管理server的配置managerServer_config.xml :
<moduleConfig>
<!-- 目录相对于filter配置目录下-->
<params>
<port value="8082" />
<maxTaskNumb value="20" />
<serviceHander value="managerMsgMap" />
<serviceAuth value="myServiceAuth" />
</params>
<msg-mapping>
<msgid value="reloadConfig" >
<msg destination="HSServer" action="reloadConfig" />
</msgid>
<msgid value="setTaskNumb" >
<msg destination="HSServer" action="setTaskNumb" />
</msgid>
<msgid value="stopServer" >
<msg destination="HSServer" action="stop" />
</msgid>
<msgid value="startServer" >
<msg destination="HSServer" action="run" />
</msgid>
<msgid value="getTasks" >
<msg destination="HSServer" action="getTasks" />
</msgid>
<msgid value="getParams" >
<msg destination="HSServer" action="getParams" />
</msgid>
<msgid value="reloadConfig" >
<msg destination="HSServer" action="reloadConfig" />
</msgid>
</msg-mapping>
</moduleConfig>
管理server收到的控制消息直接送给了应用server,控制应用server的启动、停止等。
server只负责接收客户端的消息,并负责转解码,对于客户端的消息,不能随便在server上执行,因此<msg-mapping>定义客户消息与服务消息的映射,服务端不开放的消息不在映射表内。TLWebServiceMsgMap负责消息的映射,功能类似web框架(后面介绍)中的url到消息的转换。
http案例包下的clientkeepalive为一个客户端,stopServer为一个管理客户端,都仅为代码测试。
在我的设想中,应用server布置在webserver后端,主要负责业务逻辑。这时候webserver即为客户端。为保持效率,默认采用长连接方式。应用server类似于远程过程调用或webservice,不过在我们对象消息编程概念里,一切都是消息对象,因此不存在远程调用的概念,只能说消息可以通过网络传输到位于其他机器上的模块。
我觉得我的网络部分模块代码写的很差,我自己都不满意,当一个参考思路吧。但我觉得我的设计思想还是是对的。