5.2 模型创建


  • Xml 基本节点

    • project

      应用的根

      namespace,state,service,zdb 节点均可以定义在project节点下,命名了名字空间的根


      <project name="limax" xmlns:xi="http://www.w3.org/2001/XInclude">                                   
      </project>
      

      定义项目limax, 启用XInclude支持


    • state

      定义在project节点下,被manager节点引用

      网络服务器可以运行在多个状态下,state节点定义了该状态下网络服务器允许接受的protocol,rpc——尽管强烈推荐使用View,但是protocol,rpc也可以支持,毕竟View本身在系统内部也是通过protocol实现


      <state name="XXX">
          <namespace ref="a"/>
          <protocol ref="b.a"/>
          <rpc ref="b.c"/>
      </state>
      

      定义状态XXX,引入了a名字空间下的所有protocol,rpc,b名字空间下的a协议,b名字空间下的c协议。

      名字空间允许嵌套,为了避免混淆,使用state引用名字空间时,只允许引用最外层。


    • namespace

      定义在project,namespace节点下,被state节点引用

      namespace, bean, protocol, rpc, view 均可以定义在namespace下


      <namespace name="gs" pvid="12">                                   
      </namespace>    
      

      定义了名字空间名gs,设定该名字空间的pvid为12。名字空间嵌套的情况下,最外层pvid有效,内层pvid被忽略。


    • bean

      定义在project,namespace下,作为结构化类型被用来定义变量

      enum,variable可定义在bean下


      <bean name="b1">
          <enum name="e0" value="1"/>
          <variable name="v0" type="int"/>
          <variable name="v1" type="binary"/>
      </bean> 
      
      <bean name="b2">
          <variable name="a0" type="b1">
          <variable name="a1" type="string"/>
          <variable name="a2" type="list" value="float"/>
          <variable name="a3" type="vector" value="b1"/>
          <variable name="a4" type="set" value="b1"/>
          <variable name="a5" type="map" key="int" value="b1"/>    
      </bean>    
      

      在这里,bean b2引用了b1,具体的类型映射后文描述。


    • protocol

      定义在namespace下,被state引用

      protocol下可定义enum,variable,与bean类似


      <protocol name="CHandShake" type="101" maxsize="1030">
          <variable name="dh_group" type="byte" />
          <variable name="dh_data" type="binary"/>
      </protocol>    
      

      定义了101号协议CHandShake,该协议允许的最大尺寸为1030。一个pvid——最外层名字空间决定——下的协议编号不可重复,最大尺寸定义为0表示不限制,实际运行环境中服务器存在一个约束所有协议尺寸的硬限制,可以通过虚拟机运行参数调整,java -D limax.net.Engine.limitProtocolSize=xxx …,默认为1M。


    • rpc

      定义在namespace下,被state引用


      <bean name="CheckProviderKeyArg">
          <variable name="pvid" type="int" />
          <variable name="pvkey" type="string"/>
      </bean>
      <bean name="CheckProviderKeyRes">
          <variable name="errorcodes" type="int" />
      </bean>
      <rpc name="CheckProviderKey" type="305" argument="CheckProviderKeyArg"
              result="CheckProviderKeyRes" maxsize="1024" timeout="10000" />        
      

      定义了305号rpc, CheckProviderKey, 允许最大尺寸1024, 10000毫秒超时, rpc参数和结果必须是bean。rpc与protocol在同一空间下编号, 互相不可重复, 最大尺寸单独限制参数和结果, 应该取两者的最大值。

      特别注意,rpc仅在java环境下支持,提供给limax框架自身使用, java之外的客户端版本均不提供支持,不建议使用。


    • view

      定义在namespace下,被state引用。

      不包含ref节点的bind节点,不可引用包含any类型xbean的table;包含ref节点的bind节点,ref指向的xbean字段不能包含any类型。


      <view name="globalview" lifecycle="global">
          <variable name="var4" type="list" value="MyBeanAll" />
          <bind name="bindfirst" table="first">
              <ref name="s" />
              <ref name="sets" />
          </bind>
          <bind name="bindsecond" table="second" />
          <control name="control1">
              <variable name="var1" type="int" />
              <variable name="var2" type="MyBean" />
          </control>    
      </view>    
      

      定义了全局globalview,包含字段var4,bindfirst,bindsecond,控制control1。var4为一般字段,通过在view对象上set来改变;bindfirst为bind字段,first表的value结构中s,sets两字段任何一个发生变动,bindfirst被设置成value.s,value.sets组织起来的结构;bindsecond为bind字段,second表的value发生变动,bindsecond被设置为value;contol1为control,参数为var1,var2组织起来的结构。


      <namespace name="gs" pvid="12">
          <namespace name="for_session">
              <view name="firstview" lifecycle="session">
                  <variable name="var1" type="int" />
                  <bind name="bindsecond" table="second" />
              </view>
          </namespace>
          <namespace name="for_temp">
              <view name="TestTempView" lifecycle="temporary">
                  <variable name="var1" type="int" />
                  <subscribe name="_var1" ref="gs.for_session.firstview.var1" />
                  <subscribe name="_bindsecond" ref="gs.for_session.firstview.bindsecond" />
              </view>
          </namespace>    
      </namespace>    
      

      I在这里,临时View TestTempView的字段var1发生变动,所有TestTempView成员都将收到这一变动。TestTempView的订阅字段_var1,_bindsecond分别订阅了会话View firstview的var1,bindsecond字段,TestTempView成员中,某一成员的firstview的var1或者bindsecond字段发生变动,该变动通过_var1或者_bindsecond被广播给所有成员。实际上_var1,_bindsecond被作为map实现,key为SessionId。特别要注意的是,成员用户的firstview.var1发生变动,该用户将收到两个变动,firstview.var1和TestTempView._var1[SessionId]。


    • service

      定义在project下,描述了该项目支持的服务,一个项目下允许多个


      <service name="switcher">
          <manager name="SwitcherServer" type="server" initstate="EndpointKeyExchange" port="10000">
              <state ref="EndpointSessionLogin"/>
              <state ref="EndpointClient" />
          </manager>
          <manager name="ProviderServer" type="server" initstate="ForProvider" port="10100" />
          <manager name="AuanyClient" type="client" initstate="AuanyClient" port="10200" />
      </service>    
      

      这是limax Switcher服务器组件的定义。

      一个服务器由一系列网络服务构成,manager定义了网络服务。SwitcherServer作为网络服务器运行在10000端口上,接受客户端的连接请求;ProviderServer作为网络服务器运行在10100端口上,接受Provider的连接请求;AuanyClient作为Auany服务器组件的客户端,连接Auany的端口10200。

      一个网络服务可以运行在一个或多个状态下,通过state引用了该状态下允许的protocol,rpc,view,初始状态由属性initstate指定,其他状态定义在manager节点内。


      <state name="GsProvider">
          <namespace ref="gs" />
      </state>
      <service name="demogsd" useGlobalId="true" useZdb="true">
          <manager name="Provider" type="provider" initstate="GsProvider" port="10100"/>
      </service>    
      

      这里定义了服务demogsd,注意manager下的type为provider,意味着生成provider需要的代码,前面提到实现limax应用一般就是实现provider,这是最常用的。事实上provider是Switcher服务器组件的客户端,所以port 10100决定了该provider连接Switcher服务器的10100端口。同时provider又是应用客户端的服务器,应用客户端与provider之间通过Switcher转发protocol,rpc,view数据。

      useGlobalId, 在服务配置文件中生成使用GlobalId服务组件需要的配置。

      useZdb,在服务配置文件中生成使用Zdb的启动配置模板。

      特别要注意,provider模式下的manager仅支持一个状态,定义多个没有意义,并且该状态只能引用一个namespace,namespace的PVID被作为该provider的PVID。


    • zdb

      描述了应用使用的数据库的全部信息,一个应用只允许使用一个zdb数据库,project下最多定义一个。


    • cbean

      定义在project,namespace,zdb下。

      结构化类型作为表的key,map的key,set的value时,必须定义为cbean,与bean的定义类似,字段成员的类型可以是除binary和any以外的基本类型以及cbean类型,不能使用容器类型。

      特别的,cbean可以被bean,protocol,rpc,view,control引用。


      <cbean name="xcompare">
          <variable name="b" type="boolean" />
          <variable name="s" type="short" />
          <variable name="i" type="int" />
          <variable name="l" type="long" />
          <variable name="text" type="string" />
      </cbean>
      <cbean name="xcompare2">
          <enum name="eX" value="1" />
          <variable name="xc1" type="xcompare" />
      </cbean>    
      

    • xbean

      定义在zdb下

      结构化类型作为表的value时,必须定义为xbean,与bean的定义类似,字段成员类型可以是各种基本类型,容器类型,如果需要嵌套结构化类型可以使用cbean或者xbean,既然cbean是常量bean,意味着该嵌套类型的对象没有修改的机会。xbean支持一种特殊类型any,any类型不可持久化,使用了any类型的xbean作为表的value时,该表只能是内存表。

      特别的,不包含any类型的xbean可以被bean,protocol,rpc,view,view,control通过variable节点引用。bind不能完整引用值为包含any类型的xbean的table,但是bind部分引用非any成员是允许的。


      <xbean name="BasePlayerInfo">
          <variable name="name" type="string" />
          <variable name="level" type="int" />
          <variable name="sid" type="long"  />
      </xbean>
      
      <xbean name="WaitingPlayerInfo">
          <variable name="baseinfo" type="BasePlayerInfo" />
          <variable name="ready" type="boolean" />
      </xbean> 
      
      <xbean name="ObservePlayerInfo">
          <variable name="baseinfo" type="BasePlayerInfo"/>
          <variable name="obpos" type="int" />
      </xbean> 
      
      <xbean name="WaitingTableInfo">
          <enum name="POS_EAST" value="0"/>
          <enum name="POS_SOUTH" value="1"/>
          <enum name="POS_WEST" value="2"/>
          <enum name="POS_NORTH" value="3" />
          <enum name="POS_COUNT" value="4"/>
          <enum name="POS_OBSERVE" value="4" />
          <variable name="tableid" type="int"/>
          <variable name="players" type="vector" value="WaitingPlayerInfo" />
          <variable name="observe" type="vector" value="ObservePlayerInfo"/>
          <variable name="playing" type="boolean" />                
      </xbean>  
      
      <xbean name="RoomInfo">
          <variable name="name" type="string" capacity="32" />
          <variable name="hallid" type="long"  />
          <variable name="players" type="set" value="long" />
          <variable name="tables" type="vector" value="WaitingTableInfo"/>
      </xbean>  
      

      这里定义了一个比较复杂的xbean结构。


      <xbean name="Any">
          <variable name="any" type="any:Object" capacity="32"/>
          <variable name="anyset" type="set" value="any:Object"/>
          <variable name="boolv" type="boolean" />
      </xbean>   
      

      这里定义了包含any类型的xbean,Object是java.lang.Object,可以引用任何对象。any冒号之后可以引用所有合法的java类型以及描述中出现的普通bean,view,因为bean,view本身在生成代码以后也会生成对应的java类。


      <view name="Info" lifecycle="session">
          <variable name="info" type="BasePlayerInfo"/>
          <variable name="testany" type="Any"/>
      </view>   
      

      这个view的变量字段引用了xbean,字段testany是any类型的,是错误的,代码生成过程中会抛出异常指出Info.testany存在any类型依赖。any依赖是递归检测的。


      <table name="anytable" key="int"  value="testany"  persistence="MEMORY"/>
      <view name="Info" lifecycle="session">
          <variable name="info"type="BasePlayerInfo"/>
          <bind name="bindtestany" table="anytable">
              <ref name="boolv"/>
          </bind>
      </view>   
      

      这里的bind绑定了anytable,anytable的值类型是xbean Any,尽管这个xbean包含了Any类型,但是这里只引用了boolean类型的字段,所以上面的描述是正确的。


    • table

      定义在zdb下


      <table autoIncrement="true" cacheCapacity="4096" key="long" name="roominfos" persistence="MEMORY" value="RoomInfo"/>  
      

      zdb数据库中表的描述

      roominfos表,key类型为long,value类型为前面定义的xbean RoomInfo,long类型的key在配置了autoIncrement="true"的情况下支持自动增量,persistence="MEMORY"决定了这张表是内存表,cacheCapacity="4096"决定了这张表在内存中最多cache 4096条记录,对于内存表而言,记录数一旦超过cacheCapacity,某些记录将丢失。


      <table name="keyisxcompare2" key="xcompare2" value="string" cacheCapacity="4096"/>  
      

      keyiscompare2表,key为前面定义的cbean xcompare2,value为字符串,没有指明persistence,这是张磁盘表。


      <table name="tany" key="int" value="Any" cacheCapacity="4096" persistence="MEMORY"/>  
      

      tany表,value的类型是前面的定义的xbean Any,xbean包含了any类型字段,该表只能是内存表。


      <table autoIncrement="true" cacheCapacity="4096" key="long" name="testtype" value="TestType" lock="abc"/>
      <table name="secondaryindex" key="long" value="SecondaryIndex" cacheCapacity="2000" lock="abc"/>   
      

      这里多了lock这一属性,zdb的锁都是行锁,基本锁定方式是lock(table, key),具有相同lock属性的表共用锁。在这里意味着锁定了表testtype的key行,同时也就锁定了secondaryindex表的key行,反之亦然。同时锁定testtype与secondaryindex表的key行也不存在问题。适当规划lock可以简化锁。


    • monitorset

      定义在project,namespace下,为应用提供相关的运行数据监视能力。


      <monitorset name="TransactionMonitor" supportTransaction="false">
          <monitor name="runned" type="counter"/>
          <monitor name="false" type="counter" />
          <monitor name="exception" type="counter"/>
          <key name="procedureName" type="string"/>
      </monitorset>   
      

      这是limax.xml中的zdb事务监视器集合定义,key为存储过程名,分别为存储过程定义了3个计数信息,执行次数,执行返回false的次数,执行抛出异常的次数。其中:

      supportTransaction指出了这一监视器集合需不需要事务特性,简单的说如果为true,那么在事务环境下进行的计数,只有事务成功提交才真正执行,默认为支持。

      monitor的type分为counter和gauge两种。counter在生成代码中提供increament方法,gauge提供set方法。

      一个monitorset允许多个key,作更细致的划分,key的类型,只能使用除了binary与any之外的简单类型。


  • Xml描述规范

    Limax使用xml描述项目模型的构建,定义了命名节点和引用节点两类xml节点。存在同时是命名节点和引用节点的这种节点。xml内部名字的解析使用内部名字空间规范。

    • 命名节点

      命名节点包含name属性,生成代码按照特定规范连接这些name,组织项目名字空间。zdb中table的name必须全部小写,其它name不限。


    • 引用节点

      凡是使用了ref属性的节点属于引用节点,ref属性的值,必须是命名节点的引用。临时View节点下的subscribe节点,既是引用节点——引用了会话View的字段,又是命名节点——作为当前View的字段被命名。

      各种节点下的variable节点,必须指定type属性,当type指向了xml内部描述的结构,bean,cbean,xbean时,可以理解为引用节点。variable节点作为上层节点的字段节点被命名,因此也是命名节点。


    • 内部名字空间规范

      同一xml节点下的命名节点不可重名。

      父节点名 “.” 子节点名,构成节点路径,节点路径允许逐层向外扩展,直到根节点,根节点开始的节点路径为全路径。节点名,节点路径,都可以合法地引用节点自身。

      代码生成过程中,报告二义性节点引用时,应该使用更长的节点路径解决二义性。例如,名字空间A下定义bean X,名字空间B下定义bean X。某一variable定义字段时指定了type为X,代码生成过程报告二义性错误,这种情况下,必须正确选择type为A.X或者B.X,消除二义性。


    • 特殊情况讨论

      1. zdb节点也是命名节点,它的名字不会应用到生成代码中,所以允许不进行命名,这种情况下默认名为空串。为了名字空间解析方便,需要的情况下也可以命名。

      2. cbean作为特殊的常量类型bean,具有全局性,不受名字空间约束,整个项目内,任何地方定义的cbean均不可重名。

      3. table的name要求必须小写基于此原因:使用文件数据库时,该name被用来直接命名表文件。如果操作系统使用了大小写不敏感的文件系统,比如windows,应用运行在这类操作系统上时,为了规避可能产生的冲突,全部小写是最好的解决方案。

      4. bind节点下的ref节点,看起来是命名节点,但是应该解释成,与bind节点的table属性结合到一起,引用了xbean的字段。这样设计的原因:描述简单清晰,否则就应该在bind节点中加入ref属性,xbean的字段名逗号分隔。


上一页 下一页