我理解的分布式访问与微服务是这样的,所谓分布式访问就是通过网络调用另一台机器上的模块或对象。所谓微服务,就是将一个程序的功能、模块拆解出来,放到另一台机器上运行,将一个大的程序拆解成小的服务单独运行,这方便系统的扩容及维护。
在传统编程模式中,实现分布式访问、模块拆解成微服务是比较麻烦的,根本原因是对象之间的强耦合。在消息编程中,因为对象主要是通过消息来传递信息,因此消息编程天生具有分布式和拆解微服务的优势。现在我们实例看看如何的实现。
我们的实现要求:不修改代码就将一个程序中的模块拆解出来,放到另一台机器上运行,并实现原有的功能。现在以我们拿之前演示消息编程的一个案例——hello小明来实现,原案例文章见——统一对象消息编程(11)—对象消息编程框架应用2(demo—Hello小明)。
在hello小明案例中,我们演示了消息编程的基本模式。最新代码及配置见框架的demo/base/ ,配置文件目录/WEB-INF\classes\conf\demo\base 。程序运行逻辑流程如下:
1、小明回家打开灯。
2、灯被打开,灯发消息给屋子。
3、屋子亮了,同时发消息给小明、妻子。
4 妻子正在睡觉,屋子亮了,她醒了。
5、屋子亮了后,小明开始上网。
程序通过消息配置、及自动传递实现上述过程。大概框图如下:
上面四个模块小明、妻子、屋子、灯都在一台机器内运行。现在我们要把上面的模式改成分布式或微服务,把妻子模块拆解出来放到另一台机器运行,框图如下:
原程序是单独一台机器内运行,无需网络。现在分开到两台机器,那么必然要加网络接口或模块。因为是屋子发消息给妻子,因此我们将屋子所在的原来机器为client端,妻子所在机器为service端。
现在分两步:
client (配置目录 \WEB-INF\classes\conf\demo\service\client):
client端在原来的基础上,无需更改代码,在配置文件中增加网络访问模块 socketClientAgentPool,其配置文件为 socketClientAgentPool_config.xml 。内容如下:
<moduleConfig>
<params>
<defaultServer value="clientSocketServe" />
</params>
<servers>
<!-- 如设定cerFile ,并且协议为wss 则启动ssl传输 -->
<server name="clientSocketServe" userType="username" cerFile__="/mycerts.cer"
url="ws://127.0.0.1:9081/ws" userName="tianlong118" userPasswd="12345" autoConnect="false" />
</servers>
</moduleConfig>
配置定义了妻子模块所在service端的网络参数 。屋子发消息的对象是妻子,现在如果不更改屋子的代码,如何实现通过网络给妻子发消息呢,我们还是通过配置,更改模块工厂的配置文件妻子对应的模块:
<modules>
<!-- 用户自定义模块 -->
<module name="appCenter" classfile=".base.MyAppCenter" configfile="appConfig.xml"/>
<module name="house" classfile=".base.House" singleton="true" />
<module name="light" classfile="cn.tianlong.java.demo.base.Light" singleton="ture"/>
<module name="xiaoming" classfile="cn.tianlong.java.demo.base.Person" />
<!--module name="wife" classfile="cn.tianlong.java.demo.base.Person" /-->
<module name="wife" proxyModule="socketClientAgentPool" configfile="socketClientAgentPool_config.xml" />
</modules>
之前妻子wife的创建是通过本地的类cn.tianlong.java.demo.base.Person 来实例化的。现在更改为网络接口模块 socketClientAgentPool (proxyModule参数的意思是,当工厂实例化wife时,用proxyModule对应的类来实例化)。这里socketClientAgentPool模块冒充了妻子wife模块,这样任何发给wife的消息将发给模块socketClientAgentPool,对发送者屋子来说,该过程完全透明,屋子完全不知道它发送给wife的消息其实是发给了socketClientAgentPool,而socketClientAgentPool根据配置将消息发送到service端。再看service端:
service(配置目录WEB-INF\classes\conf\demo\service\server):
在工厂配置中定义socketserver和wife 模块:
<modulesParams>
<!-- 如配置sslCerFile , 则启动ssl传输 -->
<module name="clientSocketServer" serverName="clientSocketServer" maxClient="5000"
loglevel="debug" nologtags="checkNoAuthChanaels"
port="9081" hostname="127.0.0.1" sslCerFile__="/tomcat.keystore" sslCerFilePwd="XXXXX" />
<module name="userManagerModule" userType="username" logInOne="true"
users ="taskuser:54321;tianlong118:12345"
serverModuleName="clientSocketServer" />
</modulesParams>
<modules>
<module name="wife" classfile="cn.tianlong.java.demo.base.Person" />
<module name="clientSocketServer" proxyModule="webSocketServer" loglevel="trace"
serverName="clientSocketServer" clientMsgHandler="clientMsgHandler"/>
</modules>
<factoryBoot>
<msg destination="clientSocketServer" action="run" waitFlag="false" usePreReturnMsg="false"/>
<msg action="getModule" moduleName="wife" usePreReturnMsg="false" />
</factoryBoot>
</moduleConfig>
这里clientSocketServer 为我们要启动的server,它将接收来自client端的消息。这里wife 定义了具体的Person 类。client与server采用websocket的方式,server接收到屋子发来的消息,将消息传递给wife模块。
至此,我们没有修改任何代码,就将wife模块透明拆解出来运行在其他机器上了,同理,我们也可将小明、屋子、灯都拆解到不同的机器上运行。这就将原本一台机器上运行的程序变成了分布式或各微服务组成的的网络服务。随着网络负载的增大,通过增加服务代理接口,还可将再复制多个屋子、小明等。现在我们看运行情况:
首先,演示之前单程序的情况:
我们看到,wife模块和其他所有模块在一个程序内运行。
现在看看拆分之后的分布式模式:
1、首先服务端启动:
wife模块启动,状态我睡觉,服务器运行,端口9081
2、client端启动:
比较单程序运行,没有wife模块的运行,现在再我们看服务端.:
这时服务端显示了wife的消息,说明屋子发送的消息通过网络透明的传给了wife。
到这里,我们轻松的实现了一个单一程序的分布式。或者说将一个大服务拆解成了多个小服务,而我们没有更改任何的代码 和业务逻辑。
案例代码见框架源码src\cn\tianlong\java\demo\service 下。