368
Limax Projects Manual 1

Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Limax ProjectsManual

1

Page 2: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

ContentsIntroduction to the Limax.................................................................................................................4The main feature of the Limax..........................................................................................................4View Fundamentals..........................................................................................................................7

Design Objective.......................................................................................................................7Basic Concept............................................................................................................................7The relative Concepts...............................................................................................................8

体系结构 Architecture...................................................................................................................10服务器框架 Server Architecture............................................................................................10客户端框架 Client Architecture.............................................................................................13

应用开发 Development of the Application....................................................................................15基本概念 Basic Concepts.......................................................................................................15模型创建 Model Creation......................................................................................................15类型映射 Type Mapping.........................................................................................................29服务器开发 Server development...........................................................................................31客户端开发 Client development............................................................................................68高级特性 Advanced Characteristic.......................................................................................119极简模式客户端 Minimalist mode client.............................................................................127Provider 间数据交换 Data exchange between Providers.....................................................132Limax Http.............................................................................................................................144

运行管理 Operation management...............................................................................................154配置 Configuration...............................................................................................................155部署 Deployment.................................................................................................................171运行状态监视 Running status monitoring...........................................................................173维护 Maintenance................................................................................................................180

附录 Appendix..............................................................................................................................191支付框架 Payment Framework............................................................................................191账号系统 Account System....................................................................................................197应用配置 Application Configuration....................................................................................204JSON 支持 JSON Support......................................................................................................211CLR/Lua.................................................................................................................................226CLR/Javascript(SpiderMonkey).............................................................................................237脚本语言访问 C#规范 The specification that the scripting language accesses C#..............251Node.js 兼容框架 Node.js compatible framework...............................................................261PKIX 支持 PKIX support.........................................................................................................290Key 分发系统 Key distribution system.................................................................................309外部数据 External data........................................................................................................323ProviderLogin........................................................................................................................326

2

Page 3: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

LimaxKey...............................................................................................................................329动态 XBean Dynamic XBean.................................................................................................337QR Code................................................................................................................................344JDBC 连接池 JDBC Connection pool.....................................................................................351

3

Page 4: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Introduction to the Limax

Limax provides a rich application server architecture and a data synchronization framework that allows you to develop the network applications in a Java language environment.

Limax also provides an operation system that allows you to easily monitor, manage and maintain the contents, information and data of the network applications.

If you are new to the Limax development, it is important that you understand the fundamental concepts about the Limax platform.

The main feature of the Limax

突破传统协议方式的束缚,使用 View 这样一个概念,应用服务器处理请求逻辑生成表现数据;客户端根据数据进行表现,大大降低逻辑处理复杂性。The application server processes the request logic and generates the presentation data based on the concept of the View, without using the traditional protocol model. The client directly presents the data, which significantly reduces the logic complexity.

协议模式的应用设计——服务器处理逻辑功能,产生输出数据,根据功能定义协议,根据协议打包数据,数据发送到客户端,客户端解码协议,根据解码数据处理后续逻辑或者直接表现数据。The application design of the protocol model: the server processes the logic functions, generates the output data, defines the protocols based on the functions, packages the data according to the protocol, and sends the data to the client; the client decodes the protocol, and processes the following logic according to the decoded data or directly presents the data.

服务器打包代码,通讯代码,客户端解码代码都可以根据协议描述自动生成,这看起来已经解决了很多问题。不足在哪里呢?These processes such as the server packaging source codes, the server communicating the source codes, and the client decodes the source codes could be automatically generated according to the protocols. It sounds amazing and many issues have been resolved. However, is there any defect?

4

Page 5: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

协议本身这个概念没有任何问题,ISO 还下个 7 层的定义,网络上各种协议满天飞。正确设计协议才是最大的麻烦,往往是个权衡利弊不断妥协的结果。设计上最大的麻烦有两个:其一,粒度;其二,时序。The concept of the protocol has no any problem. Even the ISO has 7 tier protocol, and all kinds of protocols are used in the network. The biggest problem is how to correctly design the protocol, which is the result continuing to weigh the compromise. There are two biggest issues: the granularity, and the sequence.

粒度——实现一个复杂应用,服务器可能有各种各样的数据需要传送,设计多少协议合适?少了,某些协议就带有冗余数据,客户端不得不根据具体上下文决定哪些数据是需要的,哪些是冗余,这就成了接收方必须处理的逻辑功能之一,这些逻辑功能与具体项目功能无甚关系,完全是额外劳动;设计多了,几十上百条协议如何维护?协议重用吧,数量看似可以减小,牵一发动全身的问题出来了,维护可能更难。The granularity -- when implementing a complicity application, the server need to transfer all kinds of datum. How many protocols to be designed are appropriate? If the protocols designed are limited as little as possible , some protocols might include the redundant datum, that causes the client to identify which data is necessary and which one is redundant through analyzing the related context. This is the one of the logical functions which acceptor must process even though these logical functions have no any relationship with the detailed project. If the protocols designed are expanded as many as possible, how to maintain the hundreds of the protocols? Even the amount of the protocols may be reduced through reusing the protocol, more serious issue which is related to the whole project may be caused, and the maintenance is harder.

时序——实现某一功能,可能需要发送多条协议。服务器先发谁,后发谁,客户端先处理谁,后处理谁,都是需要仔细权衡的问题。为了正确处理顺序,在服务器逻辑功能的设计上有两种考虑。其一,处理过程中该发就发,问题在于,如果需要实现的是一个事务逻辑,也许后续操作导致了回滚,前面的发送显然就不应该了。其二,逻辑功能处理完成以后发送,那么就必须根据结果数据,组织一系列协议的发送,这种组织静态的还好,一旦是个动态的,处理起来就非常麻烦。服务器实现既然那么麻烦,干脆把一个功能的数据打包到一条协议好了,这下好了,同一协议的字段按何种顺序解释变成了客户端需要解决的问题,逻辑处理被推给了客户端;更进一步,服务器要求先关灯,后开灯,打包到一条协议,客户端糊涂了,到底解释成,关然后开,或者开然后关?天哪,这个责任推还推不干净。The sequence -- It is possible to send many pieces of protocols when implementing one function. It is need to considerate the following questions: which protocol should be sent firstly by the server, which one is the next, which protocol will be processed in the first place by the client and which one is the last. There are two choices when designing the logical functions in the server in order to properly process the order. Firstly, the protocol will be sent as needed in the processing. However, there is a problem that the sent protocol could not be recalled when the next operation causes the rollback in a transaction logic. Secondly, Protocols could be organized and sent out based on the result data after the logical function has been finished. It is acceptable for the static order of the protocol organization. (Two protocols A and B, B always is sent behind the A, that is

5

Page 6: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

called the static order.) However, it is a big trouble to handle if the dynamic order of the protocol organization uses this choice.(Two protocols A and B, which should be sent firstly is decided by the execution result of the application, that is called the dynamic order.) Is it possible to package all the datum related to a function into one protocol to reduce the complexity of the server? If so, the client need to resolve the issue that which order is applied in a protocol to present the fields, and the client also has to handle the logic function at the same time. For example, the server requires to process the events in this order: turning off the light first, then turning on the light. All these events are packaged into one protocol and sent to the client. After receiving the protocol, the client gets into the trouble that how to present the sequence? First turn off the light then turn on the light? Fist turn on the light then turn off the light? It is crazy.

粒度,时序,两个问题综合到一起考虑——按简化时序设计,协议粒度变粗,冗余出现了,客户端必须识别冗余,解释时序;按精细粒度设计,协议维护代价大大增加,服务器时序设计变得更加困难——原来,这是一对矛盾。The granularity and the sequence should be integrated to be considered at the same time. If the sequence is simplified for the design, the granularity of the protocol will be thicker, which causes the redundancy. The client has to identify the redundancy and present the sequence. If the design focuses on the meticulous granularity, the maintenance cost will be sharply increased and the sequence design for the server will be harder. Actually, this is a contradiction.

协议模式搞设计,这对矛盾不可解决,只能调和。客户端,服务器都很难避免与最终需求无关的逻辑处理,处理起来不但成本高昂,而且往往最容易出现 bug。调和出来的设计,在需求发生变动的时候,非常难以维护,从一种调和方式转向另一种调和方式,也许就是伤筋动骨——最终需求无关的逻辑处理都必须挖掘出来,仔细调整,而且这些逻辑处理往往就在某一个程序员的脑子里——项目很容易被绑架。This contradiction could not be resolved in the protocol model design, but be reconciled. It is hardly impossible for the client and the server to avoid the logic processing which is not related to the final requirements, which not only brings the high cost, but also causes the bugs. The reconciled design is hard to be maintained when the requirements are changed. When the reconciled design is changed to another one, there might be a big problem: all the logic processing which is not related to the final requirements need to be found out and modified carefully. However, sometimes, some logic processing is just in the head of some developers. That will be the disaster to the project.

拒绝协议模式的应用设计也就避免了调和带来的不确定性,Limax 通过 View 这一概念,解释了数据意义上的 MVC,回避了粒度问题——哪些数据改变了就发送哪些;明确了时序问题——客户端完全重现服务器对 View 的修改顺序.The uncertainty caused by reconciled application design is gotten rid of due to no protocol model used. Limax presents the MVC in the data meaning by using the concept of the View, avoids the granularity issue -- only the data which is changed will be sent; clarities the sequence factor -- the client totally reproduces the modification order of the view changed by the server.

[6

Page 7: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

The traditional application process is based upon the protocol model: the server processes the logic functions, generates the output datum, defines the protocols based on the functions, packages the datum according to the protocols, and sends the datum to the clients; the clients decodes the protocols, and processes the next logic functions with the decoded datum or directly presents the datum.

However, two important factors need to be considered in the processing of the protocols: the granularity and the sequence. The granularity measures the sum of the protocols; and the sequence defines the order that the protocols are sent and received by the server and the clients. How to handle these two factors not only brings the complexity of the design and development, but also increase the maintenance cost and defects happened.

The developers of the Limax are lucky to get far away from these two issues because the Limax has resolved them.

The Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the data tier, avoids the granularity issue -- the data which has been changed will be sent; clarities the sequence factor -- the clients totally reproduce the modified sequence of the view which is consistent with the one modified by the server.]

View Fundamentals

Design Objective

完全消除为了维护协议体系添加的结构性逻辑代码——上文提及的那些与最终需求无关的,难以维护的逻辑代码——实现的精力集中到最终需求上来。服务器的精力集中到功能性数据上,客户端的精力集中到数据表现上。The server focuses on the functional data and the client concentrates on the data presentation. It is no necessary to implement the extra structural logic source codes which are not related to the final requirements and difficult to be maintained any more.

Basic Concept

View,被命名的一个结构描述,名字的定义应该反映具体功能。这个数据结构包含了一系列的字段,服务器 View 与客户端 View 一一对应,服务器 View 的字段发生改变同步到

7

Page 8: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

客 户 端 对 应 View 的 对 应 字 段 。 这 里 的 改 变 有 4 种 类 型 ,NEW,REPLACE,TOUCH,DELETE。NEW——服务器 View初始化了这个字段,字段数据从无到有;REPLACE——服务器设置了这个字段,字段数据不同于前一个;TOUCH——服务器设置了这个字段,但是字段数据没有变化;DELETE——服务器删除了这个字段,字段数据从有到无。更进一步,以 View 为单位的时序是严格保证的,也就是说,客户端可以重现服务器设置同一 View 上的字段的顺序;不同 View 之间的时序不作保证。(换成这个)

Control,定义在 View名字空间下,客户端给服务器发送的控制命令。View,Control 两个概念合到一起看,就是客户端给服务器发送控制命令,服务器设置

View 的状态,这个状态被反映到客户端。当然了,Control 与 View 状态改变的因果关系不是强制的,即便没有 Control,服务器也可以改变 View从而实现推送;Control尽管定义在View名字空间下,也不意味着这个 Control 的实现只能改变当前这个 View。A View is a named structure description, and the definition of the name should reflect the specific feature. A serial Fields are included in this data structure, where the client View is consistent with the server View. The modification in the field of the server View will be synchronized to the same field of the client View.

There are four types of modification for the field: NEW, REPLACE, TOUCH and DELETE. NEW: the server has set the field, and this field's value is changed from none to existed. REPLACE: the server has set the field, but this field's value is different from the previous one. TOUCH: the server has set the field, and this field's value is the same as the previous one. DELETE: the server removes the field, and this field's value is changed from existed to none. Further, the change order of the View's fields is strictly guaranteed so that the client could reproduce the fields' order in the same View which is modified by the server. The sequence between the different Views could not be ensured.

A Control is the control commands sent from the client to the server, which is in the View's namespace.

Combining the two concepts of the View and the Control together could be explained like that the client sends the control commands to the server, the server modifies the View's status and this status is reflected to the client. The causality between the Control and the View's status changing is not compulsive. Even without Control, the server still could change the View in order to achieve the push operation. It does not mean the implementation of this Control only could modify the current View although its definition is in the View's namespace.

The relative Concepts

SessionId,客户端登录服务器,相应的会话建立起来的时候系统给客户端分配的 ID,8

Page 9: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

作为客户端的唯一标识。A SessionId is the client's unique identification which is the ID assigned to the client by the system when the client login the server and the corresponding session is established.

三种 View 的类型,GlobalView,SessionView,TemporaryView。There are three types of View: GlobalView, SessionView, and TemporaryView.

GlobalView,具有服务器生命周期的 View。View 的改变不能自动同步到客户端,必须手工调用同步方法,把整个 View 或者 View 的某个字段的最新版本数据同步到一个或者多个客户端。A GlobalView is a View with the server's lifecycle. The change of the View could not be automatically synchronized to the client. Manually invoking the synchronization method to synchronize the latest data of the entire View or some field of the View to one or multiple clients is essential.

SessionView,具有会话生命周期的 View。客户端登录服务器建立会话时自动创建,依据 SessionId 分类。View 的字段发生变化时,字段数据自动同步到对应客户端。A SessionView is a View with the session's lifecycle. The SessionView will be automatically created according to the SessionId's classification when the client login the server and a session is established. When the View' field is changed, the value of this filed will be automatically synchronized to the corresponding client.

TemporaryView,具有临时生命周期的 View,服务器应用执行过程中手工创建,手工删除。临时 View 是 一 种 最灵活的 , 使 用 上 也 最 复 杂 的 View 。 通 过 Membership 维 护SessionId集合实现成员管理,提供广播特性,也就是说 View字段发生变化时,字段数据自动广播给所有 Membership 成员。临时 View 还拥有 更强大 的 能力—— 订阅成员的SessionView字段,某一成员的被订阅 SessionView字段发生改变,这一改变自动广播给所有 Membership 成员。A TemporaryView is a view with the temporary lifecycle, which is manually created and deleted during the execution of the server application. The TemporaryView is the most flexible and most complicated in the usage View. By using Membership, the TemporaryView maintains the set of the SessionId to manage the members, and provides the broadcast feature, through which the field's value will be automatically broadcasted to all the members of the Membership when View's field changes. The TemporaryView has a more powerful capability: subscribing the SessionView field of the members. When one member's subscribed SessionView field changes, this change will be automatically broadcast to all the members of the Membership.

ViewContext,View 上下文,服务器,客户端,拥有各自的 ViewContext,具有服务器9

Page 10: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

或者客户端生命周期,通过它可以访问 View,多数情况下被 Limax 生成代码使用。A ViewContext is the context of the View. The server and the client have their own ViewContext. The ViewContext has the server's lifecycle or the client's lifecycle, through which the View could be accessed. The ViewContext is usually used by the codes generated by the Limax.

ViewChangedListener,客户端的关键应用接口,接收服务器同步过来的 View字段改变事件,主要提供 View改变的 5 个信息,哪一个 View,哪一个字段,哪一个 SessionId,字段当前值,此次改变的类型,NEW,REPLACE,TOUCH,DELETE。(换成这个)A ViewChangedListener is a key application interface of the client, which is used to receive the change event of the View's fields synchronized from the server. The ViewChangedListener mainly provides the five piece of information related to the change of the View: which View, which field, which SessionId, the current value of this field, the modification type --NEW, REPLACE, TOUCH and DELETE.

Control,客户端发送控制命令给客户端,有两种类型,Control,Message

A Control is the control commands that the client sends to the server. There are two types of the Control: the Control, and the Message.

Control,定义在 View名字空间下,一个名字空间下可以定义多个。类型化客户端框架支持该类型 Control。The Control is defined in the namespace of the View. Several Controls could be defined in the same one namespace. The typed client framework supports this type of Control.

Message,系统实现中用 Message 描述,理解为发送给 View 的 Message,每个 View默认有一个。类型化客户端,脚本客户端都支持。The Message is the description used in the system implementation. The Message is the Control of the string parameter predefined by system to each View. The Message is supported by both the typed client and script client.

实现一个应用,需要同时支持类型化客户端和脚本化客户端,只能使用 Message。If an application implemented need to support the categorized client and script client at the same time, the Message control is the only choice.

10

Page 11: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

体系结构 Architecture

服务器框架 Server Architecture

基本概念 Basic Concepts

Environment,整个框架实现为一个运营体系Environment: the whole framework is implemented as an operation system.

Application,一个运营体系里可以运行多个应用Application: multiple applications could be run in one operation system.

Provider,一个应用可以由多个 Provider组成Provider: an application may consist of many providers.

PVID,一个 Provider由一个 PVID唯一决定,整个 Environment 可以分配 2^24 个 PVIDPVID: a provider is uniquely identified by a PVID, and till 2^24 PVID could be assigned to the entire Environment.

服务器组件 Components of the Server

Switcher,接受客户端连接,其它服务器组件的汇集点,根据 PVID 完成信息交换,一个运营体系里可以部署多个 Switcher,典型的情况下,一台服务器运行一个。A Switcher is the collection point of the server's other components, which is used to accept the connection from the client, and finish the information exchange according to PVID. Multiple Switchers could be deployed in one operation system. One Switcher running on one server is the typical case.

GlobalId,全局 Id 分配服务,一个应用可能需要运行在多台服务器上,可以使用GlobalId 服务提供全局 Id;同一应用隔离运行的情况下,为了未来的合并方便,也可以使用 GlobalId 服务提供全局 Id。根据具体需求在运营体系下部署。A GlobalId is the service to assign the global Id. The GlobalId is used to provide global Id when an application is run on several servers. The GlobalId is also used to provide the global Id when the same application need to be separately run in order to be conveniently merged in future. The GlobalId need to be deployed on the operation system according to the detailed requirement.

11

Page 12: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Auany,统一认证服务,可以提供运营体系自己的认证服务,也可以通过插件方式支持其它运营体 系 的第三方认证服 务 。认证成 功 后 ,给客 户 端提供当前运营体 系内的SessionId,标识该用户。一个运营体系下可以部署一个或者多个运营体系共用一个。Auany同时提供支付框架的支持,可以通过插件方式扩展第三方支付系统。The Auany provides the unified authentication service, which not only provides its own authentication service for the operation system, but also supports the third party's authentication service for other operation systems through plug-in. After successful authentication, the Auany provides the SessionId within the current operation system to identify the user. One Auany could be deployed for one operation system or more to share. The Auany also provides the support to the payment framework, and extends the third party's payment system by plug-in.

Provider ,提供具 体 应 用 的 服 务 , 应 用 开 发 的主体 , 开 发 应 用 就 是 开 发 应 用 的Provider,严格意义上讲,上文描述 View 时涉及到的服务器特指 Provider,没有特别说明,后文的服务器开发特指 Provider 开发。The Provider is the main subject of the application implementation and provides the service for the specific application. Actually, developing the application is implementing the Provider of this application. Strictly speaking, the server mentioned in previous contents to describe the View should be Provider in particular. And if there is no special statement, the server development in the following contents should be the development of the Provider.

服务器开发接口 Development Interface of the Server

服务器开发纯 java 实现,关键的两个包,provider,zdbThe Java language is used in the server development. The provider and zdb are the key packages.

limax.provider,提供了 View相关的基础类,主要由生成的框架代码引用,应用开发应该在生成的代码基础上完成;支持对 GlobalId 服务的访问。The limax.provider provides the fundamental classes related to the View, is mainly referenced by the generated framework codes, and the application development should be implemented based on the generated codes. It also supports the access to the GlobalId service.

limax.zdb,数据库支持,使用键值对的方式访问的事务化数据库,支持 ACI除了 D。底层通过配置,使用 limax 自带的 edb键值对数据库,或者 mysql。存储过程是事务化的,支持 level2,level3 两种事务隔离度,是 zdb 的关键,实现一个应用的大部分代码都可以在存储过程中实现,保证事务完整性。The limax.zdb provides the database support, and accesses the transactional database by using the key-value pairs mode, supporting ACI except D. Its bottom tier uses the EDB key-value pairs database embedded in the Limax or MySQL database via configuration. The transactional storage procedure is the key feature of the ZDB, supporting level2 and level3 two kinds of transaction isolation. The most code of an application could be implemented in the storage procedure to

12

Page 13: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

guarantee the integrity of the transaction.

Zdb对 View 的扩充,可以在 View 描述中使用 bind 特性,将一张表上一个特定 key对应的 value,或者 value 中的某些字段 bind 到 View 上,事务成功提交后,发现相应 value 或者value 中相应字段在数据库操作时发生改变,value 或者 value 中相应字段的数据作为字段数据自动设置到 View 上——如果是 SessionView 或者 TemporaryView 这些数据也就自动同步到客户端。View 与表的 bind 实现为多对多关系。特别要注意的是,同一 View 上的 bind字段的改变时序不作假设,这些改变具有事务原子性。The ZDB is the expansion of the View. When describing the View, the corresponding value of a specific key in one table or some fields of the value could be bund to the View by using the bind feature of the ZDB. After the transaction is successfully committed, if there is change in the corresponding value or the corresponding fields of the value during operating the database, the value or the data of the value's corresponding field will be automatically set to the View. If this View is the SessionView or TemporaryView, these datum will be automatically synchronized to the client. The bind relationship between the View and the table is multiple to multiple. Please pay special attention that there is no any assumption for the change sequence of bind fields of the same View, and these change have the transaction atomicity feature.

服务器管理 The Sever Management

框架提供的基本服务都可以通过 JMX控制,并且提供一系列运行状态数据。应用可以定义自己的数据监视集合,在运行阶段执行数据采集。

All the basic services provided by the framework could be controlled via JMX and provide a serial running status datum.

The application could define its own data monitoring set, which performs the data collection during the operating phase.

客户端框架 Client Architecture

基本概念 Basic Concepts

类型客户端,JAVA,C#,C++实现的客户端The Typed Client is the client implemented in Java, C# and C++ languages.

脚本客户端,通过嵌入 LUA,Javascript等脚本语言实现的客户端The Script Client is the client implemented in embedded script languages such as LUA and Javascript.

13

Page 14: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Endpoint,端点,类型化客户端框架总入口The Endpoint is the entire entrance of the typed client framework.

EndpointConfig,端点配置,提供登陆服务器所需要的用户名,密码,请求的认证平台请求的 PVID集合等信息。The EndpointConfig which is the configuration of the endpoint provides the required information as the username, password, requested authentication platform, and requested PVID set, etc when login server.

EndpointListener,基础网络事件接收接口,报告连接进度,链路测试结果,发生的网络错误等信息。The EndpointListener, which is the receiving interface of the basic network event, reports the information as the connection progress, test result of the link, occurred network error, etc.

EndpointManager,端点管理器, EndpointConfig 与 EndpointListener,共同决定一个EndpointManager。EndpointManager 代表了一次网络会话,一个客户端应用允许存在多个,通过 SessionId区分。EndpointManager将所有的 View汇集到以 PVID 分类的 ViewContext 中。The EndpointManager is decided by the EndpointConfig and EndpointListener together to manage the endpoint. An EndpointManager represents a network session and multiple EndpointManagers distinguished by SessionId could be existed in one client application. The EndpointManager collects all Views to the ViewContext classified with PVID.

Java 客户端开发接口 Development Interface of the Client in Java language

limax.endpoint,提供 Endpoint 基础支持The limax.endpoint provides the basic support from the Endpoint.

limax.endpoint.script,提供嵌入脚本语言的接口,鉴于 OracleJDK内嵌了 javascript 脚本引擎,示例性实现了 javascript 脚本支持The limax.endpoint.script provides the interface to the embedded script languages. The demo implements the javascript scripting support because the OracleJDK embeds the javascript script engine.

limax.endpoint.variant,动态类型化的 View 访问支持,这种模式下开发的客户端无需生成静态的 View 代码。The limax.endpoint.variant provides the access support for the dynamic typed View. It is no need to generate the static View source code for the client developed under this mode.

14

Page 15: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

C#客户端开发接口 Development Interface of the Client in C# language

与 Java版本基本一致,如果要求实现脚本支持,需要按照脚本语言接口规范,集成需要的脚本引擎。It is consistent with the Java version. If it is required to achieve the script support, the demanded script engine need to be integrated according to the interface specification of the script language.

C++客户端开发接口 Development Interface of the Client in C++ language

除了没有实现嵌入脚本语言的接口,与 Java版一致It is consistent with the Java version, except no interface implemented for the embedded script language.

提供了可直接嵌入 Lua 的版本The version which could directly embed the Lua language is provided.

脚本开发接口 Development Interface of the Script

limax.lua,Lua版本,完整的 View 支持,所有的 View 数据按照 View 定义的名字空间,完全映射到脚本名字空间,只需要按照规范定义各个 view 的 onXXX 方法接收 View改变消息,驱动客户端表现The limax.lua is the Lua version, and provides the full support for the View. All the datum of the view are totally reflected to the script's namespace according to the namespace defined by the View. The change message of the View could be received via each View's onXXX method defined following the standard to drive the client to present.

limax.js,Javascript版本,实现上与 limax.lua 完全一致The limax.js is the Javascript version and is consistent with the limax.lua in the implementation.

HTML5 开发接口 Development Interface of the HTML5

直接使用 limax.js,limax.js 已经内建了 WebSocket 访问The limax.js is directly used because the limax.js has internally implemented the WebSockect access.

15

Page 16: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

应用开发 Development of the Application

基本概念 Basic Concepts

Limax 项目的开发遵循如下步骤:通过 xml 文件描述应用模型,包括数据库表的描述,View 的描述,服务描述等等通过 xmlgen 生成需要的服务器,客户端框架代码,模板代码,运行配置模板等等编辑生成的代码加入应用逻辑

The development of the Limax project follows the following steps: Describe the application model by xml file, including the description to the table of the

database, the description to the View, the description to the service, etc. Generate the required server and client framework source codes, template source codes

and configuration template for execution, etc. via xmlgen. Compile the generated source code to the application logic.

模型创建 Model Creation

Xml 基本节点 The basic Element of XML

project

应用的根The project element is the root of the application.

namespace,state,service,zdb 节点均可以定义在 project节点下,命名了名字空间的根These elements such as namespace, state, service, and zdb could be defined in the project element, which names the root of the namespace.

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

16

Page 17: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

</project>

定义项目 limax, 启用 XInclude 支持The project "limax" is defined and the support of "XInclude" is enabled in the project

element.

state

定义在 project节点下,被manager节点引用The state element is defined in the project element and referenced by the manager element.

网络服务器可以运行在多个状态下,state节点定义了该状态下网络服务器允许接受的protocol,rpc——尽管强烈推荐使用 View,但是 protocol,rpc 也可以支持,毕竟 View 本身在系统内部也是通过 protocol 实现The network server could run in multiple status. The state element defines the protocol and rpc which are allowed to accept by the network server in the current status. Though the View is strongly recommended, the protocol and rpc are also supported, because the View itself is implemented in protocol within the system.

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

</state>

定义状态 XXX,引入了 a名字空间下的所有 protocol,rpc,b名字空间下的 a 协议,b名字空间下的 c 协议The state element is used to define the status "XXX", in which all the protocols and rpc in the namespace of "a", the protocol "a" in the namespace of the "b", and the protocol "c" in the namespace of the "b" are imported.名字空间允许嵌套,为了避免混淆,使用 state引用名字空间时,只允许引用最外层。

(增加)The namespace allows the nesting. When using state to refer to the namespace, only the outermost layer could be referenced to avoid the confusion.

namespace

定义在 project,namespace节点下,被 state节点引用The namespace element is defined in the project and namespace elements and referenced by the state element.

namespace, bean, protocol, rpc, view 均可以定义在 namespace 下The elements namespace, bean, protocol, rpc and view could be defined in the namespace element.

17

Page 18: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

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

定义了名字空间名 gs,设定该名字空间的 pvid 为 12。名字空间嵌套的情况下,最外层pvid 有效,内层 pvid被忽略。The namespace element defines the namespace "gs" and the pvid of this namespace is set as 12. If there are nested namespaces, the outmost pvid is valid and the inner pvid is ignored.

bean

定义在 namespace 下,作为结构化类型被用来定义变量The bean element is defined in the namespace elements, and used to define the variable as structured type.

enum,variable 可定义在 bean 下Both the enum and variable elements could be defined in the bean element.

<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,具体的类型映射后文描述。

In this example, the bean element "b2" refers to the bean element "b1". The detailed type reflection will be described in the next content.

protocol

定义在 namespace 下,被 state引用The protocol element could be defined in the element of namespace, and be referenced by the state element.

protocol 下可定义 enum,variable,与 bean 类似Both the enum and variable elements could be defined in the protocol element, which is similar with the bean element.

18

Page 19: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

<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。The "101" protocol "CHandShake" is defined, and the maximum size allowed by this protocol is 1030. The protocol number in one PVID which is determined by the outermost namespace is not repetitive. If the value of the maximum size is set as 0, that means there is no limitation. In the actual operating environment, there is a compulsive constraint to limit the size of all the protocols for the server. The limitation could be adjusted via the execution parameter of the virtual machine just like java -D limax.net.Engine.limitProtocolSize=xxx , and the default value is 1M.

rpc

定义在 namespace 下,被 state引用The rpc element is defined in the namespace element, and referred by the state element.

<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 在同一空间下编号,互相不可重复,最大尺寸单独限制参数和结果,应该取两者的最大值。The "305" rpc "CheckProviderKey" is defined , whose allowed maximum size is 1024, and the value of the timeout is set as 10000 millisecond. The parameters and results of the rpc should be the bean element. The rpc and protocol is numbered in the same namespace and could not be duplicated each other. The maximum size is used to limit the parameter and result individually and the bigger value of them is applied.

特别注意,rpc仅在 java环境下支持,提供给 limax 框架自身使用, java 之外的客户端版本均不提供支持,不建议使用。Please pay special attention to that the rpc is only supported in the Java environment and used

19

Page 20: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

by the Limax framework itself. The other client versions except the Java do not support it. We suggest that DO NOT USE it.

view

定义在 namespace 下,被 state引用。不包含 ref节点的 bind节点,不可引用包含 any 类型 xbean 的 table;包含 ref节点的

bind节点,ref指向的 xbean字段不能包含 any 类型。The view element is defined in the namespace element, and referred by the state element.

If the bind element has no ref element contained, it should not refer to the table with xbean field containing ANY type. If the bind element has ref element, the xbean field referred by the ref element should not contain ANY type.

<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组织起来的结构。The global view "globalview" is defined here and contains the fields whose names are "var4", "bindfirst", "bindsecond" and the control "control1". The field "var4" is the normal field and changed via the set field in the view object. The field "bindfirst" is the bind field, and when the s field or the set filed in the value structure is changed, the value of the "bindfirst" is set as the structure organized by the "value.s" and "value.sets". The field "bindsecond" is the bind field and is set as the value when the value of the "second" table is changed. The "control1" is the control and the parameters are the structure organized by the "var1" and "var2".

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

20

Page 21: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

<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>在这里,临时 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]。In this example, when the "var1" field of the temporary view "TestTempView" is changed, all the members of the "TestTempView" will receive this change. The subscribed fields "_var1" and "_bindsecond" of the "TestTempView" view separately subscribe the "var1" and "bindsecond" fields of the session View "firstview". In the members of the "TestTempView", if the "var1" or "bindsecond" field of one member's "firstview" is changed, this change will be broadcasted to all the members via "_var1" or "_bindsecond". Actually, the "_var1" and "_bindsecond" are implemented as map with the SessionId as the key. In particularly, when the member's "firstview.var1" is changed, this member will receive two changes, including "firstview.var1" and " TestTempView._var1[SessionId]".

service

定义在 project 下,描述了该项目支持的服务,一个项目下允许多个The service element is defined in the project element and describes the services supported by this project. Multiple services are allowed in one 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"

21

Page 22: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

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节点内。This is the definition for the "Switcher" server components of the Limax.

A server is composed by a serial network services and the manager element defines the network services. The "SwitcherServer" as the network server runs at the 10000 port and accepts the request from the client. The "ProviderServer" as the network server runs at the 10100 port and accepts the connection request from the Provider. The "AuanyClient" as the client of the Auany server component connects to the 10200 port of the Auany server.

One network service could run in one or multiple status, refer to the protocol, rpc and view allowed in this status via state. The initial status of the service is defined by the initstate attribute and the other statuses are defined in the manager element.

<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。The service "demogsd" is defined here. The value of the type attribute in the manager element is "provider" which means that the source codes required by the provider is generated. The implementation of the Limax application noted in the previous contents is implementing the provider, that is the mostly common usage. Actually, the provider is the client of the Switcher

22

Page 23: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

server component, so the port 10100 determines that the connection between the provider and the Switcher server is the 10100 port. At the same time, the provider is the server of the application client, and the Switcher is used to forward the data of the protocol, rpc and view between the application client and the provider.

The useGlobalId is used to determine whether to generate the configuration demanded by the GlobalId service components in the service configuration file.

The useZdb is used to determine whether to generate the launch configuration template using Zdb in the service configuration file.

In particular, the manager in the provider model supports only one status. There is no mean to define multiple status and this status refers to only one namespace, the PVID of which is used as the provider's PVID.

zdb

描述了应用使用的数据库的全部信息,一个应用只允许使用一个 zdb 数据库,project下最多定义一个。The zdb element describes all the information of the database used by the application. One application use only one zdb database and at most only one zdb element could be defined in the project element.

cbean

定义在 project,namespace,zdb 下。结构化类型作为表的 key,map 的 key,set 的 value 时,必须定义为 cbean,与 bean 的

定义类似,字段成员的类型可以是除 binary和 any 以外的基本类型以及 cbean 类型,不能使用容器类型。

特别的,cbean 可以被 bean,protocol,rpc,view,control引用。The cbean is described in the project, namespace and zdb.

When the structured type is the key of the table, the key of the map, and the value of the set, it must be defined as the cbean. The cbean element is similar with the definition of the bean element, whose field members' types could be the primitive types except the binary and any, and the cbean, the container type excluded.

In particular, the cbean element could be referred by the bean, protocol, rpc, view and control elements.

<cbean name="xcompare"><variable name="b" type="boolean" /><variable name="s" type="short" /><variable name="i" type="int" />

23

Page 24: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

<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,但是部分引用非 any 成员是允许的。(上一段换成这个)The xbean is described in the zdb.When the structured type is the value of the table, it must be defined as the xbean. Similar with the definition of the bean, the types of the xbean's field members are all basic types, container type , cbean or xbean if the nested structured type could be used. Since the cbean is the constant bean, the object with this nested type could not be modified. The xbean also supports a special type --- any, which is none-persistent. When the xbean using the any type is the value of the table, the table only could be the memory table.

In particular, the xbean without containing ANY type could be referred via the variable element by the bean, protocol, rpc, view and control. The bind element could not totally refer to the xbean table containing ANY type as the value, however, the partial reference to none ANY members is allowed.

<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>

24

Page 25: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

<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 结构。

A complicated xbean structure is defined here.

<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 类。An xbean element containing "any" type is defined, and the Object is "java.lang.Object" which could refer to any object. All legal java type, and common bean and view appeared in the description could be referenced in the behind of the "any:", because the bean and view themselves will generate the corresponding java class after generating the source code.

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

25

Page 26: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

</view>这个 view 的变量字段引用了 xbean,字段 testany 是 any 类型的,是错误的,代码生成

过程中会抛出异常指出 Info.testany存在 any 类型依赖。any依赖是递归检测的。The variable field of this view refers to the xbean. The "testany" field is an "any" type, which is false because the exception threw in the code generation process will indicate that the "Info.testany" has the dependency on the "any" type. The any dependency is the iterative detection.

<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 类型的字段,所以上面的描述是正确的。(新加的)In this example, the bind element binds the "anytable" whose attribute type is xbean Any. Even though this xbean contains ANY type, only boolean field is referenced. So the description here is correct.

table

定义在 zdb 下zdb 数据库中表的描述

The table element is used in the zdb database to describe the table.

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

roominfos 表,key 类型为 long,value 类型为前面定义的 xbean RoomInfo,long 类型的key 在配置了 autoIncrement="true"的情况下支持自动增量,persistence="MEMORY"决定了这张表是内存表,cacheCapacity="4096"决定了这张表在内存中最多 cache 4096 条记录,对于内存表而言,记录数一旦超过 cacheCapacity,某些记录将丢失。The table is described in the zdb.In the "roominfos" table, the type of key is long, and the type of the value is the xbean "RoomInfo" which is defined in the previous content. The key with long type supports the automatic incremental under the condition that the attribute autoIncrement="true" is configured. The attribute persistence="MEMORY" determines that this table is memory table. The attribute cacheCapacity="4096" determines that this table could cache at most 4096 piece of records in the memory. For the memory table, once the number of the record exceeds the value of the cacheCapacity attribute, some records will be discarded.

26

Page 27: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

<table name="keyisxcompare2" key="xcompare2" value="string" cacheCapacity="4096" />keyiscompare2 表,key 为前面定义的 cbean xcompare2,value 为字符串,没有指明

persistence,这是张磁盘表。In the "keyiscompare2" table, the key is the cbean type "xcompare2" defined in the previous content, and the value is the string. Without defining the persistence, it is a disk table.

<table name="tany" key="int" value="Any" cacheCapacity="4096"persistence="MEMORY" />tany 表,value 的类型是前面的定义的 xbean Any,xbean 包含了 any 类型字段,该表只

能是内存表。In the "tany" table, the type of the value is the xbean "Any" which is defined in the previous content. The xbean contains the "any" type field. The table is the memory table.

<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 可以简化锁。There is a new attribute lock. The lock of the zdb database is row lock, in which the basic lock mode is lock(table, key) and the table with same lock attribute shares the lock. In this example, when the key row of the table "testype" is locked, the key row of the table "secondaryindex" is also locked. On the contrary, it is also correct. There is no error when locking the key row of the tables "testtype" and "secondaryindex" at the same time. The proper planning could simplify the lock.

monitorset

定义在 project,namespace 下,为应用提供相关的运行数据监视能力。The monitorset is described in the project or namespace to provide the relative monitor ability to the running data for the application.

<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 的次数,执行抛出异常的次数。其中:This is the definition of the zdb transaction monitor set in the limax.xml. The key is the storage procedure name, which seperately defines three piece of count information for the storage

27

Page 28: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

procedure: the sum of the exectution, the sum of the false returned by the execution, and the sum of the exception threw by the execution.

supportTransaction指出了这一监视器集合需不需要事务特性,简单的说如果为 true,那么在事务环境下进行的计数,只有事务成功提交才真正执行。默认为支持。The supportTransaction points out that whether this monitor set needs the transaction feature or not. If the value is true, the count in the transaction, will be actually executed only if the transaction is successfully submitted.default is true.

monitor 的 type 分为 counter和 gauge 两种。counter 在生成代码中提供 increament 方法,gauge提供 set 方法。The type of the monitor has two: counter and gauge. The counter provides the increment function in the generated source code, and the gauge provides the set funtion.

一个 monitorset允许多个 key,作更细致的划分,key 的类型,只能使用除了 binary 与any 之外的简单类型。A monitorset allows multiple keys. Divided more finely, the type of the key only use the primitive type except for the binary and any.

Xml 描述规范 Specification of the xml description

Limax 使用 xml 描述项目模型的构建,定义了命名节点和引用节点两类 xml节点。存在同时是命名节点和引用节点的这种节点。xml内部名字的解析使用内部名字空间规范。The Limax describes the architecture of the project model via xml, and defines the named element and referenced element these two kinds of xml elements. There is this kind of element which is named element and referenced element at the same time. The resolution to the internal name of the xml follows the specification of the internal namespace.

命名节点 The named element

命名节点包含 name属性,生成代码按照特定规范连接这些 name,组织项目名字空间。zdb 中 table 的 name 必须全部小写,其它 name 不限。(换这句)The named element includes the name attribute. The generated code link the name according to the particular specification and organizes the namespace of the project. The names of the table in the zdb database must be all low case, and the other names have no limitation.

引用节点 The referenced element

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

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

28

Page 29: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

All the elements using ref attribute belong to the referenced element and the value of the ref attribute should be the reference of the named element. The subscribe element of the temporary View element not only belongs to the referenced element because it refers to the field of the session View, but also belongs to the named element which is named as the field of the current View.

The variable element in all elements must define the type attribute. When the type attribute defines the structure described in the internal xml, bean, cbean and xbean, this variable element could be called as the referenced element. The variable element as the field element of the upper level element is named, so it is also the named element.

内部名字空间规范 The specification of the internal namespace

同一 xml节点下的命名节点不可重名。父节点名 “.” 子节点名,构成节点路径,节点路径允许逐层向外扩展,直到根节点,

根节点开始的节点路径为全路径。节点名,节点路径,都可以合法地引用节点自身。代码生成过程中,报告二义性节点引用时,应该使用更长的节点路径解决二义性。例

如,名字空间 A 下定义 bean X,名字空间 B 下定义 bean X。某一 variable 定义字段时指定了type 为 X,代码生成过程报告二义性错误,这种情况下,必须正确选择 type 为 A.X 或者B.X,消除二义性。The named element in the same xml element should not have the same name.

The element path is constituted by the parent's element name plus "." plus children's element name. The element path could expand out layer by layer till the root element, and the element path beginning from the root element is the full path. Both the element name and element path could legally refer to the element itself.

In the processing of the generating source code, when the ambiguous element reference issue is reported, the longer element path should be used to resolve the ambiguity issue. For example, the bean "X" is defined both in the namespace A and in the namespace B. When some variable element defines the field, the "X" is appointed as the type. So there is ambiguous error reported when generating the source code. In this case, "A.X" or "B.X" should be correctly chosen to eliminate the ambiguity.

特殊情况讨论 The special conditions

zdb节点也是命名节点,它的名字不会应用到生成代码中,所以允许不进行命名,这种情况下默认名为空串。为了名字空间解析方便,需要的情况下也可以命名。The zdb element is also the named element. Because its name would not be applied in the generated source code, no name is allowed and the default name is empty string in this case. For the convenient of resolving the namespace, the name is acceptable in the required condition.

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

29

Page 30: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

As the special const type bean, the cbean is the global and does not be restricted by the namespace. In the whole project, the name of the cbean described in anywhere could not be duplicated.

table 的 name 要求必须小写基于此原因:使用文件数据库时,该 name被用来直接命名表文件。如果操作系统使用了大小写不敏感的文件系统,比如 windows,应用运行在这类操作系统上时,为了规避可能产生的冲突,全部小写是最好的解决方案。The name of the table must be low case is because that the name will be directly used to name the table file when using the file database. If the operating system uses a case-insensitive file system such as windows, the applications running on this kind of operating system should totally use low case as the best solution to avoid the conflict that may arise.

bind节点下的 ref节点,看起来是命名节点,但是应该解释成,与 bind节点的 table属性结合到一起,引用了 xbean 的字段。这样设计的原因:描述简单清晰,否则就应该在bind节点中加入 ref属性,xbean 的字段名逗号分隔。The ref element in the bind element, which looks like the named element, should be interpreted that it combines with the table attribute of the bind element and refers to the fields of the xbean. The reason why it is designed like this is that the description is simple and clear, or the ref attribute should be added into the bind element, and the names of the xbean's fields are separated by the comma.

类型映射 Type Mapping

正确处理类型是数据库,服务器,客户端,正常工作的关键Correctly processing the type is the key that the database, the server, the client work properly.

简单类型 Primitive Type

Xml 描述Description

Java C# C++ Javascript Lua

boolean boolean bool bool Boolean Booleanbyte byte byte int8_t Number Numbershort short short int16_t Number Numberint int int int32_t Number Numberlong long long int64_t Number Numberfloat float float float Number Numberdouble double double double Number Numberstring string string std::string String Stringbinary Octets Octets Octets Array(Number) Table(Number)any:[type] type 不支持 不支持 不支持 不支持

30

Page 31: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Not support Not support Not support Not support

除了 c#的 byte 为无符号类型之外,其它的所有数值类型均为有符号类型,可能导致处理上的差异,需要特别小心。Except that the byte of the C# is the unsigned type, all the other numeric types are signed type, which might cause the difference in the processing. Be careful.

使用脚本客户端的情况下需要小心使用 long 类型,脚本客户端的 Number 在内部表示为 double,long 的精度达不到 64位,根据 IEEE754标准,javascript 能提供 52位有效位,不知道为什么 Lua5.1 更少,基于这样的现实考虑,在脚本数据编码时作了一个特殊处理,绝对值小于等于 0x3FFFFFFFFFFFL 的 long按照数值编码,否则编码为串,解码之后至少能确保表达的一致性。The long type should be carefully used in the script client. The Number of the script client is internally represented as the double. The accuracy of the long is less than 64 bits. According to the starndard of the IEEE754, the javascript could provide 52 effective bits, however the Lua5.1 provides less bits without the reasonable reason. Based on this practical consideration, a special processing is made during the script data encoding, that the long type that absolute value is less than or equals to 0x3FFFFFFFFFFFL is encoded according to the numerical value, the other is encoded as the string, to guarantee the consistency of the expression after decoding.

服务器的字符串均以 UTF8格式编码,客户端在必要情况下需要自己转换。没有编码解码库的情况下,可以考虑使用 binary替换,自己解释。The string of the server encodes with UTF8 schema and the client need to convert by itself if necessary. Without the encode and decode library, the binary could be used as the replacement and interpreted by itself.

any 类型,不能直接或间接地被任何 bean,protocol,rpc,view引用。The any type could not directly or indirectly referred by any bean, protocol, rpc, and view.

容器类型 Container Type

Xml 描 述 Description

Java C# C++ Javascript Lua

List LinkedList LinkedList std::list Array TableVector ArrayList List std::vector Array TableSet HashSet HashSet std:unordered_set Array TableMap HashMap Dictionary std::unordered_map Map Table

类型化语言中所有容器均使用泛型类型。All the containers in the typed languages use the generic type.

Javascript 规范本身不包含Map,各种浏览器上的 javascript环境都有各自的实现版本;OracleJDK 自带的 javascript引擎实现不包含Map,使用 limax内置的简单实现——map.js。

31

Page 32: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

The specification itself of the Javascript does not contain the Map, and the Javascript environment in the all browsers has its own implemented version. The implementation of the Javascript engine coming from the OracleJDK itself has no Map, and the simple implementation -- map.js built in the Limax is used.

构造类型 Constructor Type

Xml 描 述 Description

Java C# C++ Javascript Lua

Bean Class Class class Map TableCbean Class Class class Map TableXbean Class Class class Map Table

类型化语言中各种 bean 都定义成类,另外 protocol,rpc,view,control,引用部分xbean字段的 bind,均生成类代码。All the bean in the typed languages are defined as the class. The protocol, rpc, view, control, and the bind which refers to partial fields of the bean generate the class code.

允许的引用关系的粗略描述(直接或者通过容器间接引用):The simple description of the allowed reference relationship (directly or indirectly refer via container):

bean := bean* | cbean* | xbean*,bean 不允许最终引用到 Any 类型上。The bean is not allowed to be finally referred to the Any type.

cbean := cbean*xbean := cbean* | xbean*

脚本语言中,没有类的概念,运行过程中,对象数据直接生成到以字段名为 key 的关联容器中。In the script languages, there is no the concept of the class. During the execution, the object data is directly generated to the associated container with the field name as the key.

服务器开发 Server development

代码生成 Code Generated ——xmlgen

操作 Operations

limax.jar 包集成了代码生成工具。The Limax.jar package integrates the tools to generate source codes.

java –jar limax.jar xmlgen –java server.xml

32

Page 33: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

按照 server.xml 描述的模型在当前路径下生成代码,详细使用帮助可以执行 java –jar limax.jar xmlgen获得。Generating the code in current path according to the model described by server.xml. Executing the command java –jar limax.jar xmlgen to get the detailed help information.

一般来说,构建一个项目的 xml 描述使用类似如下的组织方式:Generally speaking, the description of the xml to build a project using the following organization:

example.share.xml

<?xml version="1.0" encoding="utf-8"?><namespace name="share" pvid="100"><!—

服务器客户端需要支持的 bean,protocol,rpc,view 描述rpc 不适合应用项目使用,下面以 bean,protocol,view作为例子

The description of the bean, protocol, rpc and view supported by the server and the client.The rpc is not suitable for the application project. The below examples bases on the bean,

protocol and view.-->

<bean name="MyBean"><enum name="e0" value="0"/><enum name="e1" value="1"/><variable name="var0" type="int"/>

</bean><protocol name="MyProtocol" type="101" maxsize="4">

<variable name="var0" type="MyBean"/></protocol><view name="MyGlobalView" lifecycle="global">

<variable name="var0" type="MyCbean"/></view><view name="MySessionView" lifecycle="session">

<variable name="var0" type="example..MyXbean"/><bind name="bind0" table="mytable"/><bind name="bind1" table="mytable">

<ref name="var0"/></bind><control name="control">

<variable name="var0" type="int"/></control>

</view><view name="MyTemporaryView" lifecycle="temporary">

<variable name="var0" type="int"/><subscribe name="_var0" ref="MySessionView.var0" />

33

Page 34: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

</view></namespace>

example.zdb.xml

<?xml version="1.0" encoding="utf-8"?><zdb><!—

zdb 使用的 cbean,xbean,table 描述The description of the cbean, xbean and table used by zdb.-->

<cbean name="MyCbean"><variable name="var0" type="int"/>

</cbean><xbean name="MyXbean">

<variable name="var0" type="int"/><variable name="slist" type="vector" value="string" />

</xbean><table name="mytable" key="long" value="MyXbean" autoIncrement="true"/>

</zdb>

example.server.xml

<?xml version="1.0" encoding="utf-8"?><project name=”example” xmlns:xi="http://www.w3.org/2001/XInclude">

<xi:include href="example.share.xml" /><xi:include href="example.zdb.xml" /><state name="Server">

<namespace ref="share" /></state><service name="ExampleServer" useGlobalId="true" useZdb="true">

<manager name="ExampleServer" type="provider" initstate="Server" port="10100"/>

</service></project>

example.client.xml

<?xml version="1.0" encoding="utf-8"?><project name="example" xmlns:xi="http://www.w3.org/2001/XInclude">

<xi:include href="example.share.xml" /><xi:include href="example.zdb.xml" />

34

Page 35: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

<state name="Client"><namespace ref="share" />

</state><service name="ExampleClient">

<manager name="ExampleClient" type="client" initstate="Client"port="10000"/>

</service></project>

通过 xml 的 XInclude组织描述文件,划分出服务器,客户端的共享部分和 zdb 部分。项目比较复杂的情况下,应该考虑从功能模块的角度使用 XInclude作更合理的细分。Use XInclude of the xml to organize the description files, and divide the parts shared by the server and the client, and the part belonging to the zdb. In the case of the complicated project, using the XInclude from the functional modules for more reasonable subdivision is considerable.

必要的时候,服务器 xml 与客户端 xml允许合并成一个,xmlgen加入-service参数可以指定只生成某一个 service 的代码。不推荐这样使用。The server's xml and the client's xml could be merged to the one if necessary. The -service parameter is added in the xmlgen command to generate the source code for some appointed service. Such use is NOT recommended.

脚本环境的客户端不需要依据客户端 xml 生成代码,应该通过指定 xmlgen参数在生成服务器的同时生成对应脚本语言的代码模板。The scripting environment client does not need the client's xml to generate code. The xmlgen parameter should be appointed to generate the source code template corresponding to the script language when generating the server.

按照 xml 描述生成的服务器源代码,存放在当前目录下的两个子目录内,src 目录与gen 目录。src 目录为源码目录,下面的文件按照应用需求修改编辑,这个目录应该提交到版本控制系统内;gen 目录不需要作版本控制,每次执行 xmlgen 都会重建,任何修改都会丢失。另外,当前目录下生成了 service-XXX.xml 文件,XXX 为 xml 描述中的 service name,运行服务器需要的配置生成在这里,部署到具体运行环境的时候可以按照运营需求适当调整。一个描述文件下有多个 service 的情况下,service-XXX.xml 生成多个。The server's source code generated according to the xml's description is stored in the src directory and gen directory these two subdirectories within the current directory. The src directory is the source code directory. The files in this directory are updated and edited according to the requirement and this directory should be submitted to the version control system. It is not necessary for the gen directory for version controlling because it will be rebuilt when executing xmlgen and any modification will be lost. In addition, the service-XXX.xml file is generated in current directory and XXX is the service name described in the xml. The configuration required by running server is generated in this file and could be adjusted according to the operation requirement when deploying the detailed operating environment. If there are multiple services in one description file, the will be multiple generated service-XXX.xml files.

35

Page 36: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

新建 eclipse Java 项目,Location指定当前目录。Package Explorer 中,右键编辑项目属性Java Builder Path – Source 下,加入 src 与 gen 目录Java Builder Path – Projects 下,加入对 limax 的引用,或者 Java Builder Path – Libraries 下,加入 limax.jar。这时就能清晰看见项目组织结构。

New a eclipse Java project and appoint the current directory in the Location.In the Package Explorer, right click the mouse to edit the project property.In the Java Builder Path – Source, add the src directory and gen directory.In the Java Builder Path – Projects, add the reference to the Limax.Or in the Java Builder Path – Libraries, add the limax.jar.

Then, the project organization could be clearly seen.

名字空间映射 Namespace Mapping

把 xml 描述中的名字空间映射到 java名字空间下。Mapping the namespace described in the xml to the Java namespace.

protocol, rpc, view命名规则: 项目名,service名, (protocol, rpc, view)所在的名字空间名, (protocol, rpc, view)名。例如,上面定义的 MyGlobalView被命名为 example.ExampleServer.share.MyGlobalView特别的,gen 目录下能看到 example.ExampleServer.states 这样一个名字空间,这个名字

空间下放置了 service 通过 manager节点引用的所有 state节点相关的生成代码。为了避免混乱,项目的最外层名字空间不允许命名为 states,否则生成阶段报错。The name specification of protocol,rpc,view:The project name, the service name, the namespace name of the (protocol, rpc, and view), the name of the (protocol, rpc, and view).For example, the MyGlobalView defined in the previous content is named as example.ExampleServer.share.MyGlobalView.In particularly, there is the namespace example.ExampleServer.states in the gen directory, and all the generated source codes corresponding to the state element referred by the service via manager element in this namespace. The outermost namespace of the project could not be named as states to avoid the confusion, or there is error reported during generating.

bean,monitorset命名规则:项目名,(bean,monitorset)所在的名字空间名,(bean,monitorset)名。例如,上面定义的 MyBean,被命名为 example.share.MyBean。尽管 bean 与 protocol, rpc, view 一同定义在 xml 的 namespace节点下,还是为之采用不

36

Page 37: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

同命名规则的原因在于 bean 可以被多个服务中的 protocol,rpc,view引用,这种方案可以减少重复代码的生成。对于 monitorset而言,这种命名方式容易映射到期望的 jmx domain。

The name specification of the bean,monitorset:The project name, the namespace name of the (bean,monitorset), the name of the (bean,monitorset).For example, the MyBean defined in the previous content is named as example.share.MyBean.Although the bean and the protocol, rpc and view together are defined in the namespace element of the xml, the reason that using the different name specification is that the bean could be referenced by the protocol, rpc and view in multiple services. It could help to reduce generating the duplicated source code.To the monitorset, this name mode could be easily reflected to the expected jmx domain.

cbean,xbean,table命名规则:cbean放置在 cbean名字空间下。xbean放置在 xbean名字空间下。table放置在 table名字空间下,table 的名字被修订为首字母大写。

The name specification of the cbean,xbean,table:The cbean is placed in the namespace of the cbean.The xbean is placed in the namespace of the xbean.The table in placed in the namespace of the table, and the table's name is revised as the initial upper case.

在 使 用 cbean , xbean 与 table 时 , 不 要 import 这 些名字空间 , 应 该 直 接 使 用xbean.MyXbean,table.Mytable,这样就能清晰看出代码使用了 zdb 的某个元件,便于维护。When using the cbean, xbean and table, do not import the corresponding namespace, but directly use xbean.MyXbean and table.Mytable. It is clear to figure out which component of the zdb is used by the code in order to conveniently maintain.

View

代码生成以后,只需要编辑 src 目录下的几个文件,MyProtocol 不作讨论。After generating the source code, it is only necessary to edit some files in the src directory. The MyProtocol is not be discussed here.

观察MyGlobalView,MySessionView,MyTemporaryView三个段代码。Observing the source codes of the MyGlobalView,MySessionView,MyTemporaryView.

三 段 代 码 分 别 继 承 自 gen 目 录 下 的 _MyGlobalView, _MySessionView, _MyTemporaryView,父类里的公有方法,即包含了操作 view所需要的代码。These three section source codes separately inherit from the _MyGlobalView, _MySessionView, _MyTemporaryView in the gen directory, and the public functions in the parent's classes contain the required code to operate the view.

37

Page 38: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

View对象的获得与维护 Obtain and maintain the View object

获取全局 View Obtain the global ViewMyGlobalView gview = MyGlobalView.getInstance();全局 View 全局一个,获取不需要参数。There is only one global View and no parameter required to obtain.

手工同步全局 View Manually synchronize the global View

gview.syncToClient(sessionid)同步全局 view 到客户端 Synchronize the global View to the clientgview.syncToClient(sessionid, “var0”);同步全局 View 的 var0字段到客户端Synchronize the var0 field of the global View to the clientgview.syncToClient(Arrays.asList(sessionid1, sessionid2));广播全局 view 到指定的客户端集。Broadcast the global View to the appointed client setgview.syncToClient(Arrays.asList(sessionid1, sessionid2), “var0”);广播全局 view 得 var0字段到指定的客户端集Broadcast the var0 field of the global View to the appointed client set

获取会话 View Obtain the session ViewMySessionView sview = MySessionView.getInstance(sessionid);这里需要 sessionid参数,因为会话 View 在会话建立时以 sessionid 为 key 分类自动创建。

The parameter sessionid is required because the session View classified with the sessionid as the key will be automatically created when the session is created.

创建临时 View Create the temporary ViewMyTemporaryView tview = MyTemporaryView.createInstance();

销毁临时 View Destroy the temporary Viewtview.destroyInstance();

获取临时 View Obtain the temporary ViewMyTemporaryView tview = MyTemporaryView.getInstance(sessionid, instanceid);每个临时 View 维护了一个 Membership,该 Membership 中的 sessionid,才能用作这里

的 sessionid参数,临时 View 创建以后 tview.getInstanceIndex(),可以获取第二个参数所需的 instanceid。Each temporary View maintains a Membership, and the sessionid in the Membership could be used as the parameter sessionid. After creating the temporary view, calling the function tview.getInstanceIndex() to obtain the instanceid required by the second parameter.

加入临时 View Add the temporary View

38

Page 39: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

tview.getMembership().add(sessionid);执行成功以后临时 View 代码中的 onAttached 方法被调用,见MyTemporaryView 的生

成代码,这里应该填写加入成功后应该执行的动作。After successful execution, the onAttached method in the source of the temporary View will be called. Please refer the corresponding generated code of the MyTemporaryView. The action which will be executed behind the success should be added here.执行失败以后临时 View 代码中的 onAttachAbort 方法被调用,见 MyTemporaryView 的

生成代码,AbortReason指出了失败原因,比如加入 sessionid 过程中,sessionid对应用户离线了。After the unsuccessful execution, the onAttachAbort method of the temporary View will be called. Please refer the corresponding generated code of the MyTemporaryView. The method AbortReason points out the failure reason, such as that the corresponding user of the sessionid is offline in the processing of adding sessionid.

离开临时 View Remove the temporary Viewtview.getMembership().remove(sessionid, reason); 执行成功以后临时 View 代码中的 onDetached 方法被调用,见 MyTemporaryView 的生

成代码,reason将传递给 onDetached; reason 必须为非负值,负值内部使用,指出系统原因,比如整个 view 关闭,导致了用户 detach。After successful execution, the onDetached method of the temporary View will be called. Please refer the corresponding generated code of the MyTemporaryView, where the parameter reason will be forwarded to the onDetached method. The reason should be none-negative because the negative value is used internally to define the system reasons, for example, closing the whole view causes the user detach.执行失败以后临时 View 代码中的 onDetachAbort 方法被调用。

After unsuccessful execution, the onDetachAbort method of the temporary View will be called.这里要明确,onDetachAbort 是对Membership.remove 动作失败的响应,销毁临时 View

导致的离开 , 系 统 以负值调用 onDetached 。 如 果销毁临时 View 的 动作出 现 在Membership.remove 之前,会出现这样的状况,首先 onDetached 以负值 reason被调用,然后 onDetachAbort 以 VIEWCLOSED 为 reason被调用,如果需要精确处理离开的行为,这种情况应该要考虑到。There must be clear that the onDetachAbort method is the feedback to the operation failure of the Membership.remove. Destroying the temporary view causes the leave, and the system call the onDetached method with the negative value. If the operation of destroying the temporary view occurs before calling Membership.remove, there will be this situation, first the onDetached method is called with the negative value of the reason, then the onDetachAbort method is called with the VIEWCLOSED as reason. If the behavior of the leave operation need to be accurately processed, this situation should be considered.

View对象上的操作 Operation to the View object

View对象上 3 种操作,分别与 xml 描述中 view节点下三个子节点 variable, bind, control一一对应。

39

Page 40: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

There are three operations to the view object, which respectively correspond to the three sub-element variable, bind, and control of the view element described in the xml.

variable 上的操作 Operation to the variable<variable name="var0" type="int"/>生成 setVar0(Integer),_setVar0(Integer)提供两个版本 set 方法与事务支持相关。事务环境下,使用 set版本,事务成功后 set

动作被真正执行,事务失败 set 动作不执行,_set版本 set 动作立即执行与事务成功失败无关;非事务环境下,两个版本行为一致。以 null 为参数执行 set,意味着删除该字段。Generating the setVar0(Integer),_setVar0(Integer) methods.Providing two versions of the set method is related to the supporting to the transaction. Under the transaction environment, if using set version, the set operation will be really executed after transaction success, and the set operation will not be executed if transaction failure; however, in _set version, the set operation will be immediately executed and has no relationship with the success or failure of the transaction. Under the none-transaction environment, the behavior of these two versions is consistent. Performing the set with the null as the parameter means deleting this field.

bind 上的操作 Operation to the bind<table name="mytable" key="long" value="MyXbean" /><bind name="bind0" table="mytable"/>生成 bindMytable(long) 方法,这里的参数类型 long即是 mytable 的 key 的类型 long。例如,view.bindMytable(100);执行以后,一旦mytable 表的 key=100 这一行的数据,也

就是 MyXbean 类型的 xbean 在事务成功以后检测到改变,改变的数据自动设置到 view 的bind0字段上。Generating the bindMytable(long) method, and the parameter type long is the long as the type of key in the mytable.For example, after the method view.bindMytable(100) is executed, if the data (which is the xbean of the type MyXbean) of the row key=100 in the table mytable is changed at the point of the transaction success, the changed data will be automatically set to the view's bind0 field.

control 上的操作 Operation to the control<control name="control">

<variable name="var0" type="int"/></control>生成如下方法 Generating the following methods:protected static final class control implements limax.codec.Marshal, Comparable<control> {

public int var0;……

}protected void onControl(_MySessionView.control param, long sessionid) {}

40

Page 41: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

需要在 onControl内填写 control 的响应代码。The response code to the control should be added in the onControl.特别的,每个 View 的生成代码中,均有In particularly, in the generated source codes of each view, there isprotected void onMessage(String message, long sessionid) {}填写消息处理代码,脚本客户端的情况下,脚本系统难以有效构造类型化数据,只能

使用默认的 onMessage 传递字符串消息,应用应该定义自己的串数据格式规范,服务器端按规范解析message字符串。When writing the message handling code, it is difficult to effectively construct the typed data for script client, so the default onMessage method will be used to forward the string message. The application should define its own schema specification for the string data and the server decodes the string message according to the specification.

Zdb

存储过程 The Storage Procedure

zdb 通过存储过程实现数据库的事务支持,需要使用的包是 limax.zdb.Procedure,过程返回 true 事务提交,返回 false 事务回滚。有 3 种基本执行方式,execute,submit,call。The zdb provides the transaction support to the database through the storage procedure by using the limax.zdb.Procedure package. When the storage procedure returns the value TRUE, the transaction should be committed. If the value returned from the storage procedure is FALSE, the transaction should be rollback. There are three kinds of executing operation: execute, submit and call.

execute:异步执行存储过程 asynchronously execute the storage procedureProcedure.execute(()->{

过程代码,返回 true 或者 falsethe content of the storage procedurereturn true or false

});上面的方法不关心返回结果,是最简单常用的方法。The above method does not care about the returned result, and is the easiest and

commonly used method.

Procedure.execute(()->{过程代码,返回 true 或者 falsethe content of the storage procedurereturn true or false

}, (procedure, result)->{if (!result.isSuccess()) {

System.err.println("Procedure " + procedure + " fail");

41

Page 42: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

if(result.getException() != null)result.getException().printStackTrace(System.err);

elseSystem.err.println(“procedure return false;”);

}});上面的方法通过第二个 lambda 表达式参数获得过程的返回值,返回值提供了成功失败

的信息,如果失败可以检测是否由异常导致。(这样说吧)The returned value of the storage procedure could be obtained through the parameter of the second lambda expression, and the returned value provides the information of success or failure. If failed, it could detect whether it is caused by the exception.

submit:异步执行存储过程,返回 Future<Procedure.Result>submit: asynchronously execute the storage procedure and return the

Future<Procedure.Result>Future<Procedure.Result>future = Procedure.submit(()->{

过程代码,返回 true 或者 falsethe content of the storage procedurereturn true or false

});可以通过 future.get(),等待过程结束,通过 get 可以获得执行结果。

The execution result could be obtained via get function through calling future.get() and waiting for the storage procedure to finish.

call: 同步执行存储过程,返回 Procedure.Resultcall: synchronously execute the storage procedure and return the Procedure.Result.Result result = Procedure.call(()->{

过程代码,返回 true 或者 falsethe content of the storage procedurereturn true of false

});

这里需要特别注意,submit 不允许在存储过程内调用,否则抛出 IllegalStateException异常。存储过程嵌套调用,必须使用 call,存储过程的嵌套导致事务嵌套,内层事务失败回滚不影响外层事务;存储过程内部使用 execute将启动新的过程,不能实现嵌套。It is particularly noted that the submit MUST NOT be called in the storage procedure, or the IllegalStateException exception will be thrown. If the storage procedure is nested called, the call function MUST be called. The nestification of the storage procedure causes the transaction nested, the failure of the inner transaction which causes the rollback would not affect the outer transaction. Calling execute in the internal storage procedure will start a new procedure, which could not achieve the nestification.

42

Page 43: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

异常规范 Specification for the exception

Zdb 框架划分两类异常:用户异常,派生自 java.lang.Exception框架异常 XError,派生自 java.lang.Error,由框架和生成代码使用。

The ZDB framework has two kinds of exception:The User Exception, derives from java.lang.Exception.The Framework Exception XError, derivies from java.lang.Error, and is used by the framework and the generated source code.

用户在实现存储过程时允许抛出或者捕获用户异常。禁止捕获 Error,Throwable。The user could throw or catch the user exception when implementing the storage procedure. It is forbidden to catch the Error and Throwable.

存储过程执行时,抛出用户异常,导致当前事务回滚,效果如同在过程中 return false,并且将异常设置到过程返回结果中。The storage procedure throws the user exception when executing, which causes the current transaction to rollback, and the effect is similar to return false in the procedure. The storage procedure sets the exception to the return result.

存储过程执行时,抛出 XError,导致事务回滚到最外层,直接设置最外层过程的返回结果。The storage procedure throws the XError when executing, which causes the current transaction to rollback to the outermost and directly set the outermost procedure's return result.

submit 方式下,如果存储过程执行时抛出异常至最外层,应该捕获 java.util.concurrent. ExecutionException e,e.getCause(),获取该异常。In the submit mode, if the storage procedure throws the exception to the outermost when executing, the exception java.util.concurrent.ExecutionException e should be catched by using e.getCuase() to obtain this exception.

正确设置过程的日志级别,可以将框架捕获到的异常记录下来。Correctly set th procedure's log level, could record the exception catched by the framework.

标准表操作 Standard operation on the table

table 的 value 有两种类型,一是 xbean 类型,二是常量类型(cbean 以及除了 binary 之外的简单类型),这两种类的操作稍有区别,分别介绍。There are two types for the value of the table. One is the xbean type, the other is the const type (cbean and the other primitive types except for the binary). There is a little difference between these two types and will be introducted separately in the following.

value 为 xbean 类型的表操作:43

Page 44: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

The operation on the table with xbean type as the value:

xbean = table.Tablename.insert(key);根据 key插入一条记录,记录存在插入失败,返回 null,否则返回 xbean,这之后可以

填写 xbean内容,提交以后内容被加入数据库。This statement is inserting one record according to the key. If the record has existed, the insert operation fails and returns null as value, or returns xbean. Then fill the content for the xbean, and the content will be added into the database after submitting.

key = table.Tablename.newKey();允许自增量的表,可以用这个操作获取新的 key,再作插入操作。

The autoincremental table could use this method to get the new key, then call the insert operation.

pair = table.Tablename.insert();允许 自增量 的 表 支 持 该操作, 功 能相当于 table.insert(newKey()); ,返回的 pair 为

limax.util.Pair 类型,pair.getKey(),获取 key,pair.getValue()获取 xbean,接下来填写 xbean内容。除非自增量配置在使用以后,又进行了错误的重配置,返回的 xbean 不可能为null。The autoincremental table supports this operation, and the function equals the table.Tablename.insert(table.Tablename.newKey()). The returned pair is the limax.util.Pair type, which uses the pair.getKey() to get key and the pari.getValue() to get xbean, then fills the content for the xbean. Unless the autoincremental configuration is wrongly reset after it has been used, the returned xbean has no possible be the null.

xbean = table.Tablename.update(key);获取与 key 关联的 xbean记录,获得的 xbean 进行修改以后,事务提交后更新到数据库。

This statement is used to get the xbean record related to the key. After the obtained xbean has been modified and the transaction has been submitted, the modification will be updated to the database.

xbean = table.Tablename.select(key);获取与 key 关联的 xbean记录,获得的 xbean只能读,一旦修改将抛出异常导致事务失

败。This statement is obtaining the xbean record related to the key. The obtained xbean is READ ONLY, so the exception will be thrown and the transaction fails once the xbean is modified.

result = table.Tablename.delete(key);删除与 key 关联的记录,返回类型为 boolean,成功 true,失败 false。

This statement is deleting the records related to the key and the return type is boolean. If success, return true, or return false.

value 为常量类型的表操作:(以 cbean 为例)44

Page 45: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

The operation on the table with the const type as the value (a case in cbean):

cbean = table.Tablename.insert(key, cbean);除非记录存在,返回原来的 cbean,否则 null。

Unless the record has existed, the previous cbean will be returned, or the null will be returned.

pair = table.Tablename.insert(cbean);除了多个 cbean参数与 value 为 xbean 的自增量表类似。

In addition to the multiple cbean parameters, it is similar to the autoincremental table with the xbean as the value.

update操作返回常量本身,但是无法修改。与 select 的差别在于 update获取了写锁,而 select只能获取读锁。The update operation returns the const itself, but could not be modified. The difference between the select and update is that update obtains the write lock, however the select only obtains the select lock.

select,remove操作与 value 为 xbean 的情况相同。The operation select and remove of this table is similar to the table with the xbean as the value.

非标准表操作:None-standard operation on the table

table.Tablename.get().getCache().walk((key, value) -> {//readonly action

});遍历表 cache,遍历过程逐个读锁定记录,提供的记录只读。既可用于磁盘表也可用于

内存表。This statement is traversing the cache of the table. The record will be READ locked one by one during the traversing, and the provided record is READ ONLY. This method could used not only on the disk table, but also on the memory table.

table.Tablename.get().walk((key, value) -> {//readonly action

});遍历低层数据库内容,内存数据库中未 checkpoint 的记录不会访问到,zdb周期性将提

交数据 checkpoint 到低层。虚拟机参数 limax.zdb.Checkpoint.SCHED_PERIOD控制 checkpoint检测最小周期,默认 100ms,具体的 Checkpoint周期由运行时的 xml参数配置。因为看到的低层数据库内容,所以与 zdb锁无关系。这种 walk只能用于磁盘表。

This statement is used to traverse the contents of the underlying database. The uncheckpointed records of the memory database would not be accessed. The zdb periodically checkpoints the submitted data to the underlying database. The paramter limax.zdb.Checkpoint.SCHED_PERIOD of the virtual machine controls the minimum period of the checkpoint check and the defaul value is 100ms. The detailed Chekpoint period is configured by the running xml parameter.

45

Page 46: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Due to the accessed contents in the underlying database, it is not related to the zdb lock. This walk is only used on the disk table.

对于这 2 种 walk,结果的顺序不保证,不需要在事务上下文中执行。For the two kind of the walk, the result is unordered, execute in transaction context is not needed.

事务隔离度 Transaction isolation level

默认隔离度为 level2,非常必要的情况下可以使用 level3,这种情况下事务被串行执行,效率低。The default isolation level is level2 and the level3 could be used in the very necessary condition. In this condition, the transactions are serially executed and the performace is low.

Transaction.setIsolationLevel(Transaction.Isolation.LEVEL3);将当前线程事务隔离度设置为 level3,也就是说,凡是通过当前线程启动的事务,均

拥有 level3隔离度。The current transaction isolation level is set as level3, which means that any transaction started by current thread has the level3 isolation.

Transaction.setIsolationLevel(Transaction.Isolation.LEVEL2);隔离度修改回 level2。

The isolation level is modified as level2.

锁 Lock

zdb 的锁为行锁,默认支持隔离度 level2,实现 holdlock,确保可重复读,直到事务结束。select操作获得读锁,insert,update,delete操作获得写锁。(执行 select 之后,如果在同一 key 上 update,先前的读锁被释放,再进行写锁定,这意味着读锁被升级为写锁);反过来锁不会被降级,原因在于:修改记录后如果把锁降级为读锁,那么别的事务就有可能读取到刚修改过的数据,事务隔离度就降级为 level0 了,发生了脏读,往往容易造成逻辑错误。这意味着,The lock of the zdb is the row lock and the default transaction isolation level is level2. The holdlock is implemented to guarantee the repeatedly read till the end of the transaction. The select operation obtains the READ lock, and the insert, update, and delete operations obtain the WRITE lock. (After the select option is executed, if the update option need to be executed next in the same key, the previous READ lock need be released in order to add the WRITE lock, which means that the READ lock is upgraded as the WRITE lock.) In reverse, the lock does not be downgraded because if the lock is downgraded as the READ lock after modifying the record, the other transaction has the possible to read the modified data, which causes the transaction isolatoin level to be downgraded to the level0, so the dirty read happens, and it might cause the logic error. That means:

46

Page 47: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

xbean = table.Tablename.select(key);table.Tablename.update(key);

这一序列执行完成以后,xbean允许修改,即便再次 select(key),该 xbean 还是能够修改。经验上,判断一个 key 不存在再插入这一典型事务与关系数据上的操作非常类似:After these serial operations have been finished, the xbean allows to be modified. Even table.Tablename.select(key) operation is called again, the xbean still could be modified. From the experience, the typical transaction that one key is judged as not existed and then is inserted is similar to the operations on the relational database.

if (table.Tablename.select(key) == null)table.Tablename.insert(key);

往往被认为是一个不好的操作,因为有可能在 select读锁释放以后 insert写锁定之前,另外的事务用这个 key执行了 insert,导致这里的 insert失败,直接违反了上面语句序列的初衷。合理的写法应该是:The above operation is considered as the bad one becuase it is possible that between releasing the select READ lock and inserting the WRITE lock, the other transction uses this key to execute the insert operation and causes the insert operation failure, which directly breakes the original intention of the above statements. The reasonable statement should be:

xbean = table.Tablename.update(key)if(xbean == null)

table.Tablename.insert(key);

复杂的事务可能导致死锁,zdb 支持死锁检测。死锁检测周期,重试次数,重试退避的最大时间均可配置。死锁被作为异常记录到过程返回结果中,可以通过日志配置记录下来。乐观模式下的设计,不需要考虑锁问题。需要解决的情况下检查日志,发现死锁热点,再考虑使用显式的预先锁定解决。The complicated transaction may result in the deadlock. The zdb supports the detection to the deadlock. The detection period of the deadlock, the frequency of the retries, and the maximum time for the retry backoff could be configured. The deadlock as the exception is recorded to the return value of the storage procedure and could be recorded through log configuration. The design under the optimistic mode does not consider the lock issue. Checking the log, finding the deadlock hot, considering the explicit pre-lock solution are the necessary steps if there is case to be solved.

通过预先一次性锁定事务将涉及到的表的行,可以有效解决死锁问题。需要使用的包是 limax.zdb.Transaction.LockContext。By locking the row of the table involved in the transaction one time in advance, the deadlock issue could be effectively resolved. The package limax.zdb.Transaction.LockContext will be used.

例如:For example:同时写锁定表 ta,tb 的行 row1,row2,可以写为:

47

Page 48: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

WRITE locking the row row1, row2 of the table ta, tb at the same time could use the below:Transaction.getLockContext().wAdd(row1, row2, table.Ta.get(), table.Tb.get()).lock();同时写锁定表 ta,tb 的行 row1,row2 以及读锁定表 tc 的行 row3,可以写为:WRITE locking the row row1, row2 of the table ta, tb, and READ locking the row3 of the

table tc at the same time could use this:Transaction.getLockContext().wAdd(row1, row2, table.Ta.get(),table.Tb.get()).rAdd(row3,

table.Tc.get()).lock();一个 add操作请求锁定,参数中所有列举出来的表的所有列举出来的行,有非常大的

灵活性,可以一一列出,也可以通过容器给出,顺序也没有限制。An add operation requires to lock all the listed rows of all the listed tables in the parameters.

It is flexible and could be listed one by one, or given through the container. There is no any limitation for the order.

例如:For example:

wAdd(table.Ta.get(),table.Tb.get(), row1, row2);wAdd(new Object[]{row1, row2}, new Object[]{table.Ta.get(),table.Tb.get()});wAdd(new Object[]{table.Ta.get(),table.Tb.get(), row1, row2});wAdd(Arrays.asList(table.Ta.get(), row1), Arrays.asList(table.Tb.get(), row2));

均是等价的。All of the above examples are the same.实际上,容器(Collection)类型或者数组类型的参数值,被全部递归解析出来,区分出

表类型对象构造锁定的表集,非表类型对象构造行集。Actually, the parameter value of the collection type or array type are totally recursively parsed to distinguish the table set construct locked by the table type object, and the construct row set of the none-table type object.

xbean 访问 Access Xbean

Xbean 只能在事务环境下访问,确保访问时拥有相应的锁,避免并发冲突,生成不正确的序列化数据,使得错误蔓延到下次数据库读取,影响服务正常运行。实现上, Zdb 运行配置中,属性 zdbVerify,控制相应的锁检测,默认 zdbVerify=true,每次 Xbean 访问均检测锁有效性,如果访问缺少锁,则抛出 limax.zdb.XLockLackedError。除非服务器经过严格的覆盖测试,不要为了少量的性能提升将 zdbVerify 设置为 false,特别是闭包的使用很容易将 Xbean 带出锁范围之外。只读操作获得的 xbean 比如通过 select,walk 表 cache,只能进行读访问,写访问将抛出 limax.zdb.XLockLackedError。需要定期检查服务器运行日志,一旦发现 limax.zdb.XLockLackedError,则根据对应的栈信息修改代码,确保并发安全性。The Xbean only could be accessed in the transaction environment. Ensure that the appropriate lock exists when accessing to avoid the concurrency conflicts which leads to incorrectly serializing so that the error is spread to the next database read and affect the normal operations of the service. In the implementation, the zdbVerify of the zdb running configuration controls the relative lock detection, and the attribute zdbVerify is true as default. The lock is verified when xbean is accessed. If the accessing lacks of the lock, the

48

Page 49: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

limax.zdb.XLockLackedError is thrown. Unless the strict coverage test is finished, do not set the zdbVerify=false for a small quantity of performance improvement, especially the usage of the closure easily brings the Xbean outside the scope of the lock. The xbean obtained by the read-only operation such as the select and walk table cache, only supports the read accessing, and the limax.zdb.XLockLackedError will be thrown when write accessing. The server running log needs to be checked regularly. Once limax.zdb.XLockLackedError is found, it needs to revise the code according to the relative stack information to ensure the concurrency safety.

<xbean name="MyXbean"><variable name="var0" type="int"/><variable name="slist" type="vector" value="string" />

</xbean>

MyXbean xbean = new xbean.MyXbean ();

xbean对象使用 get,set存取简单类型数据。The xbean object uses the get and set methods to access the primitive type data.int x = xbean.getVar0();xbean.setVar0(100);

xbean对象访问容器类型时,直接 get获得容器进行修改The xbean object uses get method to obtain the container to modify when accessing the

collection type.xbean.getSlist().add(“abc”);xbean.getSlist().remove(0);

Xbean 在组织结构上,把记录看作根节点 , 层层连接起来,形成一棵树。作为一棵树上节点的 xbean 不能连接到另一棵树上,例如:

The record is regarded as the root element by the Xbean from the organization structure, the layers connect together one by one and form a tree. The xbean as the element in a tree could not connect to another tree.For example:

Xbean1 x1 = table.Table1.select(key1)Xbean2 x2 = table.Table2.update(key2);x2.getArray().add(x1); //这里假设 Xbean2 的 array字段定义为 Xbean1 的 vector//It is assumed that the array field of the Xbean2 is defined as the vector of the Xbean1. 是禁止的,这种情况下将抛出异常报告 Xbean 管理错误,结束事务。上面的例子应该

通过拷贝数据解决问题,例如:x2.getArray().add(new Xbean1(x1));

The statement x2.getArray().add(x1) is forbidden. In this condition, the exception is thrown to report the Xbean management error and the transaction ends. The above example could resolve the issue by copying the data. For example:

x2.getArray().add(new Xbean1(x1))49

Page 50: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Monitor

这里简单解释Monitor 的使用。Here simply explain the usage of the Monitor.

limax.xml 定义了 TransactionMonitor,于是生成了代码 limax.zdb.TransactionMonitor,提供了 6 个方法:The limax.xml defines the TransactionMonitor. The generated source code limax.zdb.TransactionMonitor provides six methods for application development:

public static void increment_runned(String procedureName);public static void increment_runned(String procedureName, long _delta_);public static void increment_false(String procedureName);public static void increment_false(String procedureName, long _delta_);public static void increment_exception(String procedureName);public static void increment_exception(String procedureName, long _delta_);供应用开发使用。.两个方法:two methods are provided for the collection application in the execution environment to use

:public static String buildObjectNameQueryString(String procedureName);public interface Collector;

提供给运行环境下的采集应用使用。

服务器简单验证 Simple verification of the server

这里先不介绍客户端实现,而用 HTML5 方法,通过 chrome,直观体验服务器开发的效果。No mention to the implementation of the client, but visually experience the effect of the server development by using the HTML5 via chrome browser.

简单准备服务器组件 Simply prepare the server components

eclipse导入项目 auany 启动 limax.auany.Main注意,第一次启动 auany前请在 auany当前目录下手工创建 zdb 目录。

Import the auany project in the eclipseLaunch the limax.auany.MainNote:Manually create the zdb directory in the current directory of the auany before the first launching the anany

50

Page 51: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

eclipse导入项目 switcher启动 limax.switcher.Main

Import the switcher project in the eclipseLaunch the limax.switcher.Main

xmlgen参数 The parameters of the xmlgen

加入-script参数生成服务器脚本支持代码加入-jsTemplate参数生成 javascript 模板代码例如:java –jar <path to limax.jar> xmlgen –script –jsTemplate example.server.xml执行以后,在当前目录下,能看到一个名为 template.js 的文件

Add -script parameter to generate the source code supported by the server script.Add -jsTemplate parameter to generate the source code of the javascript template.For example:java –jar <path to limax.jar> xmlgen –script –jsTemplate example.server.xmlAfter executing the command, there is a file named as template.js in the current directory.

制作实验用 html Finish the experimental html

拷贝 limax.js 到当前目录 Copy the limax.js to the current directory

example.html

<!DOCTYPE html><html><script type="text/javascript" src="limax.js"></script><body><script>

在这里拷入 template.js 的全部内容Copy and paste all the contents of the template.js

</script></body></html>

在其中修改:Modify the contents here:var login = {

scheme : 'ws',host : '127.0.0.1:10001', // 配置服务器地址 configure the server addressusername : 'XXX', //用户名随意 the usernametoken : '123456', //必须用 123456作为 token MUST use '123456' as the tokenplatflag : 'test', //使用 auany 的 test认证模块

//use the test authentication module of the auanypvids : [100], //PVID=100

51

Page 52: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

}这个配置与 auany 的 test 模块相关,详见 service-auany.xml,及 test 模块的实现。This configuration is related to the test module of the auany. Please refer the service-

anany.xml and test module for the detailed information.

添加 Session 管理器类 Add the Session manager class

SessionManager.java

import java.io.IOException;

import limax.net.Config;import limax.net.Manager;import limax.net.ServerManager;import limax.net.Transport;import limax.provider.ProviderListener;import limax.provider.ProviderTransport;import limax.util.Trace;

public class SessionManager implements ProviderListener {private ServerManager manager;@Overridepublic void onManagerInitialized(Manager manager, Config config) {

try {this.manager = (ServerManager)manager;this.manager.openListen();

} catch (IOException e) {if (Trace.isErrorEnabled())

Trace.error("SessionManager.onManagerInitialized", e);this.manager.close();

}}@Overridepublic void onManagerUninitialized(Manager manager) {}@Overridepublic void onTransportAdded(Transport transport) throws Exception {

long sessionid = ((ProviderTransport) transport).getSessionID();if (Trace.isInfoEnabled())

Trace.info("SessionManager.onTransportAdded " + transport+ " sessionid = " + sessionid);

}@Override

52

Page 53: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

public void onTransportRemoved(Transport transport) throws Exception {long sessionid = ((ProviderTransport) transport).getSessionID();if (Trace.isInfoEnabled())

Trace.info("SessionManager.onTransportRemoved " + transport+ " sessionid = " + sessionid);

}@Overridepublic void onTransportDuplicate(Transport transport) throws Exception {

manager.close(transport);}

}

服务器初始化的时候触发 onManagerInitialized,这里应该首先加载服务器运行所需的应用资源,加载完成以后调用 openListen(),openListen()首先初始化所有全局 View,接下来告知 Switcher准备就绪,允许客户端连接。The onManagerInitialized is triggered when the server initiates, first the application resource required by the server running is loaded, then the openListen() is called after the load is finished. The openListen() first initializes all the global View firstly, then informs that the Switcher is ready and allows the client to connect.

服务器停止的时候触发 onManagerUninitialized()这时候可以清理所有应用资源。The onManagerUninitialized() is triggered when the server stops to clean all the application resource.

客户端连接上服务器后触发 onTransportAdded消息,提供机会准备相应用户的 View 系统之外的资源,在这之后用户的 SessionView被创建出来。The onTransportAdded message is triggered when the client connects the server to provide the opportunity to prepare the resource outside of the View system of the corresponding user. Then the user's SessionView is created.

客户端从服务器断开触发 onTransportRemoved消息,提供机会释放相应用户的 View 之外的资源,多数情况下,记录日志即可。The onTransportRemoved message is triggered when the client disconnects from the server to provide the opportunity to release the resource except the View of the corresponding user. In most case, this message is recorded as log.

客户端重复登录触发 onTransportDuplicate 消息,这里关闭 transport,前一个用户Session被 Kick掉;这里什么都不做,后一个用户被禁止登录。The onTransportDuplicate message is triggered when the client repeatedly login. If closing transport here, the session of the previous user will be kicked; if nothing done here, the later user will be forbidden to login.

53

Page 54: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

修改 service-ServerExample.xml Modify service-ServerExample.xml

Provider节点下加入属性 className =”SessionManager”,这样服务器启动的时候才能创建 SessionManager 通告消息。Add the attribute className =”SessionManager” in the Provider element so that the SessionManager notification message could be created when server launches.

Trace节点下,修改属性 level =”INFO”,设置 log 级别,可以看到更多信息,比如上面SessionManager.java 中的日志记录就是记录在 INFO 级别。Modify the attribute level =”INFO” in the Trace element and set the log level to get more information, such as the log in the above SessionManager.java which is recorded in the INFO level.

不 使 用 GlobalId 服 务组件的情况下 可 以注释掉 GlobalId 节点,同时去掉example.server.xml 中 useGlobalId=”true”这个属性,重新生成,否则下一次代码生成又会生成新的 GlobalId节点。Under the condition that not use the GlobalId service components, the GlobalId element could be commented, and at the same time the useGlobalId=”true” attribute in the example.server.xml should be deleted and regenerated, or the new GlobalId element will be generated in the next generating code process.

服务器启动主函数 The server launches the main function

Main.java

import limax.xmlconfig.Service;public class Main {public static void main(String[] args) throws Exception {

Service.run("service-ExampleServer.xml");}

}

src 目录加入服务器运行主函数,并且在当前目录建立 zdb 目录。Add the main function of the server in the src directory, and create the zdb directory in the

current directory.运行 Main,eclipse控制台返回Run the Main and get the return information in the eclipse console.

2015-03-19 21:10:08.322 INFO <main> ServiceConf load service-ExampleServer.xml

2015-03-19 21:10:08.323 INFO <main> ServiceConf runTaskBeforeEngineStart

2015-03-19 21:10:08.354 FATAL <main> zdb start begin

2015-03-19 21:10:08.410 FATAL <main> zdb start end

2015-03-19 21:10:08.410 INFO <main> ServiceConf startNetEngine

54

Page 55: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

2015-03-19 21:10:08.433 INFO <main> ServiceConf runTaskAfterEngineStart

2015-03-19 21:10:08.465 INFO <main> ProviderManager SessionManager@15327b79 opened!

2015-03-19 21:10:08.471 INFO <limax.net.io.NetModel.processPool.17> provider client

manager limax.net.StateTransportImpl (/127.0.0.1:11705-/127.0.0.1:10100)

setInputSecurityCodec key = compress = false

2015-03-19 21:10:08.472 INFO <limax.net.io.NetModel.processPool.17> provider client

manager limax.net.StateTransportImpl (/127.0.0.1:11705-/127.0.0.1:10100)

setOutputSecurityCodec key = compress = false

2015-03-19 21:10:08.475 INFO <ProviderConnectorExecutor.ExampleServer.18>

SessionManager@15327b79 onTransportAdded limax.net.StateTransportImpl

(/127.0.0.1:11705-/127.0.0.1:10100)

2015-03-19 21:10:08.480 INFO <limax.net.Engine.protocolScheduler.21> provider had bind

success! pvid = 100

看到最后一行,pvid = 100,这就是 example.share.xml 中指定的那个 PVID,一切OK,可以开始进行实验了。

In the last line, pvid = 100, it is the PVID assigned in the exmaple.share.xml file. Everything is OK and the experiment could begin.

实验 1 Experiment 1

啥代码也不写,打开 chrome,F12 打开调试窗口,将前面准备好的 example.html拖入chrome。No any source code written, launch the chrome browser, click the F12 to active the debug window, drag the prepared example.html in the chrome.

观察服务器日志:check the log of the server:2015-03-20 16:37:56.679 INFO <limax.net.Engine.applicationExecutor.24>

SessionManager.onTransportAdded limax.provider.ProviderTransportImpl (49152 -

/127.0.0.1:40505) sessionid = 49152

这 一 行 就 是前面的 ViewManager 代 码记录 下 来 的 , 客 户 端 连 接 上 来 了 , 用 户 的sessionid 为 49152。This line is recorded by the previous code of the ViewManager, where the client connects and the user's sessionid is 49152.

观察 chrome控制台:check the chrome console:约一分钟后,chrome控制台上显示了 keepalive,这指出 limax.js向服务器定时发送了

keepalive消息。About one minute later, the keepalive is displayed on the chrome console. That indicates that the limax.js periodically sends the keepalive message to the server.

停止服务器,chrome控制台:Stop the server and check the chrome console:

55

Page 56: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

拷贝过来的模板代码中的 ctx.onerror报告了异常,连接随即关闭,ctx.onclose报告了关闭原因——错误码 17。错误码存在的情况下,不用去关心上面的异常,直接查找 limax框架描述文件中的 defines.beans.xml,然后看到这一行:The ctx.oneerror in the copied template source code reports the exception, then the connection is closed and the ctx.onclose reports the reason --- error code 17. In the codition that the error code existes, it is no need to care the above exception, but directly find the description file defines.beans.xml in the Limax framework. Then check this line:

<enum name="SWITCHER_PROVIDER_UNBIND" value="17" />这个意思大致就是 switcher报告 provider 解除了绑定,意味着服务器停止。

The Switcher reports that the Provider unbounds, which means the server stops.

实验 2 Experiment 2

启动服务器,刷新 chrome页面。Launch the server and refresh the chrome web page.

这又回到了刚才的初始状态。Then return to the previous initial status.

启动一个新的 chrome 实例,拖入 exmaple.html。Launch a new instance of the chrome , and drag the example.html in the chrome.

chrome控制台:Check the chrome console:

这里看到错误码 3008,对应了:There is error code 3008, which corresponds to the below:

<enum name="PROVIDER_KICK_SESSION" value="3008" />这意味着 SessionManager.onTransportDuplicate 正常工作了,前一个用户的会话被 Kick

掉。This means that SessionManager.onTransportDuplicate works correctly and the privous user's session is kick off.

实验 3 Experiment 3

一般来说,一个应用至少应该有一个 SessionView,用户的应用级信息,应该持久化。所以:Generally speaking, an application at least has a SessionView. The user's application-level

56

Page 57: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

information should be persistent.

private MySessionView(SessionView.CreateParameter param) {super(param);// bind herelong sessionid = param.getSessionId();bindMytable(sessionid);Procedure.execute(() -> {

xbean.MyXbean xb = table.Mytable.insert(sessionid);if (xb != null)

xb.setVar0(100);return true;

});}这段代码首先将 view 的 bind0字段关联到当前的 sessionid 上,接下来使用一个存储过

程判断 Mytable 中该 sessionid 的记录是否存在,不存在则创建一条新记录,记录中的 var0字段初始化为 100。先 bind,然后再使用存储过程决定是否初始化记录,是比较常用的方式。The above souce code associates the bind0 field of the view to the current sessionid, then uses a storage procedure to judge whether the record of the sessionid in the Mytable is existed. If the record is not existed, a new record will be created and the var0 field in the record will be initiated as 100. Binding first, then using the storage procedure to decide whether to initiate the recorde, is the common way.

运行服务器,刷新 chrome页面:Run the server and refresh the chrome web page:

这里清楚看到,sessionid = 49152 这一用户的 bind0字段在客户端新创建出来了,其中bind0字段内 var0 = 100。From the above information, the bind0 field of the user sessionid = 49152 has been created in the client and the var0 = 100 is set in the bind0 field.

再次刷新 chrome页面,内容不会变化。Then refreshing the chrome web page, there is no change for the content in the web page.

修改 example.html 中的 login.username,换成一个没有使用过的用户名。Modify the login.username in the example.html, change to a never used usernanme.

刷新 chrome页面:Refresh the chrome web page:

57

Page 58: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

注意到 sessionid = 53248,这说明 sessionid区分了用户。The sessionid = 53248 here is noted, which means that the sessionid distincts the user.

实验 4 Experiment 4

private MySessionView(SessionView.CreateParameter param) {super(param);// bind herelong sessionid = param.getSessionId();bindMytable(sessionid);Procedure.execute(() -> {

xbean.MyXbean xb = table.Mytable.insert(sessionid);if (xb != null)

xb.setVar0(100);setVar0("oh, my god. it works.");setVar0("the sequence is right.");return true;

});setVar0("hello world");

}注意到,这里补充了 3 个 View 上的 setVar0。运行服务器,刷新 chrome。

The setVar0 in three View are supplemented in the above code. Run the server and refresh the chrome web page.

第一行,view 的 var0,首先被创建为”hello world”。尽管这一 set调用在函数最后一行,但是前面的存储过程是在别的线程异步执行的,所以它被首先执行并不意外。Line 1: the var0 of the view is created as "hello world". Even though the set funtion is call in the last line of the code, the previous storage procedure is executed in the other thread. So it is not surprise that this function is executed firstly.第二行,view 的 var0,被替换为”oh, my god.it works.”。

Line 2: the var0 of the view is replaced as the "oh, my god.it works.”.第三行,view 的 var0,被替换为”the sequence is right.”

58

Page 59: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Line 3: the var0 of the view is replaced as the ”the sequence is right.”.第四行,同前一个实验,view 的 bind0 的字段被初始化。

Line 4: the bind0 field of the view is initiated just like the previous experiment.这里的 bind0 实际上是由 bindMytable初始化的,在另外一个存储过程中被设置,我们

并不知道它应该什么时候发生。The bind0 here is actually initiated by the bindMytable and set in another storage procedure. So we do not know when it happens.所以,修改 example.html,再换一个没有使用过的用户名。

So, modify the example.html and change to another never used username.

现在已经能够确保 insert 发生在两次 setVar0 之前了,为什么 bind0 的设置还是在最后?这就要需要进一步理解本手册前面的叙述了:“特别要注意的是,同一 View 上 bind 字段的改变时序不作假设,这些改变具有事务原

子性。”而 view 的 var0 的设置表现上也可以看出:“客户端可以重现服务器设置同一 View 上的字段的顺序”variable 的设置顺序是有保证的,bind 没有,其实道理也很简单,存储过程中,在同一

xbean 上反复修改完全有可能,从事务的角度看,只有最终那一次修改结果才被提交。In the above information, it is certain that the insert operation occurs before the two setVar0, but why is the set of the bind0 in the last?Further understanding the previous description in this manual is needed:"It is specially noted that there is no any assumption for the change sequence of bind field of the same View, and these change have the transaction atomicity feature."And the set for the var0 in the view could explain:" The client could reproduce the fields' order in the same View which is modified by the server"The variable's set order could be guaranteed, but the bind could not. The reason is simple. In the storage procedure, the repeated modification in the same xbean is possible, and only the last modification result will be committed from the consideration of the transaction.

将 setVar0("the sequence is right.");修改为_setVar0("the sequence is right.");Replace setVar0("the sequence is right.") with _setVar0("the sequence is right.").

59

Page 60: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

在这里,bind0 的又放到了第二行,前面已经讲清楚了,它出现的位置不需要关心。第三第四行,”the sequence is right.”跑到了前面。这里体现了_set版本的意义:使用

_set 版本,字段数据被立即设置到 view 上,无需等到事务成功。In the above information, the bind0 is placed in the Line 2. As already made clear, it is no need to care where it appears.In the Line 3 and Line 4, ”the sequence is right.” appears to the front. The signifiance of the _set version is reflected here: when using the _set version, the data of the field is immediately set to the view and it is no need to wait for the success of the transaction.

setVar0("hello world");之后增加两行 setVar0("hello world");和 setVar0(null);Add two lines setVar0("hello world") and setVar0(null) after the setVar0("hello world").

第二行,view 的 var0被新创建为”hello world”Line 2: the var0 of the view is created as ”hello world”.第三行,view 的 var0 还是”hello world”,状态是 TOUCH

Line 3: the var0 of the view is ”hello world”, and the status is TOUCH . 第四行,view 的 var0 “hello world”,被删除,状态为 DELETE

Line 4: the var0 of the veiw is ”hello world”, which is deleted and the status is DELETE.第五行,view 的 var0又被新创建为”the sequence is right.”

Line 5: the var0 of the view is created as ”the sequence is right.”.实际上 TOUCH 实现了网络流量的优化,服务器发现字段数据没有变动,就简单告知客

户端字段被 TOUCH 了。要删除 view 的字段,就设置成 null。此外,bind 的记录在表中删除后,bind 字段被设

置成 null。Actually, the TOUCH achieves the optimization to the network traffic. If the server finds that

60

Page 61: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

the data of the field has no change, it simply informs the client that the field is TOUCHed.If the field of the view need to be deleted, it is set as null. In addition, when the record of the bind is deletd from the table, the bind field is set as null.

实验 5 Experiment 5

private MySessionView(SessionView.CreateParameter param) {super(param);// bind herelong sessionid = param.getSessionId();bindMytable(sessionid);Procedure.execute(() -> {

xbean.MyXbean xb = table.Mytable.insert(sessionid);if (xb != null)

xb.setVar0(100);return true;

});MyGlobalView gview = MyGlobalView.getInstance();gview.setVar0(new xbean.MyCbean(123));

}运行服务器,刷新 chrome页面,结果与实验 3 的输出完全一样。

Run the server, refresh the chrome web page, and the result is the same as the output of the experiment 3.函数最后加入一行 gview.syncToClient(sessionid);

Add a new line gview.syncToClient(sessionid) after the last line of the function.

第一行可以看到,MyGlobalView 的 var0字段设置的 123被发送到客户端了。使用GlobalView 需要手工同步。From the line 1, the var0 field of the MyGlobalView is set as 123 and sent to the client. The GlobalView need to be manually synchronized if used.函数最后再加入两行 gview.syncToClient(sessionid);

Add two lines gview.syncToClient(sessionid) in the last of the functoin.

61

Page 62: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

注意到第二行和第三行,状态都是 REPLACE,明明数据没有改变,怎么不是 TOUCH?In the line 2 and line 3, the status is REPLACE. But the data is no changed, why is it not TOUCH?原因很简单,GlobalView 维护数据的最新版本,通过手动刷新将最新版本的数据同步

到一个或者多个客户端,并不会为个别客户端记录发送历史,所以 GlobalView 上永远不会通告 TOUCH 状态。The reason is simple: the GlobalView maintains the latest version of the data, and synchronizes the latest version data to the one or multiple clients through manually refreshing. The GlobalView would not record the sending history for the particular client. So the GlobalView never notifies the TOUCH status.同样 的 , 如 果删除了字段数 据 ,字段数 据 的 状 态 就 是 不存在 ,既然不存在 ,

syncToClient 时也就不会同步到客户端,这就是说,GlobalView 上永远不会通告 DELETE 状态。

为了客户端实现方便,避免使用 GlobalView 字段的删除语义。Equally, if the field data is deleted, the status of the field data is not existed, so it is not be synchronized to the client when syncToClient. It means that the GlobalView never notifies the DELETE status.For the convience of the client implementation, avoid to use the DELETE semantics of the GlobalView field.

实验 6 Experiment 6

private MySessionView(SessionView.CreateParameter param) {super(param);// bind herelong sessionid = param.getSessionId();bindMytable(sessionid);Procedure.execute(() -> {

xbean.MyXbean xb = table.Mytable.insert(sessionid);if (xb != null)

xb.setVar0(100);return true;

});MyTemporaryView tview = MyTemporaryView.createInstance();tview.getMembership().add(sessionid);

}

62

Page 63: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

MyTemporaryView.java 中:@Overrideprotected void onAttached(long sessionid) {

MySessionView.getInstance(sessionid).setVar0("onAttached");}

运行服务器,刷新 chrome页面。Run the server and refresh the chrome web page.

第二行,TemporaryView 的 onopen被调用,于是:Line 2: the TemporayView's onopen function is called.第三行,SessionView 上的 var0被创建为”onAttached”。

Line 3: the var0 of the SessionView is created as "onAttached".注意到 xml 的描述。

Check the below xml description.<subscribe name="_var0" ref="MySessionView.var0" />

在这里,临时 view订阅了 MySessionView.var0,命名为_var0,于是有了:The temporary view subscribes the MySessionView.var0 and names it as _var0 here.第四行,TemporaryView 的_var0同样设置为”onAttached”。

Line 4: the _var0 of the TemporaryView is set as " onAttached " .在这里把 View对象的内容展开看,其实那个_var0,挂在了 57344 这个节点下,这个

57344 实际上就是当前的 sessionid。TemporaryView 的订阅效果在客户端表现就是:每个成员用户的被订阅信息挂在以成员用户 sessionid 为 key节点下。Unfolding the content of the View object, the _var0 is described in the 57344 element which is the current sessionid. The subscription effect of the TemporaryView is expressed in the client as the following: the subscribed information of each member is described in the member element with the sessionid as the key.

修改 example.html,在 v100.share.MyTemporaryView.onchange 的方法之后添加一行。63

Page 64: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Modify the example.html, add the below line after the v100.share.MyTemporaryView.onchange.ctx.send(v100.share.MyGlobalView, e.view.__i__);在 MyGlobalView.java 中实现 onMessage 方法:

Implement the onMessage function in the MyGlobalView.java.@Overrideprotected void onMessage(String message, long sessionid) {

MyTemporaryView.getInstance(sessionid, Integer.parseInt(message)).getMembership().remove(sessionid, (byte) 33);

}

重新运行服务器,刷新 chrome页面Run the server and refresh the chrome web page.

在这里多出了第 5 行。应该这样解释:There is extra line 5. It should be explained like the below:第 4 行输出以后,TemporaryView 的 instanceid,被作为消息发送给 GlobalView

After the line 4 is output, the instanceid of the TemporaryView as the message is sent to the GlobalView.

GlobalView 实现了 onMessage 方法,解析出 instanceid获得该 TemporaryView,将用户从Membership 中移除,导致客户端结束了该 TemporaryView。The GlobalView implements the onMessage method, resolving the instanceid and obtaining the TemporaryView, removing the user from the Membership, which causes the client close this TemprorayView.

这里可以看出,Control,Message 具有全局意义,这就是本手册前面叙述的:“Control 尽管定义在 View 名字空间下,也不意味着这个 Control 的实现只能改变当前

这个 View。”It can be seen here that the Control and Message have the globl meaning, just like the description in this manual:"Although the Control is defined in the namespace of the View, it does not mean that the implementation of this Control can only change the current View."

64

Page 65: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

实验 7 Experiment 7

该实验详细解释 TemporaryView 的行为,行为比较复杂。This experiment detailedly explains the behavior of the TemporaryView, and the behavior is complicated.清理掉前面所有修改。

Remove all the previous modification.拷贝一份 example.html 到 example1.html,example1.html 中更换一个 username

Copy example.html to example1.html, and change the username in the example1.html.

private static Object lock = new Object();private static MyTemporaryView tview;private MySessionView(SessionView.CreateParameter param) {

super(param);// bind herelong sessionid = param.getSessionId();setVar0("hello " + sessionid);synchronized (lock) {

if (tview == null)tview = MyTemporaryView.createInstance();

tview.getMembership().add(sessionid);}

}

MyTemporaryView.java 中@Overrideprotected void onAttached(long sessionid) {

MySessionView.getInstance(sessionid).setVar0("onAttached " + sessionid);}

运行服务器,刷新 chrome页面。Run the server and refresh the chrome web page.

另外启动一个 chrome,F12 开启调试,拖入 example1.html。Launch a new chrome, press F12 to active the debug, and drag the example1.html into the chrome.

图 1,example.html调试窗口Figure 1, the debug window of the example.html

65

Page 66: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

图 2,example1.html调试窗口Figure 2, the debug window of the example1.html

66

Page 67: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

图 1,前 5 行按前一个解释即可。In the figure 1, the first 5 lines could be explained by the previous example.图 2,

In the figure 2, 第一行,加入同一 TemporaryView,onopen 后面[57344, 53248]可以看到现在有 2 个成

员。Line 1, the user represented by the figure 2 adds into the same TemporaryView of the user represented by the figure 1. So the two members [57344, 53248] could be seen behind the onopen.第二行,”_var0 onAttached 57344”,这是 57344 成员被订阅的 var0 的最新信息

Line 2, ”_var0 onAttached 57344”, is the latest information of the subscribed var0 of the member 57344.第三行,”_var0 hello 53428”,这是当前用户被订阅的 var0 最新信息

Line 3, ”_var0 hello 53428”, is the latest informationn of the subscribed var0 of the current user.第四行,当前用户的 MySessionView.var0被设置为“hello 53428”

Line 4, the MySessionView.var0 of the current user is set as “hello 53428”.这里,出现一个疑问,第三行,第四行搞反了吧?其实,这就是本手册前面叙述的: “客户端可以重现服务器设置同一 View 上的字段的顺序;不同 View 之间的时序不作保

证。”So, a question is raised here that maybe the line 3 and line 4 are in the wrong order. Actually, it is the description in this manual:" The client could reproduce the fields' order in the same View which is modified by the server; the sequence between the different Views could not be ensured".

67

Page 68: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

回到图 1,In the figure 1, 第六行,onattach 53428,这表示第二个成员 53428加入进来了

Line 6, "onattach 53428", means that the second member 53428 is added.第七行,53428 成员被订阅的 var0信息”hello 53428”被送过来了

Line 7, the information ”hello 53428” of the subscribed var0 of the member 53428 has been sent here.回到图 2,

In the figure 2,第五行,MySessionView.var0 在 onAttached 时被设置为”onAttached 53428”

Line 5, the MySessionView.var0 is set as "onAttached 53428” when onAttached.第六行,被订阅的信息也送给自己

Line 6, the subscribed information is sent to itself.回到图 1,

In the figure 1,第八行,53428 成员被订阅的信息”onAttached 53428”被送过来了。

Line 8, the subscribed information ”onAttached 53428” of the member 53428 is sent here.

比较,图 1第七行,图 2第八行展开的信息,完全一样,这里可以看见两个客户端的TemporaryView内容上完全同步的。Comparing the expanded information between the line 7 in the figure 1 and the line 8 in the figure 2, they are exactly the same. Here you could see the contents of the TemporaryView of these two clients are fully synchronized.

这个比较复杂,简单总结一下行为。用户加入Membership 时:1. 收集新用户的所有被订阅信息,作为 attach 消息广播给其它用户。2. View 信息(variable, bind),连同其它用户的所有被订阅信息被发送给新用户。3. 框架以新用户 sessionid 为参数调用服务器端 View 实例的 onAttached方法

It is more complicated and briefly summarizes the behavior. When the user joins the Membership:1. Collect the new user's all subscribed information and broadcast to the other users as the attach message.2. The View information (variable, bind), with the other user's all subscribed information together are sent to the new user.3. The framework with the new user's sessionid as the paramter calls the onAttached method of the View instance of the server.

离开 view 的实验建议自己做了,否则截图太多,这个相对简单,有两种情况。We suggest the user try the experiment of leaving the view by himself because it is simpler.There are two cases for consideration.

Membership.remove移除用户:1. 发送 close 消息给离开用户。

68

Page 69: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

2. 发送 detach 消息(连同 Membership.remove 的 reason参数)给其它用户。3. 框架以离开用户 sessionid与 reason 为参数调用服务器端 View 实例的 onDetached方法。

用户离线:1. 发送 detach 消息(以-1 为 reason)给其它用户。2. 框 架 以断线用 户 sessionid 以及 reason = -1 为参数调用 服 务 器 端 View 实例的

onDetached方法。Membership.remove removes the user:1. Send the close message to the leaving user.2. Send the detach message (with the reason parameter of the Membership.remove together) to ther other users.3. The framework with the left user's sessionid and reason as the parameters calls the onDetached method of the View instance of the server.

User disconnect:1. Send the detach message (with the -1 as the reason ) to the other users.2. The framework with the disconnected user's sessionid and reason = -1 as the parameters calls the onDetached method of the View instance of the server.

最后,临时 View 关闭时1. 发送 close 消息给所有用户。2. 框架遍历所有用户 sessionid,连同 reason = -1 为参数逐一调用服务器端 View 实例

的 onDetached方法。Finally, when the temporary View closes,1. Send the close message to all the users.2. The framework reverses all users' sessionid, and with reason = -1 together as the paramaters to call the onDetached method of the View instance of the server one by one.

总结 Conclusion

上面几个实验,简单示例了 View 的使用。类比协议模式可以看出,协议模式的设计可以映射到 View 系统里面来:站在服务器端的角度看,相当于创建一个唯一 SessionView,只使用 variable节点,用协议名命名节点,类型为按照协议字段组织的 Bean。所以,View提供了远比协议模式强大的网络应用编程能力,而又完全不用关心任何网络开发细节。The several experiments above simply demonstrate the use of the View. The design of the protocol mode could be reflected to the View system through analogying the protocol mode: considered from the server, an unique SessionView is created, only uses the variable element, names the element with the protocol name, and the type is the Bean organized according to the protocol's field. Therefore, the View provides a far more powerful network application programming ability compared with the protocol mode, and completely does not care about any network programming detail.

69

Page 70: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

客户端开发 Client development

Javascript 客户端 Javascript client

前面的几个实验已经涉及到了 javascript 开发,这里只需要明确客户端名字空间组织方式即可。The previous examples have involved in the javascript development. Here only clarify the client namespace's organization mode.对照 template.js 可以看出,客户端所有数据全部挂接在以 ctx 为根的一棵树上。在上面

的实验中适当位置 console.log(ctx);即可输出这棵树,一层一层讨论。Contrasted to the template.js, all the datum of the client are organized in the tree with the ctx as the root. In the examples above, using the console.log(ctx) in the proper position could output this tree. We will clarify it layer by layer below.

第一层 The first layer

100即为当前请求服务的 PVID,如果有一个客户端请求多个,则会并列出现多个。f: -1, auany返回的状态标记,和认证模块相关。i: 57344,用户的 SessionIdonclose,onerror,onopen,template.js 中设定的网络消息 handle。register,这个方法是系统注入的,如果 view 的字段太多,需要单独监听个别字段的变

化,可以使用 register。三个参数,r 为 view对象;v 为需要监听的字段名;f 为监听器,参数解释与 onchange 一样。

send,这个方法是系统注入的,用于向服务器发送控制消息。两个参数, r 为 view对象,s 为需要发送的消息字符串。100, is the PVID of the current request service. If one client has more than one request service, multiple PVID will be parallel list here.f: -1, the status flag returned by the auany, which is related to the authentication module.i: 57344, the SessionId of the user.onclose,onerror,onopen, the handle of the network message set in the template.js .register, the function is injected by the system. If the view has many fields and need to monitor the change in the individual fields separately, the register function could be used. There are three parameters: r is the view object; v is the field's name which need to be monitored; f is the monitor. The interpretation of the parameters is similar to the onchange.

70

Page 71: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

send, the function is injected by the system, and is used to send the control message to the server. There are two parameters: r is the view object; s is the message string which need to be sent.

第二层 The second layer

这里可以看见按服务器 xml 描述中的名字空间组织起来的三种 View。观察 example.share.xml:<namespace name="share" pvid="100">这里可以看到 share和 pvid=100 是并列的,为什么会专门制造一个层次出来?原因在于,客户端框架允许连接同一运营体系下多个服务,换言之,即是连接多组

xml 定义的项目,所以无法保证两个项目没有使用一样的最外层名字空间名,只有 pvid 能做出正确的区分。Here you can see the three Views organized according to the namespace described in the xml of the server.Check the example.share.xml:<namespace name="share" pvid="100">Here you can see that the share and pvid=100 are parallel. Why a special layer is divided here? The reason is that the client framework allows to connect to multiple services in the same operation system, which means connecting multiple sets of the projects defined by the xml. So there is no guarantee that two projects have not use the same outermost namespace and only PVID could correctly distinguish.

第三层 The third layer

这一层需要分两组讨论,GlobalView,SessionView 一组,TemporaryView 一组。This layer need to be interpreted in two groups: one group includes the GlobalView and

71

Page 72: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

SessionView; the other includes the TemporayView.

全局 View和会话 View:The GlobalView and the SessionView:

对待 SessionView 的观点,与服务器不同,一个客户端的 Session 自然只能有 1 个,所以从客户角度来看与 GlobalView 应该同等对待,这里用 MySessionView 解释:The perspective to the SessionView is different between the endpoint and the server. An endpoint has only one Session, and should be equally treated just like the GlobalView, considered from the endpoint. The MySessionView is used to explain.

__c__: 1 这个是生成服务器代码时提供给各个 View 的唯一编号。__n__: “share.MySessionView” 很明确,View 的名字。__p__: pvid,既然 View 上没有类似__parent__的字段引用回上层,记录在这里就行了。onchange: template.js注入的这个 View 的监听器。var0: 这就是这个 View 定义的字段。

__c__: 1, this is the unique number of the View provided when generating the source code for the server.__n__: “share.MySessionView”, is the name of the View.__p__: pvid, is used to record since the View has no similar field as _parent_ to reference back to the top layer.onchange: the monitor injected by the template.js to this view.var0: the field defined by this view.

这里需要注意,xml 描述里面定义了的字段这里可能没有,要么是没有初始化,要么就是删除了。It should be noted that the field defined in the xml description may not be existed, either not be initiated, or be removed.

临时 View:The TemporayView:

1: Object,这个 1 是临时 View 的 instanceid,一个临时 View允许有多个实例,在这里进行区分。

__c__, __n__, __p__,同上。onchange: template.js 注入的这个 View 的监听器模板onopen: 服务器通告临时 View 创建时调用,参数一为临时 View 的 instanceid,参数二

为当前临时 View 的成员 sessionid 数组,这里将 onchange 方法的引用拷贝给了创建出来的临时 View 实例。

onattach: 新成员加入临时 View 时调用,参数一为临时 View 的 instanceid,参数二为新成员的 sessionid。

ondetach: 成员离开临时 View 时调用,参数一为 View 的 instanceid,参数二为离开成员的 sessionid,参数三为离开原因。

72

Page 73: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

onclose: 服务器关闭临时 View 时调用,参数为临时 View 的 instanceid。1: Object , the number 1 is the instanceid of the temporay view. A temporay view allows multiple instances and is distinguished here.__c__, __n__, __p__, the same as the above.onchange: the monitor template injected by the template.js to this view.onopen: is called when the server notifies that the temporary view is created. The first parameter is the instanceid of the temporary view, and the second parameter is the sessionid array of the the current temporary view's members. The onchange function is reference copied to the instance of the temporay view created.onattach: is called when the new member is added into the temporary view. The first parameter is the instanceid of the temporary view, and the second parameter is the sessionid of the new member.ondetach: is called when the member leaves the temporary view. The first parameter is the instanceid of the temporary view, and the second parameter is the sessionid of the leaving member, and the third parameter is the leaving reason.onclose: is called when the server closes the temporary view. The parameter is the instanceid of the temporary view.

第四层 The forth layer

展开临时 View 实例,进入第四层57344: Object,这是 sessionid=57344 的用户的被订阅信息,如果临时 View 有多个用户

加入,会并列多个。上文实验 7 的图 2,能清楚看到这一点。__i__: 1 代表了 instanceid = 1__c__, __n__, __p__ 同上。onchange: 之前 onopen 的时候拷贝过来的那个 onchange。临时 View 定义的 variable,bind字段也应该在这一层,这里可以看出服务器没有创建

出来。Unfold the instance of the temporay view and enter the forth layer.57344: Object, is the subscribed information of the user sessionid=57344. If the temporay view has multiple users added, the multiple objects will be list parallel. Refer to the figure 2 in the experiment 7 above for the detailed information.__i__: 1, represents the instanceid = 1.__c__, __n__, __p__, the same as above.onchange: the onchange copied when onopen.The variable and bind fields defined by the temporary view are in this layer. Here you can see that

73

Page 74: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

the server does not create them.

第五层 The fifth layer

展开临时 View 的 sessionid=57344 用户的订阅信息这里看到订阅字段_var0对于 sessionid=57344 的用户而言是”onAttached 57344”

Unfold the subscribed information of the user sessionid=57344 of the temporay view.The subscribed field _var0 is ”onAttached 57344” for the user sessionid=57344.

几点说明 Some explanations

1. 分析 template.js 代码可得出结论,全局 View和会话 View,在 ctx.open被执行之前就已经创建出来,等待服务器的数据改变通告;临时 View,在 ctx.open被执行之前仅仅创建了一个模板,只有等到模板上的 onopen被调用前才真正创建出临时View 实例,按 instanceid区分挂接在模板上。

2. onchange消息 e 中 e.sessionid比较特殊,除了通告临时 View订阅字段变化时,使用发生变化的那个成员的 sessionid 外,使用当前用户的 sessionid。仔细观察上文实验 7 的图二的第二行,可以看到这一情况。

3. 全局 View,会话 View 的字段可以标识为:ctx[pvid].path_to_view.fieldname

临时 View 的 variable,bind字段可以标识为:ctx[pvid].path_to_view[instanceid].fieldname

临时 View 的 subscribe字段可以标识为:ctx[pvid].path_to_view[instanceid][sessionid].fieldname

在这里,path_to_view 解释为 xml 描述中 service节点下的 manager节点通过 state节点引用的名字空间到 View 的路径名。

4. 使用 javascript 脚本客户端,定义 View 的字段时避免使用 onXXX,__XXX__命名,防止命名冲突

Through analyzing the source cod of the template.js , the conclusion could be drawn that the global view and session view have been created before the ctx.open is executed, and wait for the notification of the data change from the server; only a template of the temporary view is created before the ctx.open is executed, and the actual instance of the temporay view will be created when the onopen of the template is called and attached on the template distinguished by the instanceid.

The e.sessionid of the onchange message (e) is a little special. Except for using the sessionid of the changed member when notifying that the subscribed field of the temporay view has changed, the sessionid of the current user is also used. Refer to the line 2 of the figure 2 in the experiment 7 for the detailed information.

74

Page 75: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

The field of the global view and session view could be identified as:ctx[pvid].path_to_view.fieldname

The variable and bind field of the temporay view could be identified as:ctx[pvid].path_to_view[instanceid].fieldname

The subscribe field of the temporary view could be identified as:ctx[pvid].path_to_view[instanceid][sessionid].fieldname

The path_to_view above could be explained as the path name from the namespace to the view, referrenced by the manager element in the service element described in the xml, through state element.

Design the javascript client, and avoid naming the field of the view with the onXXX and __XXX__ when defining avoid the conflict.

最简单服务器 The simplest server

为了实验后面各种客户端实现,这里首先提供一个足以说明问题的最简单服务器,以及 chrome演示结果用以对比。清理掉前面实验中的所有修改,然后:

In order to try the implementation of the clients behind, a simplest server which could adequatly explain the problems is provided here, including the chrome demo for the contrast.Clear all the modification in the previous experiment, then:

MySessionView.java 中:In the MysessionView.java:

private MySessionView(SessionView.CreateParameter param) {super(param);// bind herelong sessionid = param.getSessionId();setVar0("Hello " + sessionid);MyTemporaryView.createInstance().getMembership().add(sessionid);

}

protected void onControl(_MySessionView.control param, long sessionid) {onMessage(Integer.toString(param.var0), sessionid);

}protected void onMessage(String message, long sessionid) {

setVar0(message);}

example.html 中:In the example.html:

75

Page 76: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

v100.share.MyTemporaryView.onopen = function(instanceid, memberids) {this[instanceid].onchange = this.onchange;console.log("v100.share.MyTemporaryView.onopen", this[instanceid], instanceid,

memberids);ctx.send(v100.share.MySessionView, "99999");

}

注意加入的最后一句 send,临时 View onopen 的时候发送一个控制触发一次改变动作。Take note of the last statement " ctx.send(v100.share.MySessionView, "99999") " which sends a control to trigger a change operation when temporary view onopen.

Java 版客户端 The client of the Java version

java版本客户端支持 3 种工作模式,静态模式,Variant 模式,脚本模式,分别介绍。The client of the Java versoin supports three modes: the static mode, the Variant mode, and the script mode, which will be separately introduced.

静态模式 The static mode

静态模式需要使用 xmlgen 生成客户端代码,是最不容易出错的模式。The static mode need to use the xmlgen to generate the client source code, and is the least error-prone mode.

java -jar <path to limax.jar> xmlgen -java –noServiceXML example.client.xml–noServiceXML 参数,申明不需要生成 service-ExampleClient.xml 这样的启动配置文件。

不同于服务器,多数情况下,生成的客户端框架只是应用的一部分,应用提供主函数,不会通过这个配置文件启动。接下来将会介绍如何使用手工启动。The parameter -noServiceXML declares it is no need to generate the launch configuration file like service-ExampleClient.xml. Different from the server, in the most conditions, the generated client framework is only a part of the application. The application provides the main function and does not be launched via this configuration file. Next introuduce how to use the manually launch.

76

Page 77: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

创建 eclipse 项目,生成的 src,gen 目录设置为源码目录,建立 limax 项目的依赖关系。Create the eclipse project, set the generated src and gen directories as the source code directories, and add the dependency relationship to the Limax project.

创建一个 Main.java,逐条解释。New the Main.java file which will be explained line by line here.

public class Main {private final static int providerId = 100;private static void start() throws Exception {

Endpoint.openEngine();EndpointConfig config = Endpoint

.createEndpointConfigBuilder("127.0.0.1", 10000, LoginConfig.plainLogin("testabc","123456", "test"))

.endpointState(example.ExampleClient.states.ExampleClient.getDefaultState(providerId)).staticViewClasses(example.ExampleClient.share.ViewManager.createInstance(providerId))

.build();Endpoint.start(config, new MyListener());

}

private static void stop() {Runnable done = new Runnable() {

@Overridepublic synchronized void run() {

notify();}

};synchronized (done) {

Endpoint.closeEngine(done);try {

done.wait();} catch (InterruptedException e) {}

}}

public static void main(String args[]) throws Exception {start();Thread.sleep(2000);stop();

}}

77

Page 78: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

1. 首先注意到 main函数,Thread.sleep(2000);一般情况下在这个地方进入应用主循环,sleep仅仅是个示例。之前的 start()启动 Endpoint,之后的 stop 结束。

2. start函数首先启动 Endpoint引擎,然后创建配置,使用配置与一个 Listener启动Endpoint 连接服务器。a. createEndpointConfigBuilder,创建了服务器登录所需配置,参数分别是服务器

ip,端口,LoginConfig.plainLogin 包装的用户名,用户 token,auany认证模块名。

b. endpointState,直接使用依据 xml 描述中声明的那个客户端 service节点下的manager节点引用的 state节点生成的 service 代码提供的方法即可。如果需要支持多个 PVID提供的服务,参数逗号分隔列出各段生成代码中的相应方法。实际上,这个方法提供了对协议的支持,如果完全不使用协议,这个方法无需调用。

c. staticViewClasses,设置了静态模式下 view 的管理类实例,这个管理类在生成代码时已经自动生成出来了。如果需要支持多个 PVID提供的服务,参数逗号分隔列出各段生成代码中的相应方法。

3. stop函数用同步方式结束 Endpoint引擎。 First, the statement Thread.sleep(2000) in the main function is noted. In the normal

cicumstances, the application enters the main loop here, and sleep used here is for the example. The previous start() launches the Endpoint and the next stop() ends.

The start() function starts Endpoint engine first, then creates the configuration and uses the configuration and a Listener to launch the Endpoint connection server.a. The createEndpointConfigBuilder() function creates the required configuration when the server logins, and the parameters are the server ip, server port, LoginConfig.plainLogin packed username, user token, and the auany authentication module name.b. The endpointState() function directly uses the method provided by the service code generated by the state element which is referenced by the manager element in the service element of the client defined in the xml description. If it needs to support the service provided by the multiple PVID, the parameters separated by the comma list the corresponding methods related to the each generated source code. Actually, this method provides the support to the protocol. If no protocol is used, it is no need to call this function.c. The staticViewClasses() function sets the management class instance of the view in the static mode. This management class is automaticly generated when generating the source code. If it needs to support the service provided by the multiple PVID, the parameters separated by the comma list the corresponding methods related to the each generated source code.

The stop() function ends the Endpoint engine using synchronization mode.

使用 EndpointListener 接收来自 Endpoint 的网络交互信息。Use EndpointListene to receive the network interaction information from the Endpoint.

class MyListener implements EndpointListener {public MyListener() {

78

Page 79: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

}@Overridepublic void onAbort(Transport transport) throws Exception {

Throwable e = transport.getCloseReason();System.out.println("onAbort " + transport + " " + e);

}@Overridepublic void onManagerInitialized(Manager manager, Config config) {

System.out.println("onManagerInitialized "+ config.getClass().getName() + " " + manager);

}@Overridepublic void onManagerUninitialized(Manager manager) {

System.out.println("onManagerUninitialized " + manager);}@Overridepublic void onTransportAdded(Transport transport) throws Exception {

System.out.println("onTransportAdded " + transport);}@Overridepublic void onTransportRemoved(Transport transport) throws Exception {

Throwable e = transport.getCloseReason();System.out.println("onTransportRemoved " + transport + " " + e);

}@Overridepublic void onSocketConnected() {

System.out.println("onSocketConnected");}@Overridepublic void onKeyExchangeDone() {

System.out.println("onKeyExchangeDone");}@Overridepublic void onKeepAlived(int ms) {

System.out.println("onKeepAlived " + ms);}@Overridepublic void onErrorOccured(int source, int code, Throwable exception) {

System.out.println("onErrorOccured " + source + " " + code + “ “ + exception);}

}

启动服务器,运行客户端以后大致能得到这样的信息:79

Page 80: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Launch the server, run the client and get the informaiton:

onManagerInitialized limax.endpoint.EndpointConfigBuilderImpl$2 limax.endpoint.EndpointManagerImplonSocketConnectedonKeyExchangeDoneonTransportAdded limax.net.StateTransportImpl (/127.0.0.1:26240-/127.0.0.1:10000)onKeepAlived 8onTransportRemoved limax.net.StateTransportImpl (/127.0.0.1:26240-/127.0.0.1:10000) java.io.IOException: channel closed manuallyonManagerUninitialized limax.endpoint.EndpointManagerImpl

对照代码,逐一解释:1. Endpoint.start 以后,首先调用 onManagerInitialized,这时候可以准备与网络相关的

应用初始资源。对应的,最后结束的时候 onManagerUninitialized被调用,这里可以释放那些初始资源。

2. onSocketConnected,这是一个进度指示,告知 socket 连接完成。如果连接过程中失败,则 onAbort被调用,通过 transport.getCloseReason();可以获知 abort原因。

3. onKeyExchangeDone,这是另一个进度指示,告知与服务器的登录握手动作已经完成。如果登录过程失败,则 onErrorOccured被调用,通过 source,code,可以获知失败原因。source,code 的定义详见 limax源码中 defines.beans.xml。这之后如果发生了其它错误,比如用户被踢下线,onErrorOccured 也将被调用。

4. onErrorOccured,source 为 ErrorSource.ENDPOINT 时,code 必为 0,exception 为框架内部操作时产生的异常。

5. onTransportAdded,所有 Endpoint 层面的连接初始化动作完成以后,该方法被调用,可以在 transport 上收发信息了。对于 View而言,这里是注册全局 View 与会话View 的 Listener 的合适的地方。onTransportRemoved 与之对应,连接结束的时候被调用。

6. onKeepAlived,客户端定时向服务器发送 Keepalive消息,服务器响应以后被调用,这个方法粗略提供客户端服务器端的以毫秒为单位的往返时间。

Contrast the source code and explain line by line: After calling Endpoint.start(), first the onManagerInitialized() function is called to prepare

the initial resource for the application related to the network. In the last, the onManagerUninitialized() function is called to release the initial resource.

The onSocketConnected() function is a progress indicator to inform that the socket connection has finished. If fail in the connecting, the onAbort() function is called and the abort reason could be obtained through calling transport.getCloseReason() function.

The onKeyExchangeDone() function is another progress indicator to inform that the handshake operation of the login with the server has finished. If fail in the logining, the onErrorOccured() function will be called and the failure reason could be gotten through source and code. The definition of the source and code is in the defines.beans.xml of the Limax source code.

The onErrorOccured() function, when the source is ErrorSource.ENDPOINT, the code must be 80

Page 81: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

0, and the exception is the exception from the operation in the internal framework. The onTransportAdded() function is called to trigger the transferring the message on the

transport after the initial operation of the connection in the Endpoint layer has finished. For view, it is the proper position to register the Listener of the global view and the session view. The corresponding onTransportRemoved() function will be called when the connection ends.

The onKeepAlived() function which is used to periodly send the Keepalive message to the server from the client, is called after the server responses. This method roughly provides the round-trip time in milliseconds between the client and the server.

使 用 View 的 关 键就 是注 册需 要 的 Listener 获取 改变 信息 , 接 下 来 修改onTransportAddedThe key of using view is to register the required Listener to get the change information. Then modify the onTransportAdded() function.

public void onTransportAdded(Transport transport) throws Exception {System.out.println("onTransportAdded " + transport);MySessionView.getInstance().registerListener(e -> System.out.println(e));

}

修改MyTemporaryView.javaModify the MyTemporaryView.java file.

protected void onOpen(java.util.Collection<Long> sessionids) {// register listener hereSystem.out.println(this + " onOpen " + sessionids);registerListener(e -> System.out.println(e));try {

MySessionView.getInstance().control(99999);} catch (Exception e) {}

}protected void onClose() {

System.out.println(this + " onClose ");}运行程序,获得类似如下的结果:Run the program and get the following result:

onManagerInitialized limax.endpoint.EndpointConfigBuilderImpl$2 limax.endpoint.EndpointManagerImplonSocketConnectedonKeyExchangeDoneonTransportAdded limax.net.StateTransportImpl (/127.0.0.1:33440-/127.0.0.1:10000)

81

Page 82: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

onKeepAlived 11[class = example.ExampleClient.share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 1] onOpen [61440][class = example.ExampleClient.share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 1] 61440 _var0 Hello 61440 NEW[class = example.ExampleClient.share.MySessionView ProviderId = 100 classindex = 1] 61440 var0 Hello 61440 NEW[class = example.ExampleClient.share.MySessionView ProviderId = 100 classindex = 1] 61440 var0 99999 REPLACE[class = example.ExampleClient.share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 1] 61440 _var0 99999 REPLACEonTransportRemoved limax.net.StateTransportImpl (/127.0.0.1:33440-/127.0.0.1:10000) java.io.IOException: channel closed manually[class = example.ExampleClient.share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 1] onClose onManagerUninitialized limax.endpoint.EndpointManagerImpl

对照前面的 chrome 输出可见,数据改变的通告是一致的。倒数第二行需要注意一下,chrome 的输出结果里没有这一行。这不是错误,原因在于:javascript版本严格按照服务器的通告触发消息。 java版本中的这个 onClose并不是来自服务器的通告,而是Endpoint 关闭的时候,在所有的临时 View 上调用了这个方法,目的在于与前面的onOpen对应,提供给应用一个释放资源的机会。Contrasting the output of the chrome, the notice of the data change is consistent. The penultimate line should be noted because the output of the chrome has no this line. It is not error and the reason is that the javascript version triggers the message strictly according to the notice of the server. The onClose of the Java version does not come from the notice of the server, but the Endpoint calls it in all temporay view when closing in order to correspond to the previous onOpen and provide the application a chance to release the resource.

几个基本注意事项:Matters need attention:1. 消息处理过程中抛出任何异常都将导致网络连接关闭,通过 EndpointListener 中的

onManagerUninitialized 可以获知 Endpoint 结束,所有相关资源完全释放,在这之后可以重启 Endpoint。Throwing any exception in processing message will lead to the close of the network connection. The framework calls the onManagerUninitialized() function of the EndpointListener to know that the Endpoint ends and all related resouce has been released. After that, it is ok to relaunch the Endpoint.

2. 框架保证 EndpointListener 中 onManagerInitialized和 onManagerUninitialized消息成对通告,除非 onManagerInitialized抛出异常。The framework guarantees the onManagerInitialized() and onManagerUninitialized() messages of the EndpointListener are notified as a pair, except that the onManagerInitialized() throws the exception.

82

Page 83: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

3. 框架保证 EndpointListener 中 onTransportAdded和 onTransportRemoved消息成对通告,除非 onTransportAdded抛出异常。The framework guarantees the onTransportAdded() and onTransportRemoved() messages of the EndpointListener are notified as a pair, except that the onTransportAdded() throws the exception.

4. onTransportAdded,报告了网络活动的开始,各种后续数据可能已经到来,即便在这里抛出异常终止连接,连接终止前投递的数据依然会正常通告,建议不要在这里抛异常。The onTransportAdded reports the beginning of the network activity. Various follow-up data might have arrived, so even the exception is thrown here to close the connection, the data deliveried before the termination of the connection are still notified. We suggest that do not throw exception here.

5. onManagerUninitialized消息被触发才表示所有服务器数据已经处理完毕。框架严格保证不丢失任何数据。正如上一条提到的 onTransportAdded抛出异常可能带来的问题,严格保证不丢失数据并不意味着广泛的逻辑功能适应性,某一消息的处理导致了错误,应用有责任自己忽略后续可能的无意义的数据,避免产生连带错误。Only that the onManagerUninitialized message is triggered means that all the datum of the server have been handled. The framework strictly guarantees that no data is lost. As memtioned in the previous one that the onTransportAdded might cause the problem when throwing the exception, strict guarantee that no data loss does not mean a wide range logic function adaptability. When the processing of some message causes the error, the application itself is responsible for ignoring the follow-up meaningless data to avoid to generate the associated error.

6. 临时 View 的 onOpen 无论是否抛出异常,onClose 最终必然调用。Regardless of the onOpen() function of the temporary view throwing the exception or not, the onClose() function eventually will be called.

7. 静态方式的代码也可以使用字符串 Message作为控制发送给服务器,在这个例子中MySessionView.getInstance().control(99999); (这里的 control函数名与参数定义源于 example.share.xml 中的 <control name="control">)换成:getViewContext().sendMessage(MySessionView.getInstance(), "99999");将获得一样的结果。如果需要同时支持脚本客户端,建议直接使用字符串方式,不用定义自己的 control,服务器实现起来会更加统一。The code of the static mode could use the string Message as the control to send to the server. In this example, if the MySessionView.getInstance().control(99999) is replaced with the MySessionView.getInstance.sendMessage("99999"), the result is the same. (The control function name and parameter definition here come from the <control name="control"> in the example.share.xml.) If it needs to support the script client at the same time, directly using the string mode, but not defining the own control is more unified when the server implements.

8. View对象上提供两个版本 registerListener 方法,可以注册 ViewChangedListener 监测整个 View对象的字段变化,或者个别字段的变化。 registerListener 方法返回

83

Page 84: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Runnable对象,运行该对象的 run 方法即可撤销注册,上面的代码都没有取消注册,原因在于 View对象释放的时候所有的注册自动取消。The View object provides two versions' registerListener method, which could register the ViewChangedListener to monitor the fileds' change of the whole view object or the particular field' change. The registerListener method returns the Runnable object. Executing this object's run method could unregister. The above code has not unregistered because all the registers will be automatically unregistered when the view object is released.

9. ViewChangedListener.onViewChanged 通告发 生 在 框 架内部 的线程 中 , 通告中 的Value 是 View 上相应字段的引用,把 Value 传递给另外的线程,另外的线程可能得不到及时的数据,原因很简单,有可能另外的线程在通过 Value 访问 View 的对应字段时,已经发生了另一轮通告,更新了 View。有两种方法解决这个问题。其一,如果要把 Value 传递给别的线程,拷贝一份。其二,让使用数据的线程直接调度通告,方法是:创建 EndpointConfig 的时候,使用 executor 方法,指定一个自己的线程调度器。以 Android 为例,如果多数 View 数据都是 UI线程使用,那么就用 UI线程执行通告:The ViewChangedListener.onViewChanged notification happens in the thread inside the framework, and the value of the notification is the reference of the corresponding field of the view. The reason that sending the value to another thread but another thread could not get the timely data is simple: there is might that when another thread access the corresponding field of the view via value, there is another notification sent/received and view is changed. There are two solutions to resolve this issue. First, when transferring the view to another thread, copy it. Second, the thread using the data directly schedules the notification, and the method is: when creating the EndpointConfig, use executor method to specify an own thread scheduler.Using the Android as the example, if most data of the view are used by the UI thread, the UI thread is used to execute the notification.EndpointConfig config = Endpoint

.createEndpointConfigBuilder("127.0.0.1", 10000,LoginConfig.plainLogin("testabc","123456","test"))

.executor(r -> runOnUiThread(r))

.endpointState(example.ExampleClient.states.ExampleClient

.getDefaultState(providerId)).staticViewClasses(

example.ExampleClient.share.ViewManager.createtInstance(providerId)).build();

10. 如果用 executor指定了自己的调度线程,例如 UI线程,那么必须理解如下几个设计要点:A. Endpoint.start 必然启动一个新线程创建 Endpoint,因为不可确认当前是否是由

UI线程执行 Engine.start,如果是,在通告 onManagerInitialized消息时将产生饥饿,因为启动过程必须严格等待 onManagerInitialized 完成。(UI线程等待onManagerInitialized 完成,而执行 onManagerInitialized又必须是 UI线程)

84

Page 85: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

B. 在某个 Manager 上调用 close 方法时,将启动新线程作异步关闭。所以通过onManagerUninitialized 确认 Endpoint活动结束是必要的,见第 5 条。

C. Endpoint.closeEngine将启动新线程,通过异步方式停止引擎,原因类似 A。通过传入一个 Runnable done,可以获知关闭完成消息,前面的例子将异步方式转换为同步方式执行,如果 done 为 null,则得不到任何结束消息。使用 UI 系统的环境里,可以实现一个自己的 done,设置一个标记,UI线程轮询标记获知是否完成关闭。

If the own shedule thread is specified by using executor, such as UI thread, it is necessary to understand the below design points:a. The Endpoint.start certainly start a new thread to create Endpoint, because it is

uncertain that whether the UI thread executes the Engine.start currently. If yes, there is hungry when notifying the onManagerInitialized message, because the launch process must strictly wait for the onManagerInitialized to finish. (The UI thread waits for the onManagerInitialized to finish, however, it must be the UI thread to execute the onManagerInitialized.)

b. When some Manager calls the close function, the new thread will be started as the asynchronous close. So it is necessary to confirm the end of the Endpoint activity through onManagerUnitialized. Please refer to the 5.

c. The Endpoint.closeEngine will start the new thread to stop the engine via asynchronous mode, and the reason is similar as the "a". Through passing a "Runnable done", the close finished message could be obtained. The previous example converts the asynchronous mode to the synchronous mode to execute. If the done is null, there is no any finished message to obtain. In the environment of using UI system, an own done could be implemented. Setting a flag, UI thread polles the flag to obtain whether the close is finished.

11. 生成的所有 View 的实现代码都提供 getInstance 方法获取 View对象实例,在 View对象实例上使用 ViewVisitor调用 visitXXX 可以线程安全地访问字段数据。对于非订阅字段,如果数据不存在,ViewVisitor 不会被调用;对于订阅字段,始终提供一个Map给 ViewVisitor,Map 包含了有效的 SessionId及其对应数据。The generated all implemation code of the view provides the getInstance method to get the instance of the view object. Using ViewVisitor as the parameter of the visitXXX accesses the data of the field in the thread-safe manner. To the non-subscribed field, if the data does not exist, the ViewVisitor would not be called; to the subscribed field, a map is always provided to the ViewVistor, and the Map contains the effective SessionId and the corresponding data.

12. 不论是通告给出的 View 字段数据,还是在 View 对象上调用 visit方法获得的字段数据,必须认为是只读数据,不应该修改。Not only the field data of the view provided by the notification, but also the field data of the view obtained through calling the visit method, MUST be the READ-ONLY data and could not be modified.

85

Page 86: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Variant 模式 The Variant mode

Variant 模式无需生成客户端代码,但是必须明确理解 xml 描述的内容,xml调整后必须仔细检查代码,否则容易造成错误(静态模式修改以后,重新生成客户端代码,编辑器编译器都有机会报告错误)。优点在于可以使得客户端代码相对较小。It is no need to generate the source code for the client in the Variant mode, but the content described in the xml must be clearly understood. When the xml is changed, it is necessary to check the code to avoid the error. (After the modification in the static mode, when regenerating the source code for the client, both the editor and the complier have the chance to report the error.) The advantage is that the size of the soure code of the client is relatively smaller.

生成服务器代码的时候需要加入-variant参数,服务器端将生成相关的支持代码,例如:When generateing the code for the server, the -variant parameter is added so that the server could generate the corresponding support source code.

java –jar <path to limax.jar> xmlgen –variant example.server.xml为了比较,直接用静态模式代码修改:

To contrast, directly use the source code of the static mode to modify:首先,创建 Variant 模式需要使用这样的启动配置:

Firstly, the launch configuration is needed when creating the variant mode:int providerId = 100;EndpointConfig config = Endpoint.createEndpointConfigBuilder("127.0.0.1", 10000,

LoginConfig.plainLogin("testabc","123456","test")).variantProviderIds(providerId).build();注意到,endpointState,staticViewClasses,这两个依赖客户端生成代码的方法不需要

了,多了一个 variantProviderIds,指定 PVID,如果需要支持多个 pvid,参数逗号分隔。executor 这样的方法在这里也可以使用。It is noted that the endpointState and staticViewClasses which dependents on the client to generate the source code are not required, and the variantProviderIds is added to assign the PVID. If it needs to support multiple PVID, the parameter is separated by the comma. The method like the executor could be used here.

接着修改 Listener 的 onTransportAdded 方法。Next, modify the onTransportAdded function of the Listener.

public void onTransportAdded(Transport transport) throws Exception {System.out.println("onTransportAdded " + transport);VariantManager manager = VariantManager.getInstance(

(EndpointManager) transport.getManager(), providerId);VariantView mySessionView = manager

.getSessionOrGlobalView("share.MySessionView");mySessionView.registerListener(e -> System.out.println(e));manager.setTemporaryViewHandler("share.MyTemporaryView",

new TemporaryViewHandler() {@Overridepublic void onOpen(VariantView view,

86

Page 87: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Collection<Long> sessionids) {System.out.println(this + " onOpen " + sessionids);view.registerListener(e -> System.out.println(e));try {

Variant param = Variant.createStruct();param.setValue("var0", 99999);mySessionView.sendControl("control", param);

} catch (Exception e) {}

}@Overridepublic void onClose(VariantView view) {

System.out.println(view + " onClose ");}@Overridepublic void onAttach(VariantView view, long sessionid) {}@Overridepublic void onDetach(VariantView view, long sessionid,

int reason) {}

});}运行程序,获得如下结果:

Run the program and get the below result:onManagerInitialized limax.endpoint.EndpointConfigBuilderImpl$2 limax.endpoint.EndpointManagerImplonSocketConnectedonKeyExchangeDoneonTransportAdded limax.net.StateTransportImpl (/127.0.0.1:47922-/127.0.0.1:10000)[view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 2] onOpen [61440][view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 2] 61440 _var0 Hello 61440 NEW[view = share.MySessionView ProviderId = 100 classindex = 1] 61440 var0 Hello 61440 NEWonKeepAlived 11[view = share.MySessionView ProviderId = 100 classindex = 1] 61440 var0 99999 REPLACE[view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 2] 61440 _var0 99999 REPLACEonTransportRemoved limax.net.StateTransportImpl (/127.0.0.1:47922-/127.0.0.1:10000) java.io.IOException: channel closed manually[view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 2] onClose onManagerUninitialized limax.endpoint.EndpointManagerImpl

87

Page 88: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

与静态模式的结果输出比较,结果一致。Contrasting with the output of the static mode, the result is consistent.

参考 onTransportAdded 代码,使用 Variant 模式需要理解这几些关键点:Referencing the source code of the onTransportAdded, it is need to understand the blew key points when using the variant mode:1. 首先需要获取 VariantManager,ViewManager由 EndpointManager,PVID共同决定。一

个 EndpointManager 对应 了 一 个 网 络 连 接 , 可 以从 Transport 上获取, 这 个EndpointManager 与 onManagerInitialized 通 告 的 manager 是 同 一 个 , 但 是VariantManager 本身必须在 onTransportAdded阶段获取,原因在于,Variant 模式下,所有 View 的结构信息通过服务器传送过来,onTransportAdded阶段些信息才准备完毕。前面提到过,同一连接上允许使用多个 PVID 请求多个服务,从 VariantManager 的获取方式可见,一个 VariantManager对应了一个服务,管理这个服务下所有 View。Fistly, obtaining the VariantManager is necessary. The ViewManager is decided by the EndpointManager and PVID together. One EndpointManager corresponds to one network connection and could be gotten from the Transport. The EndpointManager and the manager noticed by the onManagerInitialized are the same one. But the VariantManager MUST be obtained in the onTransportAdded phase, and the reason is that in the Variant mode, all the structure information of the view is transferred via the server and these information are prepared in the onTransportAdded phase. Mentioned in the previous content, the same connection allows to use multiple PVID to request multiple services. Knowledged from the obtainment mode of the VariantManager, a VariantManager corresponds to a service and manage all the views in this service.

2. ViewManager 上 可 以 直 接获取全局 View 与会话 View , 可 以 在临时 View 上 设 置Handler,获取临时 View 动作的通告。在 View 上注册 Listener 与静态方式下一致。The global view and the session view could be directly obtained in the ViewManager, and the Handler could be set in the temporary view to obtain the action's notice of the temporary view. Registering the Listener in the view is consistent with the one in the static mode.

3. VariantView 使用 Variant 类型来表示所有数据类型,可以创建所有基本类型,容器类型 ,结构类型的 Variant 表示,sendControl 之前便创建了一个结构类型,作为 control参数。事实上 Variant提供了一系列 createXXX,getXXX,setXXX 来维护数据结构,更细节的使用可以参考 javadoc 文档。The VariantView uses Variant type to represent all the data type, and could create the Variant presentation of all the primitive types, the container type, and the structure type. A structure type is created as the parameter of the control before sendControl. Actually, the Variant provides a serial functions such as createXXX, getXXX, setXXX to maintain the data structure, and the javadoc document is referenced for the more detailed information.

4. Variant 模 式 下 同 样 可 以 向 服 务 器 对 应 View 发 送 字 符 串 消 息 , 例 如 ,mySessionView.sendMessage("99999");Send the string message to the view of the server in the Variant mode, such as manager.sendMessage(mySessionView, "99999").

5. 类似静态模式下的 View 通过 visitXXX 访问字段数据,在 Variant 模式下可以使用字段名88

Page 89: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

和 ViewVisitor调用 VariantView 的 visitField 方法。Similar to accessing the field data via visitXXX by the view in the static mode, the field name and the ViewVisitor could be used to call the visitField method of the VariantView in the Variant mode.

6. 特 别 需 要 注 意 代 码 中 的 字 符 串 , 必 须 与 xml 描 述 严 格 对 应 , 比如”share.MyTemporaryView”,使用了从最外层名字空间开始的 View 的全名,不能简写为”MyTemporaryView”,sendControl 使用的 bean 的参数”var0”,对应 xml 描述中的<variable name="var0" type="int"/>,也不能拼错。It is particularly noted that the string in the code should strictly correspond to the xml description, such as ”share.MyTemporaryView”, which uses the view's full name begining from the outermost namespace and could not be abbreviated as ”MyTemporaryView”. The parameter "var0" of the bean used by the sendControl, which corresponds to the <variable name="var0" type="int"/> in the xml description, could not be wrongly spelled.

7. 其它注意事项与静态 View 的注意事项相同。The other considerations are the same as the ones of the static view.

脚本模式 The script mode

脚本模式支持也不需要生成代码,OracleJDK提供了 javascript引擎,在这里示例脚本模式的使用。It is no need to generate the source code to support the script mode because the OracleJDK has provides the javascript engine. The below example represents the usage of the script mode.

生成服务器代码的时候需要加入-script参数,服务器端将生成相关的支持代码,例如:The -script parameter need to be used when generating the server source code so that the server could generate the relative supporting code. For example:

java –jar <path to limax.jar> xmlgen –script example.server.xml将前面的 example.html 文件中的 var providers,var limax 两个变量的定义拷贝出来,贴

到 example.js 文件中 ,将 console.log , console.err 全 部替换 为 print ,因为 OracleJDK 的javascript引擎没有 console 这样的全局对象。最后将 example.js拷贝到 bin 目录与 Main.class放在同一层。Copy the definition of the two variables var providers, var limax in the example.html and paste to the example.js. Replace the console.log and console.err with the print because the javascript engine of the OracleJDK has no global object as the console. Finally, copy the example.js to the directory bin and place it in the same layer as the Main.class.

前面的 MyListener 代码中的方法都清理掉,留下 System.out.println,显示进度即可。Clear all the functions of the MyListener and just leave the System.out.println to display the progress.

使用这样的启动配置:The launch configuration:EndpointConfig config = Endpoint

.createEndpointConfigBuilder("127.0.0.1", 10000,

89

Page 90: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

LoginConfig.plainLogin( "testabc", "123456", "test")).scriptEngineHandle(

new JavaScriptHandle(new ScriptEngineManager().getEngineByName("javascript"),new InputStreamReader(Main.class

.getResourceAsStream("example.js")))).build();

在这里,使用了 scriptEngineHandle 方法指定需要使用的脚本引擎 Handler,一个配置只能指定 一 个 。 JavaScriptHandle 由框 架示例性提供,参数 为 脚 本引擎对象与 脚 本 的Reader,读取脚本内容。The scriptEngineHandle method is used to assign the required Handler of the script engine, and one configuration only assigns one Handler. The JavaScriptHandle is provided by the framework as example, with the script engine object and the script's Reader as the parameters to read the content of the script.

运行程序,获得如下结果:Run the program and get the below result:

onManagerInitialized limax.endpoint.EndpointConfigBuilderImpl$2 limax.endpoint.EndpointManagerImplonSocketConnectedonKeyExchangeDoneonTransportAdded limax.net.StateTransportImpl (/127.0.0.1:53610-/127.0.0.1:10000)v100.share.MyTemporaryView.onopen [object Object] 9 61440v100.share.MyTemporaryView.onchange [object Object] 61440 _var0 Hello 61440 NEWv100.share.MySessionView.onchange [object Object] 61440 var0 Hello 61440 NEWonKeepAlived 151v100.share.MySessionView.onchange [object Object] 61440 var0 99999 REPLACEv100.share.MyTemporaryView.onchange [object Object] 61440 _var0 99999 REPLACEonTransportRemoved limax.net.StateTransportImpl (/127.0.0.1:53610-/127.0.0.1:10000) java.io.IOException: channel closed manuallylimax close nullonManagerUninitialized limax.endpoint.EndpointManagerImpl

这个输出结果与前面几个一致。This output result is consistent with the previous ones.

使用脚本模式,需要明确:1. 如果需要使用 lua 脚本,那么就应该参照 JavaScriptHandle,包装第三方的 java版

lua 引 擎 实 现 ScriptEngineHandle 接 口 。 实 际 上 , ScriptEngineHandle 与limax.js,limax.lua 的操作模式相对应。

2. 其它注意事项与静态 View 的注意事项相同。Using the script mode, some considerations need to be clarified:

90

Page 91: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

1. If the lua script is used, it is need to reference to the JavaScriptHandle, including the ScriptEngineHandle interface implemented by the lua engine of the third party's java version. Actually, the ScriptEngineHandle corresponds to the operation mode of the limax.js and limax.lua.

2. The other considerations are the same as the ones in the static view mode.

三种模式的总结 The conclusion of the three modes

1.正确选择服务器代码生成参数,Variant 模式使用”-variant”,脚本模式使用”-script”。Correctly choose the parameter for the server to generate the source code, the "-variant" is used in the variant mode, and the "-script" is used in the script mode.

2.客户端的配置决定使用哪种模式,静态模式使用 staticViewClasses;Variant 模式使用 variantProviderIds;脚本模式使用 scriptEngineHandle。如果服务器不支持客户端选择的模式,将导致协商错误,通过 EndpointListener 的消息 onErrorOccured报告错误 PROVIDER_UNSUPPORTED_VARINAT 与 PROVIDER_UNSUPPORTED_SCRIPT。The configuration of the client decides which mode is used. The staticViewClasses is used by the static mode; the variantProviderIds is used by the Variant mode; and the scriptEngineHandle is used by the script mode. If the server does not support the selected mode of the client, there is negotiation error, and the error PROVIDER_UNSUPPORTED_VARINAT and PROVIDER_UNSUPPORTED_SCRIPT are reported by the onErrorOccured of the EndpointListener message.

3.静态模式使用 endpointState 支持协议,Variant 模式,脚本模式不支持协议。The static mode uses the endpointState to support the protocol, however, the Variant mode and the script mode do not support the protocol.

4.服务器可以同时运行在 3 种模式下,同一应用,可以有不同的客户端实现。The server could run in the three modes at the same time. The same applicaton could have the different client implementation.

5.客户端框架出于完备性考虑,同一应用可以同时运行在 3 种模式下,这样使用没有实际意义,根据需求选择一种即可。The same application could run in the three modes at the same time for the consideratinn of the completeness by the client framework. However, this kind of usage has no actual meaning and choose one to use according to the requirement.

C#客户端 The C# client

C#客户端同样支持静态模式,Variant 模式和脚本模式。这里介绍静态模式和 Variant 模式。

以下代码以 VS2013控制台项目举例。The C# client supports the static mode, the Variant mode and the script mode. The static mode and the Variant mode are introduced here.The following code works on the VS2013 console project.

91

Page 92: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

静态模式 The static mode

1. 首先应该创建 C#解决方案和应用项目,确定应用项目的源码目录 sdir,执行如下生成命令:First, create the C# solution and appliction project, determine the source code directory sdir for the application project, and execute the following command:

2. java -jar <path to limax.jar> xmlgen –c# –noServiceXML –outputPath sdir example.client.xml

3. 源码目录里面将看见两个新建目录 xmlsrc 与 xmlgen。xmlgen 无需提交到版本控制系统。There are two new created directories xmlsrc and xmlgen in the source code directory. The xmlgen does not need to be submitted to the version control system.

4. 按照生成目录结构,在项目内建立同样的目录结构,然后使用添加现有项操作手工添加所有生成文件,以后的新的生成文件也必须手工添加。Create the same directory structure in the project according to the generated directory structure, use the operation of "Adding Existing Item" to manually add all the generated files, and the next new generated file is also manually added.

5. 将 C#版本的 limax 项目添加到解决方案中。Add the Limax project of the C# version into the solution.

6. 为应用项目添加引用,引用到 limax 项目。Add the reference to the Limax project for the application project.

最后形成大致如下的解决方案树结构。Finally form the following tree structure solution.

92

Page 93: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

class Program{

private const int providerId = 100; private static void start() { Endpoint.openEngine(); EndpointConfig config = Endpoint.createEndpointConfigBuilder(

"127.0.0.1", 10000, LoginConfig.plainLogin("testabc", "123456", "test")).staticViewClasses(

example.ExampleClient.share.ViewManager.createInstance(providerId)).build();

Endpoint.start(config, new MyListener()); } private static void stop() { object obj = new object(); Action done = () => { lock (obj) { Monitor.Pulse(obj); } }; lock (obj) {

93

Page 94: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Endpoint.closeEngine(done); Monitor.Wait(obj); } } static void Main(string[] args) { start(); Thread.Sleep(2000); stop(); }

}

这个主函数代码看起来和 java版本无区别。The source code of this main function has no difference with the one of the Java version.

class MyListener : EndpointListener { public MyListener() { } public void onAbort(Transport transport) { Exception e = transport.getCloseReason(); Console.WriteLine("onAbort " + transport + " " + e); } public void onManagerInitialized(Manager manager, Config config) { Console.WriteLine("onManagerInitialized " + config.GetType().Name + " " + manager); } public void onManagerUninitialized(Manager manager) { Console.WriteLine("onManagerUninitialized " + manager); } public void onTransportAdded(Transport transport) { Console.WriteLine("onTransportAdded " + transport); MySessionView.getInstance().registerListener(e => Console.WriteLine(e)); } public void onTransportRemoved(Transport transport) { Exception e = transport.getCloseReason(); Console.WriteLine("onTransportRemoved " + transport + " " + e);

94

Page 95: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

} public void onSocketConnected() { Console.WriteLine("onSocketConnected"); } public void onKeyExchangeDone() { Console.WriteLine("onKeyExchangeDone"); } public void onKeepAlived(int ms) { Console.WriteLine("onKeepAlived " + ms); } public void onErrorOccured(int source, int code, Exception exception) { Console.WriteLine("onErrorOccured " + source + " " + code + “ “ + exception); }

}除了 ViewChangedEvent,内部用属性的方法实现访问,其它方面也无区别。Except that the ViewChangedEvent implements the access using the internal attribute

method, there is no difference in other aspects

MyTemporaryView.java内部实现:The internal implementation of the MyTemporaryView.java:override protected void onOpen(ICollection<long> sessionids) {

Console.WriteLine(this + " onOpen " + sessionids);registerListener(e => Console.WriteLine(e));MySessionView.getInstance().control(99999);

}override protected void onClose() { Console.WriteLine(this + " onClose"); }

编译运行程序,结果:Comiple and run the program, the result:

onManagerInitialized DefaultEndpointConfig EndpointManagerImplonSocketConnectedonKeyExchangeDoneonTransportAdded limax.net.StateTransportImpl[class = MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 6] onOpen

System.Collections.Generic.HashSet`1[System.Int64][class = MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 6] 61440 _var0

Hello 61440 NEW[class = MySessionView ProviderId = 100 classindex = 1] 61440 var0 Hello 61440 NEW

95

Page 96: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

onKeepAlived 47[class = MySessionView ProviderId = 100 classindex = 1] 61440 var0 99999 REPLACE[class = MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 6] 61440 _var0

99999 REPLACEonTransportRemoved limax.net.StateTransportImpl System.Exception: channel closed

manually[class = MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 6] onCloseonManagerUninitialized EndpointManagerImpl

跟前面各版本均一致。It is consistent withe the previous versions.

Variant 模式 The Variant mode

修改 EndpointConfig 的定义为:Modify the definition of the EndpointConfig:

EndpointConfig config = Endpoint.createEndpointConfigBuilder(LoginConfig.plainLogin("127.0.0.1", 10000, "testabc", "123456", "test"))

.variantProviderIds(100)

.build();使用 variantProviderIds提供 PVID参数。

Use variantProviderIds to provide the PVID parameter.

修改 onTransportAdded 为:Modify the onTransportAdded:

private class MyTemporaryViewHandler : TemporaryViewHandler { private readonly VariantView mySessionView; public MyTemporaryViewHandler(VariantView v) { mySessionView = v; } public void onOpen(VariantView view, ICollection<long> sessionids) { Console.WriteLine(view + " onOpen " + sessionids); view.registerListener(e => Console.WriteLine(e)); Variant param = Variant.createStruct(); param.setValue("var0", 99999); mySessionView.sendControl("control", param); } public void onClose(VariantView view) { Console.WriteLine(view + " onClose"); } public void onAttach(VariantView view, long sessionid) { } public void onDetach(VariantView view, long sessionid, int reason) { } } public void onTransportAdded(Transport transport) {

96

Page 97: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Console.WriteLine("onTransportAdded " + transport); VariantManager manager = VariantManager.getInstance((EndpointManager)transport.getManager(), providerId); VariantView mySessionView = manager.getSessionOrGlobalView("share.MySessionView"); mySessionView.registerListener(e => Console.WriteLine(e)); manager.setTemporaryViewHandler("share.MyTemporaryView", new MyTemporaryViewHandler(mySessionView)); }和 Java 版本比起 来 ,除了 C# 不 支 持 通 过匿名类 直 接 创 建对象, 不 得 不 定 义

MyTemporaryViewHandler 外,没有任何区别。Compared with the Java version, execpt that the C# does not support to directly create the object through anonymous class and have to define the MyTemporaryViewHandler, there is no any difference.

编译运行程序,结果:Compile and runthe program, the result:

onManagerInitialized DefaultEndpointConfig EndpointManagerImplonSocketConnectedonKeyExchangeDoneonTransportAdded limax.net.StateTransportImpl[view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 8] onOpen System.Collections.Generic.HashSet`1[System.Int64][view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 8] 61440 _var0 Hello 61440 NEW[view = share.MySessionView ProviderId = 100 classindex = 1] 61440 var0 Hello 61440 NEWonKeepAlived 38[view = share.MySessionView ProviderId = 100 classindex = 1] 61440 var0 99999 REPLACE[view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 8] 61440 _var0 99999 REPLACEonTransportRemoved limax.net.StateTransportImpl System.Exception: channel closed manually[view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 8] onCloseonManagerUninitialized EndpointManagerImpl跟前面各版本均一致。

It is consistent with the previous versions.

C#版本总结 The conclusion of the C# version

C#版本客户端除了少数语言特性带来的差异外,使用方法与注意事项均与 Java版本一致。框架提供的类库结构,直接参考 javadoc 文档即可。

C#异常规范不够完备,处理各种 EndpointListener消息时,建议不要抛出任何异常。框架使用了.NET3.5 的 C#语言特性,由于网络认证过程使用了 Diffie-Hellman 协议作密

钥交换,需要 BigInteger 支持。如果只能使用 .NET3.5,就应该另找一个 .NET4.0 以上的97

Page 98: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

BigInteger库引用进来。Except few difference brought by the language feature, the usage and the precautions of the C# version are consistent with the ones of the Java version. The framework provides the class library structure and references to the javadoc document for the detailed information.The exception specification of the C# is not perfect. So when dealing with all kinds of EndpointListener messages, it is suggested that do not throw any exception.The framework uses the C# language feature of the .NET 3.5. Because the Diffie-Hellman protocol is used as the key exchange in the network authentication process, the support for the BigInteger is needed. If only the .NET 3.5 could be used, another BigInteger library over the .NET 4.0 should be referred.另外,limax.util 包下提供了两个辅助方法,runOnUiThread 与 uiThreadSchedule,便于

应 用 在 自己的 UI 线程 中调度 View 消息通告。 创 建 配 置 的 时候 .executor(r => runOnUiThread),然后在自己的 UI线程空闲时调用 uiThreadSchedule即可。In addition, two assistant methods runOnUiThread and uiThreadSchedule are provided in the limax.util package so that the application could schedule the View message notification in its own UI thread. Execute the statement .executor(r => runOnUiThread) when creating the configuration, then call the uiThreadSchedule when its own UI thread is idle.

C++客户端 C++ client

C++客户端支持静态模式,Variant 模式。脚本模式通过与 Lua库集成实现 Lua 支持,这个单独介绍。The C++ client supports the static mode, and the Variant mode. The script mode is integrated with the Lua library to support the Lua, which will be separately introduced.

C++客户端使用 C++11标准实现,最大限度保持和 java版本,C#版本一致。C++库在各常用平台上提供支持(g++,ndk,clang),这里以 VS2013控制台项目举例。The C++ client uses the C++ 11 standard to implement, and furthest are consistent with the Java version and the C# version. The C++ library provides the support in all common platforms (g++, ndk, clang). The example bases on the VS2013.

静态模式 The static mode

1. 首先应该创建 C++解决方案和应用项目,确定应用项目的源码目录 sdir,执行如下生成命令:Firstly, create the C++ solutions and the application project, determine the source code directory sdir for the application project, and execute the below command:

2. java -jar <path to limax.jar> xmlgen –c++ –noServiceXML –outputPath sdir example.client.xml

3. 源码目录里面将看见两个新建目录 xmlgeninc 与 xmlgensrc。xmlgeninc 无需提交到版本控制系统。There are two new created directories xmlgeninc and xmlgensrc in the source code directory. The xmlgeninc does not need to submit to the version control system.

4. 按照生成目录结构,在项目内建立同样的目录结构,然后使用添加现有项操作手98

Page 99: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

工添加所有生成的.cpp 文件,以后的新生成的.cpp 文件也必须手工添加。为了编辑器查看方便建议.h 也添加进来。Create the same directory structure according to the generated directory structure, then manually add all generated .cpp files by using "add the existing item" option, and the future new generated .cpp files should also be manually added. For the convenience of checking files through editor, we suggest to add the .h files.

5. 将 C++版本的 limax 项目添加到解决方案中。Add the Limax project of the C++ version into the solution.

6. 为应用项目添加引用,引用到 limax 项目。Add the reference for the application project, and refer to the Limax project.

7. 编辑项目属性,将 limax 项目的 include 目录作为附加包含目录。Edit the project attribute, and add the include directory of the Limax project as the "Additional Include Directories".

主函数代码:The source code of the main funtion:

#include "stdafx.h"#include <iostream>#include "limax.h"#include "xmlgeninc/xmlgen.h"using namespace limax;class MyApp : public EndpointListener{

const int providerId = 100;public:

MyApp(){

Endpoint::openEngine();auto config = Endpoint::createLoginEndpointBuilder(

"127.0.0.1", 10000, LoginConfig::plainLogin("testabc", "123456", "test"))->staticViewCreatorManagers(

{ example::getShareViewCreatorManager(providerId) })->endpointState({ example::getExampleClientStateClient(providerId) })

99

Page 100: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

->build();Endpoint::start(config, this);

}~MyApp(){

std::mutex mutex;std::condition_variable_any cond;std::lock_guard<std::mutex> l(mutex);Endpoint::closeEngine([&](){

std::lock_guard<std::mutex> l(mutex);cond.notify_one();

});cond.wait(mutex);

}void run() { Sleep(2000); }void onManagerInitialized(EndpointManager*,EndpointConfig*) { std::cout <<

"onManagerInitialized" << std::endl; }void onManagerUninitialized(EndpointManager*) { std::cout << "onManagerUninitialized"

<< std::endl; }void onTransportAdded(Transport*) {

std::cout << "onTransportAdded" << std::endl;View* mysessionview = example::ExampleClient::share::MySessionView::getInstance();mysessionview->registerListener([](const ViewChangedEvent &e){std::cout <<

e.toString() << std::endl; });}void onTransportRemoved(Transport*){ std::cout << "onTransportRemoved" << std::endl; }void onAbort(Transport*) { std::cout << "onAbort" << std::endl; }void onSocketConnected() { std::cout << "onSocketConnected" << std::endl; }void onKeyExchangeDone() { std::cout << "onKeyExchangeDone" << std::endl; }void onKeepAlived(int ping) { std::cout << "onKeepAlived " << ping << std::endl; }void onErrorOccured(int errorsource, int errorvalue, const std::string& info) { std::cout <<

"onErrorOccured " << errorsource << " " << errorvalue << " " << info << std::endl; }void destroy() {}

};

int _tmain(int argc, _TCHAR* argv[]){

MyApp().run();return 0;

}与 java版本 C#版本比起来除语言特性外无太大差异。onErrorOccured稍有差别,当

source 为 SOURCE_ENDPOINT 时,code对应那些 SYSTEM 类型错误,info报告了具体错误内容,source/code 的定义参见 endpoint.h头文件。另外 MyApp 类中多了一个方法 destroy。具体原因下面介绍。

100

Page 101: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Constrasted to the Java version and the C# version, execept for the language feature, there is no much difference. There is a little difference for the onErrorOccured. When the source is SOURCE_ENDPOINT, the code corresponds to the SYSTEM type error, and the info reports the detailed error content. The definition of the source/code refers to the endpoint.h header file. In addition, there is an extra destroy method in the MyApp class. And the detailed reason will be explained in the following.

share.MyTemporaryView.cpp:#include "share.MyTemporaryView.h"#include "../xmlgeninc/views/share.MySessionView.h"

#include <iostream>using namespace limax;namespace example { namespace ExampleClient { namespace share {

void MyTemporaryView::onOpen(const std::vector<int64_t>& sessionids) {std::cout << toString() << " onOpen";for (auto &l : sessionids)

std::cout << " " << l;std::cout << std::endl;registerListener([](const ViewChangedEvent &e){std::cout << e.toString() << std::endl; });MySessionView::getInstance()->control(99999);

}void MyTemporaryView::onAttach(int64_t sessionid) {}void MyTemporaryView::onDetach(int64_t sessionid, int reason){

if (reason >= 0){

//Application Reason}else{

//Connection abort Reason}

}void MyTemporaryView::onClose() {

std::cout << toString() << " onClose " << std::endl;}

} } }

101

Page 102: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

这个看起来差异也不大,注意一下,因为使用了 MySessionView,所以需要的头文件share.MySessionView.h 必 须 自己手工 include 。另外 ,同一 目 录 下 生 成 的头文件share.MyTemporaryView.h 中,可以添加需要的成员变量,成员方法。It seems no much difference. It is noted that the required header file share.MySessionView.h must be manually included due to using the MySessionView. In addition, the generated header file share.MyTemporaryView.h in the same directory could add the necessarey member variables and member functions.

运行结果:The execution result:

onManagerInitializedonSocketConnectedonKeyExchangeDoneonTransportAdded[class = example.ExampleClient.share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 78] onOpen 61440[class = example.ExampleClient.share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 78] 61440 _var0 00A3BCBC NEWonKeepAlived 15[class = example.ExampleClient.share.MySessionView ProviderId = 100 classindex = 1] 61440 var0 0088585C NEW[class = example.ExampleClient.share.MySessionView ProviderId = 100 classindex = 1] 61440 var0 0088585C REPLACE[class = example.ExampleClient.share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 78] 61440 _var0 00A3BCBC REPLACEonTransportRemoved[class = example.ExampleClient.share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 78] onCloseonManagerUninitialized

这个结果看起来与前面各版本均一致,只不过 View 的值被显示为了一个地址了,这是因为消息通告 ViewChangedEvent 的定义中,value 的类型只能是 void*。This result seems to be consistent with the previous versions, and only the value of the View is displayed as an address. That is because the type of the value should only be the void* in the definition of the ViewChangeEvent message notification.

Variant 模式 The Variant mode

修改构造函数,创建 Variant 模式下的配置Modify the constructor function, and create the configuration in the Variant mode.

auto config = Endpoint::createEndpointConfigBuilder(

102

Page 103: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

"127.0.0.1", 10000, LoginConfig::plainLogin("testabc", "123456", "test"))->variantProviderIds({ providerId })->build();

修改 onTransportAdded 方法:Modify the onTransportAdded method:

class MyTemporaryViewHandler : public TemporaryViewHandler{

VariantView* mySessionView;public:

MyTemporaryViewHandler(VariantView* v) : mySessionView(v) {}virtual void onOpen(VariantView* view, const std::vector<int64_t>& sessionids){

std::cout << view->toString() << " onOpen";for (auto &l : sessionids)

std::cout << " " << l;std::cout << std::endl;view->registerListener([](const VariantViewChangedEvent &e){std::cout <<

e.toString() << std::endl; });Variant param = Variant::createStruct();param.setValue("var0", 99999);mySessionView->sendControl("control", param);

}virtual void onClose(VariantView* view){

std::cout << view->toString() << " onClose " << std::endl;}virtual void onAttach(VariantView* view, int64_t sessionid) {}virtual void onDetach(VariantView* view, int64_t sessionid, int reason) {}virtual void destroy() { delete this; }

};void onTransportAdded(Transport*transport) {

std::cout << "onTransportAdded" << std::endl;VariantManager* manager = VariantManager::getInstance(transport->getManager(),

providerId);VariantView* mySessionView = manager-

>getSessionOrGlobalView("share.MySessionView");mySessionView->registerListener([](const VariantViewChangedEvent &e){std::cout <<

e.toString() << std::endl; });manager->setTemporaryViewHandler("share.MyTemporaryView", new

MyTemporaryViewHandler(mySessionView));}这 段 代 码 看 起 来 和 C# 版 本 非 常 类 似 , 除 了 语 言 特 性 差 异 。 另 外

MyTemporaryViewHandler 类中多了一个方法 destroy。具体原因下面介绍。103

Page 104: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

This source code looks like the C# version, except for the languge feature. In addition, there is an extra destroy method in the MyTemporaryViewHandler class. And the detailed reason is in the following.

运行结果:The execution result:

onManagerInitializedonSocketConnectedonKeyExchangeDoneonTransportAdded[view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 79] onOpen 61440[view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 79] 61440 _var0 Hello 61440 NEWonKeepAlived 16[view = share.MySessionView ProviderId = 100 classindex = 1] 61440 var0 Hello 61440 NEW[view = share.MySessionView ProviderId = 100 classindex = 1] 61440 var0 99999 REPLACE[view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 79] 61440 _var0 99999 REPLACEonTransportRemoved[view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 79] onCloseonManagerUninitialized

这里的结果就能看到实际的 value 了,因为 Variant知道一些类型信息。This result reflects the actual value because that the Variant knowleges some type information.

各种注意事项 Matters need attention

1. 主函数 代 码 必 须 包含头文件 limax.h , 使 用静态 模 式 需 要 额 外 包含生 成 的头文件xmlgeninc/xmlgen.h。生成的代码修改时使用别的生成代码时,需要哪些包含哪些。参见前面对 share.MyTemporaryView.cpp 的修改。The main function must include the limax.h header file, and extra include the generated xmlgeninc/xmlgen.h header file when using the static mode. When modifying the generated source code which uses the other generated source code, which is used should be included. Refer to the previous modification to the share.MyTemporaryView.cpp.

2. 为了简单起见,limax库代码只使用 limax名字空间,不再作进一步的划分,所以 using namespace limax;For the convenience, the Limax library source code only uses the Limax namespace and has no further division. So use "using namespace limax".

3. 创建配置时,staticViewCreatorManagers,endpointState,variantProviderIds 的参数 使 用 C++11 特 性 的初始化列表作为参数 , 可 以 支 持 多 个 。同 C# 一 样 ,同样提供runOnUiThread 与 uiThreadSchedule 两个辅助方法方便使用,->executor([](Runnable

104

Page 105: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

r){ runOnUiThread(r); })。When creating the configuration, the parameters of the staticViewCreatorManagers, endpointState, and variantProviderIds use the specific initial list of the C++11 and could support multiple. Just like the C#, the runOnUiThread and uiThreadSchedule two assistant methods are provided for the convenient usage, ->executor([](Runnable r){ runOnUiThread(r); }).

4. 由库提供出来的各种指针 EndpointManager*,Trasnport*,以及各种形式的 View*,用 户 可 以 直 接 使 用 不 允 许 delete 。 EndpointManager 的 寿 命 到onManagerUninitialized 为止;Transport 以及全局 View,会话 View 的寿命到onTransportRemoved 为止;临时 View 的寿命到临时 View 自身 onClose 为止,如果一定需要持有这些指针,必须严格注意寿命问题,尤其是通过 lambda 表达式的闭包引用更要小心。All kinds of pointers EndpointManager*, Transport* provided by the library and all types of View* could be directly used and the delete is forbidden. The life time of the EndpointManager is till the onManagerUninitialized; the life time of the Transport, the global View, and the session View are till the onTransportRemoved; the life time of the temporary View is till the temporary view onClose itself. If holding these pointers is necessary, the life time issue must be strictly concerned, especially when reference to the closure of the lamba expression.

5. 需要应用提供对象的情况下,对象应该由用户自己创建出来提供指针交由库使用,并且 提 供 自 己 的 destroy 方 法 , 前 面 的 MyApp::destroy 和MyTemporaryViewHandler::destroy 就 是典型情况。 这 是由于库内部 使 用 的new/delete 与应用本身的 new/delete 可能不配对。When the system need the application to provide the created object, the object should be created by the user iteself and provide the pointer used by the library, and the destroy method of itself. The previous MyApp::destroy and MyTemporaryViewHandler::destroy are the typical examples. It is because that the new/delete used in the internal library and the new/delete of the application itself might be not consistent.

6. 如果使用 Objective-C++,生成静态模式代码时使用-oc参数代替-c++参数,确保生成需要的.mm 文件。If using Objective-C++, uses -oc parameter to replace the -c++ parameter when generating the source code for static mode to guarantee to generate the required .mm file.

Lua/C++客户端 Lua/C++ client

1. 生成服务器代码的时候,通过-luaTemplate 生成 lua 模板代码 example.lua,java –jar <path to limax.jar> xmlgen –script –luaTemplate example.server.xmlWhen generating the server source code, generate the lua template code example.lua via -luaTemplate. java –jar <path to limax.jar> xmlgen –script –luaTemplate example.server.xml

2. 客户端首先创建 C++解决方案和应用项目105

Page 106: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Create the C++ solution and the applicate project for the client.3. 解决方案中加入 Limax源码中 cpp 目录下的 limax 项目,lua 目录下的 limax.lua 项目,

liblua 项目。Add the limax project under the cpp directory, the limax.lua project and the liblua project of the Limax source code under the lua directory into the solution.

4. 为应用项目添加引用,引用上述 3 个项目。Add the reference for the application project, referring to the above three projects.

5. 编辑应用项目属性,将 limax源码目录,lualib头文件目录,lualib源码目录,3 个目录作为附加包含目录添加进来。Edit the application project's attribute, and the limax source code directory, lualib header file directory, lualib source code directory as the "Additional Include Directories".

主函数代码:The source code of the main function:

#include "stdafx.h"#include <limax.h>#include <iostream>#include <lua.hpp>#include <limax.lua.h>using namespace limax;class MyLuaApp : public EndpointListener{

lua_State* L;public:

MyLuaApp(){

L = luaL_newstate();luaL_openlibs(L);int e = luaL_dofile(L, "callback.lua");if (e != LUA_OK){

std::cout << "lua load 'callback.lua' failed! " << lua_tostring(L, -1) << std::endl;exit(-1);

}auto sehptr = LuaCreator::createScriptEngineHandle(L, -1, false, [this](int s, int e, const

std::string& m){ onErrorOccured(s, e, m); });if (!sehptr)

exit(-1);lua_pop(L, 1);Endpoint::openEngine();auto config = Endpoint::createEndpointConfigBuilder(

106

Page 107: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

"127.0.0.1", 10000, LoginConfig.plainLogin("testabc", "123456", "test"))->scriptEngineHandle(sehptr)->build();

Endpoint::start(config, this);}~MyLuaApp(){

std::mutex mutex;std::condition_variable_any cond;std::lock_guard<std::mutex> l(mutex);Endpoint::closeEngine([&](){

std::lock_guard<std::mutex> l(mutex);cond.notify_one();

});cond.wait(mutex);

}void run() { Sleep(2000); }void onManagerInitialized(EndpointManager*, EndpointConfig*) { std::cout <<

"onManagerInitialized" << std::endl; }void onManagerUninitialized(EndpointManager*) { std::cout << "onManagerUninitialized"

<< std::endl; }void onTransportAdded(Transport*) { std::cout << "onTransportAdded" << std::endl; }void onTransportRemoved(Transport*){ std::cout << "onTransportRemoved" << std::endl; }void onAbort(Transport*) { std::cout << "onAbort" << std::endl; }void onSocketConnected() { std::cout << "onSocketConnected" << std::endl; }void onKeyExchangeDone() { std::cout << "onKeyExchangeDone" << std::endl; }void onKeepAlived(int ping) { std::cout << "onKeepAlived " << ping << std::endl; }void onErrorOccured(int errorsource, int errorvalue, const std::string& info) { std::cout <<

"onErrorOccured " << errorsource << " " << errorvalue << " " << info << std::endl; }void destroy() {}

};int _tmain(int argc, _TCHAR* argv[]){

MyLuaApp().run();return 0;

}

EndpointListener消息代码与其它 C++版本无区别。主要差别就在构造函数初始化引擎之前初始化了 Lua虚拟机,在虚拟机上装载了脚本代码callback.lua,创建了脚本引擎的 handler,在创建配置的时候设置进来。析构函数在结束引擎之后结束 Lua虚拟机。The EndpointListener message code has no any difference with the other C++ version.The major difference is that the constructor function initiates the Lua virtual machine before initiating the engine, loads the script code callback.lua on the virtual machine, creates the

107

Page 108: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

handler of the script engine and sets it when creating the configuration. The destructor stops the Lua virtual machine after stopping the engine.

callback.lua:callback.lua 来自生成服务器时生成的 example.lua 代码,为了实现例子的功能,添加了ctx.send 一行。 The callback.lua comes from the generated example.lua code when generating the server, and adds the ctx.send line to implement the function in the example. v100.share.MyTemporaryView.onopen = function(this, instanceid, memberids) this[instanceid].onchange = this.onchange print('v100.share.MyTemporaryView.onopen', this[instanceid], instanceid, memberids) ctx.send(v100.share.MySessionView, "99999") end

这里可以看出来,使用上除了语言特性与 javascript版本没有差异。这里需要注意一下 callback.lua 的放置路径。如果在控制台中运行,应该放置在 exe所在目录;如果在 VS2013 中运行,放置在项目目录下。Known from here, except for the language feature, there is no any other difference with the javascript version.The path of the callback.lua should be noted. If running in the console, it should be placed in the same directory as the exe files; if running in the VS2013, it should be placed in the root directory of the project.

启动服务器,运行客户端,获得结果:Launch the server, run the client, and get the result:

onManagerInitializedonSocketConnectedonKeyExchangeDoneonTransportAddedv100.share.MyTemporaryView.onopen table: 00674C50 3 table: 00674C00v100.share.MyTemporaryView.onchange table: 00674C50 61440 _var0 Hello 61440 NEWonKeepAlived 16v100.share.MySessionView.onchange table: 00674840 61440 var0 Hello 61440 NEWv100.share.MySessionView.onchange table: 00674840 61440 var0 99999 REPLACEv100.share.MyTemporaryView.onchange table: 00674C50 61440 _var0 99999 REPLACEonTransportRemovedlimax closeonManagerUninitialized

这个结果看起来和前面版本均一致,除了 table: XXXXXXXX,lua 的对象均为 table。这里print 没有解析出来。

108

Page 109: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

The result looks like the same as the previous versions, except for the table: XXXXXXXX because that the object of the lua is the table. The print here does not resolve it.

各种注意事项:Matters need attention:1. 头文件顺序,头文件 limax.lua.h 必须包含在 lua.hpp 之后。

The header file limax.lua.h should be included behind the lua.hpp2. 这里为了示例在 openEngine 之前创建了 lua虚拟机。具体项目应用中,应该直接使用

应用提供的虚拟机,保证脚本中 View 的 onXXX 中实现的控制动作能够直接驱动应用逻辑。The lua virtual machine is created before the openEngine to give an example. In the detailed project application, the virtual machine provided by the application should be directly used to the control operatoin implemented in the onXXX of the View could directly drive the application logic.

3. 创建配置时,一般需要调用.executor 方法指定应用期望的线程。When creating the configure, .executor method is normally called to assign the thread expected by the application.

Lua/C#客户端 Lua/C# client

1. 生成服务器代码的时候,通过 -luaTemplate 生成 lua 模板代码 example.lua, java –jar <path to limax.jar> xmlgen –script –luaTemplate example.server.xml

When generating the server source code, generate the lua template code example.lua through the -luaTemplate, java –jar <path to limax.jar> xmlgen –script –luaTemplate example.server.xml2. 客户端首先创建 C#解决方案和应用项目

First, the client creates the C# solution and the application project.3. 解决方案中加入 Limax源码中 csharp 目录下的 limax 项目, lua 目录下的 luacs 项目clrlua 项目,liblua_s 项目

Add the limax project under the csharp directory, the luacs project, the clrlua project and the liblua_s project under the lua directory of the Limax source code into the solution.4. 为应用项目添加项目引用,引用 limax 项目,luacs 项目,clrlua 项目。

Add the project reference for the application project, reference to the limax project, the luacs project and the clrlua project.5. 项目属性中将当前项目的目标平台由 AnyCPU改为 x86,与其它几个保持一致,均为x86。

Change the object platform of the current project in the project attributes from the AnyCPU to the x86, and is consistent with the others as the x86.

程序代码source code:

109

Page 110: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

using System;using System.Text;using System.IO;using System.Threading;using limax.net;using limax.script;using limax.endpoint;using limax.endpoint.script;

namespace ConsoleApplicationLuaCS{ class MyListener : EndpointListener { public void onAbort(Transport transport) { Exception e = transport.getCloseReason(); Console.WriteLine("onAbort " + transport + " " + e); } public void onManagerInitialized(Manager manager, Config config) { Console.WriteLine("onManagerInitialized " + config.GetType().Name + " " + manager); } public void onManagerUninitialized(Manager manager) { Console.WriteLine("onManagerUninitialized " + manager); } public void onTransportAdded(Transport transport) { Console.WriteLine("onTransportAdded " + transport); } public void onTransportRemoved(Transport transport) { Exception e = transport.getCloseReason(); Console.WriteLine("onTransportRemoved " + transport + " " + e); } public void onSocketConnected() { Console.WriteLine("onSocketConnected"); } public void onKeyExchangeDone() { Console.WriteLine("onKeyExchangeDone"); }

110

Page 111: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

public void onKeepAlived(int ms) { Console.WriteLine("onKeepAlived " + ms); } public void onErrorOccured(int source, int code, Exception exception) { Console.WriteLine("onErrorOccured " + source + " " + code); } }

class Program { private static void start() { string callback = File.ReadAllText("callback.lua", Encoding.UTF8); Endpoint.openEngine(); EndpointConfig config = Endpoint.createEndpointConfigBuilder(

"127.0.0.1", 10000, LoginConfig.plainLogin("testabc", "123456", "test")).scriptEngineHandle(

new LuaScriptHandle(new Lua((string msg)=>Console.WriteLine(msg)), callback))

.build(); Endpoint.start(config, new MyListener()); } private static void stop() { object obj = new object(); Action done = () => { lock (obj) { Monitor.Pulse(obj); } }; lock (obj) { Endpoint.closeEngine(done); Monitor.Wait(obj); } } static void Main(string[] args) { start(); Thread.Sleep(2000); stop(); } }}

111

Page 112: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

实际上 , 这 个 代 码 与 C# 其它版本 代 码并无太大差别,除了 创 建 配 置 时 使 用 了LuaScriptHandle,这一点也可以与 Java版本的脚本模式作比较。Actually, this code is not much different from the code of the other C# versions, except for using the LuaScriptHandle when creating the configure, which also could compare with the script mode of the Java version.

callback.lua:callback.lua 来自生成服务器时生成的 example.lua 代码,为了实现例子的功能,添加了ctx.send 一行。 The callback.lua comes from the generated example.lua code when generating the server code, and adds the ctx.send line to implement the function of the sample.

v100.share.MyTemporaryView.onopen = function(this, instanceid, memberids) this[instanceid].onchange = this.onchange print('v100.share.MyTemporaryView.onopen', this[instanceid], instanceid, memberids) ctx.send(v100.share.MySessionView, "99999") end

这里可以看出来,这个 callback.lua 与 Lua/C++版本完全相同。这里需要注意一下 callback.lua 的放置路径。应该放置在 exe所在目录。Seen from the above code, the callback.lua is identical with the Lua/C++ version.It should be noted that the callback.lua should be placed in the same directory as the exe.

启动服务器,运行客户端,获得结果:Launch the server, run the client and obtain the result:

onManagerInitialized DefaultEndpointConfig EndpointManagerImplonSocketConnectedonKeyExchangeDoneonTransportAdded limax.net.StateTransportImplv100.share.MyTemporaryView.onopen table: 048E98F0 31 table: 048E9940v100.share.MyTemporaryView.onchange table: 048E98F0 36864 _var0 Hello 36864 NEWv100.share.MySessionView.onchange table: 048E6820 36864 var0 Hello 36864 NEWonKeepAlived 10v100.share.MySessionView.onchange table: 048E6820 36864 var0 99999 REPLACEv100.share.MyTemporaryView.onchange table: 048E98F0 36864 _var0 99999 REPLACEonTransportRemoved limax.net.StateTransportImpl System.Exception: channel closed manuallylimax close nilonManagerUninitialized EndpointManagerImpl

这个结果看起来和前面版本均一致。This result is identical with the previous versions.

112

Page 113: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Javascript/C++ 客 户 端 (SpiderMonkey) Javascript/C++ client

(SpiderMonkey)

准备 javascript引擎库 Prepare the javascript engine library

1. https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Releases/45,下载 javascript引擎源码包。Download the javascript engine source code package from the website https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Releases/45 .

2. https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Build_Documentation,按照该文档描述的流程创建二进制包。Create the binary package following the procedure described in the document https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Build_Documentation.

3. 源 码 目 录 中 编 辑 js/public/CharacterEncoding.h 文 件 , 查 找UTF8CharsToNewTwoByteCharsZ 方法,将前缀的 extern TwoByteCharsZ ,修改为JS_PUBLIC_API(TwoByteCharsZ) , 这 个 方法在 limax 提供的粘合 代 码 中 是 必 要 的 。SpiderMonkey API提供了 javascript串到 UTF8串的转换,除了这个方法没有任何从UTF8串转换回 javascript串的方法,应该是个缺陷。Edit the js/public/CharacterEncoding.h file in the source code directory, search the UTF8CharsToNewTwoByteCharsZ method, and change the prefix extern TwoByteCharsZ to JS_PUBLIC_API(TwoByteCharsZ), which is necessary in the glue code provided by the Limax. The SpiderMonkey API provides the conversion from the javascript string to the UTF8 string. Except for this method, there is no way to convert the UTF8 string to the javascript string, which is a defect.

4. 之后的例子需要使用参数 --target=x86_64-pc-mingw32 --host=x86_64-pc-mingw32 创建x64版本的包,同时需要添加参数--disable-jemalloc,否则 vs2013编译出来的版本在程序退出时出错,如果当前的 vs2013版本编译时 cl报告内部错误,则需要将 vs2013升级到最新版本。此外,注意源码包展开之后的 modules 目录,如果内部包含的目录名为 src,必须修改成 zlib,至少,45.0.2版本存在这个 bug。The following example needs to create the x64 version package by using the parameter --target=x86_64-pc-mingw32 --host=x86_64-pc-mingw32. At the same time, the parameter --disable-jemalloc should be added, or there is error when the program runs with the version compiled by the vs2013. If the cl reports the internal error when compiling the current vs2013 version, the vs2013 should be upgraded to the latest version. In addition, the modules directory after the source package is expanded should be noted. It's internal directory named src must be rename to zlib. At least, this bug existes in the version 45.0.2.

5. 二进制包创建完成之后,将操作步骤中建立的 build_OPT.OBJ 目录,连同目录名整个拷贝到 limax源码目录中的 javascript/SpiderMonkey/mozjs45 目录下。After the binary package has been created, copy the build_OPT.OBJ directory created in the operating steps and the directory name totally to the javascript/SpiderMonkey/mozjs45 directory in the limax source code directory.

113

Page 114: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

6. 二进制包编译成 DEBUG版本,应用必须编译成 DEBUG版本;二进制包编译成 RELEASE版本,应用也必须编译成 RELEASE版本,否则应用可能在运行时出现 CRT错误。When the binary package is built to DEBUG build, the application must be built to DEBUG build; and when the binary package is built to RELEASE build, the application also must be built to RELEASE build, or there might be the CRT error during the application running.

开发示例(跟之前的例子一样,还是使用 vs2013)Example (as with the previous example,

using vs2013)

1. 将前面的 example.html 文件中的 var providers,var limax 两个变量的定义拷贝出来,贴到 example.js 文件中,将 console.log,console.err 全部替换为 print。Copy the definition of the two variables var providers, var limax in the example.html, past to the example.js, and replace the console.log and console.err with print.

2. 创建 C++解决方案和控制台应用项目,应用项目的平台配置为 x64。Create the C++ solution with console application project, and configure the platform of the application project as x64.

3. 解决方案中加入 Limax源码中 cpp 目录下的 limax 项目,javascript/SpiderMonkey 目录下的 limax.js 项目。Add the limax project in the cpp directory and limax.js project in the javascript/SpiderMonkey directory of the Limax source code into the solution.

4. 添加当前项目的引用,引用 limax,limax.js 两个项目。Add the reference of the current project, refer to the limax and limax.js projects.

5. 正确配置当前应用项目的头文件查找路径,包括 javascript引擎的头文件查找路径(参考 limax.js 的 配 置 ) , Limax 源 码 目 录 下 的 cpp/limax/include 以 及 javascript/SpiderMonkey/includeCorrectly configure the header file search path of the current application project, including the header file search path of the javascript engine (reference to the configuration of the limax.js), cpp/limax/include and javascript/SpiderMonkey/include in the Limax source code directory.

6. 正确配置库查找路径和依赖库,(参考 limax.js 的配置)Correctly configure the library search path and dependent library (reference to the configuration of the limax.js).

程序代码source code

#include "stdafx.h"#include <limax.h>#include <iostream>#include <jsapi.h>#include <js/Conversions.h>#include <limax.js.h>using namespace limax;

114

Page 115: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

class MyJsApp : public EndpointListener{

std::shared_ptr<JsEngine> engine;public:

MyJsApp(){

engine = JsEngine::create(1048576);engine->execute([&](JSRuntime* rt, JSContext* cx, JS::HandleObject global){

JS_SetErrorReporter(rt, [](JSContext *cx, const char *message, JSErrorReport *report){

runOnUiThread([message, report](){fprintf(stderr, "%s:%u:%s\n", report->filename ? report->filename : "[no

filename]", (unsigned int)report->lineno, message);});

});JS_DefineFunction(cx, global, "print", [](JSContext *cx, unsigned argc, JS::Value

*vp){JS::CallArgs args = JS::CallArgsFromVp(argc, vp);std::string s;for (unsigned i = 0; i < argc; i++){

char *p = JS_EncodeString(cx, JS::RootedString(cx, JS::ToString(cx, args[i])));

s += p;s += ' ';JS_free(cx, p);

}if (s.length() > 0)

s.pop_back();runOnUiThread([s](){ puts(s.c_str()); });args.rval().setUndefined();return true;

}, 0, JSPROP_READONLY | JSPROP_PERMANENT);});auto sehptr = JsCreator::createScriptEngineHandle(engine, "example.js");Endpoint::openEngine();auto config = Endpoint::createEndpointConfigBuilder(

"127.0.0.1", 10000, LoginConfig::plainLogin("testabc", "123456", "test"))->scriptEngineHandle(sehptr)->build();

Endpoint::start(config, this);115

Page 116: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

}~MyJsApp(){

std::mutex mutex;std::condition_variable_any cond;std::lock_guard<std::mutex> l(mutex);Endpoint::closeEngine([&](){

std::lock_guard<std::mutex> l(mutex);cond.notify_one();

});cond.wait(mutex);

}void run() { for (int i = 0; i < 200; i++) { Sleep(10); uiThreadSchedule(); } }void onManagerInitialized(EndpointManager*, EndpointConfig*) { std::cout <<

"onManagerInitialized" << std::endl; }void onManagerUninitialized(EndpointManager*) { std::cout << "onManagerUninitialized"

<< std::endl; }void onTransportAdded(Transport*) { std::cout << "onTransportAdded" << std::endl; }void onTransportRemoved(Transport*){ std::cout << "onTransportRemoved" << std::endl; }void onAbort(Transport*) { std::cout << "onAbort" << std::endl; }void onSocketConnected() { std::cout << "onSocketConnected" << std::endl; }void onKeyExchangeDone() { std::cout << "onKeyExchangeDone" << std::endl; }void onKeepAlived(int ping) { std::cout << "onKeepAlived " << ping << std::endl; }void onErrorOccured(int errorsource, int errorvalue, const std::string& info) { std::cout <<

"onErrorOccured " << errorsource << " " << errorvalue << " " << info << std::endl; }void destroy() {}

};

int _tmain(int argc, _TCHAR* argv[]){

{MyJsApp().run();

}uiThreadSchedule();return 0;

}

如果通过 vs2013 运行程序,需要将之前创建的 example.js放置到项目目录下,如果在控制台中运行,应该放置到 exe所在目录。If the program runs on the vs2013, the created example.js should be placed into the project directory. If the program runs on the console, it should be placed into the the same directory as the exe.

启动服务器,运行客户端,获得结果:116

Page 117: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Launch the server, run the client, and get the result:

onManagerInitializedonSocketConnectedonKeyExchangeDoneonTransportAddedonKeepAlived 1v100.share.MyTemporaryView.onopen [object Object] 3 36864v100.share.MyTemporaryView.onchange [object Object] 36864 _var0 Hello 36864 NEWv100.share.MySessionView.onchange [object Object] 36864 var0 Hello 36864 NEWv100.share.MySessionView.onchange [object Object] 36864 var0 99999 REPLACEv100.share.MyTemporaryView.onchange [object Object] 36864 _var0 99999 REPLACEonTransportRemovedonManagerUninitializedlimax close

这个运行结果与之前的版本均一致。This running result is consistent with the previous versions.

代码说明 Description of the code

1. JsEngine::create(1048576)创建了 javascript引擎,使用 1M内存,必须根据应用规模估计合适的内存大小,内存不足可能引发 javascript虚拟机 OutOfMemory。JsEngine::create(1048576) creates the javascript engine and uses 1M memory. The appropriate memory size must be estimated based on the application size, or the out of memory might cause the javascipt virtual machine OutOfMemory.

2. JsEngine 通过线程服务器的方式包装了 SpiderMonkey引擎,提供两个方法。execute和wait。execute提交 javascript 任务给 JsEngine立即返回,wait提交给 javascript 任务给JsEngine,等待该任务执行完成后返回。JsEngine释放前,保证所有通过 execute提交的任务全部执行完毕。JsEngine encapsulates the SpiderMonkey engine through the thread server and provides two methods: execute and wait. Execute submits the javascript task to JsEngine and returns immediately, wait submits the javascript task to JsEngine and returns after this task finishs. Before the JsEngine releases, all the tasks submitted by the execute are ensured to be completed.

3. JsEngine 创建之后立即安装了 javascript引擎的 ErrorHandle;在全局空间中添加了 print方法,print 方法在 example.js 中用来输出结果。The ErrorHandle of the javascript engine is immediately installed after the JsEngine is created; the print method is added into the global space, and the print method is used to output the result in the example.js.

4. 注意代 码 中 的 几 处 runOnUIThread 调用 ,显示结 果 通 过 UI 线程 输 出 ,而不 是 在JsEngine线程中输出。Please pay attention to the several runOnUIThread call of the code, the display result is

117

Page 118: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

output through UI thread, not the JsEngine thread.5. 主线程被作为 UI线程使用,注意MyJsApp.run 方法,这和前面的代码稍有区别,不是

直接 Sleep 2000ms,而是每 10ms 就通过 uiThreadSchedule()调度一次,保证 JsEngine线程提交过来的输出方法被执行。_tmain函数的实现和之前的代码也稍有区别,这样的实现保证了 "limax close" 这一行能够正确输出,原因在于, MyJsApp.run 执行完毕,MyJsApp对象析构时 Endpoint.closeEngine,导致 example.js 中的 ctx.close()被执行,UI线程任务队列中被安排了 print 任务,最后需要再调度一次 UI线程,完成这些任务。The main thread is used as the UI thead and the MyJsApp.run method should be noted, which is different with the previous code, not directly Sleep 2000ms, but schedule once every 10ms through uiThreadSchedule() to ensure the output method submitted by the JsEngine thread is executed. The implementation of the _tmain function is a little different with the previous code, which ensures the "limax close" could be correctly output. The cause is that after the MyJsApp.run is executed, MyJsApp Endpoint.closeEngine when destructing the object and causes the ctx.close() in the example.js is executed, the print task is arranged in the UI thread task queue and finally needs to schedule an UI thread to finish these tasks.

Javascript/C#客户端 (SpiderMonkey) Javascript/C# client (SpiderMondey)

参考前一节准备 javascript 引擎库 Refer to the previous section to prepare the javascript

engine

开发示例 Development example

1. 将前面的 example.html 文件中的 var providers,var limax 两个变量的定义拷贝出来,贴到 example.js 文件中,将 console.log,console.err 全部替换为 print。Copy the definition of the variables var providers and var limax of the example.html, paste to the example.js, and replace the console.log and console.err with print.

2. 创建 C#解决方案和控制台应用项目,应用项目的平台配置为 x64。Create the C# solution and console application project, and set the application project's platform as x64.

3. 解决方案中加入 Limax源码中 csharp 目录下的 limax 项目,javascript/SpiderMonkey 目录下的 jscs,clrjs,nativejs 项目。Add the limax project in the csharp directory, jscs project, clrjs project and nativejs project in the javascript/SpiderMonkey of the Limax source code to the solution.

4. 添加当前项目的引用,引用 limax,jscs,clrjs三个项目。Add the reference of the current project, reference to the limax project, jscs project and clrjs project.

程序代码Source code:

118

Page 119: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

using System;using System.IO;using System.Text;using System.Threading;using limax.net;using limax.script;using limax.endpoint;using limax.endpoint.script;

namespace ConsoleApplicationJsCS{ class MyListener : EndpointListener { public void onAbort(Transport transport) { Exception e = transport.getCloseReason(); Console.WriteLine("onAbort " + transport + " " + e); } public void onManagerInitialized(Manager manager, Config config) { Console.WriteLine("onManagerInitialized " + config.GetType().Name + " " + manager); } public void onManagerUninitialized(Manager manager) { Console.WriteLine("onManagerUninitialized " + manager); } public void onTransportAdded(Transport transport) { Console.WriteLine("onTransportAdded " + transport); } public void onTransportRemoved(Transport transport) { Exception e = transport.getCloseReason(); Console.WriteLine("onTransportRemoved " + transport + " " + e); } public void onSocketConnected() { Console.WriteLine("onSocketConnected"); } public void onKeyExchangeDone() { Console.WriteLine("onKeyExchangeDone"); }

119

Page 120: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

public void onKeepAlived(int ms) { Console.WriteLine("onKeepAlived " + ms); } public void onErrorOccured(int source, int code, Exception exception) { Console.WriteLine("onErrorOccured " + source + " " + code); } }

class Program { private static void start(JsContext jsc) { string init = File.ReadAllText("example.js", Encoding.UTF8); Endpoint.openEngine(); EndpointConfig config = Endpoint.createEndpointConfigBuilder(

"127.0.0.1", 10000, LoginConfig.plainLogin("testabc", "123456", "test")).scriptEngineHandle(new JavaScriptHandle(jsc, init)).build();

Endpoint.start(config, new MyListener()); } private static void stop() { object obj = new object(); Action done = () => { lock (obj) { Monitor.Pulse(obj); } }; lock (obj) { Endpoint.closeEngine(done); Monitor.Wait(obj); } } static void Main(string[] args) { JsContext jsc = new JsContext(); start(jsc); Thread.Sleep(2000); stop(); jsc.shutdown(); } }}

120

Page 121: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

将之前创建的 example.js和 javascript/SpiderMonkey/mozjs45/build_OPT.OBJ/dist/bin/下的所有 dll放置到 exe所在目录。Put the previous created example.js and all dll in the javascript/SpiderMonkey/mozjs45/build_OPT.OBJ/dist/bin/ directory to the same directory as the exe.

启动服务器,运行客户端,获得结果:Launch the server, run the client, and get the result:

onManagerInitialized DefaultEndpointConfig EndpointManagerImplonSocketConnectedonKeyExchangeDoneonTransportAdded limax.net.StateTransportImplv100.share.MySessionView.onchange [object Object] 36864 var0 Hello 36864 NEWv100.share.MyTemporaryView.onopen [object Object] 190 36864v100.share.MyTemporaryView.onchange [object Object] 36864 _var0 Hello 36864 NEWonKeepAlived 12v100.share.MySessionView.onchange [object Object] 36864 var0 99999 REPLACEv100.share.MyTemporaryView.onchange [object Object] 36864 _var0 99999 REPLACEonTransportRemoved limax.net.StateTransportImpl System.Exception: channel closed manuallylimax close nullonManagerUninitialized EndpointManagerImpl这个运行结果与之前的版本均一致。This result is consistent with the previous versions.

代码说明 Description of the code

1. 受限于 SpiderMonkey 的线程模型,不得不提供一个 JsContext 包装类,JsContext 创建了一个线程与 Js操作对象关联,Js操作对象支持 C#与 Javascript 交互,详见附录 CLR/Javascript 一节。所有操作完成以后,需要 shutdown JsContext对象。Restricted by the thread model of the SpiderMonkey, have to provide a JsContext encapsulated class, the JsContext creates a thread to associate with the Js operating object. Js operating object supports the interaction between the C# and javascript. Refer the appendix CLR/Javascript for the details. After all the operations have been finished, it needs to shutdown JsContext object.

客户端开发总结 The conclusion of the client development

1. Limax 客户端库覆盖了大多数常见开发环境。The Limax client library covers the most common development environment.

2. 线程的使用上,多数情况下需要在创建配置时调用 .executor 设置应用自己的消息通告线程,所有 EndpointListener消息,View消息均通告给该线程。In the usage of the thread, in the most conditions, it needs to call the .executor to set the

121

Page 122: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

message notification thread for the application its own when creating the configure. All the EndpointListener message and the View message would be notified to this thread.

3. 服务器端 View 的改变,可以在各种形式的客户端上驱动 Listener,报告 View 数据变动这里要注意一个问题,同一 View 的同一字段上如果注册多个 Listener,Listener 的通告顺序与注册顺序之间关系没有保证。The change of the view in the server could drive the Listener in all kinds of clients and report the data change of the view. A problem should be noted that if multiple Listener of the same field of the same View need be registered, the relationship between the notification order and register order of the Listener has no guarantee.

4. 再次强调,静态模式,Variant 模式,脚本模式,在各种类型化语言实现中都能混合使用。混合使用的实际意义不大,而且可能导致开发上的混乱,除非有非常必要的理由,不建议如此使用。Emphasize agian, that the static mode, the Variant mode, and the script mode could be mixedly used in various typed language implementation. The mixed usage has no much actual meaning and could lead to the confusion of the development. So unless it is necessary, we suggest that do not use like this.

高级特性 Advanced Characteristic

为了提升应用性能,View提供了一系列高级特性,扩展了 View 的概念,定义了字段采集的概念,实现了数据裁剪。实际应用环境下,通过裁剪数据可以大大节省网络带宽,防止网络拥塞。是否能够裁剪,取决于逻辑功能的设计,比如,使用了数据裁剪,时序问题自然就失去了讨论的意义,所以严格时序的应用,不应该进行裁剪。In order to improve the performance of the application, the View provides a serial of advanced characteristics, extends the concepts of the View, defines the concept of the field collecition, and implementing the data clipping. In the environment of the actual application, the network bandwidth could be largely saved through clipping data to prevent the network congestion. Whether the clipping could be implemented depends on the design of the logical function. For example, when using the data clipping, it is no need to dicuss the time sequence issue. So the application which requires the strict time sequence should not use the clipping.

基本概念 The basic concept

数据采集 Data collection

View 的发送数据时机到达时进行的字段信息收集。The field information collection processed when the data sending opportunity of the View arrives.

122

Page 123: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

过程采集 Process collection

采集的结果是从上一次采集结束算起到当前时间点为止的所有更新历史,两次采集时间范围内,同一字段更新了多少次,就有多少个结果,如果两次 update 的数据没有变化,第二次被简化为一个 Touch。The collection result is the all update history from the end of the last collection to the current time point. In the time scope of the two collections, how many times the same filed updates have the equal amount results. If the data of the two update has no change, the second update is simplified as a Touch.

集合采集(Limax 基本方式)Set collection (the basic mode of the Limax)

集合采集只应用于过程采集,集合采集将过程采集器聚合起来,内部过程采集器每一次收集的数据与过程采集器本身的作为一个二元组,被顺序记录下来,所以集合采集可以保证采集字段之间的更新顺序。The set collection is only applied to the process collection. The set collection aggregates the process collectors. The data each collected by the internal process collector combining with the process collector itself as a two-tuples is sequentially recorded. So the set collection could guarantee the update sequence of the collected fields.

状态采集(Limax扩展方式)Status collection (the extended mode of the Limax)

采集的结果是最近的一次更新结果,之前的数据全部废弃掉,状态采集的字段独立于其它字段,不存在时序上的概念,也不生成 Touch。逻辑功能许可的前提下,比起过程采集,可以过滤掉大量数据,节省了网络带宽。The collection result is the last update result and all the previous datum are discarded. The field of the status collection is independent of the other fields, has no concept of the time sequence, and does not generate the Touch. In the precondition that the logical function allowes, compared with the process collection, the status collection could discard a mass of data and save the network bandwidth.

Immutable

非易变属性,View 描述中的 Variable元素对应了具有非易变属性的字段,Bind引用的Xbean 中定义的 map,set 类型之外的字段也具有非易变属性,这样的字段不能修改,只能被原子的替换。非易变的字段,只能使用非易变采集器采集数据。The immutable property, the Variable element described by the View corresponds to the filed with the immutable property, and the fields except for the map and set type defined by the Xbean which is referred by the Bind have the immutable property. These fileds could not be modified and only could be atomically replaced. The immutable filed only could use the immutable collector to collect data.

Mutable

易变属性,View 描述中的 Bind元素对应了具有易变属性的字段,Bind引用中的 Xbean中定义的 map,set 类型的字段也具有易变属性,这样的字段可以被修改,例如,map 可以

123

Page 124: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

在上面直接增删,而map对象本身的引用并没有改变。易变的字段,多数情况下使用易变采集器采集数据,特殊情况下也可以使用非易变采集器数据,例如,map尽管可以直接增删内容,但是整体的替换也是允许的。易变属性的使用使得数据的局部更新成为可能,只发送改变的数据,有利于节省网络带宽。Immutable,Mutable由系统根据配置自动选择,无需用户干预。The mutable property, the Bind element described by the View corresponds to the field with the mutable property, and the fields of the map and set type defined by the Xbean which is referred by the Bind have the mutable property. These fields could be modified. For example, the map could directly execute the add/delete operation, however the reference of the map object itself has no change. The mutable field uses the mutable collector to collect data in most cases, and also could use the immutable collector to collect data in particular case. For example, even though the map could directly add/delete content, the entire replacement of reference is also allowed. The usage of the mutable property brings the possibility of the partial update of the data. It is benefical to save the network bindwidth when only sending the changed data. The Immutable and Mutable are automatically selected by the system according to the configure and the user does not need to intervene.

Tick

字段数据采集的周期,对于集合采集,一个周期内的所有变化,连同顺序被采集出来;对于状态采集,最后一个状态被采集出来。The period of the field data collection, for the set collection, all the change in a period together with the sequence is collected; for the status collection, the last status is collected.

xml 描述扩展 xml description extension

<view name="TestTempView" lifecycle="temporary" tick="20"><variable name="var1" type="int" clip="true" /><bind name="bindfirst" table="roles" clip="true" snapshot="true" /><subscribe name="_var1" ref="gs.for_session.firstview.var1" snapshot=”true”/>

</view>View元素的 tick属性,对于全局 View 无意义。View元素内,variable,bind 两个元素

上,提供了两个属性,clip和 snapshot;临时 View 的 subscribe字段只提供 snapshot属性。The tick attribute of the View element has no meaning to the global View. In the View element, the variable and bind elements provide two attributes, clip and snapshot; the subscribe field of the temporary View only provides the snapshot attribute.

tick属性 tick attribute

配置 SessionView和 TemporaryView 生成代码的 tick,上面设置为 20,默认为 10ms,程序内能够通过 setTick修改。Configure the SessionView and the TemporaryView to generate the tick of the source code. The above configuration is 20, and the default value is 10ms. It could be modified through setTick in the program.

124

Page 125: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

clip属性 clip attribute

定 义 了 clip 属性 , gen 目 录 下 _<ViewName>.java 中将生 成 一 个 protected boolean permitXXX(Type p)函数,默认返回 true。Define the clip attribute, the protected boolean permitXXX(Type p) function will be generated in the _<ViewName>.java file in the gen directory, and the default return value is true.

src 目录的<ViewName>.java 文件里,可以重载该函数,根据应用功能要求决定返回true 或者 false。This function could be overrided in the <ViewName>.java file in the src directory, and returns true or false according to the requirement from the application.

如果返回 false,XXX字段的修改将被忽略掉。If returns false, the modification of the XXX field will be ingored.比如,通过 View提供一个可丢弃的数据源,可以在这个点上应用令牌桶之类的算法进

行流量控制。For example, a discardable data source is provided through View, the algorithm such as token-bucket could be used to control the traffic here.

与其它的 View 方法不同的是,permitXXX 方法不一定在 View对应的线程中调用,所以使用该方法需要注意线程安全性,传入的参数可以为过滤提供参考,但是不可修改,不可存储,除非另外拷贝一份。Different from the other View methods, the permitXXX method may not be called in the thread relative to the View. So the thread-safe problem should be considered when using this method. The input parameter could provides the reference for the filter, but could not be modifed and stored, except a copy.

3 种 View 的描述中都可以应用 clip。The description of the three kinds of View could apply the clip.

特别的,Bind元素如果定义了 clip属性,生成的代码将选择 Immutable采集方式,这是因为,工作在 Mutable 方式下,可能进行数据的部分更新,这样的更新如果被丢弃,将破坏服务器,客户端的数据一致性。例如,一个 Xbean 包含两个字段 A,B,第一次更新A,第二次更新 B,如果第一次更新被丢弃,第二次更新发送给客户端,这种情况下,服务器,客户端看到的 A字段必然不一致。然而,只要用 Immutable 方式对待这个 Xbean,每次都发送全部数据,这种情况下,即使第一次更新被丢弃,第二次更新也包含了之前的全部修改。In particular, if the Bind element defines the clip attribute, the generated source code will choose the Immuable collection mode, because in the Mutable mode, the partial update of the data is possible. If this update is discarded, the consistency of the data between the server and the client will be destroyed. For example, a Xbean includes A and B fields, the first time update A, and the second time update B. If the first update is discarded and the second update is sent to the client, the A filed in the client and the server is inconsistent in this case. However, only if the Immutable mode is used to process this Xbean to send all the data each time, even though the first update is discarded, the second update also includes all the previous update in this case.

snapshot属性 snapshot attribute

声明该字段为一个状态采集字段,关联一个状态采集器进行管理。125

Page 126: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Declare this field as a status collection field, and associate to a status collector to be managed.对于全局 View,snapshot 的设定没有意义,全局 View 的数据发送时间点由用户自己决

定,发送的都是最新一个版本的数据,所以全局 View 的字段都使用状态采集器。更进一步,Mutable采集决定的局部更新对于全局 View 的字段也没有意义,最终,全局 View 的所有字段都选择 Immutable 状态采集器。For the global View, the setting of the snapshot has no meaning. The data send timepoint of the global View is determined by the user, and the latest data is sent. So the field of the global View uses the status collector. More, the partial update determined by the Mutable collection has no meaning to the field of the global View. Finally, all the field of the global view choose the Immutable status collector.

SessionView 的字段采集方式与对应的临时 View 的订阅字段的采集方式不相干,例如,TV._a 订阅了 SV.a,SV.a采用过程采集,TV._a 采用状态采集没有任何问题,实现上同一份更新数据被提交给两个 View 各自的字段采集器上,最终输出结果取决于采集器类型。The collection mode of the SessionView's field is irrelevant to the collection mode of the subscribed field of the corresponding temporary View. For example, TV._a subscribes SV.a, and there is no any problem for SV.a to use the process collection, and TV._a to use the status collection. In the implemention, the same updated data is provides to the each field collector of two View. The final output result is determined by the collector type.

没有定义 snapshot属性的字段集合被提交给一个集合采集器管理。The field set without the snapshot attribute is provided to a set collector to be managed.

采集器选择 Collector selection

1. 对于 SessionView,临时 View:For the SessionView and temporary View:

Variable Bind Subscribe Subscribe/snapshot="trueVariable Bind Variable Bind

PCS PCS PCS PCS ISC MSCnapshot="true" ISC MSC PCS PCS ISC MSCclip="true" PCS PCS PCS PCS ISC ISCsnapshot="true",clip="true" ISC ISC PCS PCS ISC ISC

第一列表示 Variable/Bind 上定义的属性,最后两个 Subscribe列只对临时 View 有意义,用是否定义 snapshot属性区分两种情况,临时 View 上 Subscribe 关联的采集器,每个成员一份。The first column expresses the attribute defined in the Variable/Bind. Tha last two Subscribe columns only has the meaning to the temporary View, and uses true/false to define the snapshot attribute to distinguish. Each member has a collector associated to the Subscribe in the temporary View.PCS:集合采集器,每次添加的数据被排队。 the set collector, the data added every time is queued.ISC:Immutable 状态采集器,每次添加数据,使用新值简单替换前一个值。 the Immutable status collector, when adding data every time, the new value is used to simply replace the previous one.

126

Page 127: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

MSC:Mutable 状采集器,每次添加数据,新值归并到前一个值中。 the Mutable status collector, when adding data every time, the new value is merged to the previous one.实际采集过程为:首先读取 PCS,然后逐个读取 ISC/MSC,对于临时 View 的订阅字段,为本 tick内进行过订阅字段的更新的成员,逐个执行上述过程。The actual collection process: first read the PCS, then read the ISC/MSC one by one. For the subscribed field of the temporary View, the member who updates the subscribed field in this tick execute the above process one by one.

2. 全局 View所有字段,全部使用 ISC。All the fields of the global View use the ISC.

大数据集合广播 Large-scale data set broadcast

全局 View扩展 Global View extension

提供一个保护方法 protected void onUpdate(String varname);重载以后可以获知某个字段已经发生了变化,这个地方提供一个 syncToClient 的机会,将数据发送给用户。A protected void onUpdate(String varname) method is provided; after overriding, it could know that some filed has changed. A syncToClient chance is provided here to send the data to the user.

不过,需要注意的是,异步触发方式发送出去的数据可能不是当前更新的数据,而是最新的数据。这是因为全局 View 上的操作,都统一调度到到全局 View对象自身的线程上,例如,同一字段上连续更新同一字段两次,通过 onUpdate触发发送,setA 之后 setB,setA触发的 syncToClientA将放到 setB 之后,所以发送的数据为 B,B触发的 syncToClientB 还是发送 B。这个例子从另一个角度说明了全局 View 的字段属于状态采集字段。However, it should be paid attention is that the data sent by asynchronous trigger mode might not be the current updated data, but the lastest data. It is because that the operation on the global View is totally scheduled to the thread of the global View object itself. For example, the same field is continuely updated twice, and sent through onUpdate trigger, first setA then setB. The syncToClientA triggered by setA is placed behind the setB, so the sent data is B, and the syncToClientB triggered by B also sends the B. This example explains that the field of the global View is the status collection field from the other side.

松散临时 View Loose temporary View

如果临时 View 的定义中没有包含任何订阅,生成代码中可以看见 createLooseInstance()方法,该方法可以创建松散临时 View。松散临时 View将标准临时 View 的 Membership同步功能裁剪掉,在操作大规模 Membership 的情况下可以提高广播性能,减小网络开销。If the definition of the temporary View has no any subscription, the createLooseInstance() could be found in the generated source code. This method could create the loose temporary View. The loose temporary View will clip the Membership synchronization funtion of the standard temporary View. The broadcast perfomance could be improved and the network bindwidth will be reduced when operating the large-scale Membership.

MyTemporaryView tview = MyTemporaryView.createLooseInstance();服务器端看,松散临时 View 与标准临时 View 在使用上没有差别。

127

Page 128: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Seen from the server, there is no any difference between the loose temporary View and the standard temporay View.从客户端看,Membership 的变动被裁减掉。所以,onOpen被调用时传入空 sessionid

列表;onAttach,onDetach 方法永远不会被调用。Seen from the client, the change of the Membership is clipped. So the empty sessionid list is imported when calling onOpen, and onAttach and onDetach methods are never be called.

如上面的例子,异步方式的全局 View广播,需要仔细控制时序,否则可能导致不合预期的结果,如果是同步方式,不使用 onUpdate,则不存在这样的问题,比如执行序列setA,syncToClientA,setB,syncToClientB,4 个任务会正确排序到全局 View线程上。异步方式,又必须保证按正确的序列发送,相应发送字段采用了过程采集方式的松散临时 View是一个比较好的选择。Just like the above example, the asynchronous global View broadcast should carefully control the time sequence, or the unexpected result might be generated. If it is synchronous and does not use the onUpdate, this kind of issue will not exist. For example, executing the sequence setA, syncToClientA, setB, syncToClientB, these four tasks will correctly be sorted to the global View thread. For the asynchronous mode and the correct sending sequence is required, it is a better choice to use the loose temporary View with the process collection mode for the relative sending field.

状态采集方式下,全局 View 的用户可控程度更高,Membership集合非常大的情况下,可以将Membership 分割成多个片段,每个片段之间插入一些延迟逐个发送,可以有效防止网络拥塞。In the status collection mode, the user controllability of the global View is higher. For the large-scale Membership set, the Membership could be divided to many segments, and the delay is inserted into each segment and sent one by one, which could effectvely prevent the network congestion.

设计上必须注意,大数据集广播的频率一定不能太高,否则数据总量决定了网络拥塞还是难以避免。It should be considered from the design that the frequence of the large-scale set broadcast should not be too high, or the data total amount determines that the network congestion could not be avoided.

分区临时 View Partitional temporary View

生 成 代 码 中 可 以 看 见 createInstance(int partition); 方 法 , 如 果 存 在createLooseInstance(),createLooseInstance(int partition);也会同时存在。The createInstance(int partition) method could be found in the generated source code. If there is createLooseInstance(), the createLooseInstance(int partition) should exist at the same time.

用分区的方式对Membership 进行划分,总是选择包含较少成员的分区接受新成员,所以分区成员数量相对均衡。每个分区维护一个私有的数据投递队列。Partition the Membership by using the partition mode, the partition with the less member is always selected to accept the new member so that the partition member amount is relatively balanced. Each divison maintains a private data delivery queue.每次 Tick,进行一次数据采集,往队列追加数据,刷新当前分区队列,最后将当前分

128

Page 129: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

区切换成下一个分区。Each Tick processes a data collection, appends the data to the queue, refreshes the current partition queue, and finally switch the current partition to the next one.对于集合采集,采集的数据被追加到所有分区的队列中,所有的数据将会发送给所有

用户,只不过时机不同。For the set collection, the collected data is apended to all the partition queues, and all the data will be sent to all the users just in different occasion.对于状态采集,采集数据仅追加到当前分区队列,每次 Tick获取的状态数据,只发送

给当前分区的这一部分用户。For the status collection, the collected data is just appended to the current partition queue. The status data obtained by each Tick is only sent to this partial user of the current partition.普通临时 View 是只包含 1 个分区的分区临时 View 的特例。

The normal temporary View is the special case of the partition temporary View with only one partition.对于任何一个用户,等效延迟为 tick * partition,如果设计决定单个用户最长接受 TICK

延迟,创建分区临时 View 之后,应该 view.setTick(TICK/partition);For any user, the equivalent delay is tick * partition. If the design determines the longest acceptable Tick delay for each single user, the view.setTick(TICK/partition) should be called after creating the partition temporary View.从原理可以推论,使用分区临时 View,可以减少突发性的网络拥塞。

Inferred from the theory, using the partition temporary View could reduce the burst network congestion.

比较 Comparison

1. 大数据集广播,需要通告成员信息的情况下,从网络负担的角度看,分区临时 View总是好于普通临时 View,不过为了进行分区,CPU,内存开销会更大。The large-scale data set broadcast, when it needs to announce the member information, seen from the network load, the partition temporary View is always better than the standard temporay View. But the CPU and memory cost is bigger.

2. 大数据集广播,不需要通告成员信息的情况下,可以按下表判断The large-scale data set broadcast could make judgement according to the below table in the condition without announcing the member information.

全局 View 松散临时 View 松散分区临时 View过程采集 差,根本不适合 中 好状态采集 很好,可控性高 中 好,自动控制

Global View Loose temporaryView

Loose partition temporary View

Process collection Bad, not suitable Medium GoodStatus collection Excellent, high

controllbilityMedium Good, automatic controll

129

Page 130: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

总结 Summary

1. 使用 snapshot/clip属性定义能够有效裁减数据传输,减少带宽占用The snapshot/clip attributes could effectively cut down the data transmission, and reduce the bindwidth occupancy.

2. 过程采集需要保留所有历史数据,tick 到达时发送,所以 tick延迟越长,内存开销越大。The process collection needs to keep all the history data, and send when the tick arrives. So the tick delay is longer, the memory cost is larger.

3. Mutable 状态采集需要保留所有历史修改信息,在 tick 到达时归并发送,所以 tick延迟越长内存开销越.大,并且突发的 cpu占用也越大。The Mutable status collection needs to keep all the history updated information, and merge and send when the tick arrives. So the tick delay is longer, the memory cost is larger and the burst CPU cost is larger.

4. Immutable 状态采集是最节省内存和 cpu 的方式——直接丢弃之前的数据,也不存在归并需求。The Immutable status collection is the mode which saves memory and CPU cost most -- directly discards the previous data and has no merge requirement.

5. 对于结构简单,数据量较小的的 Xbean,有时候可以利用 clip 的副作用,将本来属于Mutable采集的 Bind字段强制改变成 Immutable采集,减少一些 cpu和内存的开销。For the Xbean with simple structure and small data, the side effect of the clip could be used to compulsively change the Bind field of the Mutable collection to the Immutable collection and reduce the expense of the CPU and the memory.

6. 一般情况下有效 tick(也就是 tick*partition)决定了内存占用,有效 tick越大,内存占用越多;有效 tick内,分区越多,cpu占用越多——需要进行 partition次处理。Normally, the effective tick (tick*partition) determines the memory usage, the longer effective tick, the more memory usage; in the effective tick, the more partition, the more CPU expense -- partition process is required.

7. 大 多 数情况下 ,只要 确认了 某 个字段可 以 接受状 态 数 据 ,那么修改 xml , 设 置snapshot属性,适当调整 tick,无需修改任何代码,即可有效提升性能。In most case, only confirm that some field could accept the status data, through modifying the xml, setting the snapshot attribute, properly adjusting the tick, the performance could be effectively improved without modifying any code.

极简模式客户端 Minimalist mode client

Limax扩展了 Websocket 协议,支持极简模式客户端的实现。极简模式客户端可以看作是 脚 本 模 式 客 户 端将原生 协 议替换 为扩展 Websocket 协 议 的 结 果 , 简化掉EndpointListener,同时又进行了相应的增强,概念上更贴近 HTML5浏览器。Limax提供的Websocket 服务器在同一 websocket 配置端口上,同时支持极简模式客户端与 HTML5浏览器。

130

Page 131: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

The Limax extends the Websocket protocol, and supports the implementation of the minimalist mode client. The minimalist mode client can be regarded as the result that the script mode client replaces the native protocol with the extended Websocket protocol, simplifies the EndpointListener, makes relevant enhancement and is conceptually closer to the HTML5 browser. The minimalist mode client and the HTML5 browser can connect to the same port configured by Limax Websocket server simultaneously.

客户端示例 The example of the client

这里延续客户端开发一节中的示例。C++,C#,Java版本的实现在概念上完全相同,这里仅示例 C++版本实现。The example is from the client development section. The implementation of the C++, C# and Java versions is conceptually identical. Here only the example of the C++ version implementation is given.

#include "stdafx.h"#include <limax.h>#include <iostream>#include <lua.hpp>#include <limax.lua.h>#include <set>#include <octets.h>using namespace limax;class MyLuaApp{

lua_State* L;public:

MyLuaApp(){

L = luaL_newstate();luaL_openlibs(L);int e = luaL_dofile(L, "callback.lua");if (e != LUA_OK){

std::cout << "lua load 'callback.lua' failed! " << lua_tostring(L, -1) << std::endl;exit(-1);

}auto sehptr = LuaCreator::createScriptEngineHandle(L, -1, false, [this](int s, int e, const

std::string& m){ onErrorOccured(s, e, m); });if (!sehptr)

exit(-1);lua_pop(L, 1);

131

Page 132: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Endpoint::openEngine();Endpoint::start("127.0.0.1", 10001, "testabc", "123456", "test", sehptr);

}~MyLuaApp(){

std::mutex mutex;std::condition_variable_any cond;std::lock_guard<std::mutex> l(mutex);Endpoint::closeEngine([&](){

std::lock_guard<std::mutex> l(mutex);cond.notify_one();

});cond.wait(mutex);

}void run() { Sleep(2000); }void onErrorOccured(int errorsource, int errorvalue, const std::string& info) { std::cout <<

"onErrorOccured " << errorsource << " " << errorvalue << " " << info << std::endl; }};

int _tmain(int argc, _TCHAR* argv[]){

MyLuaApp().run();return 0;

}

可以类比客户端开发->Lua/C++客户端一节,MyLuaApp 无需再实现 EndpointListener 接口,此外,直接使用方法 Endpoint.start 的极简模式客户端版本启动连接,无需再创建相应EndpointConfig。Comparing the client development -> Lua / C ++ client section, the MyLuaApp no longer need to implement the EndpointListener interface. In addition, directly using the minimalist mode client version of the Endpoint.start to launch the connection no longer need to create the revelant EndpointConfig.

启动服务器,运行客户端,获得结果Launch the server, run the client, and get the result.

v100.share.MyTemporaryView.onopen table: 00403000 2 table: 00403028v100.share.MyTemporaryView.onchange table: 00403000 36864 _var0 Hello 36864 NEWv100.share.MySessionView.onchange table: 003D5360 36864 var0 Hello 36864 NEWv100.share.MyTemporaryView.onchange table: 00403000 36864 _var0 99999 REPLACEv100.share.MySessionView.onchange table: 003D5360 36864 var0 99999 REPLACElimax close 0

这个结果与之前的各个版本均一致。132

Page 133: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

This result is consistent with the previous versions.

实现流程 Implementation process

启动 Endpoint引擎 Launch the Endpoint engine

Java

void Endpoint.openEngine()

C# void Endpoint.openEngine()C++ void Endpoint::openEngine()

使 用 应 用 脚 本 创 建 ScriptEngineHandle Use the application script to create

ScriptEngineHandle

Java

类 limax.endpoint.script.JavaScriptHandle

C# 类 limax.endpoint.script.LuaScriptHandle类 limax.endpoint.script.JavaScriptHandle

C++ 4 个版本的 limax::LuaCreator::createScriptEngineHandle 方法4 个版本的 limax::JsCreator::createScriptEngineHandle 方法

上表中列举了 Limax当前支持的 ScriptEngineHandle 实现。Java

The class limax.endpoint.script.JavaScriptHandle

C# The class limax.endpoint.script.LuaScriptHandleThe class limax.endpoint.script.JavaScriptHandle

C++ The method limax::LuaCreator::createScriptEngineHandle of the four versionsThe method limax::JsCreator::createScriptEngineHandle of the four versions

The above table lists the ScriptEngineHandle implementation currently supported by the Limax.

启动 Endpoint 连接 Launch the Endpoint connection

Java

limax.util.Closeable Endpoint.start(String host, int port, String username, String token, String platflag, ScriptEngineHandle handle, Executor executor)

C# limax.util.Closeable Endpoint.start(string host, int port, string username, string token, string platflag, ScriptEngineHandle handle, Executor executor)

C++ std::shared_ptr<limax::util::Closeable> Endpoint::start(const std::string& host, short port, const std::string& username, const std::string& token, const std::string& platflag, ScriptEngineHandlePtr handle)

1. host,port,username,token,platflag,指定了连接的 Switcher 服务器与登录参数。host, port, username, token, platflag, specify the connection's Switcher server and login parameters.

2. handle 为上一步创建的 ScriptEngineHandle

133

Page 134: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

handle is the ScriptEngineHandle created in the previous step3. 脚本在 executor指定的执行器中执行,C++版本中,脚本必定在 UI线程中执行,所以

无需该参数。The script is executed in the specified executor. In the C++ version, the script must be executed in the UI thread and this parameter is not required.

4. 返回的 Closeable对象上执行 close,效果等同于浏览器上点击 STOP按钮,立刻关闭连接,脚本 context 上安装的 onclose 方法被调用。onclose获得的消息串格式为,“[宿主语言错误信息描述]<空格><错误码>”。Executing the close on the returned Closeable object, the effect is equivalent to clicking the STOP button on the browser, which immediately closes the connection and calles the onclose method installed on the context of the script. The message string format gotten by the onclose is "[host language error information description]<space><error code>".

5. 网络连接在任何情况下断开,脚本 context 上安装的 onclose 方法被调用。在这之后调用返回的 Closeable对象上的 close 方法,没有任何效果,事实上,内部的连接对象已经被释放。这种实现与浏览器一致,完成装载,或者装载失败的页面上,点击 STOP按钮,没有任何效果。When the network connection disconnects in any condition, the onclose method installed on the context of the script is called. After that, calling the close method on the returned Closeable object has no effect. Actually, the internal connection object has been released. This implementation is consistent with the browser. Clicking the STOP button on the webpage with the completion of loading or failure of loading has no effect.

6. 应该注意到,所有这些启动方法均不提供连接超时的支持,如果需要支持短于操作系统默认的连接超时,必须应用实现自己的 timer,超时以后在返回的 Closeable对象上执行 close即可。这种实现当然也与浏览器一致,浏览器上找不到任何连接超时这样的底层配置。It should be noted that all these launch methods do not support connection timeout. If the connection timeout shorter than the OS default value is required, the own timer of the application should be supplied, and execute close method on the returned Closeable object after the timeout. This implementation is consistent with the browser, and such kind of the underlying configuration for any connection timeout can not be found on the browser.

结束 Endpoint引擎 Close Endpoint engine

Java

void Endpoint.closeEngine(Runnable done)

C# void Endpoint.closeEngine(Action done)C++ void Endpoint::openEngine(limax::Runnable done)

引擎内所有活动的 Endpoint 连接将自动被关闭。All the active Endpoint connections in the engine will be automatically closed.

134

Page 135: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

与脚本模式客户端,HTML5 客户端的比较 Compare with script mode client

and HTML5 client

极简模式客户端 脚本模式客户端 HTML5 客户端承载协议 Websocket/Ext. Limax Native Protocol Websocket

安全性 支持 通过运行时配置支持 支持/https,不支持/http压缩 支持 通过运行时配置支持 不支持脚本支持 ScriptEngineHandle ScriptEngineHandle javascript

Minimalist mode client Script mode client HTML5 clientLoad protocol Websocket/Ext. Limax Native Protocol WebsocketConfidentiality Support Support through runtime

configurationSupport/https, not support/http

Compression Support Support through runtime configuration

Not support

Script support ScriptEngineHandle ScriptEngineHandle Javascript

Provider 间数据交换 Data exchange between Providers

Limax 通过客户端隧道方式支持信任 Provider 间安全数据交换,具体的数据内容由Provider 自己解释。Limax supports secure data exchange between trusted Providers through client-site tunneling, and the specific data content is interpreted by Provider itself.

基本框架 Basic framework

135

Page 136: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

客户端隧道 Client-site tunneling

通常的服务器间数据交换需要在服务器间建立网络连接,与客户端隧道方式有很大不同,这里作一比较。The usual server-to-server data exchange needs to establish a network connection between servers, which is very different from the client-site tunneling mode. Here is a comparison.

客户端隧道 服务器直连大量数据 不适合 适合服务器防火墙 不需要 服务器网络拓扑越复杂,防火墙越

复杂,现实运营环境下,网络与服务器分属不同部门管理,防火墙配置随服务器配置调整通常不现实,更不用说跨组织机构的协同了。

服务器信任关系 通过证书实现 也可以通过证书,没有实现的版本客户数据传输 简单。客户端连通两端服务器

后可以执行即时传输。客户端也可以暂存数据,执行非即时传输。

复杂。服务器间连接采用传输时连接,还是持久连接是两难问题。客户 端终究需 要介入传 输 过 程 的 协调,需要复杂流程。

Client-site tunneling Server direct connectionLarge amounts of data

Not suitable Suitable

Server firewall Not required The more complex the network topology of the server, the complex the firewall. In the actual operating environment, the network and server belong to different departments. It is usually unrealistic to adjust the firewall configuration with server configuration, not to mention the cooperation of organizations.

Server trust relationship

Implemented through the certificate

Also through the certificate, no implemented version

Client data transfer

Simple. The client can perform instant transfer after connecting both servers. The client can also temporarily store data and perform non-instantaneous transfer.

Complex. It is a dilemma whether the server-to-server connection on demand or persistent. After all, the client needs to get involved in the coordination of the transmission process, which requires the complex process.

136

Page 137: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

数据安全 Data security

1. 通过证书方式划定信任域,信任域内的服务器可以安全交换数据。详见附录《PKIX 支持》《Key 分发系统》Determine the trust domain by certificate, and the servers in the trusted domain can exchange data securely. Please refer the appendix “PKIX support” and “Key distribution system” for the detail.

2. 通过隧道的数据通过外部数据方式编码。详见附录《外部数据》Data passing through the tunnel is encoded by external data. Please refer the appendix “External data” for the detail.

数据标签 Data label

1. 隧道数据的交换过程是一个异步过程The exchange process of tunnel data is an asynchronous process.

2. 客户端服务器(可能是所有参与服务器)首先需要执行协商过程,协商结果产生了隧道交换 Session。完整的理解,label 就是一次隧道交换过程的 SessionId。The client and server (possibly all participanting servers) first perform the negotiation process, which results in a tunnel exchange Session. The complete understanding is that the lable is the Sessionid of a tunnel exchange process.

3. 实际上,绝大多数应用,不需要那么复杂的交换行为,可以重新解释 label,简化应用开发。例如,标记应用数据的类型,直接决定目标 ProviderID,等等。Actually, the most applications do not need so complicated exchange behavior, and can reinterprete the label and simplify the application development. For example, mark the type of application data, and directly determine the ProviderID of the target, and so on.

数据交换基本流程 Basic process of data exchange

1. 客户端服务器协商,决定信任域,决定数据标签,在服务器端打包应用数据。这一步骤取决于应用实现。The client and server negotiate, decide the trusted domain, determine the data label, and package the application data on the server site. This step depends on the implementation of the application.

2. 服务器发送数据标签和隧道数据。The server sends data label and tunnel data.

3. 客户端收到数据标签和保护过的隧道数据,根据协商结果,转发数据或者暂存之后转发。The client receives the data label and protected tunnel data, and forwards the data or forwards the data after temporarily storing according to the negotiation result.

4. 接收数据的服务器用数据标签与解码后的应用数据执行异步回调。回调过程取决于应用实现。The server receiving data executes the asynchoronus callback with the data label and the decoded application data. The callback process depends on the implementation of the

137

Page 138: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

application.

服务器开发 Server development

隧道异常 Tunnel exception

隧道操作时可能出现各种异常,用 limax.provider.TunnelException 表示,这些异常分为 4 种类型,使用枚举 limax.provider.TunnelException.Type 描述,分别是All kinds of exceptions may appear when tunnel operates, with limax.provider.TunnelException to represent. These exceptions are divided into 4 types and described by using the enumeration limax.provider.TunnelException.Type, respectively as1. NETWORK,网络操作时出现异常。

NETWORK, exception appears when network operates.2. CODEC,编解码异常,通常是数据被篡改。

CODEC, encoding and decoding exception, this usually is that the data is tampered with.3. EXPIRE,隧道数据过期,源服务器将过期时间编码到被保护数据中,目的服务器用自己的当前时间校验该过期时间,失败抛出这一异常。EXPIRE, the tunnel data expires. The source server encodes expire time into the protected data. The destination server verifies this expiration time with its own current time, and throws this exception when failing.

4. LABEL,标签异常,源服务器明文传送标签时,同时将标签编码到被保护数据中,如果客户端执行转发时修改了标签,目的服务器校验时将抛出这一异常。LABEL, label exception. When the source server sends the label in plaintext, the label is also encoded into the protected data. If the label is modified when the client performs forwarding, this exception is thrown by the destination server when it verifies.

TunnelException.getType() 可以获取异常的类型。对于 NETWORK,CODEC 类型,可以通过TunnelException.getException()获取导致 TunnelException 的异常。TunnelException.getType() can get the type of exception. For NETWORK and CODEC type, the exception that causes the TunnelException can be obtained through TunnelException.getException().

发送数据 Send data

完成客户端服务器的协商,准备好信任域、数据标签、应用数据之后,通过 4 个可选方法发送数据。After completing the negotiation between the client and server, preparing the trust domain, data label and application data, the data can be sent through 4 optional methods.

1. limax.provider.View.tunnel(long sessionid, java.net.URI group, int label, Octets data) throws TunnelException;

2. limax.provider.View.tunnel(long sessionid, int label, Octets data) throws TunnelException;3. limax.provider.SessionView.tunnel(java.net.URI group, int label, Octets data) throws

138

Page 139: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

TunnelException;4. limax.provider.SessionView.tunnel(int label, Octets data) throws TunnelException;

第一个方法为基础版本,sessionid 决定了参与隧道的客户端,group 为信任域(见附录《Key 分发系统》),label 为数据标签,data 为应用数据。SessionView 上保存有客户端的sessionid,所以 SessionView 上的方法省略了 sessionid参数。调用缺省 group 的方法时使用服务器配置的 defaultGroup作为信任域,详见运营配置。The first method is the basic version. The sessionid determines the clients participating in the tunnel, the group is the trusted domain (refer the appendix “Key distribution system”), the label is the data label, and the data is the application data. The SessionView has the sessionid of client, so the method of SessionView ommittes the sessionid parameter. The defaultGroup configured by server is used as trusted domain when calling default group method. Please refer the “Operation configuration” for the detail.隧道与 View 没有 任何联系 ,之所以把隧道方法放在 View 上, 完全 是为 了编程方便。Provider编程时,所有操作都应该在 View 上完成,包括协商过程。在特定的 View 上完成协商,准备好参数之后,直接在该 View 上执行 tunnel即可。The tunnel and View have no any relationship. The reason why the tunnel method is on the View is entirely for programming convenience. When Provider is programming, all operations should be completed on the View, including the negotiation process. After the negotiation is completed on the specific View and the parameter is prepared, the tunnel is directly executed on this View.

接收数据 Receive data

Provider 实现 limax.provider.ProviderListener 时,并列实现 limax.provider.TunnelSupport 接口。When Provider implements limax.provider.ProviderListener, it also implements limax.provider.TunnelSupport interface at the same time.

public interface TunnelSupport {void onTunnel(long sessionid, int label, Octets data) throws Exception;default void onException(long sessionid, int label, TunnelException exception) throws

Exception {throw exception;

}}

实 现 onTunnel 方法,即可 收 到 客 户 端转发 过 来 的标签与 应 用 数 据 。 onTunnel 方法在sessionid对应的线程上调度,调用同一用户相关的方法是线程安全的。Implement the onTunnel method to receive the label and application data forwarded from client. The onTunnel method is dispatched on the thread corresponding to the sessionid, and calling the method associated to the same user is thread-safe.解码数据的过程可能抛出 CODEC,EXPIRE,LABEL 类型的 TunnelException,这样的异常可以通过实现 onException 方法截获。The process of decoding data may throw a TunnelException of CODEC, EXPIRE, or LABEL type.

139

Page 140: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Such an exception can be caught through implementing onException method.默认的 onException 方法重新抛出这个异常,将导致 Provider记录错误日志,并且使用错误码 ErrorCodes.PROVIDER_TUNNEL_EXCEPTION 关闭与客户端之间的连接。The default onException method rethrows this exception, which causes the Provider to record the error log and use the error code ErrorCodes.PROVIDER_TUNNEL_EXCEPTION to close the connection with client.

注意事项 Matters need to attention

不支持 TunnelSupport 的 Provider 也可以发送隧道数据,这样的 Provider 接收到隧道数据后记录错误日志,使用错误码 ErrorCodes.PROVIDER_TUNNEL_EXCEPTION 关闭与客户端之间的连接。The Provider that does not support TunnelSupport also can send tunnel data. After receiving the tunnel data, such Provider records the error log, and use the error code ErrorCodes.PROVIDER_TUNNEL_EXCEPTION to close the connection with client.

客户端开发 Client development

所有类型客户端均支持隧道数据转发。All kinds of clients support tunnel data forwarding.

C++/C#/Java 客户端,以 Java版本为例 C++/C#/Java client, with Java version as example

实现 limax.endpoint.EndpointListner 的同时,并列实现 limax.endpoint.TunnelSupport 接口。When implementing limax.endpoint.EndpointListener, implement limax.endpoint.TunnelSupport interface at the same time.

public interface TunnelSupport {void onTunnel(int providerid, int label, Octets data) throws Exception;void registerTunnelSender(TunnelSender sender);

}public interface TunnelSender {

void send(int providerid, int label, Octets data)throws InstantiationException, SizePolicyException, CodecException,

ClassCastException;}

实现 onTunnel,即可收到服务器发送过来的隧道数据。Limax 框架决定了,一个 Endpoint可以同时连接多个 Provider,这里的 providerid 进行了区分。onTunnel 可以实现为即时转发,也可以暂存数据(暂存时长不可超过配置的有效期),以后转发。Implement onTunnel to receive the tunnel data sent from the server. The Limax framework determines that an Endpoint can connect to multiple Providers at the same time, and providerid here is used to distinguish. The onTunnel can be implemented as instant forwarding, or temporarily storing data (temporary storage duration can not exceed the validity of the

140

Page 141: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

configuration), then forwarding later.Endpoint 初始化时,将回调 registerTunnelSender,必须实现 registerTunnelSender 将当前Endpoint 的 tunnelSender保存下来,之后在该 tunnelSender 上转发隧道数据。When Endpoint is initialized, registerTunnelSender will be called back. It must be implemented that the registerTunnelSender saves the tunnelSender of the current Endpoint, and then forwards the tunnel data on this tunnelSender.通常情况下 , 客 户 端 需 要 使 用 两 个 Endpoint , EA , EB 分别连 接隧道两边的Provider,EA.onTunnel 收到隧道数据时,通过 EB.tunnelSender转发。Usually, the client needs to use two Endpoint, EA and EB to separately connect to Provider on both sides of the tunnel. When EA.onTunnel receives the tunnel data, it is forwarded through EB.tunnelSender.这 里 必 须 当 心 , TunnelSupport.onTunnel 中 的 providerid 是 源 Provider 的 id , TunnelSender.send 中的 providerid 是目的 Provider 的 id,不能像 label,data 一样直接转发。It must be noted here that the providerid in the TunnelSupport.onTunnel is the id of the source Provider, and the providerid in the TunnelSender.send is the id of the destination Provider, which can not be directly forwared as label and data.不支持 TunnelSupport 的 Endpoint,忽略接收到的隧道数据。Then Endpoint of TunnelSupport is not supported, ignoring the received tunnel data.

脚本客户端,以 Javascript版本为例 Script client, with Javascript example

脚本客户端在创建 Limax 实例时,将一个函数作为第三个参数传入,即可支持隧道数据接收。When creating a Limax instance, the script client passes a function as the third parameter to support the tunnel data receiving.

var limax = Limax(function(ctx) {ctx.onerror = function(e) {

print('limax error', e);}ctx.onclose = function(e) {

print('limax close', e);}ctx.onopen = function() {

.........................}}, cache, function(pvid, label, data) {});limax 实例本身就是一个函数,直接执行 limax(pvid, label, data); 即可发送隧道数据。The Limax instance itself is a function that directly execute limax(pvid, label, data); to send tunnel data.

141

Page 142: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

使用了 ScriptEngineHandle 的混合客户端(Java/javscript,C#/javascript,C#/Lua,C++/

javascript,C++/Lua,各种形式的极简模式客户端),以 Java/javascript 为例 Mixed client

using ScriptEngineHandle (Java / javscript, C # / javascript, C # / Lua, C ++ / javascript, C +

+ / Lua, various forms of minimalist client), with Java/javascript example

使用带有 TunnelReceiver参数的构造函数创建相应的 ScriptHandle,即可实现隧道数据的接收。Use the constructor with the TunnelReceiver parameter to create the corresponding ScriptHandle to receive the tunnel data.

new JavascriptHandle(new ScriptEngineManager().getEngineByName("javascript"),new InputStreamReader(Main.class.getResourceAsStream("example.js")), null, null,new TunnelReceiver() {

@Overridepublic void onTunnel(int providerid, int label, String data) {

System.out.println(providerid + " " + label + " " + data);}

});

这里的 data 来自脚本系统所以类型为 String,实际上,这是 Octets 类型隧道数据的 Base64编码结果。The data here comes from the script system, so the type is String, which is the Base64 encoding result for Octets-type tunnel data.注意观察 ScriptEngineHandle 接口的定义Observe the definition of the ScriptEngineHandle interface.

public interface ScriptEngineHandle {.......................void tunnel(int providerid, int label, String data) throws Exception;void tunnel(int providerid, int label, Octets data) throws Exception;

}

ScriptEngineHandle 上直接调用 tunnel 方法即可发送隧道数据。来自于脚本系统的隧道数据使用带有 String参数的方法发送,来自 TunnelSupport 的隧道数据使用带有 Octets参数的方法发送。The tunnel method on the ScriptEngineHandle can be called directly to send the tunnel data. The tunnel data from the scripting system is sent by using the method with the String parameter, and the tunnel data from TunnelSupport is sent by using the method with the Octets parameter.注意:Notice:1. 如 果 客 户 端 的 EndpointListener 实 现 了 TunnelSupport 接 口 , 又 使 用 了 支 持

142

Page 143: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

TunnelReceiver 的 ScriptEngineHandle,同一份隧道数据,两处都能接收到。If the client's EndpointListener implements the TunnelSupport interface and uses the ScriptEngineHandle that supports the TunnelReceiver, the same tunnel data can be received in both places.

2. 如果客户端的 EndpointListener 实现 TunnelSupport 时记录了 TunnelSender,又使用了ScriptEngineHandle,两处都可以用来发送隧道数据。If the client's EndpointListener recorded TunnelSender and used ScriptEngineHandle when implementing TunnelSupport, the both places can be used to send the tunnel data.

运行管理 Operation management

配置文件 Configuration file

Provider 配置文件中,Provider节点内,需要添加一个 Tunnel节点。In the Provider configuration file, add a Tunnel node in the Provider node.

<Provider ...> <Tunnel compressor="ZIP" keyProtector="HMACSHA256" defaultExpire="86400000" defaultGroup="prjA"> <PKIX location="pkcs12:/work/keyalloc.p12" passphrase="123456" revocationCheckerOptions="SOFT_FAIL"/> <SharedKey group="prjA" key="123456" /> <SharedKey group="prjB" key="123456" />

<Expire group="prjA" value="3600"/> </Tunnel> <Manager .../> <Manager .../> ...</Provider>

Tunnel节点 4 个属性4 attributes of Tunnel node:compressor:源数据压缩方法,可选值 NONE,RFC2118,ZIP,默认为 NONE。参见附录《外部数据》compressor: source data compression method, the optional values are NONE, RFC2118 and ZIP. The default is NONE. Please refer the appendix “External data”.keyProtector : 源 数 据 保 护 算 法 , 可 选 值HMACSHA224,HMACSHA256,HMACSHA384,HMACSHA512,TripleDESCBC,AESCBC128,默认为 HMACSHA256。参见附录《外部数据》keyProtector: source data protection algorithm, the optional values are HMACSHA224, HMACSHA256, HMACSHA384, HMACSHA512, TripleDESCBC, and AESCBC128. The default is

143

Page 144: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

HMACSHA256. Please refer the appendix “External data”.defaultExpire:隧道数据默认过期时间,单位毫秒,默认 3600000。过期时间的配置必须小于 key寿命,参见附录《Key 分发系统》defaultExpire: tunnel data default expiration time, with milliseconds as unit. The default is 3600000. The expiration time must be configured less than key lifetime. Please refer the appendix “Key distribution system”.defaultGroup:默认信任域。defaultGroup: default trust domain.Tunnel节点下支持 2 种节点,PKIX 与 SharedKey,PKIX节点具有高优先级,配置了 PKIX节点SharedKey节点被忽略。Tunnel node supports two kinds of nodes, PKIX and SharedKey. PKIX node has high priority. SharedKey node configured with PKIX node is ignored.

配置了多个 PKIX节点,第一个节点有效,PKIX节点配置了 Key 分发系统客户端,请求 Key分发网络分配 Key保护隧道数据。Multiple PKIX nodes are configured. The first node is valid. The PKIX node is configured with Key distribution system client to require the Key distribution network to allocate Key to protect tunnel data.

PKIX节点 5 个属性。5 attributes of PKIX node:location:provider证书包的 location。location: the location of provider certificate package.passphrase:location 的私钥启用密码,实际运营时,不应该填写该属性,而应该在服务器启动时输入。passphrase: the active password of location private key. In the actual operation, this attribute should not be filled, but should be entered as server launching.trustsPath:信任证书路径,location 中已经包含了派生该证书的 ROOTCA,如果要信任其它ROOTCA(或者当前 ROOTCA即将过期,应该将新的 ROOTCA放置在这里),则需要配置trustsPath,trustsPath 可以是一个文件,也可以是一个目录,如果是目录,遍历一层,获取文件,文件允许任何证书格式,包括 CER,DER,PKCS7,PKCS12,KeyStore,从中获取ROOTCA,忽略所有错误。trustsPath: the trusted certificate path. The location includes the ROOTCA derivated from this certificate. If it needs to trust the other ROOTCA (or the current ROOTCA will expire soon, the new ROOTCA should be placed here), it should configure the trustsPath. The trustsPath can be a file or a directory. If it is a directory, traverse one level, and get the file. The file allows any certificate format, including CER, DER, PKCS7, PKCS12, and KeyStore, from which the ROOTCA is obtained, ignoring all errors.revocationCheckerOptions:服务器检测对方证书回收状态的选项,可选的值为 DISABLE, NO_FALLBACK, ONLY_END_ENTITY, PREFER_CRLS, SOFT_FAIL,大小写不敏感,后 4 个参数的解释见 java.security.cert.PKIXRevocationChecker.Option,DISABLE 具有最高优先级,如果配置了DISABLE,其它配置无意义。revocationCheckerOptions: the option that the server checks the certificate recovery status of

144

Page 145: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

the peer. The optional values are DISABLE, NO_FALLBACK, ONLY_END_ENTITY, PREFER_CRLS, and SOFT_FAIL. Case is not sensitive. The definitions of the last four parameters refer to java.security.cert.PKIXRevocationChecker.Option. The DISABLE has the highest priority. If the DISABLE is configured, the other configuration has no meaning.httpsHost:指定 key 分发网络中特定的入口服务器 IP 或者域名,不配置该属性,使用provider证书中提供的域名。httpsHost: specify the particular entrance server IP or domain name in the key distribution system. If not configure this attribute, the domain name provided by the provider certificate is used.PKIX节点配置初始化之后,从 Provider证书中提取支持的信任域集合校验 defaultGroup 配置,如果配置了 defaultGroup,defaultGroup 必须在集合中存在,否则抛出运行时异常;如果没有配置 defaultGroup,在集合中挑选一个作为 defaultGroup。After the PKIX node configuration is initialized, a set of trusted domains is extracted from the Provider certificate to verify the defaultGroup configuration. If defaultGroup is configured, the defaultGroup must exist in the set, or a runtime exception is thrown. If no defaultGroup is configured, one is selected from the set as defaultGroup.

不存在 PKIX节点的情况下,使用 SharedKey节点的配置,SharedKey节点允许多个,静态配置了信任域和相应的 key。SharedKey 配置方式不使用 Key 分发系统,用于调试环境,或者极其简单的应用场景。In the condition without PKIX node, the configuration of the SharedKey node is used. Multiple SharedKey nodes are allowed, statically configuring trust domain and corresponding key. SharedKey configuration mode does not use Key distribution system for debugging environment, or extremely simple application scenarios.

SharedKey节点 2 个属性2 attributes of SharedKey node:group:信任域名称group: the name of trusted domainkey:信任域 keykey: the key of trusted domain所有 SharedKey 配 置初始化之 后 , 生 成 了静态 的信任域集合 , 如 果 配 置 了defaultGroup,defaultGroup 必须在集合中存在,否则抛出运行时异常;如果没有配置defaultGroup,在集合中挑选一个作为 defaultGroup。After all SharedKey configurations are initialized, a set of static trusted domain is generated. If defaultGroup is configured, defaultGroup must exist in the set, or a runtime exception is thrown. If no defaultGroup is configured, one is selected from the set as defaultGroup.

Expire节点 2 个属性2 attributes of Expire node:group:信任域名称group: trusted domain namevalue:过期时间

145

Page 146: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

value: expiration timeExpire节点允许精细配置特定信任域的隧道数据过期时间。The Expire node allows fine configuration for the tunnel data expiration time of a particular trusted domain.

运行时注意事项 Runtime considerations

1. 运行 ntp 服务,同步 provider 时钟Run the ntp service, synchronize provider clock.

2. 配置正确的 dns 服务。Configure the correct dns service.

3. 按照 CA 的要求,配置 trustsPath,添加 CA提供的额外 ROOTCA。Configure the trustsPath according to the requirement of CA, add the extra ROOTCA provided by CA.

4. 开启防火墙,许可 443 目的端口,确保与 CAServer,Key 分发网络的连通性。Turn on the firewall, and allow the 443 destination port to ensure the connectivity between the CAServer and Key distribution network.

高级话题 Advanced topic

应用数据组织 Application data organization

1. Provider 间数据交换框架不考虑具体的应用数据,专注于转发 OctetsThe data exchange framework between Provider does not consider the specific application data, focusing on forwarding Octets.

2. 信任域内 Provider,应该设计私有的可交换数据结构,使用 xml 描述生成交换用的公共bean 是理想方式。The Provider in the trusted domain should design a private exchangeable data structure, and it is the ideal way to use xml description to generate the public bean to exchange.

3. 如果存在多种公共 bean,应该设计类型字段予以区分,简单的情况下,可以考虑利用label区分类型。If there are multiple public beans, the type field should be designed to distinguish. In simple case, consider to use label to distinguish type.

4. 使用 JSON 表示用户数据也是一种可行的选择,JSON串上可以统一使用 UTF8编码getBytes,然后 wrap 为 Octets。Using JSON to represent user data is a variable option. The JSON string can unifiedly getBytes by UTF8,then wrap to Octets.

5. 应用数据尺寸可能较大的情况,配置压缩。In the case that the size of application data may be larger, the configuration of compressor is preferred.

146

Page 147: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

隧道数据尺寸 The size of tunnel data

1. 隧道数据通过特定协议发送。The tunnel data is sent via a specific protocol.

2. 出于服务器的抗攻击考虑,虚拟机参数 limax.net.Engine.limitProtocolSize,限制了最大协议大小 1M,隧道数据尺寸不可超出这一约束,否则,目的服务器解析协议时将出错。For the consideration of server’s anti-attack, the virtual machine parameter limax.net.Engine.limitProtocolSize limits the maximum size of protocol as 1M. The size of tunnel data can not exceed this constraint. Otherwise, there is error when destination server resolves the protocol.

3. 客户端信任服务器,所以客户端不控制数据尺寸。The client trusts the server, so the client does not limit the size of data.

4. 对于大量数据,应该考虑多次发送的方式。For large amounts of data, consider the way of multiple sending.

隧道谁的数据 Whose data is tunneled

1. 仔细规划应用自己的协商过程,决定隧道谁的数据。Carefully plan its’ own negotiation process of application, and decide whose data is tunneled.

2. 多数情况,通过隧道转发用户自己的数据。In most cases, forward the user’s own data through the tunnel.

3. 特定情况下,可以通过没有利害冲突的第三方传送。这时,真实的 sessionid 打包在应用 数 据 中 , 解 码获得 该 sessionid 之 后 如 果 需 要线程安全地处 理 , 应 该 使 用View.schedule(sessionid, task)将后续任务调度到 sessionid对应线程上。In certain case, it can be transmitted through third parties that have no conflicts of interest. At this time, the actual sessionid is packaged into the application data. After decoding to obtain this sessionid, for thread-safe handling, View.schedule(sessionid, task) should be used to schedule subsequent tasks to the thread corresponding to sessionid.

4. 出于规避防火墙的考虑,可以设计专用客户端,部署在连通性良好的网络中,转发 Provider 间数据。For the consideration to avoid the firewall, a dedicated client can be designed and deployed in the network with good connectivity to forward the data between Providers.

Limax Http

绝大多数服务器应用或多或少需要提供一些 HTTP 服务,Limax 服务框架也不例外。sun从 JDK6 开始就提供了一个简单的服务器开发包 com.sun.net.httpserver。实际使用会发现这个包的功能太弱,所以提供开发包 limax.http作为替代。Most server applications need to provide some HTTP services more or less, and the Limax service framework is no exception. Sun has provided a simple server development package

147

Page 148: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

com.sun.net.httpserver since JDK6. The actual use will find that the function of this package is too weak, so the development package limax.http is provided as an alternative.

limax.http 支持 HTTP/1.1(兼容 HTTP/1.0),支持 HTTP/2,支持 WebSocket(从 HTTP/1.1 upgrade,HTTP/2隧道两种方式均支持),支持 Server-Sent Events。提供一个比 Servlet 更加简单有效的服务架构(Servlet 已经经历了 4 个版本,越来越复杂)。The limax.http supports HTTP/1.1 (compatible with HTTP/1.0), HTTP/2, WebSocket (both upgraded from HTTP/1.1 and HTTP/2 tunnel), and Server-Sent Events. Provide a simpler and more efficient service architecture than Servlet (Servlet has gone through 4 versions and is getting more and more complex).

服务实现无需关心当前到底运行在 HTTP/1.1环境下还是 HTTP/2环境下,如果条件具备,自动选择 HTTP/2。The service implementation does not need to care whether it is currently running in the HTTP/1.1 environment or the HTTP/2 environment. If the conditions are met, HTTP/2 is automatically selected.

HTTP 服务架构 HTTP service architecture

public interface HttpHandler extends Handler {DataSupplier handle(HttpExchange exchange) throws Exception;default void censor(HttpExchange exchange) throws Exception {

throw new UnsupportedOperationException();}

}

1. 客户端请求完成之后 HttpHandler 的 handle 方法被调度,服务代码在 handle 中编写。The handle method of HttpHandler is scheduled following the end of request, service coding in it.

2. HttpHandler 的 censor 方法用于审查上传进度,对于 GET 请求,该方法决不会被调用。审查失败,该方法抛出异常,立即终止网络连接。默认实现禁止 POST 请求。The censor method of HttpHandler is used for censoring the progress of uploading. Fail in the censoring, any exception can be thrown, terminiate the connection immediately. POST request is forbidden by the default implementation.

3. POST 请求至少调度一次 censor,第一次调度可以获得完整的请求头,上传数据导致后续调度。POST 请求的审查详见高级话题。The POST request schedule censor one or more times, and the first scheduling can obtain a complete request header, and the uploading data leads to subsequent scheduling. See the advanced topic for the detail of censoring of POSTING.

4. exchange.getFormData()获得 FormData对象,FormData 的 getData()方法可以获取解析之后的 query 数据,即便是 GET 请求的 query 也被解析出来。返回类型为 Map<String, List<Object>>,其中 Object 可能是 String,可能是 List<ByteBuffer>。 String对应了串查询参数,List<ByteBuffer>对应了上传的文件数据,这里之所以用 List<ByteBuffer>是因为ByteBuffer尺寸上有 Integer.MAX_INTEGER 的限制。

148

Page 149: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

The FormData object can be obtained through exchange.getFormData(). The getData() method of FormData can get the query data after parsing, even if the query of the GET request is parsed. The return type is Map<String, List<Object>>, where Object may be a String, or a List<ByteBuffer>. The String corresponds to the string query parameter, and the List<ByteBuffer> corresponds to the uploaded file data. The reason why List<ByteBuffer> is used here is that the size of ByteBuffer has Integer.MAX_INTEGER limit.

5. FormData 的 postLimit(long postLimit)方法用来限制 POST 数据尺寸,超出这个尺寸,直接终止连接。FormData 的 useTempFile(int threshold)方法提供一个上传文件的尺寸阈值,阈值之下文件内容存放在堆 ByteBuffer 中,否则将文件内容存放在一个临时文件中,如果要支持上传文件应该设置一个合适的 threshold。在不支持文件上传的 POST 中使用了 useTempFile,内部将产生异常终止连接。这是合理的,上传文件通常需要设置较大的 postLimit,不支持上传文件的 POST 数据全部存储在内存中,过大的 postLimit将给恶意客户端提供攻击服务器的机会。具体使用方法可以参考示例。The postLimit(long postLimit) method of FormData is used for size limit of POSTING. Beyond the limit, Connection will be terminated directly. The useTempFile(int threshold) method of FormData provides a size threshold for uploading files. Below the threshold the contents of the file are stored in the heap ByteBuffer. Otherwise, the contents of the file are stored in a temporary file. The ByteBuffer is obtained through the memory mapping of the file. A suitable threshold should be set if the uploading file is supported. Using useTempFile method with POST request that doesn’t support file uploading, Exception will be thrown internally to terminate the connection. It is reasonable because larger postLimit is needed while file uploading, Howerver with POST request that doesn’t support file uploading whole POST data is stored in memory, larger postLimit will give malicious client a chance to attack the server. For specific usage, please refer to the example.

6. FormData 的 getRaw()方法返回一个 Octets 包含了 POST 发送的原始数据(不包括上传文件的情况,这种情况下 Octets 是空的)支持处理一些非字符串格式的查询,比如OCSP查询。The getRaw() method of FormData returns an Octets containing the raw data sent by POST (excluding uploading files, in which case the Octets is empty) to support processing of non-string-formatted queries, such as OCSP queries.

public interface limax.http.DataSupplier {interface Done {

void done(HttpExchange exchange) throws Exception;}java.nio.ByteBuffer get(); default void done(HttpExchange exchange) throws Exception;static DataSupplier from(byte[] data) ; static DataSupplier from(java.nio.ByteBuffer data); static DataSupplier from(java.nio.ByteBuffer[] datas); static DataSupplier from(java.io.File file) throws IOException; static DataSupplier from(java.nio.channels.FileChannel fc,long begin,long end) throws

IOException;149

Page 150: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

static DataSupplier from(java.io.InputStream in,int buffersize); static DataSupplier from(java.nio.file.Path path) throws IOException; static DataSupplier from(java.nio.channels.ReadableByteChannel ch,int buffersize); static DataSupplier from(java.lang.String text,java.nio.charset.Charset charset);static DataSupplier from(DataSupplier supplier, DataSupplier.Done done);static DataSupplier from(HttpExchange exchange, java.util.function.BiConsumer<String,

ServerSentEvents> consumer, Runnable onSendReady, Runnable onClose);static DataSupplier async();

}7. 从 DataSupplier 接口定义可以看到,DataSupplier提供的静态方法足以覆盖绝大多数应

用场景。 如 果 需 要释放资源可 以 使 用 带 DataSupplier.Done 参数 的 方法修饰DataSupplier , 在 done 操 作 中 执 行 释 放 操 作 。 最 后 一 个 方 法 用 来 支 持ServerSentEvents,详见下文。Seen from the DataSupplier interface definition, the static methods provided by DataSupplier are sufficient to cover most application scenarios. If the resource is needs to be released, the method with the DataSupplier.Done parameter can be used to decorate the DataSupplier and perform the release operation in the done operation. The last method is used to support ServerSentEvents, as described below.

8. 极少情况需要实现自己的 DataSupplier,如果自己实现,每次调用 get 方法应该返回一个 ByteBuffer,最后一次 get返回 null,表示完成。最终 done 方法被调用,可以在done 方法中释放资源。特别的,done 方法又传入一次 exchange,提供一个设置 trailer头的机会。(HTTP 协议定义了 Trailer头,不知道哪些浏览器支持)In rare cases, it needs to implement the own DataSupplier. If it is implemented, each call to the get method should return a ByteBuffer. The last time get returns null, indicating completion. Finally, the done method is called, and the resource can be released in the done method. In particular, the done method passes in an exchange again, providing an opportunity to set the trailer header. (The HTTP protocol defines the Trailer header. It is now unknown which browsers support it.)

9. handle返回 DataSupplier.async()指明 handle并没有完成响应,用于进一步的异步处理。这种情况下,异步任务完成之后必须执行 exchange.async(HttpHandler handler) 完成响应。费时的数据准备工作,可以采用这种方式完成,提高服务效率。(参见示例)DataSupplier.async() returned by the handle means nothing can be responded, used for further asynchronous process. In this circumstance, after asynchronous process, exchange.async(HttpHandler handler) must be called for response. Time-consuming data prepare work should be accomplished in this way for service efficiency. (see example)

10. handle允许抛出任何异常。除了 limax.http.HttpException 外,返回 InternalServerError,包含异常栈。HTTP错误码可以通过 limax.http.HttpException抛出,构造 HttpException也允许提供一个 HttpHandler,用这个特殊 HttpHandler再进行一次处理。forceClose参数,决定这次错误返回之后需不需要关闭连接,特别的,对于 HTTP/2,可以通过设置服务器参数禁止 forceClose。更进一步,可以抛出一个无参数 HttpException,立即无条件关闭连接。The handle allows any exception to be thrown. Except to limax.http.HttpException respond InternalServerError with the exception stack. HTTP error codes can be thrown by

150

Page 151: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

limax.http.HttpException. Constructing an HttpException also allows an HttpHandler to be used, which is then processed once with this special HttpHandler. The forceClose parameter, which determines that the connection needs to be closed after response. In particular, for HTTP/2, forceClose can be disabled by setting the server parameter. Further more, HttpException without any parameter can be thrown, abort the connection immediately and unconditionally.

11. 应用也可以通过 exchange.getResponseHeaders().set(“:status”, code);的方式来设置错误码,应该注意到这里的”:status”是 HTTP/2 的描述。The application can also set the error code through exchange.getResponseHeaders().set(":status", code);. It should be noted that the ":status" here is a description of HTTP/2.

12. WebSocketExchange提供 promise(URI uri)方法支持 HTTP/2 的服务器推送,promise 应该在处理服务逻辑的过程中调用。通常情况下,推送的资源应该被后续页面引用,客户端发现页面引用的资源之前已经开始推送了,就不需要再次请求该资源了,节省了一次请求过程,所以这是一个优化措施,如果没有推送最终处理结果应该一样。promise调用在 HTTP/1.1 下是个空操作,换句话说,如果需要可以在 HttpHandler 中实现推送逻辑,无需关心当前运行环境到底是 HTTP/1.1 还是 HTTP/2。值得注意的是,服务器推送在某些情况下并不一定是一个好选择,比如客户端早就 cache 了推送的资源,再执行推送反而浪费带宽。The WebSocketExchange provides a promise (URI uri) method to support HTTP/2 server push, and the promise should be called during the processing of the service. Usually, the pushed resource should be referenced by the following page. The client finds that the resource referenced by the page has already been pushed before, and then it does not need to request the resource again, saving the request process. So this is an optimization measure. If it is no push, the final result of the push should be the same. The promise call is no operation under HTTP/1.1. In other words, if it needs to implement push in HttpHandler, it is no need to care whether the current running environment is HTTP/1.1 or HTTP/2. It should be noticed that server push is not necessarily a good choice in some cases. For example, the client has already cached the pushed resources, and then performing push instead wastes bandwidth.

WebSocket 服务架构 WebSocket service architecture

public interface WebSocketHandler extends Handler {void handle(WebSocketEvent event) throws Exception;

}

1. WebSocketHandler仅需要实现一个 handle 方法,接收到客户端数据或者 websocket 关闭时,handle被调度,handle 的调度是串行的。The WebSocketHandler only needs to implement a handle method. When the client data is received or the websocket is closed, the handle is scheduled, and the handle scheduling is serial.

151

Page 152: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

2. event.type() 返 回 事 件 类 型 。 event.getWebSocketExchange() 可 以 获 取WebSocketExchange,用于发送数据,以及存取当前关联的 SessionObject。event.t ype() return the type of event, event.getWebSocketExchange() return WebSocketExchange object which is used to send data, and access the currently associated SessionObject.

3. event.type() == OPEN 时可以设置应用自己的 SessionObject 关联当前连接。When event.type()==OPEN the application's own SessionObject can be set to associate the current connection.

4. WebSocketExchange 上的 ping()方法可以用来测量 RTT,ping()方法返回一个 id,收到pong 事件后可以获取对应的 id 与 RTT值。The ping() method of WebSocketExchange can be used to measure RTT. The ping() method returns an id. After receiving the pong event, the corresponding id and RTT value can be obtained.

5. WebSocketExchange 上的 send(byte[] binary),send(String text)方法用于发送 WebSocket的二进制数据和字符串数据。event.type()==SENDREADY意味着之前的数据发送完成,可以发送新的数据,用于支持流控。The send(byte[] binary) and send(String text) method of WebSocketExchange are used to send binary data and string data of WebSocket. event.type()==SENDREADY means previous data is sent, new data can be sent, it can be used for flow control.

6. WebSocketExchange 上的 sendFinal()方法发送 CloseFrame 之后立即关闭当前 websocket连接。sendFinal(long timeout)方法发送 CloseFrame 之后半关连接,在 timeout 时限内等待对方的握手 CloseFrame 确保 RFC6455 关闭语义。实际上 timeout对于 https 连接上的WebSocket 没有意义,因为 WebSocket 关闭握手之后还会执行 SSL 关闭握手,这就确保了 WebSocket 关闭握手一定完成。同样的,timeout对于 RFC8441 定义的 HTTP/2隧道方式的 WebSocket 关闭也没有意义,因为 WebSocket 关闭只对应 Stream 关闭。The sendFinal() method of WebSocketExchange closes the current websocket connection immediately after sending CloseFrame. The sendFinal(long timeout) method half-closes the connection after sending a CloseFrame, and waits for the handshake CloseFrame of the peer within the timeout limit to ensure that the RFC6455 closes the semantics. In fact, timeout has no meaning for the WebSocket on the https connection, because the WebSocket close handshake happens before SSL close handshake, which ensures that the WebSocket close handshake must be completed. Similarly, timeout has no meaning for the HTTP/2 tunneling WebSocket close defined by RFC8441, because WebSocket close only corresponds to Stream close.

7. WebSocketExchange 上 的 resetAlarm 可 以 设 置超时 , 如 果超时限度 没 有再次resetAlarm,自动关闭WebSocket 连接。The resetAlarm of WebSocketExchange can set the timeout. If resetAlarm is not called in the timeout limit, the WebSocket connection is automatically closed.

8. 关闭 event 是最后一个 event。收到关闭 event,即可释放相应的资源,通常情况下关闭 code,reason五花八门,不要过于在意。The close event is the last event. After receiving it, the corresponding resources can be released immediately. Usually the close-code and the close-reason are complex, and don’t care too much.

152

Page 153: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

9. WebSocket存在 2 种运行模式,最初是从 HTTP/1.1升级,Mozillia提出的 RFC8441允许通过 HTTP/2 的流隧道WebSocket流量,目前已知只有某些版本 FireFox 支持这种模式。频繁使用 WebSocket 完成某些小任务时这种模式有很大优势,如果使用 WebSocket执行复杂的持久化任务,这种模式未必合适。如果要禁用,可以在 HttpServer对象上设置 参 数httpServer.set(HttpServer.Parameter.HTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL, 0) ,禁用这种模式。There are two operation modes for WebSocket, originally upgraded from HTTP/1.1. The RFC8441 proposed by Mozillia allows WebSocket traffic to be tunneled through HTTP/2 stream. It is currently known that only some release of FireFox supports this mode. This mode has great advantages when using WebSockets frequently to accomplish some small tasks. If the WebSocket is used to perform complex persistence tasks, this mode may not be appropriate. If it needs to be disabled, setting the parameter httpServer.set(HttpServer.Parameter.HTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL, 0) on the HttpServer object can be used.

Server-Sent Events

1. HttpHandle.handle 返 回 时 调 用 DataSupplier.from(HttpExchange exchange, BiConsumer<String, ServerSentEvents> consumer, Runnable onSendReady Runnable onClose)方法,可以获取一个 ServerSentEvents对象。其中,BiConsumer 的 String 为Last-Event-Id,onSent 用来获取发送完成消息,用于支持流控。onClose 用来通告网络连接已经被终止,或者 done操作已经完成,后续的 emit 已经没有意义了,应该释放ServerSentEvents对象。When HttpHandle.handle returns, call DataSupplier.from(HttpExchange exchange, BiConsumer<String, ServerSentEvents> consumer, Runnable onSendReady, Runnable onClose) to get a ServerSentEvents object. Among them, the String of BiConsumer is Last-Event-Id, and the onSendReady is used for flow control. The onClose is used for advice of terminiation of the connection or operation of done is accomplished,further operation of emit is insignificance,the ServerSentEvents object should be released.

public interface ServerSentEvents {void emit(String event,String id,String data); void emit(String data); void emit(long milliseconds);void done();

}2. 3 个参数的 emit 用于向浏览器推送含有 event,id,data 的数据,其中 event,id允许

为 null。单 String参数的 emit相当于 event,id 都为 null 的情况。long参数的 emit 用于向浏览器发送 retry命令,单位毫秒。如果推送完成,应该调用 done 方法。done 方法调用前如果在 exchange 上设置了 trailer, trailer 能够被发送出去。(http 语义允许,尽管不知道有何用处,什么浏览器支持)。The emit method with three parameters is used to push data containing event, id, and data

153

Page 154: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

to the browser, where event and id are allowed to be null. The emit method with a single String parameter is equivalent to event and id are null. The emit method with the long parameter is used to send the retry command to the browser in milliseconds. If the push is complete, the done method should be called. If the trailer is set on the exchange before the done method is called, the trailer can be sent out. (Http semantics allow and which browser support it is unknown).

3. 浏览器对应的 EventSource 收到 onerror消息,最好立即关掉 EventSource,否则浏览器将不停重连服务器,这几乎就是一种攻击,去掉示例代码中 sse.html 中的 s.close即能观察到这种情况。The EventSource corresponding to the browser receives the onerror message. It is best to turn off the EventSource immediately. Otherwise, the browser will continue to reconnect to the server. This is almost an attack. This situation can be observed when removing the s.close in sse.html in the sample code.

4. Server-Sent Events 本质就是利用 HTTP/1.1 的 chunked 传输编码实现的一个简易设计,能力太弱,最好还是使用 WebSocket 代替。Server-Sent Events is essentially a simple design that uses the chunked transport encoding of HTTP/1.1. The ability is too weak, and it is better to use WebSocket instead.

HttpServer 类 HttpServer class

1. HttpServer 的 create 方法用来在一个主机端口上创建一个 Http 服务器,调用包含SSLContext 参 数 的 方 法 即 可 以 创 建 一 个 Https 服 务 器 。 对 于 应 用 而 言 ,HttpExchange,WebSocketExchange 上通过调用 getSSLSession 方法即可知道自己到底运行在 http环境还是 https环境。The create method of HttpServer is used to create an Http server on a host port. An Https server can be created by calling the method containing the SSLContext parameter. For applications, HttpExchange and WebSocketExchange can call the getSSLSession method to know whether it is running in http or https context.

2. HttpServer 的 createHost 方法使用一个域名创建虚拟主机,HttpServer 本身就是默认虚拟主机,同一域名上第二次 createHost等同于 getHost。The createHost method of HttpServer uses a domain name to create a virtual host. HttpServer itself is the default virtual host. The second createHost on the same domain name is equivalent to getHost.

3. 虚 拟 主 机 的 createContext 方 法 在 一 个 path 上 安 装 HttpHandler 或 者WebSocketHandler,removeContext移除 path 上对应的 Handler。The virtual host's createContext method installs an HttpHandler or WebSocketHandler on a path, and the removeContext removes the corresponding Handler on the path.

4. HttpServer 的 get,set 方法用来存取 HttpServer.Parameter 定义的配置参数,配置参数非常多,类型也比较复杂,通常情况下默认参数应该够用了,如果修改可以参考源码。The get and set methods of HttpServer are used to access the configuration parameters defined by HttpServer.Parameter. There are too many configuration parameters and the type is also more complicated. Usually the default parameters should be enough, please

154

Page 155: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

refer to the source code to modify.5. HttpServer 的 start , stop 方 法 用 于 启 停 服 务 器 , 重 复 启 停 将 导 致

UnsupportedOperationException异常。The start and stop methods of HttpServer are used to start and stop the server. Repeated start and stop will cause an UnsupportedOperationException exception.

6. HttpServer 的 register 方法可以注册一个 Closeable,stop 服务器的时候被调用。The register method of HttpServer can register a Closeable, which is called when the server is stopped.

7. HttpServer 的 createFileSystemHandler 方法用于创建一个静态文件服务器 handler,效果基本等同于常见的静态文件服务器。参数比较多,htdocs指定文件服务器根目录;textCharset指定文本文件字符集;mmapThreshold指定使用文件映射的尺寸阈值,小于阈值的文件数据直接读到内存,否则使用文件映射;compressThreshold指定文件数据压缩阈值,压缩尺寸除以原始尺寸小于阈值的说明压缩有效使用压缩版本,否则使用非压缩版本,文件是否允许压缩由 limax.http.ContentType枚举类型决定,通常来说文字内容允许压缩,图片不压缩;indexes 数组指定缺省情况下默认的 index 文件,通常应该配置为 index.html和 index.htm;如果碰到目录访问,目录下不存在 indexes指定的缺省文件的情况下 , browseDir 决 定 了 是否允许浏览该 目 录 , 如 果 不允许 以403FORBIDDEN响应;browseDirExceptions指定作为例外的那些目录。The createFileSystemHandler method of HttpServer is used to create a static file server handler, and the effect is basically equivalent to a common static file server. There are many parameters: htdocs specifies the file server root directory; textCharset specifies the text file character set; mmapThreshold specifies the size threshold of the file mapping, the file data smaller than the threshold is directly read into the memory, otherwise the file mapping is used; compressThreshold specifies the file data compression threshold, compression size divided by the original size is less than the threshold means that the compression effectively uses the compressed version. Otherwise, the uncompressed version is used. Whether the file is allowed to be compressed is determined by the enumeration type of limax.http.ContentType. Generally, the text content is allowed to be compressed, and the image is not compressed; the indexes array specify the default index file by default, usually should be configured as index.html and index.htm; if encountering directory access, there is no default file specified by indexes in the directory, browseDir determines whether to allow browsing the directory. If it is not allowed to respond to 403FORBIDDEN; browseDirExceptions specifies those directories as exceptions.

示例 Example

1. 源码目录下,demo/httpserver提供一个示例,可以根据具体环境适当调整。In the source directory, demo/httpserver provides an example that can be adapted to the specific environment.

2. 如果要试验 https,可以先安装根证书 ca.p7b,然后在代码中给出 localhost.p12 的正确路径,运行服务器之后,浏览器通过 https://localhost/访问即可。(也可以参考附录

155

Page 156: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

PKIX 支持,建立自己的证书体系)If wanting to test https, first install the root certificate ca.p7b, and then give the correct path to localhost.p12 in the code. After running the server, the browser can access it through https://localhost/. (Also refer to the Appendix PKIX support to establish own certificate system).

3. 示例提供一 个静态 http 服 务 器 , 可 以试验创 建静态 网站。 访 问 /upload.html 和 /upload2.html 可以试验文件上传,了解 FormData 的特性。访问/websocket.html 可以试验 websocket。访问/sse.html 可以试验 Server-Sent Events。访问/exception 可以试验handle抛出异常的情况。访问/async 可以试验异步响应。The example provides a static http server that can be experimented with creating static websites. Access /upload.html and /upload2.html can test file uploading and learn about the features of FormData. Access /websocket.html can experiment with websocket. Access /sse.html can test Server-Sent Events. Access /exception can test the case where the handle throws an exception. Access /async can test asynchronous response.

4. 服务器支持从 HTTP/1.1 upgrade 到 HTTP/2,但是当前似乎没有任何客户端支持这一能力。The server supports HTTP/1.1 upgrade to HTTP/2, but currently there seems to be no client support for this capability.

5. 如果要试验不加密的 HTTP/2,只能搜索安装 nghttp 这样一个命令行程序,当前没有任何浏览器支持不加密的 HTTP/2。If want to test HTTP/2 without encryption, can only search and install a command line program nghttp. Currently, no browser supports HTTP/2 without encryption.

6. 如果 https 配置完成,使用 JDK8 运行服务器,也不可能支持 HTTP/2,因为 JDK8 的SSLEngine 不支持 ALPN。If the https configuration is complete, running the server with JDK8 will not support HTTP/2, because the JDK8 SSLEngine does not support ALPN.

7. JDK8 以上的 JDK 运行服务器,即可支持 HTTP/2,但是当前的 JDK11 的 SSLEngine存在bug,如果使用 firefox 访问,然后频繁刷新,SSL 的握手方法就会陷入死循环。JDK13能够通过测试。With JDK above 8 run server and can support HTTP/2, but the current SSLEngine of JDK11 has a bug. If use Firefox to access and then refresh frequently, the SSL handshake method will fall into an infinite loop. JDK13 can pass the test.

高级话题 Advanced topic

服务器模型 Server model

1. 服务器采用完全的异步模型(HTTP/2 这样的协议一个连接上就能并发大量请求,如果一个请求对应一个线程执行服务,少量的用户连接就能耗费大量服务器线程),通过计算请求的 hash值选择线程执行服务。服务 handle 在实现上尽量不要阻塞,如果存在数据库 IO尽可能使用 cache,重负荷情况下应该设置更大的服务线程池规模。The server adopts a complete asynchronous model (a protocol such as HTTP/2 can make a large number of concurrent requests on a connection. If a request corresponds to one

156

Page 157: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

thread executing service, a small number of user connections consume a large amount of server threads), and the hash value of the request is calculated to select the thread executing service. Service handles should not be blocked as much as possible. If there is a database IO, use cache as much as possible. In heavy load, should set a larger service thread pool size.

2. 为 了跟 limax 框 架 其它服 务 统 一 , 使 用 Engine.open 启动 网 络引擎。 服 务 器参数NETSERVER_WORKMODE 为 true选择异步网络模型,false选择 poll 网络模型。In order to be unified with other services of the limax framework, use Engine.open to start the network engine. The server parameter NETSERVER_WORKMODE is true to select the asynchronous network model, and false to select the poll network model.

3. Engine.open 的参数 nProcessors 决定了网络线程池规模,网络线程池负责所有请求数据的解析,关闭消息的投递。参数 protocolSchedulers 配置的调度器仅被 HTTP/2 的定时器使用,只需要 HTTP 服务的情况下通常不需要配置太大。参数 applicationExecutors 决定了服务线程池规模,服务线程池用于 handle 请求,执行流控。The nProcessors parameter of Engine.open determines the size of the network thread pool. The network thread pool is responsible for parsing all request data and delivering the closing message. The scheduler configured with the parameter protocolSchedulers is only used by the HTTP/2 timer. In the case where only the HTTP service is required, the configuration is usually not required to be too large. The parameter applicationExecutors determines the size of the service thread pool. The service thread pool is used for handle requests and performs flow control.

数据发送 Data transmission

1. HTTP/1.1,HTTP/2均支持流控,FLOWCONTROL_WINDOW_SIZE 配置了流控窗口大小。HTTP/1.1 and HTTP/2 support flow control. FLOWCONTROL_WINDOW_SIZE determines the size of the flow control window.

2. WebSocket 上如果需要发送大量数据,需要根据 event.type()==SENDREADY 决定后续发送确保正确的流控,防止数据堆积。交互式应用通常不必严格进行流控。Sending a large amount of data on WebSocket, the event.type()==SENDREADY is used to determine the subsequent transmission to ensure correct flow control and prevent data accumulation. Interactive applications usually do not strictly follow the flow controlled.

3. Server-Sent Events同WebSocket 类似,使用 onSendReady 实现流控。Similar to WebSocket, Server-Sent Events uses onSendReady for flow control.

4. CONGESTION_TIMEOUT 时限内发送缓冲区没有任何消耗,被看成是严重拥塞,连接随即关闭。Within CONGESTION_TIMEOUT no consumption of the send buffer is considered as serious congestion, following close of the connection.

数据上传 Data upload

1. HTTP 协议通过 POST 方法执行上传,但是 POST 本身的设计过于简单,实际应用中如果不小心处理 POST,恶意客户端很容易利用 POST消耗服务器资源。最大的问题在于POST 的数据尺寸。

157

Page 158: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

The HTTP protocol performs uploading through the POST method, but the design of the POST itself is too simple. If the POST is handled accidentally in the actual application, the malicious client can easily consume the server resources by using the POST. The biggest problem is the data size of POST.

2. 不涉及文件上传的 POST,通常用来发送一些尺寸较大的查询,这种情况约定最大尺寸,使用 FormData 的 postLimit 方法简单限制即可。POST, which does not involve file uploading, is usually used to send some large-sized queries. In this case, the maximum size is appointed. The postLimit method of FormData can be used to simply limit it.

3. 涉及文件上传的 POST 就要复杂得多了,文件可能几 K,几 M,几 G 都有可能,也可能一次上传多个文件,postLimit 很难确定。(为了避免过大的内存开销, FormData 的useTempFile 方法对于通常的文件上传还是必要的)POST involving file uploading is much more complicated. The file may be a few K, M, and G, or it may upload multiple files at a time. The postLimit is difficult to determine. (To avoid excessive memory overhead, the useTempFile method of FormData is still necessary for normal file uploads)

4. 示例中的/upload2.html,第一个输入框应该填写一个估计的上传尺寸(上传文件大小加上浏览器添加的 MIME头信息的大小)尺寸过小则上传失败。In the example /upload2.html, the first input box should fill in an estimated upload size (the size of the uploaded file plus the size of the MIME header information added by the browser). If the size is too small, the upload fails.

5. 实际应用中,应该在具体文件上传之前,协商上传细节,例如文件数量,文件大小,从而估算总尺寸。协商结果在协商服务与上传服务间共享,客户端交换协商结果的时效性 key,保证上传服务根据协商结果正确配置。In actual applications, the upload details, such as the number of files and the file size, should be negotiated before the specific file is uploaded to estimate the total size. The result of the negotiation is shared between the negotiation service and the upload service. The client exchanges the time-sensitive key of the negotiation result to ensure that the upload service is correctly configured according to the negotiation result.

6. HttpHandler 的 censor(HttpExchange exchange)方法由网络线程执行,只要抛出异常就可以结束连接。如果必要,可以在其中完成更多的 FormData审查(FormData 可以获取已经传完的部分信息)。FormData 也提供了 getBytesCount(),getCreateTime()这样的方法,可以用来完成上传速率管理一类的任务,例如,直接关闭速率过慢的连接。(如果上传停顿,HTTP11_REQUEST_TIMEOUT,HTTP2_IDLE_TIMEOUT 这一类的配置参数会起作用)The censor (HttpExchange exchange) method of HttpHandler is executed by the network thread, and the connection can be terminated as long as an exception is thrown. If necessary, more FormData reviews can be completed (Partial information that has been parsed can be access through FormData without full parsing). FormData also provides methods such as getBytesCount() and getCreateTime(), which can be used to accomplish tasks such as upload rate management, for example, to directly close a connection that is too slow. (If the upload is paused, the configuration parameters like HTTP11_REQUEST_TIMEOUT, HTTP2_IDLE_TIMEOUT still take effect)

158

Page 159: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

运行管理 Operation management

Limax 框 架 ,既是 一套服 务 器 / 客 户 端 开 发环境,又是 一 个 运 行环境。提供了Switcher,GlobalId,Auany 这些服务器组件,这些服务器组件与用户提供的 Provider 进行交互,最终达成完整的系统功能。正确配置,调优服务器运行参数;规划服务器互联关系;监视服务器运行状况;以及版本升级,数据迁移,故障恢复,是运行管理阶段必须完成的工作。The Limax framework is not only a server/client development environment, but also an operation environment. The provided Switcher, GlobalId, and Auany server components interact with the Provider provided by the client to implement the complete system function. Correctly configure, tune the server's operating parameters; plan the server's interconnection relationship; monitor the server's operation status; and upgrading the version, migrating the data, recovering from the fault, are the tasks which must be finished in the operation management phase.

配置 Configuration

Limax 框架下各种服务器配置大多数通过 xml 描述实现。源码中基本服务器组件的配置可以作为参照进行修改,应用服务器生成的时候也生成了相应配置根据具体运行环境进行调整。部分不常用配置表现为 java虚拟机参数形式,可以在启动时调整参数。Most of the various server's configuraton in the Limax framework is implemented through xml description. Reference to the configuration of the basic server components in the source code to modify, the generated corresponding configuration when generating the server adjusts according to the detailed operation environment. Part of the unusual configuration represents as the parameters of the Java virtual machine, and could adjust the parameters during launch.

xml 基本参数 The basic parameters of the xml

Properties

只有一个属性:file,可以指定一个符合 java Properties 规范的文件,提供给后续解析使用,影响后续部分属性字符串的解析。具体工作方式如下:Only one property: file, a file which matches the Java Properties specification could be specified, and is provided to the subsequent resolution and affect the resolution to the subsequent part property string. The specific works as follow:

159

Page 160: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

如 果属性字符串为 $key:value$格式 ,例如 serveri-switcher.xml 中 解析 remoteIp 属性"$auany.ipaddr:127.0.0.1$"时,按如下步骤进行:If the attribue string is the $key:value$ format, for example, when resolving the remoteIp property "$auany.ipaddr:127.0.0.1$" in the serveri-switcher.xml, follow the below steps:

1. 分离出 key=auany.ipaddr,用于查找Split key=auany.ipaddr for searching

2. 查找系统 Property,如果存在则使用找到的值作为 remoteIp,否则Search the system Propery, if existed, use the found value as the remoteIp, or

3. 在 Properties 中提供的属性文件中查找,如果存在则使用找到的值作为 remoteIp,否则Search in the property file provided in the Properties, if existed, use the found value as the remoteIp, or

4. 设置 remoteIp 为 127.0.0.1Set the remoteIp as 127.0.0.1.

Trace

系统日志配置,包含如下属性:outDir:日志输出目录,默认为”./trace”console:是否允许输出到控制台,默认为 truerotateHourOfDay,rotateMinute:rotate日志的时间,默认为每天早上 6点,这里要注

意,系统必须在运行中跨越这一时间点才会执行 rotaterotatePeriod:rotate日志的周期,单位毫秒,默认 86400000,即 1 天。level:日志级别,有 5 种,DEBUG, INFO, WARN, ERROR, FATAL,默认为 warn,这个属

性字符串大小写不敏感。The system log configuration includes the below properties:outDir: the log output directory, and the default directory is "./trace".console: whether allows to output to the console, and the default values is true.rotateHourOfDay,rotateMinute: rotate log time point, and the default values is the 6 AM. It should be noted that the only the system cross this time point in the running state, the rotate will be executed.rotatePeriod: the period of the rotate log with the milliseconds as the unit. The default value is 86400000 which means a whole day.level: the log level, and there are five levels, DEBUG, INFO, WARN, ERROR, FATAL. The default level is WARN, and this property string is case-insensitive.

Limit

数量限制配置,当前用于控制 ServerManager 的接入数量,允许多个 ServerManager引用同一限制。

The configuration of the amount limit, is currently used to control the access number of the ServerManager, and allows the multiple ServerManager to reference to the same limit.

name:Limit 的命名 the name of the LimitmaxSize:最大数量 the maxinum number

160

Page 161: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

JmxServer

必选三个参数 host, serverPort , rmiPort ,提供给 JMX 管 理 应 用 使 用 。启动 url为 "service:jmx:rmi://<host>:<serverPort>/jndi/rmi:// <host>:<rmiPort>/jmxrmi" 。 通常情况下配置不常用的端口,只要管理应用能够访问即可。属性 username和 password 可选。通常情况下,如果配置了 JmxServer,应该设置防火墙阻止来自 Internet 的访问。

Three mandatory attributes host, serverPort and rmiPort are provided to the JMX to manage the application. The launch url is "service:jmx:rmi://<host>:<serverPort>/jndi/rmi://<host>:<rmiPort>/jmxrmi". Normally, configure the unusual port only if the management application could access.Attributes username and password is optional.Normally firewall is needed to block access from Internet when JmxServer is configured.

ThreadPoolSize

网络服务器线程池参数,包含如下属性。nioCpus:执行网络 Poll 的 cpu 数量,超过系统 cpu 数量没有意义,默认 1。netProcessors:网络数据收发线程数量,默认 4。protocolSchedulers:协议处理线程数量,默认 4。applicationExecutors:应用线程数量,默认 16。

The parameters of the network service thread pool includes the below properties.nioCpus: the cpu amount of executing the network Poll. Exceed the number of the system cpu has no meaning and the default values is 1.netProcessors: the thread amount of the network data receiving/sending, and the default value is 4.protocolSchedulers: the thread amount of the protocol processing, and the default value is 4.applicationExecutors: the application thread amount, and the default value is 16.

Manager

与应用 xml 描述中的 type=”server”,type=”client”两种 Manager对应,描述了一个网络端点。包含以下属性。

type:”client”或者”server”Correspond to the type=”server” , type=”client” two Managers described in the xml, and describe a network endpoint. Include the below properties.type: "client" or "server"

客户端,服务器共有属性parserCreatorClass:将属性的解析工作转交给该属性指定的类的对象。className:服务器或者客户端 Listener 类,用以处理网络消息,不存在则使用默认

Listener。classSingleton:存在 className属性的情况下,如果设置了这参数,这个参数必须为类

定义中一个静态方法名,获取对象单件;如果没有这个参数,则直接创建类的对象。defaultStateClass : 如 果存在 ,则启动 时调用 该 类 的 getDefaultState 静态 方法获取

161

Page 162: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Manager初始状态;如果不存在则以 Listener对象为参数,向 className 定义的对象查询Manager 的初始状态。

以上 4 个属性,与开发过程生成代码相关,一般情况下运营维护过程不用关心。The common attributes of the client and the server:parserCreatorClass: transfer the parsering xml element to the class object assigned by this attribute.className: the Listener class of the server of the client, is used to process the network message. If not existes, the default Listener is used. classSingleton: Under the condition that the className attribute existes, if this parameter is set, this parameter must be the static function name in the class definition to obtain the object singleton; if no this parameter, directly create the object of the class.defaultStateClass: if existe, call this class's getDefaultState static function to obtain the Manager's initial status; if not exist, with the Listener object as the parameter query the Manager's inital status from the class defined by the className.The above four attributes associate with the generated source code in the development process. And normally, it is no need to care in the operation and maintainance process.

enable:允许启动该 manager,默认为 true;某些运营场景下可以设置为 disable,暂时禁止启动 manager。

name:端点名称,字符串。inputBufferSize:输入缓冲区大小,默认 16384,除非网络吞吐量很大否则无需修改。outputBufferSize:输出缓冲区大小,默认 16384,除非网络吞吐量很大否则无需修改。checkOutputBuffer:是否检查输出缓冲区大小,如果允许检查,当网络拥塞时堆积数

据超出 outputBufferSize,记录警告,关闭连接。默认为 falseinputSecurity:初始的网络输入流密钥,需要 16字节串表示十六进制数,默认没有。outputSecurity:初始的网络输出流密钥,需要 16字节串表示十六进制数,默认没有。inputCompress:初始状态下是否允许输入流压缩,默认 falseoutputCompress:初始状态下是否允许输出流压缩,默认 falseasynchronous:该 Manager工作模式为异步模式或者 poll 模式,默认 false,使用 poll

模式。enable: allow to start this manager and the default value is true; it could be set as the disable in some operating scenarios to temporarily disable starting the manager.name: the name of the endpoint, string.inputBufferSize: the buffer size of the input, the default value is 16384, and there is no need to modify unless the large network throughput.outputBufferSize: the buffer size of the output, the default value is 16384, and there is no need to modify unless the large network throughput.checkOutputBuffer: whether to check the buffer size of the output, if allows to check, when the network congests and the accumulated data exceeds the outputBufferSize, record the warning and close the connection. The default value is false.inputSecurity: the initial network input stream secret key, requires 16-byte string to represent hexademical number, and the default value is none.outputSecurity: the initial network output stream secret key, requires 16-byte string to represent

162

Page 163: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

hexademical number, and the default value is none.inputCompress: in the initial status, whether to allow the input stream compress, and the default values is false.outputCompress: in the initial status, whether to allow the output stream compress, and the default value is false.asynchronous: the manager work in asynchronous mode or poll mode, default value is false, use poll mode.

客户端独有属性:remoteIp:服务器 ip地址remotePort:服务器端口号connectTimeout:连接超时时间,默认 5秒autoReconnect:是否允许连接失败后自动重连,允许的情况下,第一次延迟 1秒后重

连,以后逐次加倍退避,直到 3 分钟,3 分钟为最长退避时间。默认 falseThe client's specific attributes:remoteIp: the ip address of the serverremotePort: the port of the serverconnectTimeout: the connection timeout, the default value is 5 secondsautoReconnect: whether to allow the automatical reconnection after the connection fails. If allow, the first time reconnect after delaying 1 second, later successive doubly retreat till 3 minutes, and the 3 minutes is the longest retreat time. The default value is false.

服务器独有属性:localIp:服务器 Ip地址,默认 0.0.0.0localPort:服务器端口号backlog:服务器 Listen参数limit:引用 Limit 配置名,控制服务器最大承载连接数量,超出数量后,记录日志,关

闭后续接入用户连接,拒绝后续用户接入。缺省情况下,配置名解释为空串,共同引用一个 maxSize=Long.MAX_VALUE 的 Limit。

autoStartListen:启动后是否允许服务器自动开始监听端口,默认为 truewebSocketEnabled:是否启动为 WebSocket 服务器,支持 HTML5 兼容客户端,默认为

false。The server's specific attributes:localIp: the ip address of the server, the default value is 0.0.0.0localPoft: the port of the serverbacklog: the Listen parameter of the serverLimit: reference the Limit configuration name, control the server's largest connection number. When exceeding the quantity, record the log, close the follow-up user connection, and delay the follow-up user access. By default, the configuration name is represented as the empty string, and reference a limit of the maxSize=Long.MAX_VALUE together. autoStartListen: whether to allow the server to automatically monitor the port after launching, the default value is true.webSocketEnabled: whether to launch as the WebSockect server, suppor the client compatible

163

Page 164: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

HTML5, and the default value is false.

如果启动为 WebSocket 服务器,则可以通过如下属性支持 https:keyStore:pkcs12格式包装的服务器证书包路径password:keyStore密码

If launched as the WebSocket server, the below attributes could be used to support https:keyStore: the certificate package path of the server packaged with pkcs12 format.password: keyStore password.

NodeService

node.js 服务组件配置,一个属性:node.js service component configuration, one property:

module:node 模块路径 node module path一系列有序子 xml节点集,对应模块需要的参数。

A serial of ordered child xml node set, corresponds to the parameters required by the module.<parameter value=”p0”/><parameter value=”p1”/>

Switcher

switcher 服务器组件专用,提供与 Endpoint 之间的连接配置参数。五个属性:cacheGroup:成功登录的情况下,switcher 通过该组播地址在服务器间同步登录响应,switcher 与 auany 断开连接的情况下,试图使用该 cache信息进行认证,默认为空串,禁止该 cache。cacheCapacity:成功登录的 login信息 cache 容量,默认 10000needcompress.s2c:是否压缩 Switcher 到 Endpoint 数据流,默认为 trueneedcompress.c2s:是否压缩 Endpoint 到 Switcher 数据流,默认为 truekey:swicher向 auany 发起验证使用的 key,具体描述参见附录 3(应用配置)。一系列子 xml节点集:<dh group="1"/>配置服务器允许的 Diffie-Hellman组。RFC2049,RFC3526隐含支持 dhgroup = 1配置支持 dhgroup = 2,5,14,15,16,17,18<native id=”1”/><ws id=”2”/><wss id=”3”>配置该服务器支持的 switcher id,具体描述参见附录 3(应用配置)。

The Switcher element is only used by the server component Switcher to provide the configuration parameter for the connection with the Endpoint.

164

Page 165: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Five attributes:cacheGroup: When login success, Switchers synchronize login information by this multicast address. When the connection between Switcher and Auany is broken, this cache is used to authenticate login request. default is empty, forbid this cache.cacheCapacity: login information cache’s capacity.needcompress.s2c: whether to compress the Switcher to the Endpoint data stream, the default value is true.needcompress.c2s: whether to compress the Endpoint to the Switcher data stream, the default vaue is true.key: the key used when the Switcher initiates the verification to the Auany. Refer the appendix 3 for the detailed description (application configuration).A serial children xml element set:<dh group="1"/>Configure the Diffie-Hellman group allowed by the server. RFC2049, RFC3526.Implicitly support dhgroup = 1Configuration supports dhgroup = 2 , 5 , 14 , 15 , 16 , 17 , 18 <native id=”1”/><ws id=”2”/><wss id=”3”>Configure the Switcher id supported by this server. Refer the appendix 3 for the detailed description (application configuration).`

Auany

Auany 服务器组件专用,包括三种类型子节点,plat,pay,appstore:plat节点:plat节点具有 2 个主要属性 name 与 className。name 大小写不敏感,为认

证平台名,与 Endpoint登录配置中的 platflag相对应,相应的认证请求交由 className指定的平台支持类进行处理。平台支持类需要实现 limax.auany.PlatProcess 接口,提供两个方法:init:参数为对应 plat节点,可以在 plat节点内扩充平台需要的其它配置,在这里解析check:具体处理方法可以参考,limax.auany.plats.Test 的实现, Test 模块允许任何用户

名,但是密码必须为 123456。limax.aunay.local.Authenticator 为本地认证方式,比较复杂,支持 3 种常见的认证系统,radius,ldap,sql 数据库,实际使用按照例子修改配置,选择某一种即可。定义在该 plat节点下的认证系统通过 RR 方式支持负载均衡及容错,这种情况下请放置多个不同条目,条目越多,timeout 配置应该越小,因为实现上,如果某一认证系统的查询不能正确返回结果——包括超时与失败——这种情况下,轮换使用下一认证系统。The Auany element is only used by the server component Auany, including three types of children element, plat, pay and appstore.The plat element: the plat element has two main attributes: name and className. The name is case-insensitive and is the authentication platform name, corresponds to the platflag in the login configuration of the Endpoint, the appropriate authentication request is processed by the platform support class appointed by the className.

165

Page 166: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

The platform support class need to implement the limax.auany.PlatProcess interface, and provides two methods.init: the parameter is the corresponding plat element, the other configuration requried by the platform is expanded in the plat element and analyzed here.check: the detailed implementation method could refer to the implementation of the limax.auany.plats.Test, and the Test module allows to use any user name but the password must be 123456. The limax.aunay.local.Authenticator is the local authentication mode, which is a little complicated and supports three kinds of common authentication systems, radius, ldap and sql database. In the actual usage, the user just need to modify the configuration according to the example and choose one kind of the authentication system. The authentication system defined in the plat element supports the load balance and error tolerance through RR. In this case, a number of different entries should be placed, the more entires, the smaller the configuration of the timeout. Because in the implementation, if the query of some authentication system could not correctly return the result, including the timeout and failure, the next authentication system is rotated in this case.

pay节点:pay节点具有 2 个主要属性 gateway 与 className。gateway 是系统为第三方支付网关分配的数字 id,与 Endpoint.AuanyService.Pay 中的 gateway相对应。className指明了第三方支付网关消息处理类,该类必须实现 limax.aunay.PayGateway 接口。The pay element: the pay element has two main attributes --- gateway and className. The gateway is the digital id assigned by the system for the third party payment gateway, and corresponds to the gateway of the Endpoint.AuanyService.Pay. The className appoints the message process class of the third party payment gateway, and this class must implement the interface of the limax.auany.PayGateway.

appstore节点:appstore 发票处理的基础配置。此外 , Auany 节点内 <xi:include href="appconfig.xml"/> 通 过 include 方 式引用 了

appconfig.xml 配置文件,提供应用配置,具体描述在附录(应用配置)中详细介绍。The appstore element: the basic configuration processed by the appstore receipt.Furthermore, the <xi:include href="appconfig.xml"/> of the Auany element refers to the appconfig.xml configure file via include way, and provides the application configuration. Please refer to the appendix (application configuration) for the detailed description.

GlobalId

Provider 服 务专用 , 配 置 类 似 于 type=”client” 的 Manager 。 一般来说应 该 配 置autoReconnect=”true”,以及正确的 GlobalId 服务器 ip地址,端口号。另外,可以配置属性timeout , 指 定 GlobalId 请 求 超 时 , 效 果 等 同 于 执 行 方 法limax.provider.GlobalId.setTimeout(long timeout),默认为 2000ms。The GlobalId is only used by the Provider service, and the configuration is simiar like the Manager with the type="client". Usually, it should configure the autoReconnect="true", and the correct GlobalId server ip address and port. In addition, the attribute timeout could be configured to specify that when the GlobalId requirement timeouts, the effect equals to

166

Page 167: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

executing the method limax.provider.GlobalId.setTimeout(long timeout) and the default valus is 2000ms.

Provider

与应用 xml 描述中的 type=”provider”类型 Manager对应,提供了 Provider 的网络端点配置。Correspond to the Manager with type="provider" described in the xml, and provide the network endpoint configuration for the Provider.

Provider内置至少一个客户端类型 Manager节点,描述了 Provider 连接 Switcher 服务器组 件 的 配 置 , 这 些 Manager 节 点 除 了parserCreatorClass,className,classSingleton,defaultStateClass属性无意义外,其它与客户端 Manager节点相同。一个 Provider内置 Manager节点允许有多个,连接多个 Switcher服务器组件,需要调整接入规模的时候,这是最常用的。The Provider has at least one built-in client type Manager element, which describes the configuration connecting the Switcher server component from the Provider. Except that the parserCreatorClass, className, classSingleton, and defaultStateClass attributes have no meaning, these Manager elements are the same as the client Manager element. A Provider allows multiple built-in Manager elements to connect multiple Switcher server components. It is usual if it needs to modify the connection scale.

Provider节点有如下属性:className : ProviderListener 类 , 用 以 处 理 Provider 消息, 不存在则使 用默认

ProviderListenerclassSingleton:存在 className属性的情况下,如果设置了这参数,这个参数必须为类

定义中一个静态方法名,获取对象单件;如果没有这个参数,则直接创建类的对象。viewManagerClass:View 的管理类。setAsOnlines:决定是否 Provider启动时立刻打开数据服务,默认 true以上 4 个属性,与开发过程生成代码相关,一般情况下运营维护过程不用关心。

The Provider element has the below attributes:className: the ProviderListener class, is used to process the Provider message. If it does not exist, the default ProviderListener is used.classSingleton: in the condition that the className attribute existes, if this parameter is set, this parameter must be the static function name in the class definition to obtain the class singleton; if there is no this parameter, the class object is directly created.viewManagerClass: the management class of the View.setAsOnlines: determine whether to start the data service immediately when the Provider launches. The default value is true.The above four attribues are related to the generated code in the development process. Usually, the operation process does not care about it.

name:Provider名称,字符串pvid:Provider 的 PVID参数,决定了系统内服务号key:Provider密钥串,用于 Auany 服务器验证 Provider 合法性。默认为空串。Auany 有

167

Page 168: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

验证需求时,应同步修改该串。useVariant:Provider 是否支持 Variant 模式 View,这与生成代码相关,但是必要的情况

下可以通过设置该配置为 false 关闭这一特性。useScript:Provider 是否支持脚本模式 View,这与生成代码相关,但是必要的情况下

可以通过设置该配置为 false 关闭这一特性。paySupportClass:如果 Provider 需要支持支付,则应该提供该支付处理类,支付处理类

必须实现 limax.provider.PaySupport 接口。name: the Provider name, is string.pvid: the PVID parameter of the Provider, determines the service number in the internal system.key: the secret key string of the Provider, is used to verify the validation of the Provider by the Auany server. The default value is null. When the Auany has the verification request, this string should be modified at the same time.useVariant: the Provider whether to support the Variant view, is related to the generated code. However, if necessary, this specification could be close through setting this attribute as false.userScript: the Provider whether to support the script view, is related to the generated code. If necessary, this specification could be close through setting this attribute as false.paySupportClass: if the Provider needs to support the payment, the payment process class should be provided, and this payment process class must implement the interface of limax.provider.PaySupport.

Zdb

Zdb 配置支持大量属性:The ZDB configuration supports a lot of attributes:

dbhome:数据库 home,如果前缀为 jdbc:mysql,表示使用 MYSQL 数据库,解释为jdbcUrl,否则解释为 EDB 数据库的文件系统路径。dbhome: the home of the database. If the prefix is the jdbc::mysql, it means that the MYSQL database is used and interpretted as the jdbcUrl, or it is interpretted as the file system path of the EDB database.

preload:表 cache预装载路径。zdb 正常停止,最后一次 checkpoint 之后,所有表的cache 都是干净的。此时,将 cache内容保存到本地磁盘,下次启动,通过保存的内容初始化 cache,后端为 mysql 的情况下,可以有效减轻启动负荷。装载过程中出现任何异常,立刻停止装载,所以表结构的变动不会造成任何影响。装载完成,清空整个目录。该属性默认不配置。preload: table cache preload path. When the zdb stops normally, after the last checkpoint, the cache of all tables is clean. At this point, save the cache content to the local disk. In the next time starting, the cache is initialized by the saved content, which can effectively reduce the startup load in the condition that the backend is mysql. If any abnormality occurs during the loading process, the loading is stopped immediately, so the change of the table structure has no any effect. After the loading is complete, the entire directory is emptied. This property is not configured by default.

edbCacheSize:使用 EDB 数据库的情况下,该参数指定了 EDB 的 cache页面最大数量。168

Page 169: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

EDB 数据页尺寸为 8K。数据吞吐量过大的情况下,可能出现内存使用过载,一旦超出这个数量 EDB将自动 checkpoint,释放干净页面。edbCacheSize: when using EDB database, this parameter specifies the maximum number of the EDB's cache page. The size of the EDB's data page is 8K. Under the condition that the data throughout is too large, the memory usage might be overlad. Once this number is exceeded, the EDB will automatically checkpoint and release the clean page.

edbLoggerPages:使用 EDB 数据库的情况下,该参数指定了 EDB 的日志文件能够存储的页面的最大数量,checkpoint 之前检查当前日志文件的页面数量是否超出这一限制,如果超出,则创建新的日志文件,如果启用了增量备份,前一个日志文件被复制到备份目录里,如果没有,前一个日志文件被简单删除。edbLoggerPages: in conditions of using EDB database, this parameter appoints the maximum sum of the page which could be stored by the EDB's log file. The page's sum of the current log file will be checked to make sure whether it exceeds the limitation before checkpoint. If the sum exceeds the limitation, the new log file will be created. If the incremental backup is enable, the previous log file will be copied to the backup directory. If the incremental backup is disable, the previous log file will be simply deleted.

jdbcPoolSize:使用 MYSQL 数据库的情况下,jdbcPoolSize 决定了连接池大小,否则无意义。jdbcPoolSize: in conditions of using MYSQL database, the jdbcPoolSize detemines the size of the connection pool, or it is no meaning.

defaultTableCache :选择表 cache 类,当前支持 limax.zdb.TTableCacheConcurrentMap, limax.zdb.TTableCacheLRU,默认为 limax.zdb.TTableCacheLRU。defaultTableCache: select the table cache class, currectly support limax.zdb.TTableCacheConcurrentMap, limax.zdb.TTableCacheLRU, and the default values is limax.zdb.TTableCacheLRU.

zdbVerify:运行中检查程序锁的使用是否合规,测试运行阶段可以设置为 true,正式运行设置为 false,提高性能。zdbVerify: check whether the usage of the lock in the program is reasonable during running. It could be set as the ture in the test running stage and set as the false in the actual running stage to improve the performance.

autoKeyInitValue,autoKeyStep:自增量 key初始值和自增量 key增长步长,如果预期到以后有合并数据库的需求,那么多个服务器可以考虑使用同样步长,不同初始值。这样使用自增量 key 的表就可以在维护时直接合并,不需要考虑重设 id。autoKeyInitValue, autoKeyStep: the automatic incremental initial value of the key and the automatic incremental growth step of the key. If the requirement of merging the database in the future is expected, the multiple server could consider to use the same growth step and different initial value. Then the table with the automatic incremental key could be directly merged when mentaining and it is not necessary to reset id.

corePoolSize,procPoolSize,schedPoolSize:分别配置了 zdb核心线程池大小;存储过程线程池大小;调度线程池大小。corePoolSize, procPoolSize, schedPoolSize: separately configure the zdb's core thread pool size; the storage procedure thread pool size; the schedule thread pool size.

checkpointPeriod,marshalPeriod,marshalN,snapshotFatalTime:checkpointPeriod 决169

Page 170: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

定了将被改变的数据存储到底层数据库的频率,marshalPeriod 决定了存储到底层数据库之前预打包未加锁的被改变数据的频率,预打包未加锁的被改变数据可以显著调高 zdb吞吐量。可以几次预打包存储一次,marshalN 决定了在最终存储之前预打包未加锁的被改变数据次数。两个频率的最小值由虚拟机参数 limax.zdb.Checkpoint.SCHED_PERIOD控制,默认100ms。如果在最终存储之前,预打包与完全打包消耗的总时间大于 snapshotFatalTime,则记录一条故障日志,多数情况表明系统负荷过重。checkpointPeriod, marshalPeriod, marshalN, snapshotFatalTime: checkpointPeriod determines the frequency that the changed data is stored to the underlying database, the marshalPeriod determines the prepackage frequency of the unlocked changed data before storing to the underlying database, and prepackaging the unlocked changed data could significantly improve the zdb throughout. The serveral prepackage and one storage is acceptable. The marshalN determines the prepackage frequency of the unlocked changed data before final storage. The minmum value of these two frequency is controlled by the virtual parameter limax.zdb.Checkpoint.SCHED_PERIOD, and the default value is 100ms. If the total time of the prepackaging and completely packaging is bigger than the snapshotFatalTime before the final storage, a piece of error log is recorded, in most condition which means the system is over load.

deadlockDetectPeriod:数据库死锁检测周期。deadlockDetectPeriod: the period of the database checking the deadlock.

Zdb节点下可以有一个 Procedure节点,配置存储过程相关参数,Procedure节点有 4个属性:The ZDB element could have one Procedure element to configure the parameters related to the storage procedure. The Procedure element has four attributes:

maxExecutionTime:存储过程执行的最大时间,执行超出该时间将报告过程执行超时,常见的 OLTP 应用,存储过程执行时间过长往往不合理。maxExecutionTime: the maximum time of the storage procedure execution. The procedure execution overtime will be reported when the execution exceeds this time. In the common OLTP application, it is not reasonable if the storage procedure execution time is too long.

retryTimes,retryDelay:这两个参数决定了死锁之后过程重试次数,以及退避时间。trace:设定了存储过程相关日志记录级别。

retryTimes, retryDelay: these two parameters determine the procedure retry times and retreat time after the deadlock.trace: set the log record level related to the storage procedure.

需要注意,所有 Zdb 配置的属性都没有提及默认值,因为这些默认值,全部来源于应用描述 xml,存储在生成的程序文件中。应用启动时,这里的配置可以覆盖程序文件中的相应配置,一把来说只需要根据运营需求作少许修改。It is noted that all the configuration attributes of the ZDB have not mentationed the default value, because these default values totally come from the xml description of the application and are stored in the generated program files. When launching application, these configuration could override the corresponding configuration in the program files. Usually, it only need to make a few modification according the operation requirement.

170

Page 171: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Switcher

Switcher 配 置 可 由 上 述Properties,Trace,JmxServer,ThreadPoolSize,Manager,Switcher节点进行组合,可参考Limax源码提供的 service-switcher.xml 进行调整,其中:

Switcher节点必选Manager节点中

name=”ProviderServer” 节点定义了该 Switcher作为 Provider 服务器的网络特性name=”AuanyClient” 定义了该 Switcher作为 Auany 客户端的网络特性name=”SwitcherServer” 定义了该 Switcher作为 Endpoint 客户端的网络特性name=”SwitcherServerWebSocket” 定义了该 Switcher作为 WebSocket 服务器的网络

特性。例如,需要接入多个 ISP,使用了多个网卡,可以克隆多份名为 SwitcherServer 的

Manager节点,调整对应的 name,配置各自的 localIp,绑定不同的 ISP 的 IP地址。The Switcher configuration could be combined with the previous Properties, Trace, JmxServer, ThreadPoolSize, Manager, Switcher elements. Refer to the service-switcher.xml provided by the Limax source code for the adjustment, including:The Switcher element must be selected.In the Manager element

name=”ProviderServer” element defines the network specification of this Switcher as the Provider server.

name=”AuanyClient” element defines the network specification of this Switcher as the Auany client.

name=”SwitcherServer” element defines the network specification of this Switcher as the Endpoint client.

name=”SwitcherServerWebSocket” element defines the network specification of this Swicher as the WebSocket server.

For example, if it is necessary to access mulitiple ISP and use several NIC (network interface card), it could clone multiple copies of the Manager elements named as SwitcherServer, adjust the relevant name, configure its own localIp to bind different IP address of the ISP.

Auany

Auany 配 置 可 由 上 述Properties,Trace,JmxServer,ThreadPoolSize,Manager,Auany,Zdb节点进行组合,可参考 Limax源码提供的 service-auany.xml 进行调整。

Zdb 在这里用于存储整个运营环境下的用户 SessionId 分配关系。应用于实际的运营环境时,应该在 Auany 框架下,创建更多的 limax.auany.PlatProcess

接口实现,提供更多的三方平台支持。The Auany configuration could be conbined with the previous Properties, Trace, JmxServer, ThreadPoolSize, Manager, Auany and ZDB elements. Refer to the service-auany.xml provided by

171

Page 172: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

the Limax source code for the adjustment.The ZDB is used here to store the assignment relationship of the user's SessionId in the whole operation environment.In the actual operation envionment, the more limax.auany.PlatProcess interface implementation should be created in the Auany framework to provide the support to the more third party platform.

GlobalId

GlobalId 配置可由上述 Properties,Trace,JmxServer,ThreadPoolSize,Manager, Zdb节点进行组合,可参考 Limax源码提供的 service-globalid.xml 进行调整。

Zdb 在这里用于存储分配的全局 ID信息。The GlobalId configuration could be conbined with the the Properties, Trace, JmxServer, ThreadPoolSize, Manager, and ZDB elements. Refer to the service-globalid.xml provided by the Limax source code for the adjustment.The ZDB is used here to strore the assigned global ID information.

Provider

Provider 配 置 可由上 述 Properties , Trace , JmxServer , ThreadPoolSize , Manager , Zdb,GlobalId,ProviderId节点进行组合,可参考生成的服务器代码中的 service-XXX.xml 进行调整。

一个 Provider 必须有且只有一个 Provider节点。如果 Provider提供的服务使用了 GlobalId 服务,则必须配置 GlobalId节点,否则不用。如果 Provider提供的服务使用了 Zdb,则必须配置 Zdb节点,否则不用。

The Provider configuration is combined with the previous Properties, Trace, JmxServer, ThreadPoolSize, Manager, ZDB, GlobalId, and ProviderId elements. Refer to the service-xxx.xml of the generated server source code for the adjustment.A Provider must have one and only one Provider element.If the service provided by the Provider uses the GlobalId service, the GlobalId element must be configured. Or it should not be used.If the service provided by the Provider uses the ZDB, the ZDB element must be configured. Or it should not be used.

java虚拟机参数 The parameters of the Java virtual machine

limax.net.io.NetModel.delayPoolSize:网络层触发各种超时动作使用的调度线程池大小,默认为 1,一般无需调整。limax.net.io.NetModel.delayPoolSize: the scheduling thread pool size used when network layer triggers various time-out operation. The default value is 1 and it is no need to adjust.

limax.util.ConcurrentEnvironment.timeoutSchedulerSize:可超时执行器的调度线程池尺寸,可超时执行器用于 Zdb 的存储过程超时。默认认为 3,一般无需调整。limax.util.ConcurrentEnvironment.timeoutSchedulerSize: the scheduling thread pool size of the executor which allows the timeout and is used for the tiemeout in the storage procedure of the

172

Page 173: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

ZDB. The default value is 3 and it is no need to adjust.limax.zdb.Checkpoint.SCHED_PERIOD : zdb 的 checkpoint 检测最 小周期,默认为

100ms,一般无需调整。limax.zdb.Checkpoint.SCHED_PERIOD: the detecting minimum period of the ZDB's checkpoint. The default value is 100ms and it is no need to adjust.

limax.zdb.Lockeys.bucketShift:zdb内部锁 hash桶尺寸移位参数。默认为 10,表示桶尺寸为 2<<10 == 1024。一般无需调整。limax.zdb.Lockeys.bucketShift: the hash bucket size shift parameter of the ZDB's internal lock. The default value is 10, which means that the bucket size is 2<<10 == 1024. It is no need to adjustlimax.zdb.Zdb.useFixedThreadPool:boolean 类型,如果设置为 true,表示 zdb 的核心线程池、过程线程池使用固定线程池,这种情况下使用 LinkedBlockingQueue排队任务,防止丢失。除非存储过程生成过多,导致瞬时重负荷,严重影响系统响应,不应该设置该参数。如果使用这种方式应该考虑 zdb 配置里面加大相应 pool 的配置。设置该参数应该被作为临时手段,修改设计才是合理考虑,必须意识到使用 LinkedBlockingQueue排队任务,在任务间存在依赖关系的情况下,可能导致难以被检查到的饥饿。(正在执行的任务依赖了还在排队的任务)。.limax.zdb.Zdb.useFixedThreadPool: boolean type. If it is set as true, it represents that the core thread pool and process thread pool of zdb use a fixed thread pool, in which case the LinkedBlockingQueue queuing task is used to prevent loss. This parameter should not be set unless the storage procedure is generated too much, resulting in an instantaneous heavy load that seriously affects the system response. If this approach is used, the configuration of corresponding pool should be increased in the zdb’s configuration. Setting this parameter should be used as a temporary measure. Modifying the design is a reasonable consideration. It must be aware of that the use of LinkedBlockingQueue queuing tasks, may lead to the hunger which is difficult to be detected, in the condition that there is dependent relationship between the tasks. (The task being performed depends on the task that is still queued).

limax.net.Engine.limitProtocolSize:协议尺寸硬限制,限制了所有协议以及 View 协议的最大尺寸,超出尺寸连接中断,记录日志,默认 1048576。一般来说对于交互性应用 View的被改变字段尺寸,一次改变的字段的数量不应该过大,过大了将导致过多的网络传输,不是好的设计。对于数据传输应用,可以考虑在 Provider,Switcher 服务器上增大该参数。limax.net.Engine.limitProtocolSize: the hard limitation of the protocol size, which limits the maximum size of all the protocols and View protocols. If the size exceeds, the connection terminates and record the log. The default value is 1048576. Usually, for the size of the changed field of the interactive application View, the sum of the changed field should not be too large one time, because it causes the excessive network traffic and is not good design. For the network transfer application, it could consider to increase this parameter in the Provider and Switcher server.limax.net.Engine.intranetKeepAliveTimeout:某些云环境下,内网可靠性不能保证,可以通过该参数配置服务器间 keepalive检测的超时时长,一旦超时,关闭连接;作为服务器的服务器(通常是 Switcher)清除相应的状态的信息,恢复到初始状态;作为客户端的服务器(通常是 Provider),配置了断线重连的情况下,通过自动重连,试图恢复到正确的状态。默认为 0ms,不作检测。limax.net.Engine.intranetKeepAliveTimeout: in some cloud environments, the internal network

173

Page 174: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

reliability can not be guaranteed. This parameter could be used to configure the timeout period for keepalive detection between the servers. Once timeout, close the connection. The server (usually the Switcher) as the server, clears the corresponding state's information, and recovers to the initial state. The server (usually the Provider) as the client, in the condition that the disconnect reconnection is configured, tries to recover to the correct state via auto-connection. The default value is 0ms and without detection.

limax.switcher.SwitcherListener.handShakeTimeout: 从 Switcher 接受 Endpoint 连接,到处理 Endpoint握手请求之间允许的时间窗口,默认 1000ms。除非 Switcher 服务器负荷过重或者客户端性能过低该限制才不能满足。服务器硬件性能不成问题的情况下可以在Switcher 服务器上适当减小该参数,有利于更好抵抗攻击。limax.switcher.SwitcherListener.handShakeTimeout: the allowed time window from that the Switcher accepts the Endpoint connection to the Switcher processes the Endpoint handshake requirement. The default value is 1000ms. Unless the load of the Switcher server is too heavy or the performance of the client is too low, this limitation could not be met. In the condition that the hardware performance of the server is not the problem, this parameter in the Switcher server could be properly decreased which is beneficial to defend the attack better.

limax.net.WebSocketServer.handShakeTimeout:上述参数对于 WebSocket 方式运行的服务器无意义,因为 WebSocket 协议的设计决定了第一个 HTTP 请求分析处理完毕才切换进入WebSocket 数据交换状态,这时所需的认证参数已经齐备。对于 WebSocket 方式下这个参数与上述参数具有同等意义。limax.net.WebSocketServer.handShakeTimeout: the previous parameters has no meaning to the server runnning in the WebSocket mode, because the design of the WebSocket protocol determines that after finishing the first HTTP request, the WebSocket data exchange status is switched, and the required authentication parameters are all ready. In the WebSocket mode, this parameter has the same meaning as the previous parameters.

limax.net.io.RFC6455Server.maxMessageSize:WebSocket 服务器允许的最大消息大小,默认 65536,如果需要通过 WebSocket 交互大尺寸消息,可以在 Switcher 服务器上增大该参数。过大的设置不利于 Switcher抵抗流量攻击。limax.net.WebSocketServer.maxMessageSize: the maximum message size allowed by the WebSocket server. The default value is 65536. If it needs to interact the huge message via WebSocket, this parameter in the Switcher server could be increased. Howerver, the excessive setting is not beneficial for the Switcher to defend the traffic attack.

limax.net.io.WebSocketServerTask.keyExchangeTimeout:扩展的 websocket 方式下,密钥交换超时,默认 3000ms。limax.net.WebSocketServer.keyExchangeTimeout: in the extended websocket mode, the key exchange timeout, and the default value is 3000ms.

limax.net.WebSocketServer.dhGroup:扩展的 websocket 方式下,密钥交换允许使用的最大 DHGroup,默认 2。limax.net.WebSocketServer.dhGroupMax: in the extended websocket mode, the max dhgroup while key exchanging, and the default value is 2.

limax.net.secureIp:如果 Switcher 处于 DNAT环境下,通过该参数指定指定外网 ip,确保 native 方式或者扩展的 websocket 方式下的密钥协商能够正确进行。limax.net.secureIp: if the Switcher is in the DNAT environment, the external network ip is

174

Page 175: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

assigned via this parameter to ensure that the key negotiation could be carried out correctly in the native mode or extended websocket mode.

limax.switcher.SwitcherListener.sessionLoginTimeout:Switcher 与 Endpoint握手成功直到正常上线允许的最长时间,默认 20000ms。超过这个时长一般意味着 Switcher 与 Endpoint或者 Auany 之间的通讯出现问题。一般无需调整。limax.switcher.SwitcherListener.sessionLoginTimeout: the allowd longest time from the successful handshake between the Switcher and the Endpoint till the nomal online. The default value is 20000ms. Exceeding this duration means that the communication between the Switcher and the Endpoint or the Auany has the problem. Usually, it is no need to adjust.

limax.switcher.SwitcherListener.keepAliveTimeout:Endpoint周期性向 Switcher 发送 Ping协议,这个参数决定了 Switcher 收到 Ping 协议的最长允许间隔,超出这个间隔,Switcher关闭网络连接。默认为 60000ms。limax.switcher.SwitcherListener.keepAliveTimeout: the allowed maximum interval that the Switcher receives the Ping protocol due to that the Endpoint periodly sends the Ping protocol to the Switcher. When this interval is exceeded, the Switcher close the network connection. The default value is 60000ms.

limax.switcher.SwitcherListener.pingProtect:Switcher 的 Ping保护周期,在保护周期内如果 Switcher 收到一个以上的 Ping 协议,服务器关闭网络连接。默认为 30000ms。limax.switcher.SwitcherListener.pingProtect: the protection period of the Switcher's Ping. In the protection period, if the Switcher receives multiple Ping protocols, the server close the network connection. The default value is 30000ms.

上述两个参数与 Endpoint相关,发布版本的所有 Endpoint 使用 50000ms 的 ping周期,在上述两个参数决定的窗口之内。如果要调整,需要保留一定宽容度。The previous two parameters are related to the Endpoint, all the Endpoint in the release version uses the ping duration with 50000ms, which is in the window determined by these two parameters. If it needs to be adjusted, it is necessary to set some tolerance.

limax.node.js.EventLoop.corePoolSize:node.js 框架事件循环线程池的最小线程数量,所有 Cluster共享同一个线程池,默认 64。limax.node.js.EventLoop.corePoolSize: the minimum number of threads for the event loop thread pool of the node.js framework. All the Cluster shares the same thread pool and the default is 64.

limax.node.js.modules.Dns.corePoolSize:node.js 框架中,dns 模块的 DirContext 池容量,默认 16,除非应用需要太多并发 dns查询,一般不需要调整。limax.node.js.modules.Dns.corePoolSize: in the node.js framework, the DirContext pool capacity of dns module, the default is 16. Unless the application requires too much concurrent dns query, it is no need to adjust generally.limax.node.js.module.Net.TLSExchange.concurrency:node.js 框架中,net 模块的 socket对象上启动 TLS 支持的情况下,使用的 SSLEngine 的并发数量,默认 32,重负荷 TLS 服务器,可以试验性增加该值。limax.node.js.module.Net.TLSExchange.concurrency: in the node.js framework, when starting the TLS support on the socket object of net module, the default of the amount of concurrent use of SSLEngine is 32. The heavy load TLS server can experimentally increase this value.

limax.node.js.module.Sql.ConnectionFadeout:node.js 框架中,sql 模块的连接池中的连175

Page 176: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

接淡出超时,默认 60000ms,一般无需调整。limax.node.js.module.Sql.ConnectionFadeout: in the node.js framework, the connection in the connection pool of the sql module fade out timeout, the default is 60000ms, and it is no need to adjust generally.

部署 Deployment

单机 Single machine

Auany,GlobalId,Switcher 以及应用提供的 Provider 可以运行在同一台机器上,一般来说开发环境即使如此。The Auany, GlobalId, Switcher and the Provider provided by the application could run on the one machine. Usually, the development environment is like this.

小规模 Small scale

Auany,GlobalId 可以合并在同一机器上运行。应 用负荷不太重 , 连 接 数 量 不太多 的情况下 可 以将 Switcher 配 置 文件中 的

Manager , Switcher 节点直 接拷贝到 Provider 配 置 文件中 ,并且将 Provider 节点下 的Manager节点的 remoteIp 设置为 127.0.0.1。这种情况下直接启动 Provider 时,Switcher 服务将被启动到同一 java虚拟机中,Switcher 服务与 Provider 服务之间除控制协议外的数据传输将被旁路,提高运行效率。The Auany and the GlobalId could merge together and run on the one machine.In the condition that the load of the application is not too heavy and the connection number is not too much, it could copy the Manager and Switcher elements in the configuration file of the Switcher to the configuration file of the Provider, and set the remoteIp of the Manager element in the Provider element as the 127.0.0.1. In this condition, when directly launching the Provider, the Switcher server will be launched on the same java virtual machine, the data transfer execept for the control protocol between the Switcher service and the Provider service will be bypassed to improve the execution performance.

大规模 Large scale

分立运行 Auany。运行多个 Switcher 服务器。配置 Provider,连接多个 Switcher 服务器,支持大量 Endpoint 接入。

Individualy run the Auany.Run multiple Switcher servers.Configure the Provider, connect multiple Switcher servers, and support various Endpoint to

176

Page 177: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

access.

注意事项 Matters need attention

正确分配 PVID Correctly assign the PVID

同一运营环境下 PVID 必须正确分配。一个 Provider对应一个唯一的 PVID。整个环境允许分配 2^24 个 PVID。

Endpoint 的设计决定了一个完整的应用允许由多个 Provider提供服务,大规模应用可以使用垂直划分的设计,解决负载问题。

框架决定了,运行环境可以设计并提供公共的 Provider 解决方案,交由各应用集成。建议这样的解决方案提供 Variant 模式或者脚本模式的实现,便于集成。The PVID must be correctly assigned under the same operating environment. A Provider corresponds to an unique PVID. The whole environment allows to assign 2^24 PVID.The design of the Endpoint determines that a complete application allows mutiple Provider to provide the service. The large-scale application should use the vertically divided design to resolve the load issue.The framework determines that the operating environment could design and provide the public Provider solution which is integrated by each application. We suggest that this kind of solution provides the implementation of the Variant mode or script mode for the convenience of the integration.

GlobalId

GlobalId原则上应该一个应用一个,如果多个应用之间可以约定一个组名的分配规范 ,那么可以共享一个。In principle, one GlobalId should correspond to an application. If the assignment specification of a group name between the mutiple applications could be promised, sharing a GlobalId is acceptable.

底层数据库 The underlying database

如果一个应用的所有 Provider 中,只需要一个 Provider提供 zdb 服务,使用 edb底层数据库是合适的,可以提供比较高的存储效率。如果一个应用需要在多个 Provider 上提供 zdb服务,应该考虑各个 zdb 连接同一 mysql 服务器,或者 mysql集群,便于维护。If only a Provider is required to provide the zdb service in all the Provider of an application, it is proper to use the EDB underlying database because it could provide a relatively high storage efficiency. If an application need to provide the ZDB service in multiple Provider, it should consider that each ZDB connects to the same MYSQL server or MYSQL cluster for the convenience of the maintainance.

177

Page 178: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

日志系统 The log system

Trace 用于记录系统日志,不适合重负荷日志记录,建议应用使用 log4j记录应用自己的日志。The Trace is used to record the system log and not proper for the heavy load log recording. We suggest to use the log4j to record the appliction's own log.

运行状态监视 Running status monitoring

系统使用 JMX提供运行状态监视的能力。其中分为独立服务的监视,与服务器组数据搜集。

The system uses JMX to provide the ability of the running status monitor.It is divided into the independent service monitoring and the data collection to the server set.

独立服务监视 Independent service mornitoring

以 switcher 服务器为例:Use the switcher server as the example:启动 switcher 服务器

Launch the switcher server运行 jdk 的 bin 目录内的 jconsole 程序,新建连接,大致出现如下内容:

Run the jconsole program in the bin directory of the jdk, and create the connection. There are the contents like the below:

连接 limax.swticher.Main 服务,切换到 MBean TAB页Connect the limax.switcher.Main service, and switch to the MBean TAB page.

可以看到这样一系列 limax相关的 mbean信息There are a serial mbean messages related to the limax.

在这里,可以查看运行中的各类相关数据。All relative data of the operation could be viewed here.

178

Page 179: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

这里建立的是本地连接,如果需要使用远程连接方式,需要配置相应服务器的JmxServer参数,制定 serverPort和 rmiPort。switcher 的服务配置中:The local connection is created here. If the remote connection mode is used, it needs to configure the JmxServer parameters for the relative server, set the serverPort and rmiPort in the service configuration of the switcher.

<JmxServer rmiPort="10002" serverPort="10003"/>对应了使用连接 url:service:jmx:rmi://localhost:10003/jndi/rmi://localhost:10002/jmxrmiCorresponds to the url:service:jmx:rmi://localhost:10003/jndi/rmi://localhost:10002/jmxrmi.

Jconsole 使用图形 UI 直观表现了 mbean 数据,但是不利于记录分析。如果需要,可以通 过 limax.jar 提供的 简单工具 来获取数 据 文 本 。例如 , 为 了获取前一 个图中 最 后 项threadpoolsize 的数据,可以运行命令:The Jconsole uses the graphic UI to directly present the mbean data, however it is not beneficial for the log analysis. If necessary, the data text could be gotten through the simple tools provided by the limax.jar. For example, to obtain the last threadpoolsize data in the previous figure, this command could be executed:

179

Page 180: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

java -jar limax.jar jmxtool attrs -c "service:jmx:rmi://localhost:10003/jndi/rmi://localhost:10002/jmxrmi" -b "limax.xmlconfig:type=XmlConfigs,name=threadpoolsize"将获得如下结果:

The below results will be gotten:4 ProtocolSchedulers16 ApplicationExecutors1 NioCpus4 NetProcessors

这与 Jconsole 中查看到的数据一致:which is consistent with the data viewed in the Jconsole:

java –jar limax.jar jmxtool 的查询子命令The query sub-command of the java –jar limax.jar jmxtool.

domains:获取注册到服务器的 JMX Domainmbeans:查询注册到服务器的所有 mbean Nameattrs:查询mbean属性,见上面的例子

domains: obtain the JMX Domain registered to the servermbeans: query all the mbean Name registered to the serverattrs: query the mbean attribute, refer the example above

通用服务器组数据收集 Data collection of the common server set

使用 jmxtool 的 monitor子命令,提供一个实现了 limax.util.monitor.Collector 的接口的类作为参数,monitor 运行时,创建该类的对象,通过 limax.util.monitor.CollectorController接口与之交互,实现收集。Use the monitor sub-command of the jmxtool, and provide an interface class which implements the limax.util.monitor.Collector as the parameter. When running monitor, this class's object is created and interactived via the interface of the limax.util.monitor.CollectorController to implement the collection.

示例 Example

demo 目录下提供了一个 testmonitor 应用作为例子,AuanyCheckPointMonitor.java 收集auany 服务器的 Zdb 的 Checkpoint相关数据,首先运行 auany 服务器,ant run即可运行收集。例子作周期为 30s 的收集,收集 1 分钟,大致结果如下:A testmonitor application in the demo directory is provided as the example. The AuanyCheckPointMonitor.java collects the data related to the Checkpoint of the Zdb of the auany

180

Page 181: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

server. First run the auany server, then execute ant run command to collect the data. The example executes the collection with the 30s period and last for one minute. The result is as below:run: [java] Sat May 09 02:59:24 CST 2015 localauany limax.zdb:type=Zdb,name=Checkpoint [java] CountMarshalN 174 [java] TimeOfNextFlush 2015-05-09 00:04:40.691 [java] TotalTimeFlush 7391194 [java] PeriodCheckpoint 60000 [java] TotalTimeMarshalN 16099374 [java] TotalTimeCheckpoint 0 [java] TimeOfNextCheckpoint 2015-05-09 02:59:49.095 [java] CountMarshal0 0 [java] CountSnapshot 0 [java] CountFlush 0 [java] CountCheckpoint 174 [java] TotalTimeSnapshot 14960553 [java] Sat May 09 02:59:54 CST 2015 localauany limax.zdb:type=Zdb,name=Checkpoint [java] CountMarshalN 175 [java] TimeOfNextFlush 2015-05-09 00:04:40.691 [java] TotalTimeFlush 7408422 [java] PeriodCheckpoint 60000 [java] TotalTimeMarshalN 16151422 [java] TotalTimeCheckpoint 0 [java] TimeOfNextCheckpoint 2015-05-09 03:00:49.145 [java] CountMarshal0 0 [java] CountSnapshot 0 [java] CountFlush 0 [java] CountCheckpoint 175 [java] TotalTimeSnapshot 14986577 [java] Sat May 09 03:00:24 CST 2015 localauany limax.zdb:type=Zdb,name=Checkpoint [java] CountMarshalN 175 [java] TimeOfNextFlush 2015-05-09 00:04:40.691 [java] TotalTimeFlush 7408422 [java] PeriodCheckpoint 60000 [java] TotalTimeMarshalN 16151422 [java] TotalTimeCheckpoint 0 [java] TimeOfNextCheckpoint 2015-05-09 03:00:49.145 [java] CountMarshal0 0 [java] CountSnapshot 0 [java] CountFlush 0 [java] CountCheckpoint 175 [java] TotalTimeSnapshot 14986577

181

Page 182: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

BUILD SUCCESSFULTotal time: 1 minute 1 second

开发接口 The development interface

Collector 接口:(收集应用必须实现该接口)Collector interface: (the collection application must implement this interface)1. void onController(CollectorController controller) throws Exception;

收集对象被创建出来之后,该方法被调用,收集对象获得收集控制器,controller 的使用见下文。After the collection object is created, this method is called so that the collection object obtains the collector controller. The usage of the controller refers to the follow-up.

2. void onRecord(String host, ObjectName objname, Map<String, Object> item);Monitor 收集到 Jmx 数据后调用该方法,区分主机名,Mbean和Mbean 下的属性数据。该方法是 收集的关键方法, 数据内容 可以存储到数 据库,提供给后续 分析使用 。Monitor 收集执行过程中,一个主机的一轮收集被分配给一个线程,该方法可能被并行调用,收集应用应该自行考虑同步问题。After the Monitor collects the Jmx data, this method is called. This method identifies the host name, Mbean and the attribute data under the Mbean. This method is the key method of the collection, where the data content could be stored to the database to be used by the subsequent analysis. During the execution of the Monitor collection, a cycle collection of a host is assigned to a thread. This method could be parallelly called, so the collection application itself should consider the synchronization issue.

3. default void onException(String host, Exception e){}某一主机的收集过程中如果出现异常,则该方法被调用,将异常通告给收集应用。注意,这种情况下收集不会停止,例如出现了网络故障,导致某一主机的一次收集失败,故障恢复之后,后续的收集任务仍将正常进行。该方法可能被并行调用。If the exception appears during a host's collection, this method is called and the exception is reported to the collection application. It is noted that the collection in this case would not stop. For example, the network issue causes the failure of a host's collection. After the issue is fixed, the subsequent collection tasks will continue correctly. This method could be parallely used.

CollectorController 接口:(收集应用用来管理收集行为)CollectorController interface: (collection application use it to manage the collection behavior)1. Runnable addHost(String host, String url, String username, String password) throws

MalformedURLException;添加主机配置,username 为 null 表示无须认证。返回的 Runnable对象上调用 run,该主机配置被删除,该主机上的所有收集被取消。This method addes the host configuration. When the username is null, it means no authentication. The run is called on the returned Runnable object so that the host configuration is deleted and all the collections of this host are cancelled.

182

Page 183: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

2. Runnable addCollector(String host, String pattern, long period) throws MalformedObjectNameException;为一个主机添加一个收集,调用该方法之前必须确保 host 已经通过 addHost 成功加入,并且没有被取消, pattern按照 ObjectName 的模板格式,使用通配符,匹配多个实际的 ObjectName,period 单位毫秒,决定了收集频率。返回的 Runnable 对象上调用run,该收集配置被取消。This method addes a collection to a host. Before calling this method, it should ensure that the host has been successfully added via addHost and not been cancelled. The parameter pattern use wildcard according to the ObjectName's template format to match multiple actual ObjectName. The parameter period with the milliseconds as the unit determines the collection frequency. The run is called on the returned Runnable object and this collection configuration is cancelled.

3. void stop();同步停止整个收集过程,返回之后 Collector 接口的任何方法都不再被调用,可以安全清理收集器。This method synchronously stop the entire collection process. After return, all the methods of the Collector interface would not be called and it is safe to clear the collector.

应用相关的服务器组数据收集 The data collection of the server set related to

the application

应用相关收集是通用收集的特化形式,与应用描述 xml 中定义的 monitorset相联系,生成了相应的收集代码,可以更加精确地进行收集编程。The collection related to the application is the special mode of the common collection, which is linked to the monitorset defined in the xml of the application. The generated relative collection source code could more accurately program for collection.

limax.util.monitor.MonitorCollecotor,实现了前面提及的 CollectorController 接口,内建Collector 接口对象,将 Collector.onRecord 采集到 的 Jmx 相关 数 据 , 分 发 到 经由MonitorCollector.addCollectorInstance添加到收集器内的收集对象。The limax.util.monitor.MonitorCollecotor implements the CollectorController interface mentioned previous, inner creates the Collector interface object, and distributes the Jmx relative data collected by the Collector.onRecord to the collection objects of the collector added by the MonitorCollector.addCollectorInstance.

通过 addCollectorInstance加入的对象,必须实现应用 xml 定义的 monitorset 生成代码中的 Collector 接口。The object added by the addCollectorInstance must implement the Collector interface in the generated source code of the monitorset defined in the application xml.例如 : auany.xml 定 义 了名为 AuthProvider 的 monitorset , 在 生 成 代 码 中 可 以找到

AuthProvider.Collector 这 样 一 个 接口定 义 , 该 接口定 义 的 onRecord 按照收集主机名,monitoset 定义的 key,monitorset 定义的 counter 的顺序,精确还原了参数类型。For example: the auany.xml defines the monitorset of the AuthProvider. The interface definition of the AuthProvider.Collector could be found in the generated source code. The onRecord

183

Page 184: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

defined in this interface according to the order of the collected host name, the key defined by the monitorset, the counter defined by the monitorset accurately reproduces the parameter type.

通过mc.addCollectorInstance((AuthProvider.Collector) (host, platflag, pvid,

_newaccount, _auth) -> {System.out.println("host = " + host + " platflag = " + platflag

+ " pvid = " + pvid + " newaccount = " + _newaccount + " auth = " + _auth);});的方式,加入收集对象实例,即可实现类型化的收集。具体使用,可以参考,testmonitor 中的例子 AuanyAuthMonitorAPI.java。

Through the method above, add the collection object instance to implement the typed collection.Please refer the AuanyAuthMonitorAPI.java in the testmonitor for the detailed information.

收集数据直接入库 Collection data stores to the database directly

某些运行环境下,需要将收集数据直接入库,事后分析。In some running environment, it needs to store the collection data to the database directly to be analyzed in the future.

通过 limax.util.monitor.MonitorCollector.addSQLExecutor 方法支持。具体使用可以参考testmonitor 中的例子 AuanyAuthMonitorDB.javaThe feature is supported by limax.util.monitor.MonitorCollecotor.addSQLExecutor method. Please refer the example AuanyAuthMonitorDB.java in the testmonitor for the detailed information.

构造带有 onexception参数的 MonitorCollector,即可通过 onexception 收集采集过程中发生的所有异常。Constructing the MonitorCollector with the onexeception parameter, all the exceptions in collect process will be collected via oneexception.查看 生 成 的 AuthProvider.Collector 代 码 ,注意到 接口内部提供了 两 个静态 方法

getCreateTableString和 createInsertStatement,采集应用启动后,首次插入数据前会根据getCreateTableString返回的 SQL 语句在数据库中试图创建表格,忽略创建错误。这就是说,如 果 有 必 要 可 以 根 据 特 定 需 求预先 建 表 , 表 的名字和基 本字段描 述 出 现 在createInsertStatement 方法中,用户定义的表格只要确保上述表名和基本描述字段存在即可,这些名字除了 host,其它均来源于 monitorset 的描述,名字前面全部加上下划线,避免与数据库保留字冲突。此外,getCreateTableString返回了符合 Mysql 规范的 CREATE TABLE 语句,如果使用别的数据库,也请仿照样式预先建表,createInsertStatement 使用的 INSERT语句是标准的,不存在数据库兼容性问题。Checking the generated AuthProvider.Collector source code, it is noted that there are two static methods getCreateTableString and createInsertStatement provided by the internal interface. After the collection application launches, it tries to create the table in the database according to the SQL statement returned by getCreateTableString before the first inserting data, and ingore the creation error. That means if necessary, the table could be pre-created according to the requirements. The name and baisc field description of the table are in the createInsertStatement

184

Page 185: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

method. The table defined by the user only need to ensure that the name and baisc field description of the table exist. Except for the host, these names come from the description of the monitorset and the undscore is added in the front of the name to avoid the conflict with the reserved word of the database. In addition, the getCreateTableString returns the CREATE TABLE statement which matches the specification of the Mysql. If the other database is used, please refer the sample to pre-create the table. The INSERT statement used by the createInsertStatement is standard and the database compatibility problem does not exist.

维护 Maintenance

停机 Stop Service

Switcher 这类服务可以用外部结束进程的方式关闭。The service such as Switcher could be stopped by killing process directly. 集成了 Zdb 服务的应用,比如 Auany,GlobalId,Provider 之类的服务,有两种关闭方

法。The application integrating the Zdb service, such as Auany, GlobalId, and Provider, has two kinds of stop methods.

1. 结束 Switcher 后,等待一个以上的 Zdb checkpoint周期,再结束进程,确保数据完全刷新到底层数据库。After stopping Switcher, wait for more than one Zdb checkpoint period, then stop the process again to ensure that the data are totally flushed to the underlying database.

2. 正确配置服务的 JmxServer参数,然后使用 jmxtool 的 Stop子命令,确保 Zdb 数据刷新到底层数据库,服务按正确顺序停止。例如,结束 auany 服务,可以执行命令:java -jar limax.jar jmxtool stop -c "service:jmx:rmi://localhost:10202/jndi/rmi://localhost:10201/jmxrmi"该命令允许一个额外的-d delay参数,delay 为毫秒,指令服务器在 delay 时限达到之后停机。Correctly configure the JmxServer parameter of the service, then execute the Stop sub-command of the jmxtool to ensure that the Zdb data are flushed to the underlying database and the services are stopped accroding to the correct order. For example, to stop the auany service, the below command should be executed: java -jar limax.jar jmxtool stop -c "service:jmx:rmi://localhost:10202/jndi/rmi://localhost:10201/jmxrmi"This command allows an extra -d delay parameter and the delay is the millsecond which orders that the server should stop when the delay time limitation reaches.

185

Page 186: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

备份与恢复 Backup and Recovery

备份与恢复的方式依赖于使用的底层数据库引擎。The mode of the backup and recovery depends on the engine used by the underlying database.

使用 EDB引擎的备份 The backup based on the EDB engine

使用 jmxtool 的 backup子命令实现备份功能,支持全备份,增量备份两种方式。Using the backup sub-command of the jmxtool to implement the backup function. It supports full backup and incremental backup two ways.

backup子命令参数:The backup sub-command parameter:1. –d <backupdirectory> 指定备份目录 appoint the backup directory2. –i <true or false>, true 表示执行增量备份,false 表示不进行增量备份。true means

executing the incremental backup, false means not executing the incremental backup

这里备份与 sqlserver 之类的备份方式稍有区别,sqlserver 类数据库,要求增量备份之前必 须存在 一 个 全备份。所以 要做增量备份必 须按照 dump database , (dump transaction)+ 的方式执行。limax 上实现为只要执行了 backup子命令作增量备份,首先执行全备份,logrotate 的时候自动复制数据库日志到备份目录。这种方式可以简化备份规则设计,例如,要求以一天为周期作增量备份,只需要在每天特定时间点执行一次增量备份即可。This backup here is different with the backup of the sqlserver because the database such as sqlserver requires that a full backup should exist before an incremental backup. So the incremental backup must follow the dump database, (dump transaction)+ way to execute. The implementation of the limax is that only if the backup sub-command is executed as the incremental backup, the full backup is executed firstly, the database log is automatically copied to the backup directory when logrotate. This way could simplify the rule design for the backup. For example, if there is a requirement of an incremental backup with one day as the period, it only needs to execute one incremental backup at the specific time each day.

例如:增量备份 auany 的 zdb 数据库,执行如下命令即可For example: incremental backup the zdb database of the auany, the below command need to be executed.

java -jar limax.jar jmxtool backup -c "service:jmx:rmi://localhost:10202/jndi/rmi://localhost:10201/jmxrmi" -d c:\temp\backup –i true

使用 EDB引擎的恢复 The recovery based on the EDB engine

如果恢复的是全备份,直接将备份目录拷贝成 zdb 目录即可。If the recovery is the full backup, it only needs to copy the backup directory as the zdb directory.

如果在增量备份上恢复,则按下述步骤进行:186

Page 187: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

If the recovery is the incremental backup, it needs to follow the below steps:1. zdb 目录重命名成 zdb.old

Rename the zdb directory as the zdb.old2. 备份目录拷贝成 zdb 目录

Copy the backup directory as the zdb directory3. 将 zdb.old/log 目录下的文件覆盖到 zdb/log 中。这是由于 zdb.old/log 目录下的日志

文件中可能存在最新的还没有提交到备份目录的 checkpoint信息。Copy and cover the files of the zdb.old/log directory to the zdb/log. It is because that the log files of the zdb.old/log might have the latest checkpoint which has not been submitted to the backup directory.

如果要按选择的时间点恢复,上面的拷贝完成之后在 zdb 目录上用命令行方式执行EDB 的交互工具 edbtool:If the recovery bases on the selected time point, the EDB interactive tool edbtool should be executed via command way in the zdb directory after the copy finishes.

java -jar limax.jar edbtool#helprescue <src dbpath> <dst dbpath>list checkpoint <dbpath>recover checkpoint <dbpath> <recordNumber>out <filename> <charset> #default System.out UTF-8exitquit#list checkpoint c:\temp\backup0 : 2015-04-18 16:15:36.210#

关键命令有两条,list checkpoint命令,列出当前数据库所有 checkpoint 时间点,从 0 开始编号,在列表中选择期望的时间点编号执行 recover checkpoint命令,数据库数据量较大,时间点较多的情况下,命令可能执行较长时间,命令完成后,数据库恢复到选择的那个时间点的状态。There are two piece of key commands: list checkpoint command, lists all the checkpoint time point for the current database, numbering from 0; executes the recover checkpoint command based on the exepected time point number selected from the list. In the case of a large amount of database data and a lot of time point, this command needs more time to execute. After the command finishes, the database recovers to the status of the selected time point.

使用 Mysql引擎的备份与恢复:The backup and recovery based on the Mysql engine

直接使用 Mysql 的备份与恢复策略。Directly use the backup and recovery strategy of the Mysql.

187

Page 188: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

数据格式转换 Conversion of the data format

应用升级以后,可能需要转换 Zdb 中存储的数据格式,需要实现类似 sql 数据库的ALTER TABLE 功能。这项工作的大部分任务应该交由应用的开发者完成,提供相应的转换程序,交由运营者执行生产环境上的转换。

Limax 框架提供转换所需的相应的支持,这里以提供一个以前面的服务器开发章节example提供的 zdb 描述为例,介绍转换方法。After the application upgrades, the data format stored in the Zdb might be converted. It needs to implement the function which is similar to the ALTER TABLE function of the sql database. The most part of this task should be finished by the developers, providing the relative conversion program to the operation staffs to execute the conversion in the produce environment.

The Limax framework provides the support to the conversion. The zdb description provided in the previous example as the example is provided here to introduce the conversion method.

转换实例 The example of the conversion

1. zdb 描述 zdb description<xbean name="MyXbean">

<variable name="var0" type="int" /><variable name="slist" type="vector" value="string" />

</xbean><table name="mytable" key="long" value="MyXbean" autoIncrement="true"/>

2. 添加一条记录 add a new recordimport limax.util.Pair;import limax.util.Trace;import limax.zdb.DBC;import limax.zdb.Procedure;import limax.zdb.Zdb;import limax.zdb.tool.DataWalker;

public final class ConvertTest {public static void main(String[] args) throws Exception {

new java.io.File("zdb").mkdir();Trace.set(Trace.ERROR);limax.xmlgen.Zdb meta = limax.xmlgen.Zdb.loadFromClass();meta.setDbHome("zdb");Zdb.getInstance().start(meta);Procedure.call(() -> {

Pair<Long, xbean.MyXbean> pair = table.Mytable.insert();pair.getValue().setVar0((short) 123);pair.getValue().getSlist().add("name123");

188

Page 189: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

return true;});Zdb.getInstance().stop();DBC.start();DBC dbc = DBC.open(meta);DataWalker.walk(dbc.openTable("mytable"), kv -> {

System.out.println(kv.getKey() + ", " + kv.getValue());return true;

});DBC.stop();

}}运行这段代码,返回如下结果 Run this program and get the below result4096, {var0:123, slist:["name123", ], }

3. 更新 zdb 的 xml 描述 Update the xml description of the zdb<xbean name="MyXbean">

<variable name="var0" type="short" /><variable name="slist" type="vector" value="string" />

</xbean><table name="mytable" key="long" value="MyXbean" autoIncrement="true"/>

注意到,MyXbean.var0 类型从 int改变为 shortIt is noted that the type of the MyXbean.var0 is changed from the int to the short.

4. 重新生成代码,刷新 eclipse,这时发现代码出现错误,将pair.getValue().setVar0(123); 改为 pair.getValue().setVar0((short)123);Regenerate the source code, flush the eclipse, then there is error for the code, change the pair.getValue().setVar0(123); as the pair.getValue().setVar0((short)123);

5. 再次运行这时,运行报告错误Exception in thread "main" limax.zdb.XError: convert needed: {mytable=MANUAL}运行结果指明了,当前版本应用与前一版本应用的 Zdb 数据库不兼容,mytable 这张表需要手动转换才能兼容。Run the program again.Then, get the running error.Exception in thread "main" limax.zdb.XError: convert needed: {mytable=MANUAL}This running result points out that the Zdb database of the current version is not compatible with the previous one. The mytable table need to be manually converted for the compatibility.

6. 运行转换工具生成转换代码189

Page 190: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Run the conversion tool to generate the source code.在应用的当前目录下创建 zdbcov 目录,执行命令Create the zdbcov directory in the current directory of the application, then execute the below command.java -cp <path to limax.jar>;bin limax.zdb.tool.DBTool -e "convert zdb zdbcov"注意,这里指明了 2 个 classpath,一个是 limax.jar,另一个是当前应用的 bin 目录。这时,获得如下输出:It is noted that there are two classpath, one is limax.jar and the other is the bin directory of the current application. The below output is obtained here:

mytable MANUAL-----COV.class not found, generate-----make dir [cov]make dir[cov\convert]generating cov\convert\Mytable.javagenerating cov\COV.java

这个输出,指明了 mytable 表需要手动转换,创建了一 cov 目录,里面放置了转换用框架代码。This output points out that the mytable table needs the manual conversion. The cov directory is created and the conversion framework code is placed in this directory.

刷新 eclipse,配置项目属性,将 cov 目录设置为源码目录。Flush the eclipse, configure the project property, and set the cov directory as the source code directory.

在 Mytable.java 里面寻找//TODO 这一行,这里就是填写手工转换代码的地方。Look for the //TODO line in the Mytable.java file, which is the position to fill the manual conversion source code. // TODO var0 = s.var0;假设我们将这行修改为:Suppose to modify this line like the below:

var0 = (short) -s.var0;

7. 再次运行代码转换工具Run the code conversion tools again.java -cp <path to limax.jar>;bin limax.zdb.tool.DBTool -e "convert zdb zdbcov"获得如下结果 Get the below result

2015-05-12 17:56:14.283 INFO <main> limax.zdb.DBC start ...mytable MANUAL-----COV.class found, manual convert start-----copying... _sys_

190

Page 191: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

converting... mytable2015-05-12 17:56:14.390 INFO <main> limax.zdb.DBC stop begin2015-05-12 17:56:14.397 INFO <main> limax.zdb.DBC stop end-----manual convert end-----

在这里,能够看到创建了新目录 zdbcov存放转换后的数据库,mytable 表上执行了转换,不需要转换的_sys_表被直接拷贝。The new directory zdbcov is created here to store the converted database. The mytable table executes the conversion, and the none transferable_sys_ table is directly copied.

8. 验证转换效果 Verify the conversion effect.前一版本的 zdb 目录重命名为 zdb.old,zdbcov 目录重命名为 zdbRename the zdb directory of the previous version as the zdb.old, and rename the zdbcov directory as the zdb.重新执行 ConvertTest.java,获得如下结果:Re-execute the ConverTest.java, and obtain the below result:4096, {var0:-123, slist:["name123", ], }8192, {var0:123, slist:["name123", ], }

注意到,key=4096 行,var0变为了-123,这正是转换代码实现的功能,转换成功。It is noted that in the key=4096 line, the var0 has changed as -123, which is the right funtion of the conversion code. The conversion successes.

转换相关的细节 The detail information related to the conversion

前面的例子mytable 的转换类型报告为 MANUAL,_sys_的转换类型报告为 SAME,事实上 系 统 中 提 供 4 种 转 换 类 型 , 定 义 在 limax.zdb.tool.ConvertType 中 , 分 别 为SAME,AUTO,MAYBE_AUTO,MANUAL。The conversion type of the mytable in the previous sample is reported as the MANUAL, and the conversion type of the _sys_ is reported as the SAME. Actually, there are four kinds of conversion types provides by the system, SAME, AUTO, MAYBE_AUTO, and MANUAL, which are defined in the limax.zdb.tool.ConvertType.

转换类型含义如下:The meanings of the conversion type are below:SAME

相同。这样的表在转换时直接拷贝。It is same, so the table is directly copied during the conversion.

AUTO转换不会损失精度,例如整数从短到长的转换;一个 bean,去掉了某些字段,并

且该 bean又没有作为任何 map 的 key存在。这类转换可以自动进行,无需用户干预。191

Page 192: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

The conversion dose not loss the accuracy, such as the integer's conversion from short to long; a bean, removes some fields, and this bean does not exist as the key for any map. This kind of conversion could be automatical and without the intervention from the user.

MAYBE_AUTO可能损失精度的转换,例如整数转换为浮点;一个 bean,添加了某些字段,需要

初始化添加的字段。这类转换可以自动进行,用户愿意干预也可以进行干预。The conversion may loss some accuracy, such as the conversion from the integer to the

float; a bean, adds some fields which need to be added in the initiation. This kind of conversion could be automatical, and the user also could interfere it.

MANUAL其他一切情况,必须用户干预才能进行的转换。All the other cases, could be converted under the intervention from the user.

如果新版应用启动失败,需要进行转换,首先应该使用命令:If the launch of the application with the new version fails, it needs to execute the

conversion. The below command needs to be executed firstly:java -cp <path to limax.jar>;bin limax.zdb.tool.DBTool -e "convert zdb zdbcov"获取转换类型,如果存在 MANUAL 或者 MAYBE_AUTO 类型的转换,则该命令会生成框

架代码,填写相应的 TODO,编译完成后再次运行代码转换工具即可。Obtain the conversion type, if conversion of the MANUAl or MAYBE_AUTO type exists, this

command will generate the framwork source code. Fill the relative TODO, then rerun the source code conversion tool after compling.

对于在上述的结果中只有 MAYBE_AUTO,没有 MANUAL 的情况下,如果允许执行损失精度的转换,则可以删除先前生成的 cov 目录,直接使用命令:

For the above result that only MABYE_AUTO without MANUAL exists, if the conversion with accuracy loss is allowed, the generated cov directory could be deleted and execute the command directly.

java -cp <path to limax.jar>;bin limax.zdb.tool.DBTool -e "convert zdb zdbcov true"例如,将上例中的 var0 类型改变为 float,运行上述命令获得如下结果:For example, change the var0 type of the above example as the float, then run the above

command and get the below result:

mytable MAYBE_AUTO-----no need generate!, auto convert start-----2015-05-12 23:40:52.532 INFO <main> limax.zdb.DBC start ...mytable MAYBE_AUTOcopying... _sys_auto converting... mytable2015-05-12 23:40:52.623 INFO <main> limax.zdb.DBC stop begin2015-05-12 23:40:52.630 INFO <main> limax.zdb.DBC stop end

192

Page 193: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

-----auto convert end-----

这里可以看到,MAYBE_AUTO 的表也被自动转换了。Viewed from above content, the MAYBE_AUTO table is automatically converted.

事实上,convert命令的参数格式如下:Actually, the paramter format of the convert command is below:convert [fromDB [toDB [autoConvertWhenMaybeAuto [generateSolver]]]]

fromDB 指定了转换源,默认为 zdbfromDB appoints the source of the conversion, and the default value is zdb.toDB指定了转换目标,默认为 zdbcovtoDB appoints the destination of the conversion, and the default value is zdbcov.autoConvertWhenMaybeAuto指示是否直接转换 MAYBE_AUTO 类型的表,默认 falsegenerateSolver 指定了是否生成合并代码,默认 false。数据库的合并后面介绍。autoConvertWhenMaybeAuto appoints whether to directly convert the table of the

MAYBE_AUTO type, the default value is false.generateSolver appoints whether to generate the merge code, the default value is false. The

merge of the database will be introduced next.

转换总结 The summary of the conversion

1. 对于应用开发者而言:For the application developer:完成新版本开发之后,应该在上一个版本的 zdb 数据库上运行应用,如果报告错误提

示数据转换,则应该在当前项目目录创建 zdbcov 目录并运行:After the development on the new version has finished, the application should be run on the zdb database with the previous version. If the error notifies the data conversion, the zdbcov directory should be created and run the below command in the current directory of the project.

java -cp <path to limax.jar>;bin limax.zdb.tool.DBTool -e "convert zdb zdbcov"如果生成了转换代码,根据需要提供自己的记录转换实现,打包新版本应用,集成转

换工具,测试通过后,提交给运营环境在生产系统中执行。If the conversion source code is generated, the developer provides his own record to implement the conversion according to the requirement, packages the application with the new version, integrates the conversion tools, and submits to the operation environment to execute in the production system after the test passes.2. 对于运营环境而言:

For the operation environment:获取新版本应用以后,如果获知需要转换,则执行转换

After obtaining the new version application, if the conversion is necessary, the below command should be executed.

java -cp limax.jar;application.jar limax.zdb.tool.DBTool -e "convert zdb zdbcov"

193

Page 194: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

执行完成以后,备份原始 zdb 目录,将 zdbcov 目录改名为 zdb。最后,启动新版应用。

After the command is executed, the operation staff backups the original zdb directory, and renames the zdbcov directory as the zdb.Finally, the operation staff launches the new version application.3. convert 的 fromDB,toDB参数如果是 MYSQL url,则需要在 java –cp参数之后,追加

mysql/Java 连接器的 jar 包。If the parameter of the fromDB and toDB of the convert is MYSQL url, the jar package of the mysql/Java connector should be added behind the java -cp parameter.

4. 转换目标 zdbcov,必须手工创建,对于 EDB引擎创建一个目录,对于 MYSQL引擎创建相应数据库。如果使用了 MYSQL 数据库,转换之后,可以将应用配置文件中 dbhome直接指向新的数据库,减少数据库拷贝。The user should manually create the conversion destination zdbcov, a directory created for EDB engine, and the relevant database created for MYSQL engine. If the MYSQL database is used, the relevant dbhome in the configuration file could be directly pointed to the new database after the conversion to reduce the copy of the database.

注意事项 Matters need attention

对于关系数据库而言,大型表格的 ALTER TABLE,非常耗时,同理,转换大型 zdb 数据库,也非常耗时。实际使用中,如果涉及到大型 zdb 数据库的转换,建议首先使用备份数据进行转换时长测试,确认停机时间上限,如果停机时间不可接受,则只能特别设计应用在运行过程中逐步转换。For the relationship database, the ALTER TABLE operation for the large-scale table is very time-consuming. Similarly, it is very time-consuming to convert the large-scale zdb database. In the actual usage, if there is the conversion for the large-scale database, we suggest to use the backup database to test the time-lenght for the conversion and ensure the time maximum of pause. If the time of pause is unacceptable, the only solution is to specially design the application to finish the conversion gradually in the operation processing.

数据库合并 The merge of the database

存在这样的应用,开始阶段分立运营,一段时间之后可能出现合并数据库的需求 。Limax 通过一系列手段支持这样的应用。This kind of application, which is separately operated in the beginning, has the requirement to merge the database some time later. The Limax has a serial methods to support this kind of application.

合并的支持 The support for the merge

1. 使用 GlobalId 服务,同一 GlobalId域之内的应用,通过 GloalId 服务提供唯一 id,相应的id作为表的 key,这样的表可以安全合并,而不会发生 key冲突。Using the GlobalId service, the application in the same GlobalId domain provides the unique id through GlobalId service. With this relevant id as the key of the table, this table could be

194

Page 195: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

safely merged without the conflict of the key.2. Zdb 的自增量 key 的配置,autoKeyInitValue,autoKeyStep,分立运营的同种应用,一开始配置相同的 autoKeyStep,不同的 autoKeyInitValue。这样,凡是使用自增量 key 的表可以安全合并,不会发生 key冲突。The configuration of the incremental key of the Zdb, autoKeyInitValue, autoKeyStep, the same kind of application with the separate operation configures the same autoKeyStep and different autoKeyInitValue in the beginning. So, all the tables with the incremental key could be safely merged without the conflict of the key.

3. 数据库合并,事实上是一种特殊类型的格式转换,与前面提到的格式转换相比,普通的格式转换目标数据库为空,执行合并时,目标数据库存在。对于同名表,如果在源数据库与目标数据库中,发现了相同的 key,则认为发生冲突,这种情况下,可以生成相应代码框架,提示用户解决冲突。In fact, the merge of the database is a special type format conversion. Compared with the previous format conversion, the destination database of the common format conversion is empty, and after executing the merge, the destination database exists. For the table with the same name, if the same key is found in the source database and the destination database, there is conflict. In this case, the relevant framework source code could be generated to notify the uses to resolve the conflict.

合并的操作 The operation of the merge

1. 备份目标数据库,zdb -> zdb.bakBackup the destination database, zdb -> zdb.bak

2. 准备源数据库,假设为 zdbsrcPrepare the source database, assume as the zdbsrc

3. 执行合并 Execute the merge commandjava -cp limax.jar;application.jar limax.zdb.tool.DBTool -e "convert zdbsrc zdb"

4. 如果没有冲突,合并完成,如果报告冲突,执行下面步骤。If there is no conflict, the merge finishes. If there is conflict, execute the below commands.java -cp limax.jar;application.jar limax.zdb.tool.DBTool -e "convert zdbsrc zdb false true"convert 的最后一个参数 true指明了需要生成冲突处理代码,将生成的 cov 目录提交给应用The last parameter of the convert as the true points out that it needs to generate the souce code to resolve the conflict and submit the generated cov directory to the application.

5. 应用将 cov 目录作为源码目录,可以看到比起前面的数据格式转换,cov 目录中多出一个 solver 包,里面存在所有数据库表的相关冲突解决代码。找到代码中的方法,The application uses the cov directory as the source directory. Compared with the previous data format conversion, the cov directory contains an extra solver package, which includes all the source code to resolve the conflict for the database tables. The method in the source code is below:public OctetsStream solve(OctetsStream sourceValue, OctetsStream targetValue, OctetsStream key)

195

Page 196: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

填写需要的 TODO 代码,重新打包应用。Fill the necessary TODO code and re-package the application.

6. 提交应用,重新执行第 3步。Submit the application, re-execute the step #3.

注意事项 Matters need attention

1. 建议尽量使用 GlobalId,和正确配置 Zdb 自增量 key,避免出现需要解决冲突的情况。We suggest the user to use the GlobalId and correctly configure the incremental key of the Zdb to avoid the conflict which need to be fixed.

2. 对于可能出现的冲突,设计上应该有预见。事实上,出现上一节提到的在运营阶段出现合并冲突,生成冲突解决代码,解决冲突的过程是不应该出现的。出现这一情况,应该理解为设计缺陷。For the possible conflict, the design should have foreseen. Actually, the process that the merge conflict appears in the operation phase, generates the souce code to resolve the conflict, and resolve the conflict in the previous content should not exist. If this case appears, it should be considered as the design defect.

3. 如果应用运行在 MYSQL引擎上,并且能够确认合并操作决不会发生冲突,则可以直接在 MYSQL 上用 SQL命令合并_meta_,_sys_之外的所有表,对于这两张表,保留目标数据库的版本即可。If the application runs on the MYSQL engine and could ensure the merge operation has no conflict, all the tables except for the _meta_ and _sys_ could be merged by using SQL command on the MYSQL. For the table _meta_ and _sys_, it is acceptable to keep the destination database version.

4. convert 可以在 EDB 数据库和MYSQL 数据库之间相互转换。The convert could exchange between the EDB database and MYSQL database.

5. convert 可以同时生成格式转换代码和合并时的冲突解决代码,也就是说,格式转换与合并可以同时进行。为了避免混乱,建议按照先转换,再合并的顺序,分步操作。The convert could generate the source code for the format conversion and the source code to resolve the conflict appeared when merging at the same time. That means that the format conversion and merge could be processed at the same time. To avoid the mess, we suggest the user follow the order of conversion first and merge second to operate step by step.

196

Page 197: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

附录 Appendix

支付框架 Payment Framework

Limax提供完整的支付框架,可以接入任意第三方支付系统。同时该支付框架隔离了第三方支付系统与应用服务器实现,便于应用服务器的开发调试。The Limax provides the complete payment framework, and could access any third party payment system. Meanwhile, this payment framework isolates the third party payment system and the implementation of the application server for the development debug of the application server.

A 类支付流程 A class payment process

1. Provider向 Endpoint 发布商品信息(可选步骤)The Provider releases the product information to the Endpoint (optional)

2. Endpoint首先向 Auany 下订单,获得订单 id,使用该订单 id向第三方支付系统发起支付The Endpoint makes order to the Auany, obtains the order id, and initiates the payment to the third party payment system by using this order id.

3. 第三方支付系统向 Auany反馈支付结果,如果支付失败,流程结束。The third party payment system sends the payment result to the Auany. If the payment fails, the process ends.

4. Auany向应用服务器投递支付数据The Auany deliveries the payment data to the application server.

B 类支付流程 B class payment process

1. Endpoint向支付服务器购买,获得发票The Endpoint purchases from the payment server, and obtains the receipt.

2. Endpoint向 Auany提交发票直到 Auany 接受发票The Endpoint submits the receipt to the Auany till the Auany accepts the receipt.

3. Auany向服务器验证发票,如果失败,流程结束The Auany verifies the receipt from the server. If fails, the process ends.

4. Auany向应用服务器投递支付数据The Auany deliveries the payment data to the application server.

197

Page 198: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

客户端 The Client

limax.Endpoint.AuanyService.pay(int gateway, int payid, int product, int price, int count, String receipt, long timeout,Result onresult);

该方法向 auany 发起支付This method initiates the payment to the Auany.gateway:接下来使用的第三方支付网关在系统内分配的 id the id assigned by the system for the third party payment gateway which will be used next.payid:接受支付的 ProviderId,第三方支付网关成功支付以后,Auany 使用该 ProviderId 进行投递。 the ProviderId which accepts the payment within the system. After the third party payment gateway successfully pay, the Auany uses this ProviderId to post.product:商品号 the product numberprice:单价 cost per unitquantity:数量 sumreceipt: 发票 receipttimeout:请求超时,单位毫秒 the timeout for the request, and Milliseconds is the unitonresult:接收返回结果 accept the return result

public interface Result {void accept(int errorSource, int errorCode, String result);

}

注意,对于 A 类支付流程,首先调用 pay,验证 errorCode == 0,获得 result,随即使用result 进行第三方支付;对于 B 类支付流程,获得发票后,本地存储发票,使用发票调用pay,返回 errorCode==0 后可删除本地存储的发票,否则应该持续重试。Please pay attentation to that the A class payment process firstly calles the pay, verifies errorCode == 0, obtains the result, and initiates the third party payment by using result immediately; the B class payment process obtains the receipt, stores the receipt locally, calles the pay by using the receipt, after returning the errorCode == 0 the local storaged receipt could be deleted, or should continue re-try.

Provider

实现 Implementation

public interface PaySupport {void onPay(long serial, long sessionid, int product, int price, int quantity, Runnable ack);void onPayConfirm(long serial, Runnable ack);

}

Provider 实现 ProviderListener 的同时,与之并列实现 PaySupport 接口The Provider implements the PaySupport interface when it implments the ProviderListener.

198

Page 199: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

首先需要提供一张表格记录 serial.Firstly it needs to provide a table to record serial.onPay 的实现The implementation of the onPay.1. 检查表格中是否存在 serial,如果存在转 4

Check whether the serial existes in the table. If existes, go to step #4.2. 对于 A 类支付检查 product 与 price 是否与系统定义的商品信息匹配,如果不匹配说明

Endpoint向 Auany 发起支付时作假;对于 B 类支付 price=-1,仅需要检查 product。发生了作假的情况可以根据应用设计要求处理这笔收入然后转 4。Check whether the product and price match the product information in the system definition. If they do not match, it means that there is fraud when the Endpoint initiates the payment to the Auany. For the B class payment preice=-1, it only needs to check the product. At this time, this income could be handled according to the application design requirment, then go to step #4.

3. 提供 quantity 个 prodcut给 sessionid对应用户,记录 serial。这两个操作建议在同一事务中完成。Provide the products to the user corresponding to the sessionid, the product sum is the quantity, and record the serial. These two operations should be implemented in one transaction.

4. 调用 ack();Call ack();

onPayConfirm 的实现 The implementation of the onPayConfirm1. 删除表格记录的 serial

Delete the serial recorded in the table2. 调用 ack();

Call ack();

注意,这两个方法必须在操作的最后调用 ack,如果没有调用,Auany将周期性向 Provider发起请求。记录支付日志时,通过转换 Long.toString(serial, Character.MAX_RADIX)获得订单号,该订单号与 Auany 的支付日志相对应,便于在必要的时候进行对账。Please pay attentation to that these two methods should call the ack in the final of the operations. If the ack is not called, the Auany will initiates the request to the Provider periodly. When recording the payment log, the Long.toString(serial, Character.MAX_RADIX) is used to obtain the order number. This order number corresponds to the payment log of the Auany so that the reconciliation is easily proceeded when necessary.

配置 Configuration

Provider 无需任何配置There is no any configuration demanded the Provider.

199

Page 200: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Auany 上的配置 The configuration of the Auany

Auany 配置节点属性 The attribute of the Auany configuration element

payEnable:开启运营环境的 支 付 支 持 ,默认为 true enable the payment support of the operating environment. The default value is true.payLoggerClass:支付日志实现类,默认为 limax.auany.PayLoggerSimpleFile,可以定义运营环境自己的支付日志类,该类必须实现 limax.auany.PayLogger 接口。the implementation class of the payment log. The default values is limax.auany.PayLoggerSimpleFile. The own payment log class of the operating environment itself could be defined, and this class must implent the limax.auany.payLogger interface.payLoggerSimpleFileHome : PayLoggerSimpleFile 记录 支 付日志使 用 的 目 录 ,默认为paylogs。the directory used by the PayLoggerSimpleFile when recording the payment log. The default values is paylogs.orderExpire:订单过期时间,默认 3600000ms即 1 小时。 the expire time of the order. The default value is 3600000ms which means 1 hour.orderQueueConcurrencyBits:订单处理队列并发参数,默认为 3,建立 1<<3 == 8 个处理队列。the concurrency paramenters of the order process queue. The default value is 3. 1<<3==8 process queue could be created.orderQueueHome:订单处理队列目录,默认为 queue。 the directory of the order process queue. The default value is queue.deliveryExpire:订单投递超时,默认为 604800000ms,即 7 天,投递超时可能有几种原因。其一,下订单时提供的 payid错误,其二,超时周期内 payid对应的 Provider从来没有启动过,其三,应用实现 limax.Provider.PaySupport 时,忘记调用 ack()。订单投递超时以后PayLogger.logDead 被 调 用 。 the expire time of the order delivery. The default value is 604800000ms which means 7 days. There are some reasons for delivery expire. First, the payid prvoided when making order is wrong. Second, the Provider corresponding to the payid has never been enabled in the expire period. Third, when the application implements the limax.Provider.PaySupport, the ack() is not called. After the order delivery expire, the PayLogger.logDead is called.deliveryQueueCheckPeriod:投递队列检查周期,默认为 60000ms,即 1 分钟。 the check period of the delivery queue. The default value is 60000ms which means 1 minute.deliveryQueueBackoffMax:投递失败退避参数 ,默认为 5 , 定 义 了退避周期序列1,2,3,4,5,5,5…,这里的 1,2,3,4,5 用来乘以 deliveryQueueCheckPeriod即为下一次检查延迟。the backoff parameter of the delivery failure. The default value is 5. The backoff period serial 1,2,3,4,5,5,5... is defined. The 1,2,3,4,5 here is used to multiply deliveryQueueCheckPeriod as the next check delay.deliveryQueueConcurrencyBits:投递队列并发参数,默认为 3,建立 1<<3 == 8 个处理队列。 the concurrency parameter of the delivery queue. The default value is 3. The 1<<3 == 8 process queue is created.deliveryQueueHome:投递队列处 理 目 录 ,默认为 queue 。 the process directory of the delivery queue. The default value is queue.

200

Page 201: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

pay 配置节点属性 The attribute of the pay configuration element

gateway:运营系统为第三方支付网关分配的 id the id assigned by the operating system for the third party payment gatewayclassName:第三方支付网关消息处理类,必须实现 limax.auany.PayGateway 接口。 the message process class of the third party payment gateway. The limax.auany.PayGateway interface must be implemented.

第三方支付网关扩展 The extension of the third party payment gateway

public interface PayGateway {void initialize(Element e, Map<String, HttpHandler> httphandlers) throws Exception;void unInitialize();void onPay(long sessionid, int gateway, int payid, int product, int price, int quantity, String

receipt,Result onresult) throws Exception;}

initialize初始化第三方支付网关配置,一般来说第三方支付网关使用 http投递支付消息,可以在 httphandlers 中设置自己的 httpContext 与 httpHandler 的关联。The initialize method initiates the configuration of the third party payment gateway. Generally, the third party payment gateway uses http to delivery the payment message, and sets the link between the own httpContext and httpHandler in the httphandlers.onPay 实现具体支付操作。The onPay method implements the detailed payment operation.

A 类支付示例 A class payment example

系统提供了 limax.auany.paygws.Simulation 模拟支付网关,可以仿照该实现扩展更多的第三方支付网关支持,该模拟支付网关也可以用来进行应用调试,默认配置如下:The system provides the limax.auany.paygws.Simulation to simulate the payment gateway. The developer could imitate this implementation to extend more support to the third party payment gateway. This simulation payment gateway could be used for application debug. The default configuration is below:<pay className="limax.auany.paygws.Simulation" gateway="0" httpContext="/pay/simulation" maxAmount="999999" maxDeliveryRandomDelay="30000"/>

有两种调试方式:two debug way:1. 通过 http,访问 http://auanyserver:8181/pay/simulation?<order> ,其中 order 为客户 pay操作返回的订单号,这种方式只支持成功支付。Access http://auanyserver:8181/pay/simulation?<order> via http. The order is the order

201

Page 202: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

number returned for the customer's payment operation. This way only support the success payment.

2. maxDeliveryRandomDelay > 0 的情况下,系统在 maxDeliveryRandomDelay 规定值的范围内随机一个延迟,然后进行结果投递。如果 price * quantity 在{0, maxAmount}范围内支付成功,否则失败。maxDeliveryRandomDelay == 0,则只支持 http 方式。In the condition of maxDeliveryRandomDelay > 0, the system randoms a delay in the range prescribed by maxDeliveryRandomDelay, then processes the result delivery. If the price * quantity is in the {0, maxAmount} range, the payment success, or fail. The maxDeliveryRandomDelay == 0 only supports the http way.

B 类支付示例 B class payment example

系统提供了 limax.auany.paygws.AppStore,支持 AppStore 的支付,默认配置如下。The system provides limax.auany.paygws.AppStore to support the payment of the AppStore. The default configuration is below.<appstore connectTimeout="15000" home="appstore" readTimeout="15000" receiptExpire="604800000" receiptReplayProtectorConcurrentBits="3" receiptVerifyScheduler="4" retryDelay="300000"/><pay className="limax.auany.paygws.AppStore" gateway="1" productPattern="[^\d]+([\d]+)$" url="https://buy.itunes.apple.com/verifyReceipt"/>

其中 appstore节点为 appstore 服务的全局配置。The appstore element here is the global configuration of the appstore service.connectTimeout : 连 接 发票验证服 务超时 ,默认 15000ms connection to the receipt verification service timeout, the default value is 15000msreadTimeout:发票验证服务器返回结果超时,默认 15000ms the return result of the receipt verification server timeout, the default value is 15000msretryDelay:访问发票验证服务器失败以后,下一次调度延迟,默认 300000ms,即 5 分钟 the next scheduling delay after the failure of the accessing the receipt verification server, the default value is 300000ms which means 5 minutesreceiptVerifyScheduler :发票验证调度器线程数,默认为 4 the thread sum of the receipt verification scheduler, the default value is 4home:appstore相关文件的根目录,默认为当前目录下的 appstore 目录 the root directory of the files relative to the appstore, the default directory is the appstore directory in the current directoryreceiptExpire:发票超时,系统拒绝接受超出该限制的旧发票,从发票的支付之间算起。默认 604800000ms ,即 7 天 the receipt expire, the system rejects to accept the old overdue receipt, beginning from the payment date of the receipt. The default value is 604800000ms which stands for 7 days.receiptReplayProtectorConcurrentBits:发票重放保护器并发参数,默认为 3,即平均允许1<<3==8 个线程同时验证重放 the concurrency parameters of the receipt replay protector, the

202

Page 203: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

default value is 3 which means that 1<<3==8 threads are allowed to verify the replay at the same time注意,所有 B 类支付方式都存在发票重放的问题,尽管实现要求客户端在验证发票前进行本地存储,服务器发货完成,通知客户端之后,删除本地存储的发票,但是这种解决方案本身并不可靠,不能避免某些恶意程序在删除本地发票时进行欺骗,从而进行发票重放。为了解决重放问题,发票超时是必要的,这个时间之内进行重放保护,这个时间之外立刻失败,这样重放保护的存储数量才是有界的。Please pay attention to that all the B class payment has the receipt replay issue. Although the implementation requires that the local storage should be proceeded before the client to verify the receipt, after the server shipment completes and notifies the client, the client deletes the locally stored receipt, this solution itself is not reliable and could not avoid the deception from some malicious programs when they deletes the local receipt for the receipt replay. To resolve this issue, the receipt timeout is necessary. The replay protection is proceeded within this time and fails immediately exceeding this time. So the storage sum of the replay protecation is bounded.

pay节点中,除了 gateway, className标准属性外:Except for the gateway and className attributes in the pay element:productPattern:指定一个正则表达式匹配发票中的 product-id,如果不能匹配系统直接拒绝,解析出匹配结果的 group(1)对应的数字,作为本框架中的 product参数,进行投递。specify a regular expression to match the product-id in the receipt. The system directly reject if none-match. The parsed number corresponding to the group(1) which matches the result is deliveried as the product paramter of this framework.url:发票验证服务器 url the url of the receipt verification server

账号系统 Account System

limax 通过 Auany提供一个支持子账号的凭证式账号系统,分配 sessionid。The Limax provides a credential account sytem supporting sub-account via Auany, assigning the sessionid.

基本概念 The basic concept

1. credential,字符串表示的由 auany 的 key签名的凭证,当前系统提供 Session凭证和Temporary凭证。credential, the credential signed by the key of the auany and represented via string. The current system provides the Session credential and Temporary credential.

2. authcode,用户授权码,参与凭证签名。authcode, the authoriztion code from the user, is used for the credential signature.

203

Page 204: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

3. serial,凭证序列号,凭证更新之后,序列号加一,系统通过序列号确认凭证有效性,拒绝过期凭证。serial, the credential serial number. After the credential updates, the serial number increases one. The system confirms the validity of the credential via serial number and denies the overdue credential.

4. loginConfig,登录配置对象。loginConfig, the object of login.

5. mainid,主账号的 sessionidmainid, the sessionid of the main account.

6. uid,系统内用户标识,为 username@platflag转换为小写的字符串。uid, the user id of the sytem, is the lowcase string convered by username@platflag.

7. appid,应用标识,aunay 下的每一个应用分配一个 appid,同一 uid 下,不同 appid 映射了不同的 mainid。appid, the application flag. An applid is assigned for each application of the Auany. In the same uid, different appid maps the different mainid.

8. derive,创建未绑定主账号,或者在主帐号下派生子账号。derive, creates a non-binding main account or derives sub-account under the main account.

9. bind,创建绑定主账号,或者进行帐号绑定,或者更新凭证序列号。bind, creates a binding main account, or binds an account, or updates the credential serial number.

Session凭证 Session Credential

为了支持子账号,下面的方法可能返回一个形为 credential[,subid]+的字符串,该字符串为完全的 Session凭证表示,其中 subid即是子账号。The below method might return a string like credential[,subid]+ to support the sub-account. This string is a complete Session credential representation, among which the subid is the sub-account.

非绑定账号的创建 Creating a non-binding account

AuanyService.derive(String httpHost, int httpPort, int appid, String authcode, long timeout, Result onresult);在这里 httpHost, httpPort 为 auany提供的 http 服务器地址,appid指明了请求的应用。如 果 onresult.errorSource == ErrorSource.LIMAX && onresult.errorCode == SUCCEED ,则onresult.result 为返回的初始 Session凭证。The httpHost and httpPost here are the http server address provided by the Auany, and the appid indicates the requested application.If onresult.errorSource == ErrorSource.LIMAX && onresult.errorCode == SUCCEED, the onresult.result is the returned initial Session credential.

绑定账号的创建 Creating a binding account

AuanyService.bind(String httpHost, int httpPort, int appid, String authcode, LoginConfig loginConfig, long timeout, Result onresult);

204

Page 205: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

与非绑定账号的创建相比,这里多出了 loginConfig参数,使用这个参数执行认证,认证成功之后创建一个已经绑定好的 Session凭证。特别的 , 如 果 username,token,platflag 认证之 后 映 射 的 uid 下 appid 已 经 关联上 一 个mainid,则递增相应的 serial 之后返回完全的 Session凭证表示。这个功能可以用于账号找回。Compared with the non-binding account, parameter loginConfig added. A bound Session credential is created after successful authentication with the parameter.In particularly, if the appid in the mapped uid has associated a mainid after the authentication of the username, token and platflag, the relative serial is incremented and the complete Session credential representation is returned. This function might be used to retrieve account.

派生子账号 Deriving a sub-account

AuanyService.derive(String credential, String authcode, long timeout, Result onresult);该方法如果执行成功,则 onresult.result返回完全的 Session凭证表示,如果之前有 N 个subid,则返回之后包含 N+1 个 subid,最后一个 subid即为派生出来的新的子账号。派生子账号上限由应用的配置参数 maxSubordinates 决定。详见,service-auany.xml。If this method executes successfully, the onresult.result returns the complete Session credential representation. If there are N subid before, the N+1 subsid are included after the return, and the last subid is the derived new sub-account.The upper limitation of the derived sub-account id decided by the configuration paramter maxSubordinates of the application. For the detailed information, please refer the service-auany.xml.

账号的绑定 Binding an account

AuanyService.bind(String credential, String authcode, LoginConfig loginConfig, long timeout, Result onresult);该方法通过 loginConfig认证之后进行绑定,返回 Session凭证的完全表示。该方法除了对未绑定账号(凭证)进行绑定外,也可以使用与绑定时相同的 loginConfig 进行重复绑定,目的是返回 Session凭证的完全表示。This method processes the binding after the authentication of the loginConfig and returns the complete Session credential representation.Except for binding the non-binding account(credential), this method also uses the same loginConfig used when binding for the repeatedly binding to return the complete the Session credential representation.

Provider 的影响 The influence of the Provider

ProviderTransport 接口提供 2 个方法The ProviderTransport interface provides two methods:

long getMainId();String getUid();

其中,通过 getMainId()返回mainid 与当前的 sessionid比较,可以得知当前的登录是在主帐号下还是子账号下。

205

Page 206: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Through comparing the mainid returned by the getMainId() with the current sessionid, we could learn whether the current login is in the main account or sub-account.

Session凭证的登录 Logining with the Session credential

登录参数 中 username 指定 为 Session 凭证, token 指定 为 authcode , platflag 指定为 ” credential” , 如 果 登 录 子 账 号 , platflag 后 缀 ” :subid” 。 尽 可 能 使 用LoginConfig.credentialLogin 方法。The username parameter of the login specifies the Session credential, the token specifies the authcode, and the platflag specifies the "credential". If the sub-account logins, the platflag add the suffix ":subid". Method LoginConfig.credentialLogin is preferred.

移动客户端的考虑 The consideration for the mobile client

凭证方式适合移动客户端实现,满足有需要时再进行帐号绑定的需求。返回的 Session凭证应该进行本地存储,便于下一次访问时直接登录。如果支持子账号,多数情况下需要考虑与相应 UI资源的关联。这里要注意的一个问题就是未绑定账号在登录后执行绑定时的可能出现的意外情况——发起 bind操作之后,网络断开——这种情况下不能确定是否绑定成功,所以下一次登录时应该首先是用原来的凭证进行登录,如果不成功,则使用非凭证方式进行登录,登录完成后再次进行 bind,直到获得 Session凭证的完全表示,完成本地存储。The credential mode is suitable for the mobile client implementation, and meets the requirement that the account binds when necessary. The returned Session credential should be stored locally for the directly logining when next accessing. If the sub-account is supported, the association with the relative UI resource should be considered in the multiple condition.A question which need to be considered is the exception which might happen when non-binding account processes the binding after logining -- after initiating the bind operation, the network disconnects -- in this condition, it is unsure whether the binding is successful. So the previous credential is firstly used for the next login. If unsuccessful, non-credential way is used to login. After the successfully login, the bind is initiated again until the complete Session credential representation is obtained, then saves it locally.

pc,web 客户端的的考虑 The consideration for the pc and web clients

这两种客户端通常不会在本地存储凭证,可以使用普通方式进行登录,如果有子账号需求可以通过 bind 方式获取 Session凭证的完全表示,再派生子账号;或者登录主账号,使用空凭证,空 authcode,调用 derive,这种情况下 onresult.result仅返回新的子账号串。These two kinds of clients usually do not store the credentail locally. The common way could be used to login. If there is requirement for the sub-account, the bind way is used to obtain the complete Session credential representation, then derives the sub-account. Or the main account is used to login using empty credential and empty authcode, and call the derive. In this condition, the onresult.result just returns the new sub-account string.

206

Page 207: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

子账号的考虑 The consideration for the sub-account

使用子账号功能的系统内,Provider 通常会维护子账号,主账号的关系。这里要注意 2 个问题。In the system using sub-account function, the Provider usually maintains the relationship between the sub-account and the main account. There are two problems to be considered.1. 需要验证 ProviderTransport提供的 mainid, sessionid 关系,如果是子账号登录,则验证从属关系是否改变,如果改变应该进行相应的修改,因为有可能发生了子账号迁移。The relationship between the mainid and the sessionid provided by the ProviderTransport should be verified. If the sub-account logins, it should verify whether the affiliation has changed. If it changed, the relative update should be executed because there might be the sub-account migration happened.

2. 不能信任客户端提供的子账号 id,例如客户端派生了子账号,不应该提供给 Provider 要求在其上创建对应记录。正确的解决办法应该是客户端派生了子账号以后,使用子账号进行登录,Provider 发现对应 sessionid 的相关记录没有创建,则创建之。The sub-account id provided by the client should not be trusted. For example, the sub-account derived from the client should not be provided to the Provider to create the relative record. The correct solution is that after the client derives the sub-account, the sub-account is used to login, and the Provider finds that the relative record of the corresponding session does not exist and create it.

Temporary凭证 Temporary Credential

Temporary凭证,在 Session凭证的基础上添加了时效性,过期则失效;添加 subid属性,可以直接标识子账号;添加用法属性,支持两种用法——临时登录,账号传送。The Temporary credential addes the timeline on the basis of the Session credential which will be invalid when expiration; addes the subid attribute which could directly identify the sub-account; addes the usage attribute which supports two usages -- temporary login and account transfer.

创建 Temporary凭证 Creating the Temporary Credential

1. AuanyService.temporary(String credential, String authcode, String authcode2, long millisecond, byte usage, String subid, long timeout, Result onresult);

2. AuanyService.temporary(String httpHost, int httpPort, int appid, String credential, String authcode, String authcode2, long millisecond, byte usage, String subid, long timeout, Result onresult);

3. AuanyService.temporary(LoginConfig loginConfig, int appid, String authcode,long millisecond, byte usage, String subid, long timeout, Result onresult);

4. AuanyService.temporary(String httpHost, int httpPort, int appid, LoginConfig loginConfig, String authcode, long millisecond, byte usage, String subid, long timeout, Result onresult);

方法 1,2,通过 Session凭证创建临时凭证,方法 2 无需在登录状态下执行。The method 1, 2, create the temporary credential via Session credential. The method 2 need not

207

Page 208: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

to be performed in the login status.方法 3,4,通过登录账号方式创建临时凭证,方法 4 无需在登录状态下执行。The method 3, 4, create the temporary credential via logining the account. The method 4 need to to be performed in the login status.方法 1,3 的 authcode2参数与方法 2,4 的 authcode参数,指定了临时凭证的 authcode。The authcode2 parameter of the method 1, 3 and the authcode parameter of the method 2, 4, identify the authcode of the temporary credential.millisecond参数指明了临时凭证自创建开始计算的有效时间,单位毫秒。The millisecond parameter specifies the effective time which is counted when the temporary credential is created, and the unit is millisecond.usage 参数指明了临时凭证用法, USAGE_LOGIN = 1 ,指示该临时凭证用 于登录 ,USAGE_TRANSFER = 2,指示该临时凭证用于账号传送。The usage parameter specifies the usage of the temporary credential. USAGE_LOGIN = 1 specifies that the temporary credential is used to login. USAGE_TRANSFER = 2 specifies that the temporary credential is used for account transferring.subid参数指明了子账号标识串,空串代表临时凭证作用于主账号。The subid parameter specifies the sub-account flag string. The empty string specifies that the temporary credential is used in the main account.执行成功后 onresult.result返回了临时凭证。After successful execution, the onresult.result returns the temporary credential.

Temporary凭证的登录 Temporary credential login

类似 Session凭证的登录,不同的是 platflag 中的子账号后缀无意义,因为临时凭证本身包含了 subid。Similar as the login of the Session credential, the difference is the suffix of the sub-account in the platflag has no meaning because the temporary credential itself includes the subid.

账号传送 Account transfer

AuanyService.transfer(String httpHost, int httpPort, int appid, LoginConfig loginConfig, String authcode, String temp, String authtemp, long timeout, Result onresult);在这里 httpHost, httpPort 为 auany提供的 http 服务器地址,appid指明了请求的应用。The httpHost and httpPort here are the http server address provided by the Auany, and appid specifies the requested application.username,token,platflag,指明了接收账号,authcode 为账号接收之后生成的 Session凭证的授权码。The loginConfig specifies the receive account. The authcode is the generated authorization code of the Session credential after the account receives.temp, authtemp,为临时凭证及其授权码。The temp and authtemp are the temporary credential and its authorization code.执行成功之后 onresult.result返回接收账号的 Session凭证的完全表示。After successful execution, the onresult.result returns the complete Session credential representation of the receive account.

208

Page 209: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

这里需要注意:The matters need to be considered:1. 必须通过一个有效账号来接收传递账号。

A valid account must be used to receive the transfer account.2. 临时凭证指定的账号被传递,可以是主账号——这种情况连同所有子账号一起被传递,

接收账号必须没有绑定过该应用,否则报告 appid冲突;可以是子账号,这种情况下如果接收账号没有绑定过该应用,则首先建立主账号,再添加该子账号,如果绑定过,在不违反maxSubordinates限制的前提下添加子账号。When the account identified by the temporary credential is transferred, it could be the main account -- in this condition, the main accound and all the sub-account are transferred together, and the receive account has not bound this application, or the appid conflict is reported; it also could be the sub-account -- in this condition, if this receive account has not bound this application, the main account should be created firstly, then add this sub-account. If this receive account has bound this application, the sub-account could be added without violating the restriction of the maxSubordinates.

3. 如果应用提供了子账号传送支持,则 Provider 实现时必须验证mainid,sessionid 的关系。If the application provides the support to the sub-account transfer, the implementation of the Provider must verify the relationship between the mainid and the sessionid.

注意事项 Matters need attention

1. 以 httpHost,httpPort,appid三个参数开头的方法不需要在账号登录的状态下执行, 这些方法为同步方法,onresult被触发之后返回;其它方法均为异步方法。The method with the httpHost, httpPort and appid three parameter as the beginning need not to be executed in the account login status. These methods are the synchronization methods and returned after the onresult is triggered; the other methods are asynchronous.

2. 只要账号进行了绑定,任何时候都可以使用绑定时的账号参数调用 bind 方法获取Session凭证的完全表示。As long as the account is bound, the account parameter used during binding could be used anytime to call the bind method to obtain the complete Session credential representation.

3. 通过方法 EndpointManager.getAccountFlags(),ProviderTransport.getAccountFlags()的调用Provider 与 Endpoint 均可获取当前登录帐号 的标记, 该标记与 SessionFlags. FLAG_ACCOUNT_BOUND = 1和 SessionFlags. FLAG_TEMPORARY_LOGIN = 2 进行掩码运算即可获知当前登录帐号是否绑定,是否临时登录帐号,从而采取特定应对。Through calling the methods EndpointManager.getAccountFlags() and ProviderTransport.getAccountFlags(), the Provider and Endpoint could obtain the flag of the current login account. This flag processes the mask operation with SessionFlags. FLAG_ACCOUNT_BOUND = 1 and SessionFlags. FLAG_TEMPORARY_LOGIN = 2 and obtains whether the current login account is bound, and whether the login account is temporary, so that the specific response is taken.

4. 临时凭证主要提供给他人使用,如果有效期之内反悔,可以使用绑定时的账号参数调209

Page 210: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

用 bind 方法,递增 serial,使得之前生成的临时凭证全部失效。没有绑定过的账号没有机会反悔,原因在于没有绑定过的账号禁止递增 serial,一旦递增 serial,新凭证因为网络原因丢失,老凭证又无法登录,就意味着账号的丢失。The temporary credential is mainly provided to be used by others. If you pull back within the validity period, the account parameters used during binding could be used to call the bind method, increments the serial and invalids all the previous generated temporary credential. The non-binding account could not be pulled back because the non-binding account forbids increment serial. Once the serial is incremented, the new credential lose due to the network issue and the old credential could not login, which means that the account lose.

应用配置 Application Configuration

一个 Limax 运营环境由一个 auany 服务器支撑,一个运营环境下可以运行多个应用下的服务,每个应用可以运行多个并列服务,一个服务可以由多个 Provider 构成。对于一个特定的应用,允许 Provider 发布公共信息提供给客户端使用。A Limax operating environment is supported by an Auany server. The services of multiple applications could be run in one operating environment. Each application could run multiple parallel services. One service could consist of multiple Providers. For a particular application, the Provider is allowed to publish the public information provided to the client to use.

Provider 信息发布 Provider publish the information

Provider 的框架周期性向应用 Provider 请求 JSON对象,该对象最终通过 auany 的 http 服务发布出去。The Provider framework periodically requests the JSON object from the application Provider. This object is eventually released through the http service of the Auany.

实现 Implementation

public interface JSONPublisher {JSONSerializable getJSON();long getDelay();

}

Provider 实现 ProviderListener 的同时,与之并列实现 JSONPublisher 接口,其中:When the Provider implements the ProviderListener, the JSONPublisher interface is also implemented at the same time. Including:getJSON:返回一个 JSON对象。return a JSON objectgetDelay:系统下一次获取 JSON对象的延迟,单位毫秒。the delay that the system obtains the JSON object next time, milliseconds is the unit.

210

Page 211: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

配置 Configuration

Provider 无需额外配置,auany 的应用配置中存在一个 jsonPublishDelayMin属性(之后介绍 ) , 该 属 性 约 束 了 delay 的 最 小 值 , 也 就 是 说 , max(jsonPublishDelayMin, JSONPublisher.getDelay())决定了实际延迟,这个配置属性可以防止 JSONPublisher 的错误实现导致 Provider攻击 auany。There is no extra configuration for the Provider. There is jsonPublishDelayMin attribute (introduce later) in the configuration of the Auany application, and this attribute constrains the minimum of the delay, which means that the max(jsonPublishDelayMin, JSONPublisher.getDelay()) determins the actual delay. This configuration attribute could avoid the error that the wrong implementation of the JSONPublisher causes the Provider to attack the Auany.

Endpoint 应用信息获取 Endpoint application information acquision

endpoint向 auany提供的 http 服务器请求应用配置信息,解析为对应语言的描述。实现上即是访问 http://auanyserver/app?{servicetype}=appid[&additionalQuery],获取一个 JSON对象 进 行 解 析 , 其 中 servicetype : { native, ws, wss } , 对 于 非 WebSocket 应 用 ,servicetype=native。additionalQuery 为 http 代理服务器约定的查询参数,用以过滤 auany返回的结果集。The Endpoint requests the application configuration information from the http server provided by the Auany, interpretting as the description of the relative language. The implementation is accessing the http://auanyserver/app?{servicetype}=appid [&additionalQuery] and obtaining a JSON object for analysis, among which the servicetype : { native, ws, wss } is servicetype=native for non WebSocket application. The additionalQuery is the query parameter promised by HTTP proxy server to filter the result set returned by auany.

实现 Implementation

List<ServiceInfo> Endpoint.loadServiceInfos(String httpHost, int httpPort, int appid, long timeout, int maxsize, File cacheDir, boolean staleEnable);List<ServiceInfo> Endpoint.loadServiceInfos(String httpHost, int httpPort, int appid, String additionalQuery, long timeout, int maxsize, File cacheDir, boolean staleEnable);

其中: Including:httpHost,httpPort:auany http 服务地址 the Auany http server addressappid:运营框架为该应用分配的 id the id allocated by the operating frameworkadditionalQuery:http 代理服务器约定的查询参数additonalQuery: the query parameter promised by http proxy server.timeout, maxsize, cachedir, staleEnable : http 服 务 请 求参数 , timeout 决 定 了 请 求超时 ,maxsize约束了返回信息的最大尺寸,cacheDir提供了本地缓存目录,staleEnable 决定了请求失败后是否使用本地缓存中的陈旧数据。the request parameters of the http service. The timeout determins the request timeout, the maxsize constrains the maximum size of the

211

Page 212: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

returned information, the cacheDir provides the local cache directory, and the staleEnable determins whether the old data in the local cache will be used when the request fails.

同一应用可能运行多个并列服务,所以返回 List,其中的 ServiceInfo,提供 5 个方法:The multiple parallel services could be run in the same application and returns List. The ServiceInfo among the List provides five methods:1. int[] getPvids();获取该服务引用的 pvid,顺序由 auany 配置决定。 obtains the pvid referenced by this service, and the order is detemined by the Auany configuration.

2. int[] getPayids();获取该服务的支付 id,一般来说一个服务应该只有一个。obtains the payment id of this service, usually one service has only one id.

3. JSON[] getUserJSONs()这里的 JSON对象就是通过前面提到的 JSONPublisher提供的,因为一个服务可能由多个provider提供,provider 是否支持 JSON 通告是可选能力,所以这里返回 JSON 数组,数组尺寸与 getPvids()返回的尺寸未必一致。The JSON object here is provided by the JSONPublisher mentioned before. Because one service could be provided by multiple Provider, and whether the Provider supports the JSON announcement is optional, the JSON array is returned here and the array size is not consistent with the size returned by the getPvids().

4. boolean isRunning()该服务是否正在运行。Whether this service is running.

5. String getOptional()这个方法与运营环境相关,返回运营环境为服务设定的特殊信息,例如,维护信息。This method is related to the operating environment, and returns the special information set by the operating environment for the service, such as maintaining information.

Auany 配置 Auany configuration

appconfig.xml提供了 auany 上的应用配置,该配置文件在系统运行过程中可以通过 merge方式添加新的信息,而不需要停机维护。The appconfig.xml provides the application configuration of the Auany. This configure file could add the new information via merge during the system operating and does not need to shutdown to maintain.

switcher相关配置 The relative configuration of the Switcher

<switcher host="127.0.0.1" id="1" key="abc" port="10000" type="native"/><switcher host="127.0.0.1" id="2" key="abc" port="10001" type="ws"/>auany内部的 switcher 与 switcher 服务的配置文件相对应,其中:The internal Switcher of the Auany corresponds to the configure file of the Switcher service, including:type:switcher 类型,包括 native,ws,wss三种,native对应 limax 网络服务,ws,wss对

212

Page 213: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

应 WebSocket 服务,其中 wss 支持 ssl。the Switcher type, includes the native, ws and wss. The native corresponds to the Limax network service, the ws and the wss correspond to the WebSockect service, among which the wss supports the ssl.id:SwitcherId,在整个运营环境中唯一分配 the SwitcherId, the unique one allocated in the entire operating environment.key:与 switcher 服务器配置的 key对应,用来认证 switcher 服务器。corresponds to the key in the configuration of the Switcher server, and is used to authenticate the Switcher server.host,port:switcher 服务器对外服务的地址。 the external service address of the Switcher server

对照 switcher 服务器配置文件 service-switcher.xml 中 Switcher 条目:Compare to the Switcher item in the Switcher server configure file service-switcher.xml:<Switcher key="abc" …… >

…………………………….. <native id="1"/> <ws id="2"/></Switcher>

Switcher启动时,通过构造 { key, native-id[], ws-id[], wss-id[] } 信息向 auany 发起通告,如果不能匹配 auany 中的配置信息,auany将命令 switcher退出,同时在 switcher日志中报告错误原因。When the Switcher launches, it sends the announcement to the Auany by constructing the { key, native-id[], ws-id[], wss-id[] } information. If the information could not match the configure information in the Auany, the Auany will order the Switcher to exit and report the error reason in the Switcher log.

注意:Attention:1. 不同 Switcher 服务器应该配置不同 key,避免 id 配置错误导致冲突。

The different Switcher server should configure different key, and avoid that the id configure error causes the conflict.

2. 同一类型的 Switcher 服务可能存在多个,对应绑定不同的网络接口或者端口。It might be multiple Switcher services with the same time, corresponding to bind different network interface or port.

3. Switcher 配置中的是否应该存在某种类型取决于接入服务的 Manager 配置。Whether the Switcher configuration should have some type depends on the Manager configuration of the access service.

4. auany 配置中的 host 无法与 Switcher 配置中接入服务 Manager 下的 localIp属性对应,因为无法确定 Switcher 之前是否存在 NAT,这里的 host 必须填写真实的对外 IP。The host of the Auany configuration could not correspond to the localIp attribute of the access service Manager in the Switcher configuration. Because it can not make sure whether the NAT exists in front of the Switcher, the host here must use the actual external IP.

213

Page 214: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

共享 provider相关配置 The relative configure of the shared Provider

<provider id="1" jsonPublishDelayMin="0" key=""/><provider id="12" jsonPublishDelayMin="0" key=""/>

id : ProviderId , 运营环境内部 全局唯一 。 ProviderId, the internal global unique in the operating environment.key:provider 的 key,用以验证 provider 服务合法性,与 Provider 配置中的 key对应。the key of the Provider, is used to verify the legitimacy of the Provider service, corresponding to the key of the Provider configuration.jsonPublishDelayMin:该 provider 的两次 JSON对象请求之间的最小时间间隔,为 0 表示禁止该 provider 发布 JSON信息。 the minimum time interval between the two JSON object requst of this Provider, 0 means that this Provider is forbiddedn to publish the JSON information.

共享 provider被应用配置中的 app 条目,或者之下的 service 条目引用。没有被任何 app 或者 service引用的 provider 为可选的共享 provider,客户端登录时可以请求这 样的 provider 进行 服务 ,也 可以 不请 求。 其中 provider id=1 被 Auany 保留,提供AuanyService。The shared Provider is referrenced by the app item of the application configuration, or the service item below.The Provider not referenced by any app or service is optional shared Provider. The client could request this Provider for service or not when login. Among them, the provider id=1 is reserverd by Auany to provide the AuanyService.

应用配置 Application configuration

<app id="1" jsonPublishDelayMin="15000" maxSubordinates="0" providerMatchBidirectionally="true" shareProvider=""> <service id="1" shareProvider="" switcher="1,2,3"> <provider id="100" key=""/> </service></app><app id="2" jsonPublishDelayMin="5000" maxSubordinates="8" providerMatchBidirectionally="true" shareProvider="12"> <service id="1" shareProvider="" switcher="1,2,3"> <provider id="200" key=""/> </service> <service id="2" shareProvider="" switcher="1,2,3"> <provider id="300" key=""/> </service></app>

app节点:app element:<app id="1" jsonPublishDelayMin="15000" maxSubordinates="0"

214

Page 215: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

providerMatchBidirectionally="true" shareProvider="">id: 应 用 id , 运营环境内部 全局唯一 。 the application id, internal globally unique in the operating environmentjsonPublishDelayMin:该应用中使用的公共 Provider 之外的所有私有 Provider 的 JSON对象发布时间间隔。 the publish time interval of the JSON objects of all private Provider except the public Provider used this applicationmaxSubordinates:该应用支持的最大子账号数,为 0 表示禁止使用子账号。 the maximum sub account quantity supported by this application, 0 means the sub account is forbidden.providerMatchBidirectionally: provider列表双向匹配,如果为 true,请求的 provider列表与配置的 provider列表双向匹配;如果为 false,允许请求配置的 provider列表的非空子集。providers match bidirectionally, if true, providers requested match providers configured bidiectionally. if false, request non-empty subset of providers configured is permitted.shareProvider:逗号分隔的共享 ProviderId列表,禁止引用 Auany 服务的 id(=1)。the shared Provider list seperated by the comma. Referring to the id(=1) of the Auany service is forbidden.

service节点:service element:<service id="1" shareProvider="" switcher="1,2,3">id:服务 id,一个应用配置下保证唯一。 service id, the unique is guaranteed in an application service configuration.shareProvider :逗号 分隔的共享 ProviderId,禁止引用 Auany 服务 的 id(=1) 。 the shared ProviderId seperated by the comma. Referring to the id(=1) of the Auany service is forbidden.switcher:逗号分隔的 SwitcherId列表。 the SwitcherId list seperated by the comma.

私有 provider节点:private Provider element:<provider id="200" key=""/>类比共享 provider节点,这里缺少 jsonPublishDelayMin属性,该属性继承自 app节点。compare to the shared Provider element, no jsonPublishDelayMin attribute here. This attribute inherits from the app element.

注意:Attention:1. 共享 provider,私有 provider 在同一分配域中分配 id,也就是说共享 provider,私有

provider 的 id 之间也不允许冲突。The shared Provider and private Provider allocate the id in the same allocation domain, which means that it does not allow the id conflict between the shared Provider and private Provider.

2. 客户端向服务器发起应用信息请求,最重要的信息就是下一步需要连接的 switcher地址,auany只通告已经启动的 switcher,如果配置文件中配置了,但是实际的 Switcher服务没有启动,对应的配置地址不会通告。如果 service 中相应 type 的所有 Switcher 服务都没有启动,则通告的服务运行状态一定为 false。When the client initates the application information request from the server, the most important information is the Switcher address of the next connection. The Auany only announces the launched Switcher. If it configured in the configure file, but the actual Switcher service does not launch, the corresponding configure address does not be

215

Page 216: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

announced. If all the Switcher services corresponding to the service type do not launch, the announced service running status is false.

3. 服务信息中 pvid 通告顺序由私有 provider列表,service引用的共享 Provider列表,app引用的共享 provider列表决定。The pvid announcement order of the service information is determined by the private Provider list, shared Provider list referenced by the service, and the shared Provider list referenced by the app.

4. 如果一个服务引用的 provider 没有全部启动,则通告的服务运行状态为 false。If the Provider referenced by a service does not totally launch, the announced service running status is false.

添加新的服务配置 Add new service configuration

auany 配置文件中,Auany节点中两个属性In the Auany configure file, two attributes of the Auany element.appConfigPatch : 服 务 patch 文件名,默认为 appnew.xml the patch file name, the default name is appnew.xmlappConfigPatchCheckPeriod:服务 patch 文件检测周期,默认 30000,即 30秒 the patch file detect period, the defalt value is 30000 which means 30 seconds.

具体操作:the detailed operation1. 建立一个格式与 appconfig.xml 相同的配置文件 appnew.xml。其中,可以创建新的

switcher,新的共享 provider,新的 app,已经存在 app 中新的 service,只要不存在 id冲突即可。New a configure file appnew.xml with the same format as the appconfig.xml. In this file, the new Switcher, new shared Provider, new app, and the new service of the existed app could be created, only if there is no id conflict.

2. 将 appnew.xml拷贝到 appconfig.xml相同目录下,最迟 30秒,appnew.xml被改名为appnew.xml.result,文件尾部追加了 merge 结果信息。Copy the appnew.xml to the same directory of the appconfig.xml. No later than 30 seconds, the appnew.xml is renamed as the appnew.xml.result, and the merge result information is appended to the end of the file.

3. 如果成功,新的配置被装载进 auany 服务,appnew.xml被merge 到 appconfig.xml 中,原来的 appconfig.xml备份为 appconfig.xml.bak。If success, the new configuration is loaded into the Auany service, the appnew.xml is merged to the appconfig.xml, and the previous appconfig.xml is backup as the appconfig.xml.bak.

4. 如果失败,追加的 merge 结果信息指出了错误原因,比如操作异常,id冲突,这种情况下正在运行的服务不受影响,appconfig.xml 没有改变。If fail, the appended merge result information indicates the error reason, such as the operation exception, or id conflict. The service running in this condition is not affected and the appconfig.xml has no change.

216

Page 217: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

更新具体服务的 Optional信息。Update the service's Optional information

public interface AppManagerMXBean {void setServiceOptional(int appid, int serviceid, String optional);

}通 过 访 问 Auany 的 JMX 服 务 , 以 appid , serviceid 为 key , optional 为 value ,调用setServiceOptional 方法。之后,相应的 Endpoint 上通过 ServiceInfo.getOptional()即可取得该信息。Through accessing the JMX service of the Auany, with the appid and serviceid as the key, and the optional as the value, calls the setServiceOptional method. Then, the optional information can be obtained by ServiceInfo.getOptional() from the relative Endpoint.

注意事项 Matters need attention

1. 应用应该通过自己的 JSON 通告提供服务名之类的信息,不要过度依赖 auany 通告的服务信息,一种典型的实现是创建一个 map,提取 JSON 通告的应用服务信息作为 key去索引通告返回的 ServiceInfo。The application should provide the information such as the service name via its own JSON announcement, and does not overly depend on the service information of the Auany announcement. A typical implementation is creating a map, obtaining the application service information of the JSON announcement as the key to index the ServiceInfo returned by the announcement.

2. Endpoint 提供的各种与 switcher ip ,switcher port 相关的请求接口都提供了对应的ServiceInfo版本,使用 ServiceInfo前,必须判断服务是否处于运行状态,避免产生运行时错误。The all kinds of request interface provided by the Endpoint related to the Switcher ip and Switcher port provides the relative ServiceInfo version. Before using ServiceInfo, it should determine whether the service is running to avoid generating the running error.

3. 大运营环境下,为了降低 auany http 服务负荷,可以部署 http 代理服务器进行加速。In the operating environment, in order to reduce the Auany http service load, the http proxy server could be deployed to accelerate.

JSON 支持 JSON Support

Limax提供完整的 JSON 支持(Java,C#,C++,Lua版本均提供与 Javascript 一致的 JSON 支持,使用上最大限度保证与 javascript相同),便于同其它支持的 JSON第三方系统进行交互。The Limax provides the complete JSON support (the Java, C#, C++ and Lua versions all provide the JSON support which is consistent with the Javascript, and maximumly ensure the usage is the same with Javascript), to communicate with the other json supported third party system.

217

Page 218: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

编码 Encoding

Javascript JSON.stringify(obj)Java String limax.codec.JSON.stringify(Object obj);C# string limax.codec.JSON.stringify(object obj);C++ std::string limax::JSON::stringify(const T& object);

std::string limax::JSON::stringify(const char *obj);std::string limax::JSON::stringify(std::shared_ptr<limax::JSON> obj);

Lua JSON.stringify(obj)

C++,Lua版本中的串应该使用 UTF8编码。In C++ and Lua,UTF8 encoded string is needed.

类型映射 Type Mapping

Javascript Java C# C++ LuaNumber byte

BytesbyteSByte

int8_t Number

byteByte

uint8_t

shortShort

shortInt16

int16_t

ushortUInt16

uint16_t

intIntegerAtomicInteger

intInt32

int32_t

uintUInt32

uint32_t

longLongAtomicLong

longInt64

int64_t

ulongUInt64

uint64_t

floatFloat

floatFloat

float

doubleDouble

doubleDouble

double

boolean booleanBooleanAtomicBoolean

boolBoolean

bool boolean

string char string

218

Page 219: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

charCharacter

String

charCharstringString

const char*const std::string&

Array java.reflect.ArrayCollection

IEnumerable std::list<T>std::vector<T>std::deque<T>std::unordered_set<T>std::set<T>

table

object JSONSerializableJSONMarshalMap

JSONSerializableJSONMarshalIDictionary

JSONMarshalstd::unordered_map<K,V>set::map<K,V>

table

null null null

JSON JSON

const JSON&std::shared_ptr<JSON>

注 意 , JSON 规 范 对 应 的 类 型 object , 在 之 后文档中 写 作 JSON/Object , 避 免与Java,C#,C++实现的 JSON 类构造出来的对象 JSON Object混淆。数组就写作 JSON/Array,以此类推。这些通称 JSON元件。Note: the object corresponding to the JSON specification is written as the JSON/Object in the following document to avoid the confusion with the JSON Object constructed by the JSON class implmented by the Java, C# and C++. The array is written as the JSON/Array, and so on. All these are called as the JSON components

1. Java 的 java.reflect.Array 表示支持原生 数组, Collection 对应 了各 种集合容 器。 The java.reflect.Array of the Java corresponds the native array, and Collection corresponds to all kinds of Collection containers.

2. C#所有容器,原生数组均派生自 IEnumerable,所以编码时优先 IDictionary检测,区分出是否需要按 JSON/Object编码。All the containers and native array of the C# are derived from the IEnumerable, so the IDictionary comparison has the priority to be executed when encoding to distinguish whether it is necessary to encode based on JSON/Object.

3. C++版本不支持任何类型的原生数组,或者说不支持指针方式指向的对象,const char*的支持只是一种语法糖,这种参数进入编码器前,立刻被转换成 std::string。既然不支持指针方式指向的对象,也就意味着不会编码生成 null,除非在 JSONBuilder 上使用null()方法硬编码一个 null,或者进行中继,见后。The C++ version does not support any type native array, in the other words does not support the object pointed to by the pointer.

219

Page 220: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

The support of the const char* is only the grammar sugar, and the parameter are immediately converted to the std::string before they enter the encoder. Because the object pointed to by the pointer is not supported, the null would not be generated, except the null() method of JSONBuilder is used manually to encode a null or execute the relay. Please refer the following content.

4. Lua 中 table 的使用乱七八糟,编码器通过 #table>0 的方法区分 JSON/Array 与 JSON/Object,如果需要正确编码,不要在一个 table 上混用数组操作和对象操作。Lua 的 nil解释为 null。The usage of the table in the Lua is a mess, and the encoder distinguishes the JSON/Array from JSON/Object through #table>0. If the correct encoding is necessary, it should not mix the array operation and object operation on a table. Lua’s nil is interpreted as null.

5. null 不利于各语言版本互操作,为了减少不必要的麻烦,尽可能避免使用。The null is not beneficial to the interoperability of each language version. In order to reduce the unncessary issue, it should be avoid to use.

6. 关联容器 Map,IDictionary,std::unordered_map<K,V>,std::map<K,V>,table被编码为JSON/Object 时,其中的 key被强制转换为字符串,按 JSON/String编码。如果容器定义了非 string 类型 的 key,应 该小心验证强制转换的 结果 是否符合预期。 When the associative containers Map, IDictionary, std::unordered_map<K,V>, std::map<K,V>, and table are encoded as the JSON/Object, the key of them is cast to the string and encoded as JSON/String. If the container defines the key of non-string type, it should take care to verify whether the convertion result is wanted.

7. 如果编码结果提交给 Javascript 处理,必须注意到 Javascript 的 Number 最多支持 53位整型。 If the encoding result is submitted to the Javascipt for handling, it must be noted that the Number of the Javascript supports maximum 53 bits integer.

8. 表格的最后一行没有 Javascript 的对应,Java,C#,C++,执行 JSON 中继任务时使用,见后。The last line of the table has no correspondance of the Javascript. It is used by the Java, C# and C++ when executing the relay tasks of the JSON. Please refer the follwing content.

代码生成实现(Java,C#,C++)Implementation of the code generation (Java, C#, C++)

Java,C#,C++三种类型化语言,使用 JSONMarshal 与 JSONBuilder 进行交互完成 JSON/Object 的编码。limax环境下,具体对象的 JSONMarshal.marshal 方法可以通过代码生成实现。只要在 bean,cbean,xbean,protocol 的 xml 描述中正确使用 json属性即可,例如:The Java, C# and C++ three types of languages use JSONMarshal and JSONBuilder to interact and complete the JSON/Object encoding. In the Limax environment, the JSONMarshal.marshal method of the detailed object could be implemented through generating code, only if the json attribute is correctly used in the xml description of the bean, cbean, xbean and protocol. For example:

<bean name="JChild" json="true"><variable name="val" type="string"/>

</bean>

220

Page 221: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

<bean name="JBean" json="true"><variable name="intset" type="set" value="int"/><variable name="child" type="JChild"/><variable name="ignore" type="binary" json="false"/>

</bean>使用生成的 JBean 类型的对象,调用 JSON 类的静态方法 stringify,即可获得类似如下的输出结果:Use the generated JBean type object, call the static stringify method of the JSON class, the similar following output result could be gotten:

{"intset":[1,2],"child":{"val":"it's child"}}

bean,cbean,xbean,protocol 元素上设定属性 json="true",允许为该类型生成 JSON/Object编码代码,同时默认下属 variable元素的 json属性为 true,除非为 variable单独设置属性 json="false",关闭该元素对应字段的代码成。上例中 JBean.ignore字段,在编码时被忽略。The atrribute json="true" is set on the bean, cbean, xbean and protocol elements, which allows the JSON/Object encoding code to be generated in correspond class source, and meanwhile the json attribute of the variable element is true as default, except that the variable sets the attribute json="false" manullly to turn off the relative field of this element to generate code. The JBean.ignore field of the above example is ignored during the encoding.

代码生成时,将严格检查 xml 描述,确保生成有效的 JSON/Object编码代码,否则抛出异常,停止生成。具体来说,3 种情况:When the code is generated, the xml description is strictly checked to ensure that this JSON/Object encoding code is generated legitimately, or throw the exception and stop generating. In detail, there is three cases:1. 使用了 binary 类型 use the binary type2. 使用了 any 类型 use the any type3. 引用的 bean 没有开启 json="true" referred bean does not turn on json="true"

反射方式实现(Java,C#)Implementation of the reflection (Java, C#)

Java,C#支持反射,只需要在定义类的时候实现 JSONSerializable标签接口即可,支持该标签的对象在编码时被直接解释为 JSON/Object,字段名即是对象的 key,例如定义两个 Java类:The Java and C# support the reflection, only implementing the JSONSerializable tag interface when defining the class, the object supporting this tag is directly explained as the JSON/Object when encoding, and the field name is the key of the JSON/Object. This example defines two Java classes:

public class JChild implements JSONSerializable {private String val = "it's child";

}

221

Page 222: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

public class JBean implements JSONSerializable {private int intset[] = new int[] { 1, 2 };private JChild child = new JChild();private transient Octets ignore;

}

System.out.println(JSON.stringify(new JBean())) 即可输出与上面例子相同的结果。 outputs the same result as the above example.在这里 Java 关键字 transient,阻止了 ignore字段的编码,C#没有类似特性,必须注意。特别的,C#类中定义的属性字段在反射时将获得一个 C#内部编码的字段名,需要 JSON编码的类尽量避免使用属性字段。The keyword transient of the Java here prevents the encoding of the ignore field. It should be noted that the C# has no similar specification. In particular, the attribute field defined in the C# class will obtain a internal encoding filed name of the C# when reflecting, and the class which needs the JSON to encode should avoid using such attribute field.

编码 器执行反射 时 ,只考 虑对象所属类 本 身 , 不 考 虑 基 类 ,即便基 类同样 实 现 了JSONSerializable 接口。The encoder only considers the class itself of the object and does not consider the base class when executing the reflecting, even though the base class also implements the JSONSerializable interface.

如果被反射的字段类型没有在类型映射一节的表格中列出,编码时将抛出 JSONException异常,编码失败。详见后节——异常规范。If the reflected field type has not been list in the table of the type mapping party, the JSONException exception will be thrown when encoding and encoding fails. Please refer the following content --- exception specification for the detailed information.

关联容器方式实现(Java,C#,C++)Implementation of the associative containers (Java,

C#, C++)

直接对一个关联容器编码即可。Directly encode an associative container.例如 Java 代码:For example, Java code:

Map<String, Object> jchild = new HashMap<>();jchild.put("val", "it's child");Map<String, Object> jbean = new HashMap<>();jbean.put("intset", new int[]{1,2});jbean.put("child", jchild);System.out.println(JSON.stringify(jbean));

可以输出与上面例子相同的结果。The similar result as the above example could be output.

C++中基本类型缺少一个公共基类,所以上述结果无法实现。222

Page 223: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

The basic type of the C++ lacks a common base class, so the above result could not be achieved.std::unordered_map<std::string, std::string> jchild;jchild.insert(std::make_pair("val", "it's child"));std::unordered_map<std::string, std::unordered_map<std::string, std::string>> jbean;jbean.insert(std::make_pair("child", jchild));printf("%s\n", JSON::stringify(jbean).c_str());

只能拼凑出规整的结果:Only the regular result could be obtained:

{"child":{"val":"it's child"}}

由上可见:Conclusion:1. 对于 C++,多数情况下只有使用代码生成方式,或者参考生成的代码,手工实现。 In

most condition, the C++ only applies the code generation or manual implemention through referencing the generated code.

2. 关联容器方式比生成代码方式,反射方式可读性差很多,非特殊情况不值得使用 。Compared with the generating code and reflection, the associative container has worse readability and is not worth using except for the special case.

解码 Decoding

Javascript JSON.parse(string)Java JSON JSON.parse(String s);C# JSON JSON.parse(string s);C++ std::shared_ptr<JSON> JSON::parse(std::string s);Lua JSON.parse(string)

实现(Java,C#,C++)Implementation (Java, C#, C++)

1. JSON对象上可以通过 isNull(),isBoolean(),isNumber(),isString(),isArray(),isObject()方法测试 JSON元件类型。The isNull(), isBoolean(), isNumber(), isString(), isArray() and isObject() methods could be used on the JSON object to test the JSON component type.

2. isObject()返回 true,指出该 JSON对象描述的是 JSON/Object,允许在该对象上执行下面的方法,否则抛出 JSONException. The isObject() returns true, which means that the JSON object describes the JSON/Object and allows the below methods to be executed on this object, or throw the JSONException.

Java JSON get(String key);C# JSON get(string key);C++ std::shared_ptr<JSON> get(const std::string& key) const;

上述方法,以 key 为字段名查询 JSON/Object,返回相应字段的 JSON 表示。如果当前JSON/Object对应的 key 不存在,同样返回一个 JSON对象,在该对象上调用 isUndefined()方法,将返回 true。The above methods use the key as the field name to query JSON/Object and return the JSON

223

Page 224: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

representation of the relative field. If the key corresponding to the current JSON/Object does not exist, a JSON object is also returned. And calling the isUndefined() method on this object returns true.Java Set<String> keySet();C# ICollection<string> keySet();C++ std::vector<std::string> keySet() const;

上述方法,返回 JSON/Object所有字段名The above methods return all the field's name of the JSON/Object.3. isArray()返回 true,指出该 JSON对象描述的是 JSON/Array,允许在该对象上执行下面

的方法,否则抛出 JSONException. The method isArrary() returns true, which means that the JSON object describes the JSON/Array and allows the below methods to be executed on this object, or throw the JSONException.

Java JSON get(int index);C# JSON get(int index);C++ std::shared_ptr<JSON> get(size_t index) const;

上述方法,以 index 为下标,查询 JSON/Array,返回数组元素的 JSON 表示。如果 index超出范围,同样返回一个 JSON对象,在该对象上调用 isUndefined()方法,将返回 true。The above methods query JSON/Array with the index and return the JSON representation of the array element. If the index is out of range, a JSON object is also returned. And calling the isUndefined() method on this object returns true.Java JSON[] toArray();C# JSON[] ToArray();C++ std::vector<std::shared_ptr<JSON>> toArray() const;

上述方法,返回 JSON/Array所有元素构成的 JSON对象数组。 The above methods return the JSON object array constructed by all the elements of the JSON/Array.4. booleanValue()方法,与 Javascript 完全一致:The booleanValue() method is consistent

with the Javascript.如果 JSON对象描述的是 JSON/true,返回 true。

If the description of the JSON object is the JSON/true, the ture is returned.如果 JSON对象描述的是 JSON/false,返回 false。

If the description of the JSON object is the JSON/false, the false is returned.如果 JSON对象描述的是 JSON/null,或者 isUndefined()测试为 true,返回 false.

If the description of the JSON object is the JSON/null or the test of the isUndefined() is ture, the false is returned.

如果 JSON对象描述的是 JSON/Number,值为 0,返回 false. If the description of the JSON object is the JSON/Number and the value is 0, the false is returned.

如果 JSON对象描述的是 JSON/String,长度为 0,返回 falseIf the description of the JSON object is the JSON/String and the length is 0, the false is returned.

其余情况返回 trueThe other case returns the true.5. intValue(),longValue(),doubleValue()方法 the intValue(), longValue() and doubleValue()

224

Page 225: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

methods如果 JSON对象描述的是 JSON/Number,执行相应的类型转换然后返回。

If the description of the JSON object is the JSON/Number, the correspondent type conversion is executed and then returns.

如果 JSON对象描述的是 JSON/String,并且该 string 能够转换为目标类型(没有数字格式错误),转换之后返回If the description of the JSON object is the JSON/String and this string could be converted as the target type (no digital format error), returns after the conversion.

其余情况抛出 JSONExceptionThe other cases throw the JSONException.6. toString()/ToString()方法 the toString()/ToString() method

如果 JSON对象描述的是 JSON/String,返回相应的串值If the description of the JSON object is the JSON/Stirng, the correspondent string is returned.

如果 JSON对象描述的是 JSON/Number,转换为数值串返回If the description of the JSON object is the JSON/Number, the converted numeric string is returned.

如果 JSON对象描述的是 JSON/true,返回串表示的 trueIf the description of the JSON object is the JSON/true, the ture represented by the string is returned.

如果 JSON对象描述的是 JSON/false,返回串表示的 falseIf the description of the JSON object is the JSON/false, the false represented by the string is returned.

如果 JSON对象描述的是 JSON/null,返回串表示的 nullIf the description of the JSON object is the JSON/null, the null represented by the string is returned.

如果 JSON对象 isUndefined()测试为 true,返回串表示的 undefinedIf the isUndefined() test of the JSON object is true, the undefined represented by the string is returned.

其余情况,不同语言有不同返回,与各自的 toString/ToString相关For the other cases, the different language has the different return value and is related to the respective toString/ToString.

示例 For example

若有串 {"x":[10,"abc", 2], "y": null, "z": true}表示的 JSON对象,返回 x字段的首个元素可以有如下实现:If there is JSON object represented by the string {"x":[10,"abc", 2], "y": null, "z": true}, the below implementation could be used to return the first element of the x field.

Javascript JSON.parse("{\"x\":[10,\"abc\", 2], \"y\":null,\"z\":true}").x[0]Java JSON.parse("{\"x\":[10,\"abc\", 2], \"y\":null, \"z\": true}").get("x").get(0).intValue();C# JSON.parse("{\"x\":[10,\"abc\", 2], \"y\":null, \"z\": true}").get("x").get(0).intValue();C++ JSON.parse("{\"x\":[10,\"abc\", 2], \"y\":null,\"z\":true}")->get("x")->get(0)->intValue();Lua JSON.parse("{\"x\":[10,\"abc\", 2], \"y\":null,\"z\":true}").x[1]

225

Page 226: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

与 Javascript版本比较,其它版本的实现抛开语言差异看,几乎完全一致。Compared with the Javascript version, the implementation of the other vesions is almost completely consistent if the language difference does not be considered.多数情况下,都只是简单使用已知格式的 JSON元件描述,像上面的例子一样,无需作更多的测试,直接连写即可。The most cases just simply use the known format JSON component description like the above example, and directly write without more test.

中继 Relay

某些情况下,需要实现这样一种系统——接收来自上游系统的 JSON串表示,解码该串执行设计要求的处理之后,将整个 JSON元件或者 JSON元件的一部分,作为本地系统的数据,重新打包为 JSON串,传递给下游系统——JSON 中继系统。Some cases need to implement this kind of system -- receive the JSON string representation from the upstream system, decode the process required by this string execution design, repackage the whole JSON component or the part of the JSON component as the data of the local system as the JSON string, and pass to the downstream system -- JSON relay system.

实现(Javascript,Lua)Implementation (Javascript, Lua)

脚本语言执行 parse 之后在名字空间内生成相应的对象层次结构。如果有中继需求,创建本地对象,按照相应语言的名字空间规范,引用或者部分引用解码结果,最后执行 stringify即可。The script language generates the corresponding object hierarchy in the name space after executing the parse. If there is relay requirement, create the local object, reference or part reference the decoding result according to the name space specification of the relevant language, then execute the stringify.

实现(Java,C#,C++)Implementation (Java, C#, C++)

参见之前的类型映射表格最后一行可知, JSON编码器的输入参数,支持 JSON 解码器返回类型。也就是说 parse/stringify 可逆。Seen from the last line of the previous type mapping table, the input parameter of the JSON encoder supports the return type of JSON decoder, which means that the parse/stringify is reversible.parse/stringify 可逆,即是实现 JSON 中继的关键,唯一需要注意的是,在 JSON/Object 或者JSON/Array 上执行相应的 get操作之后,返回的 JSON对象 isUndefined()测试为 true 的情况下,该返回对象不可中继,因为该对象并非来自上游系统,不可能存在一个正确表示,如果在这样的对象上执行 stringify将抛出 JSONException。The parse/stringify is reversible, which is the key to implement the JSON relay. The only consideration is that after executing the relative get operation on the JSON/Object or JSON/Array and the isUndefined() test of the returned JSON object is true, this returned object can not be relayed, because this object does not come from the upstream system and has no a correct representation. So the JSONException will be thrown if executing the stringify on this

226

Page 227: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

object.

异常规范 Exception specification

为了确保程序健壮性,任何语言实现执行 JSON编码解码操作,都必须考虑异常。In order to guarantee the robustness of the program, any language need to considerate the exception when executing the JSON encoding and decoding operation.

编码异常 Encoding exception

1. 遭遇不可解释的字段类型,不可编码 When suffering the unexplained field type, do not encode.

2. 遭遇 isUndefined()测试为 true 的 JSON对象 Suffer the JSON object whose isUndefined() test is true.

3. 对象环引用 Object reference is a loop 4. 各种运行时异常,例如并发访问异常,字段读取失败 All kinds of runtime exception,

such as the concurrent access exception and reading field failure

解码异常 Decoding exception

1. JSON 的串表示语法错误 The syntax error of the JSON string representation

解码数据获取异常 Decode data and catch the exception

1. 不正确地访问 JSON元件 Incorrectly access the JSON component2. 数据类型转换失败 Data type conversion fail3. 各 种 运 行 时异常,例如并发 访 问异常。 All kinds of running exception, such as the

concurrent access exception

Javascript 的考虑 The consideration of the Javascript

1. 编码过程遭遇环,抛出异常;类型映射表不支持的类型被忽略,例如 function. Suffer the loop when encoding, throw the exception; the type unsupported by the type mapping table is ignored, such as the function.

2. 解 码 过 程遭遇语法错误抛出异常 Suffer the syntax error when decoding, throw the exception.

3. 解 码 结 果 访 问 过 程 中 , 访 问对象不存在 的字段, 访 问 数组越界,返回undefined,undefined 上执行除 bool测试,算术运算之外的访问抛出异常。Access the nonexistent field of the object or the accessed array is out of range when accessing the decoded result, return the undefined. Throw the exeception when executing the accessing except for the bool test and arithmetic operation on the undefined.

4. 应该用 try {} catch(e) {} 方式捕获处理异常 Use the try{] catch(e){} way to catch and handle the exception.

227

Page 228: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Lua 的考虑 The consideration of the Lua

1. 编码 过 程遭遇环,抛出异常; 类 型 映 射 表 不 支 持 的 类 型被忽略,例如function , userdata. Suffer the loop when encoding, throw the exception; the type unsupported by the type mapping table is ignored, such as function and userdata.

2. 解 码 过 程遭遇语法错误抛出异常 Suffer the syntax error when decoding, throw the exception.

3. 解码结果访问过程中,访问对象不存在的字段,访问数组越界,返回 nil,nil 上执行除bool测试之外的访问抛出异常。Access the nonexistent field of the object or the accessed array is out of range when accessing the decoded result, return the nil. Throw the exeception when executing the accessing except for the bool test on the nil.

4. 应 该 使 用 pcall(function, args)方 式捕获处 理异常 . Use the pcall(function, args) way to catch and handle the exception.

Java,C#的考虑 The consideration of the Java and C#

1. 编码 过 程遭遇环,抛出异常;遭遇类 型 映 射 表 不 支 持 的 类 型抛出异常;遭遇isUndefined()测试为 true 的 JSON对象,抛出异常;并发访问异常。特别的,服务器端XBean 如果支持 JSONMarshal,与正常访问 XBean 规则一样,不能离开相应的锁,编码过程必须在事务环境中完成,否则抛出异常。 Suffer the loop when encoding, throw the exception; suffer the type unsupported by the type mapping table, throw the exception; suffer the JSON object whose isUndefined() test is true, throw the exception; the concurrent access exception. In particular, if the XBean supports the JSONMarshal on the server, the rule is the same as the normal accessing of XBean. The correspondent lock is necessary, and the encoding process must be completed in the transaction or the exception will be thrown.

2. 解 码 过 程遭遇语法错误抛出异常 Suffer the syntax error when decoding, throw the exception

3. 解码结果访问过程中,访问对象不存在的字段,访问数组越界,返回 isUndefined()测试为 true 的 JSON对象,这样的对象上除了 booleanValue(), isXXX()测试,toString()/ToString()操作外 ,抛出异常;非 JSON/Object 上 get(string) , keySet()抛出异常,非JSON/Array 上 get(int) , toArray()/ToArrray() 抛 出 异 常 ;intValue(),longValue(),doubleValue()转换失败时抛出异常。Access the nonexistent field of the object or the accessed array is out of range when accessing the decoded result, return the JSON object whose isUndefined() test is true. The exception will be thrown except for the booleanValue(), isXXX() test and toString()/ToString() operation on this object. Throw the exception when calling the get(string) and keySet() on the non JSON/Object; throw the exception when the intValue(), longValue() and doubleValue() convertion fail.

4. 各种类型的异常最终被 JSONException 包裹,所以应该使用 try {} catch(JSONException e){}的方式捕获处理。All kinds of exception is finally wrapped by the JSONException, so the try {} catch(JSONException e){} way should be used to catch and handle the exception.

C++的考虑 The consideration of the C++

1. C++不支持编码指针指向的对象,不可能出现环;遭遇类型映射表不支持的类型时,编228

Page 229: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

译阶段就会报错;不考虑并发;所以仅在遭遇 isUndefined()测试为 true 的 JSON对象时抛出异常。The C++ does not support the encoding of the object pointed to by the pointer, so it is impossible to suffer the loop; when suffering the type unsupported by the type mapping table, the error is reported during compiling; the concurrent access does not be considerated; so the exception is thrown only when suffering the JSON object whose isUndefined() test is true.

2. 解 码 过 程遭遇语法错误抛出异常 Suffer the syntax error when decoding, throw the exception.

3. 解码结果访问过程中,访问对象不存在的字段,访问数组越界,返回 isUndefined()测试为 true 的 JSON对象,这样的对象上除了 booleanValue(),isXXX()测试,toString()操作外,抛出异常;非 JSON/Object 上 get(std::string),keySet()抛出异常,非 JSON/Array上 get(size_t),toArray()抛出异常;intValue(),longValue(),doubleValue()转换失败时抛出异常。 Access the nonexistent field of the object or the accessed array is out of range when accessing the decoded result, return the JSON object whose isUndefined() test is true. The exception will be thrown except for the booleanValue(), isXXX() test and toString() operation on this object. Throw the exception when calling the get(std::string) and keySet() on the non JSON/Object; throw the exception when calling the get(size_t) and toArray() on the non JSON/Array; throw the exception when the intValue(), longValue() and doubleValue() convertion fail.

4. JSON 操作抛出 JSONException , 应 该 使 用 try{} cache(JSONException e){} 方 式捕获,e.message 可获取抛出异常代 码 的 行 号 ,供 debug 使 用 。 When the JSON operation throws the JSONException, the try{} cache(JSONException e){} way should be used to catch, the e.message could obtain the line number of the code which throws the exception to debug.

字符集编码问题 Issues of the character set encoding

理论上看,JSON编码解码过程只对应字符串的输出输入,传输过程中这些字符串如何进行字节流的编码解码,不是 JSON 需要考虑的问题。然而,实际应用的设计很可能涉及到底层问题。seen from the theory, the JSON encoding and decoding process only corresponds to the input and output of the string. It is no need for JSON to consider that how these string encodes to and decodes from the byte steam in the transmission process. However, the design of the practical application is likely to consider the underlying issue.

理想情况 The ideal case

发送端接收端使用兼容的字节流编码解码器,两边的 JSON编码解码器均能获得正确的字符串表示,例如,limax 通过 Websocket 与浏览器交互,两端均使用 UTF8编码解码,JSON 的使用不存在任何问题。The sender and receiver use the compatible byte stream codec, and the both JSON codecs could obtain the correct string representation. For example, the Limax interacts with the browser via Websocket, and the both sides use the UTF8 to encode and decode, so the usage of the JSON has

229

Page 230: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

no any issue.

较好情况(Java,C#)The better case (Java, C#)

Java 的 java.nio.charset 包,C#的 System.Text.Encoding 类,提供了完善的编解码器,如果有特殊需求,可以灵活适配对端系统。The java.nio.charset package of the Java, the System.Text.Encoding class of the C#, provide the complete codec. If there is special requirement, it could flexibly adapt the peer system.

糟糕情况(C++, Lua)The bad case (C++, Lua)

只支持 UTF8,\uHHHH格式编码的 Unicode字符在解码阶段被转换为 UTF8。Support UTF8 only,Unicode character encoded as \uHHHH can be transformed to UTF8.

高级功能 Advanced function

非标准 JSON串(Java,C#,C++,Lua)Non-standard JSON string (Java, C#, C++, Lua)

某些陈旧系统的编码器,将 JSON/Object串描述中的冒号写作等号,分割 JSON元件的逗号写作分号,解码器可以容忍这样的输入。另外,true,false,null三个 JSON元件,解码器按大小写不敏感处理。Some out-of-date system's encoders use the equal mark in place of the colon of the JSON/Object string description, and semicolon in place of the comma to split the JSON element. The decoder can tolerate this input. In addition, the true, false and null three JSON elements are processed as case-insensitive by the decoder.

编码流(Java,C#)Encoding stream (Java, C#)

public static void encode(Object obj, Appendable a);JSONEncoder提供上面的方法,允许编码器与 limax.codec 其它流处理模块协同,直接向目的输出。The JSONEncoder provides the above method, which allows the encoder to cooperate with the other stream processing module of the limax.codec and directly output to the target.

OutputStream os = ...;JSONEncoder.encode(json, new CharSource(StandardCharsets.UTF_8, new SinkStream(os)));例如,上面的代码,将 json对象编码结果,看作一个字符流源,通过 UTF8编码器,编码成字节流,再发送到输出流。For example, the above code encodes the json object encoded result which is handled as a character stream source to the byte stream through the UTF8 encoder, and then sends to the output stream.这样的过程,注意捕获异常,流中处理过程中所有异常最后都会包裹到 JSONException 中然后抛出。

230

Page 231: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

This process need to catch the exception. All exception during the stream processing will be finally wrapped to the JSONException, then thrown.

流解码(Java,C#,C++)Stream decoding (Java, C#, C++)

Java,C#,C++三个版本内部通过流方式解码,输入为字节流,而不是简单的串输入。The Java, C# and C++ three versions decode via stream, input the character stream not the simple string. 1. JSONDecoder 通过 void accept(char c);方法获取字符输入,该方法可能抛出异常。C++版本抛出 JSONException,Java版本抛出 RuntimeException,C#版本抛出 Exception。 The JSONDecoder gets the character input via void accept(char c) method, and this method might throw the exception. The C++ version throws the JSONException, the Java version throws the RuntimeException, and the C# version throws the Exception.

2. 通过 JSONDecoder()方法构造的解码对象只能解码一个 JSON元件,解码完毕后通过JSON get() 方法获取 JSON 对象 . The decoding object constructed via JSONDecoder()

method only decodes a JSON component, and get the JSON object via JSON get() method after the decoding finishes.3. JSONDecoder(JSONConsumer consumer)方法允许提供一个 consumer,获取解码后的对象(连续解码 JSON元件)The JSONDecoder(JSONConsumer consumer) method allows a consumer provided to get the decoded object (continuously decode the JSON component).

4. Java , C#版本 的 JSONDecoder 实 现 了 limax.codec.CharConsumer 接口,允许 与limax.codec 其它流模块协同解 码 。 The JSONDecoder of the Java and C# version implements the limax.codec.CharConsumer interface, and allows to cooperate with the other stream module of the limax.codec.

例一 (Endpoint.java 中解码来自 Auany 的服务配置信息的方法)Example 1 (The decoding method of the Endpoint.java comes from the service configuration information of the Auany)public static List<ServiceInfo> loadServiceInfos(String httpHost, int httpPort, int appid, long timeout, int maxsize, File cacheDir, boolean staleEnable) throws Exception {

JSONDecoder decoder = new JSONDecoder();new HttpClient("http://" + httpHost + ":" + httpPort + "/app?native=" + appid, timeout,

maxsize, cacheDir,staleEnable).transfer(new CharSink(decoder));List<ServiceInfo> services = new ArrayList<ServiceInfo>();for (JSON json : decoder.get().get("services").toArray())

services.add(new ServiceInfo(appid, json));return services;

}在这里 HttpClient 下载指定 url 的内容,通过 CharSink 适配,将字节流传送给解码器,之后在解码器上执行 get(),获取 JSON对象。解码器套上适配器后,异常处理只需要考虑最外层 , 在 这 里 就 是 HttpClient.transfer 抛出 的 IOException 。 之 后 的 JSON 访 问 可 能抛出JSONException,整个方法简单处理,指示抛出 Exception。Here, the HttpClient downloads the content of the specified URL, adapts via the CharSink, sends

231

Page 232: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

the byte stream to the decoder, then execute the get() operation on the decoder to obtain the JSON object. When the decoder is wrapped by adapter, only the outermost layer need to be considered when handling the exception, and here is the IOException thrown by the HttpClient.transfer. The next JSON accessing might throw the JSONException. The whole method processes simply and throws Exception.

例二(C++实现的多 JSON 解码)Example 2 (The multi JSON decoding implemented with the C++)JSONDecoder jd([](std::shared_ptr<JSON> json) {printf("%s\n", json->toString().c_str()); });for (auto c : std::string("{\"a\":10}[2,3]5[12]"))

jd.accept(c);这个例子会输出如下 4 行:The below four lines are ouput by this example:<Object><Array>5<Array>输出结果刚好反映了 JSON 输入串的结构。需要注意的是,如果在这个串的最后添加一个数字,那么输出结果还是上面 4 行。流方式处理时,数字的解码比较特殊,不参考下一个字符, 不 可 能 决 定 该 数字是否结 束 , 不像串使 用双引号 结 束 。所以 JSON.parse 通 过JSONDecoder 实现的解码代码,在最后会调用一次 JSONDecoder 的内部方法 flush,flush 简单地 accept 一个空格,JSON 规范规定了 JSON元件之间的白空格均被忽略,用最后加入空格的方法正好解决数字的问题。另外,还必须注意 JSON.parse只能解码一个 JSON元件,如果用上面代码中的串调用 JSON.parse,解码器处理完毕"{\"a\":10}",遭遇到第一个非白空格,也就是之后的"[",即会抛出异常,用 Javascript,Lua版本执行这样的 parse同样是这个效果,各语言版本语义一致。The output result just reflects the structure of the JSON input string. It should be noted that if appending a number in the end of this string, the output result is still the above four lines. In the stream mode, the decoding of the digit is special. Without referencing to the next character, it is impossible to determine whether this digit is end, unlike the string using the double quotes as the end. So the decoding code implemented with the JSONDecoder by the JSON.parse will call the internal flush method of the JSONDecoder in the end. The flush simply accept a space. The JSON specification defines that the whitespace between the JSON components is ignored, so appending a space resolves the digital issue. Moreover, it should be noted that the JSON.parse only decodes a JSON component. If the string of the above code is used to call the JSON.parse, the decoder throws the exception when suffering the first non-whitespace which is the next "[" after it completes the "{\"a\":10}". Using the Javascript and Lua versions to execute this parse has the same effect. The semanteme of each language version is consistent.

232

Page 233: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

CLR/Lua

Limax提供一个 clrlua 项目,粘合 C#与 Lua,支持 C#与 Lua 代码之间的互操作,用于实现 C#脚本模式框架下的 LuaScriptHandle。这一章介绍 clrlua提供的功能。The Limax provides a clrlua project, which combines C# with Lua, supports the interoperability between the C# and Lua, to implement the LuaScriptHandle in the framework of the C# script model. This chapter describes the functions provided by the clrlua.

编程接口 Programming interface

limax.script.Lua

创建该对象也就创建了一个 lua虚拟机,使用该对象即可与 lua虚拟机交互。When creating this object, a lua virtual machine is created. Using this object could interact with the lua virtual machine.构造函数 Constructor: Lua(limax.script.Lua.ErrorHandle eh)

ErrorHandle 定义为委托 delegate void ErrorHandle(string msg); 用以接收 lua错误信息。The ErrorHandle is defined as the delegation delegate void ErrorHandle(string msg); which is used to accept the lua error message.

成员函数 Method: object eval(string code)执行 code 表示的代码 chunk,返回结果。Execute the code chunk represented by the code and return the result.

object eval(string codepattern, params object[] parameters)用变长参数列表填充 codepattern 中的占位符<0>,<1>......<n>,然后执行 chunk,返回结果。占位符通过与之对应创建的上下文变量进行关联,在模板代码中应该当作变量名使用,而不能用引号括起来,否则将替换结果将是一个上下文变量名串。返回的结果可能是任何类型,特别的可能返回 LuaObject,LuaTable,LuaFunction三个类型。Use the variable-length argument lists to fill with the codepattern <0> ,<1>......<n> of the codepattern, then execute the chunk, and return the result. The codepatterns are associated through the corresponding created context variables, and should be used as the variable names in the template code, rather than using quotation to mark, or the replacement result will be a name string of context variable.The returned result could be any type. Especially, the LuaObject , LuaTable and LuaFunction three types could be returned.

Lua name(string chunkname)为后续 eval命名,如果运行时错误发生在被命名的 eval 的代码块中,错误信息中会前

233

Page 234: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

缀该名字,便于调试。该名字内部使用 UTF8编码,建议不要使用非 ASCII字符。Name the following eval. If the run-time error occurs in the named eval code, this name will be prefixed in the error message for debugging purpose. This name uses UTF8 encoding internally, and non-ASCII characters are not recommended.

limax.script.LuaObject

无法转换为 C#类型的 Lua对象,哪些无法转换在类型映射一节介绍,该对象可以被 C#持有,该对象上不提供任何操作,多数情况下需要传回 lua虚拟机使用。The Lua object which could not be converted to C# type could be held by the C#. This object does not provide any operation and need to be returned to the lua virtual machine to use in most cases. Those which can not be coverted are described in the type mapping section.

limax.script.LuaTable

该对象派生自 LuaObject,实现了 System.Collections.IDictionary 接口,关联到 lua虚拟机中的 table 上。访问该对象,也就访问了虚拟机中对应的 table,该对象上执行修改操作将体现到虚拟机中对应的 table 上,反之亦然。This object is derived from the LuaObject, and implements the System.Collections.IDictionary interface and associates to the table of the lua virtual machine. When accessing this object, the corresponding table of the virtual machine is also accessed, and the update operation executed on this object will be reflected to the corresponding table of the virtual machine. vice versa.

limax.script.LuaFunction

一个变长参数委托,包装了一个派生自 LuaObject,关联到 lua虚拟机中的 function 上的对象。在该委托上进行调用,相当于调用了虚拟机中对应的 function。A variable-length argument delegation, encapsulates the object derived from LuaObject and associated with function of the lua virtual machine. Calling on this delegation is equivalent to calling the corresponding function of the virtual machine.

注意,通过 limax.script.Lua 创建的对象,在之后的文字中命名为 Lua操作对象,区别于LuaObject。Note, the object created through limax.script.Lua is named as the Lua operating object in the next content to distinguish the LuaObject.

C#与 Lua 的互操作举例 Interoperability example between C# and Lua

首先定义 Lua lua = new Lua((string msg)=>Console.WriteLine(msg));First, define the Lua lua = new Lua((string msg)=>Console.WriteLine(msg));

C#操纵 Lua C# operates Lua

C#操纵 Lua,通过 eval 方法调用实现。The C# operates the Lua through calling the eval method.

234

Page 235: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

hello worldlua.eval("print('hello world')");lua.eval("print(<0>)", "hello world");Console.WriteLine(lua.eval("return 'hello world'"));Console.WriteLine(lua.eval("return <0>", "hello world"));输出:Output:hello worldhello worldhello worldhello world其中,第一行代码无需解释,第二行代码说明了占位符的使用,第三行代码说明了eval 可以返回 lua虚拟机中持有的值,第四行代码说明了一个值可以在 C#,Lua 间反复传递。It is no need to describe the first line. The second line describes the usage of the codepattern. The third line describes that the eval could return the value held by the lua virtual machine. The forth line describes that a value could be repeatedly trasferred between the C# and Lua.

好好学习,天天向上lua.eval("print('好好学习,天天向上')");lua.eval("print(<0>)", "好好学习,天天向上");Console.WriteLine(lua.eval("return '好好学习,天天向上'"));Console.WriteLine(lua.eval("return <0>", "好好学习,天天向上"));输出:Output:濂藉ソ瀛︿範锛屽ぉ澶╁悜涓濂藉ソ瀛︿範锛屽ぉ澶╁悜涓好好学习,天天向上好好学习,天天向上这个例子说明了,C#中的 Unicode字符串传递给 Lua 的时候使用 UTF8编码,从 Lua 传递回来使用 UTF8 解码。所以对于 Unicode字符,Lua虚拟机看来是乱码,回到 C#将变得正确。This example explains that the Unicode character string of the C# uses the UTF8 to encode when transffering to the Lua and uses the UTF8 to decode when passing back from the Lua. So the Unicode character is the gibberish in the Lua virtual machine, and is correct in the C#.

执行代码片断返回多值 The executing code returns the multiple valuesobject[] a = (object[])lua.eval("return 1,2,3");foreach (object o in a)

Console.WriteLine(o);输出:Output:123

235

Page 236: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

这个例子说明了,如果 Lua 代码返回多值,这些值被放置在 object[]中返回。This example explains that if the Lua code returns the multiple values, these values are placed in the object[] to return.

执行代码片段返回 table The executing code returns the tableIDictionary dict = (IDictionary)lua.eval("t = { a = 'A', b ='B' }\n return t ");foreach (DictionaryEntry e in dict) Console.WriteLine(e.Key + ":" + e.Value);输出:Output:b:Ba:A

继续执行:Continue to execute:dict.Remove("a");dict.Add("c", "C");lua.eval("print (t.a)");lua.eval("print (t.c)");输出:Output:nilC这里可以看见,C#中的修改影响到了 lua内部的 tableFrom the above result, the modification of the C# affects the internal table of the lua.

接续执行:Continue to execute:lua.eval("t.d = 100");foreach (DictionaryEntry e in dict) Console.WriteLine(e.Key + ":" + e.Value);输出:Output:d:100c:Cb:B这里可以看出,lua内部对 table 的修改,也反映到 C#一边。From the above result, the modification to the internal table of the Lua also is reflected to the C#.

执行代码片断返回 function The executing code returns the functionLuaFunction func = (LuaFunction)lua.eval("return function(a,b) return a + b; end");Console.WriteLine(func (10, 20));输出:Output:30这里可以看出,返回一个 lua函数在 C#中使用非常容易。From the above result, returning a lua function is very easy to use in the C#.

Lua操纵 C# Lua operates C#

首先定义一个 C#实验类。236

Page 237: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

First, define a C# example class.public class TestLua{

public TestLua() { Console.WriteLine("Construct TestLua"); acc = add; }public TestLua(int x) { Console.WriteLine("Construct TestLua with " + x); acc = add; }public int add(int a, int b) { return a + b; }public string mystr;public int Prop { get; set; }public int this[int x]{

get { return x + 1; }set { Console.WriteLine("Indexed Property set key = " + x + " value = " + value); }

}public delegate int ACC(int a, int b);public ACC acc;

}这里应该看到,需要通过 Lua 访问的对象构造函数,方法,字段,属性,委托,必须声明为 public。It is noted that the object construction functions, methods, fields, attributes, delegations accessed by the Lua must be declared as public. 构造对象 Construct the object

lua.eval("t0 = <0>()", typeof(TestLua));lua.eval("t1 = <0>(100)", typeof(TestLua));Console.WriteLine(lua.eval("return t0") is TestLua);输出:Output:Construct TestLuaConstruct TestLua with 100True在这里,类 TestLua被传递进 Lua虚拟机,在类上直接进行方法调用,也就创建了对象,通过参数个数的控制可以选择不同的方法进行调用。第三行把 Lua 中创建的变量 t0 传回 C#进行验证,果然是 TestLua对象。Here the TestLua class is passed into the Lua virtual machine, the methods are directly called on the class and create the object. Through controlling the number of the parameter, the different methods are selected to call. The third line passes the variable t0 created in the Lua back to the C# to verify, and the result is the TestLua object.

方法调用 Call methodlua.eval("print (t0.add(1,2))");输出:Output:3如果是类的静态方法,同样可以调用。If it is the static method of the class, it also could be called.

237

Page 238: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

字段访问 Access fieldlua.eval("print(t0.mystr)");lua.eval("t0.mystr='hello world'");Console.WriteLine(lua.eval("return t0.mystr"));Console.WriteLine(((TestLua)lua.eval("return t0")).mystr);输出 Outputnilhelloworldhelloworld第一行代码,输出 mystr 中的内容,对象构造时没有初始化该字段 C#中为 null,对应了 Lua 中的 nil,第二行设置 mystr 的字段内容,所以第三第四行访问均正确返回了设置内容。The first line code outputs the content of the mystr, this field does not be initialized when constructing the object, so the null in the C# corresponding to the nil in the Lua is output. The second line sets the field content of the mystr. So the third line and the forth line correctly return the setted content.

普通属性访问 Access the normal attributelua.eval("print(t0.Prop)");lua.eval("t0.Prop = t0.Prop + 10");Console.WriteLine(lua.eval("return t0.Prop"));输出:Output:010第一行代码输出 Prop 的默认初始化值 0,第二行相当于读取该属性,再设置该属性,属性上加上 10,所以第三行输出 10。The first line code outputs the default initialization value 0 of the Prop. The second line is equivalent to read this attribute, then sets this attribute and adds 10 into the attribute. The third line outputs 10.

索引化属性访问 Access the indexed attributelua.eval("print(t0[100])");lua.eval("t0[200] = 300");输出:Output:101Indexed Property set key = 200 value = 300这里可以看到索引化属性的两个方法均被正确调用。Here the two methods of the indexed attribute are correctly called.

委托访问 Access the delegationlua.eval("print(t0.acc(100,200))");输出:Output:300

238

Page 239: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

对照前面的 C#代码,这里的 acc 实际上是一个委托,指向了 add 方法,所以实际上执行的是 add。Compare with the previous C# code, the acc here actually is a delegation and points to the add method. So actually the add is executed.

Lua 访问 C#的具体细节参见章节《脚本语言访问 C#规范》Refer to the charpter <The specification that the script language accesses C#> for the details about the Lua accessing C#

复杂交互 Complex interaction

首先定义委托 FN First define the delegation FNpublic delegate long FN(object f, long a);然后执行下面的代码 Then execute the below codeFN fn = (object f, long a) => (long)lua.eval("if<1><2 then return<1>else return<0>(<0>,<1>-1)+<0>(<0>,<1>-2)end", f, a);lua.eval("print(<0>(<0>,<1>))", fn, 9);输出:Output:34事实上,这段程序在 C#和 Lua 之间间接递归,计算了斐波那契数列的第 9 项。说明了

C#和 Lua 进行复杂交互的可能性。Actually, this program is indirectly recursive between the C# and Lua to calculate the ninth

item of the Fibonacci sequence. This illustrates the possibility of the complex interaction between the C# and Lua.

类型映射 Type mapping

C#,Lua 两种语言的类型系统没有兼容性可言,所以类型映射无法用一张明确的表格进行描述。分 3 种规则讨论。There is no compabitility between the C# type system and the Lua type system. So the type mapping can not be described in a clear form. It is described in 3 rules.

规则 1:C#向 Lua 传递(C#通过 eval向 Lua 传递;Lua 访问 C#时,构造返回对象,方法返回值,委托返回值,读取字段,读取属性)Rule #1: C# transfers to Lua (the C# transfers objects to the Lua through eval; when the Lua accesses the C#, construct the return object, the return value of the method and the return value of the delegation, read the field, and read the attribute)这种情况下,C#类型信息可以明确获知。In this situation, the type information of the C# could be clearly obtained. null,使用 lua_pushnil 传递。null, use lua_pushinil to transfer bool 以及装箱类型 Boolean,使用 lua_pushboolean 传递。bool and boxing type Boolean,

use lua_pushboolean to transfer byte , sbyte , short , ushort , int , uint , long , ulong , 以 及 装 箱 类 型

239

Page 240: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Byte,SByte,Int16,UInt16,Int32,UInt32,Int64,UInt64,使用 lua_pushinteger 传递 byte, sbyte, short, ushort, int, uint, long, ulong, and boxing type Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, use lua_pushinteger to transfer

float,double,decimal 以及装箱类型 Single,Double,Decimal,使用 lua_pushnumber传递 float, double, decimal and boxing type Single, Double, Decimal, use lua_pushnumber to transfer

char,string 以及装箱类型 Char,String,UTF8编码后使用 lua_pushstring 传递。需要注意,字符串传递给 Lua 之后,已经变成 Lua字符串,不可能调用 C#的字符串方法进行访问。之前的字段访问例子中,如果执行 lua.eval("print(t0.mystr.Length)"),将输出nil,改为 lua.eval("print(t0.mystr:len())"),才能获得正确结果。语法上比较怪异,小心。char, string and boxing type Char, String, UTF8 after encoding, use lua_pushstring to tranfer. It should be noted that the string has changed to the Lua string after transfered to the Lua, and could not call the string methods of the C# to access. In the previous example which accesses the field, if execute lua.eval("print(t0.mystr.Length)"), the nil will be output. Using lua.eval("print(t0.mystr:len())") can get the correct result. The syntax is weird, be careful.

LuaObject,LuaTable,LuaFunction 之外的类型使用 lua_newuserdata 创建一个用户数据类型与之关联,特别的,如果类型为委托类型,则获取委托对象的 Invoke 方法作为实际需 要 关联的对象, 这 样委托对象就 能被正 确调用 了 。 The types except for the LuaObject, LuaTable and LuaFunction, use lua_newuserdata to create a user data type to associate. Particularly, if the type is the delegation type, the Invoke method of the delegation object is obtained as the object that is actually associated with it, so that the delegation object can be correctly called.

LuaObject,LuaTable,LuaFunction 是之前返回给 C#的类型,这时取回之前关联的 Lua对象。 The LuaObject , LuaTable and LuaFunction are the type returned to the C# previously, then get back the associated Lua object.

特别的,C#方法的返回 void,或者委托返回 void,或者非可读属性的读取,根本不会在栈上 push 任何值,详见章节《脚本语言访问 C#规范》中的实验。In particular, the void returned by the C#'s method, or the void returned by the delegation, or reading the non-readable attribute. It will not push any data on the stack, and reference to the experiment on the charpter <The specification that the scripting language accesses C#> for the details.

规则 2:Lua向 C#返回值(eval 的返回)Rule #2: Lua returns the value to C# (the returned object of the eval)这种情况下,只能根据 Lua当前已知的信息返回,C#获取这样的返回值以后,必须严格检查类型然后使用,不小心就可能导致类型转换异常,System.InvalidCastException。In this situation, the return is only based on the Lua's current known information. When C# gets this kind of return value, it should strictly check the type and then use it, or the type conversion exception System.InvalidCastException might be caused. LUA_TNIL,返回 null Lua_TNIL, return null LUA_TBOOLEAN,返回为 Boolean LUA_TBOOLEAN, return Boolean

240

Page 241: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

LUA_TSTRING , UTF8 解 码 后返回为 String LUA_TSTRING, return String after the UTF8 decoding

LUA_TNUMBER,根据 lua_isinteger 进行判断,返回为 Int64,或者 Double。这一点非常特殊,并不存在 LUA_TINTEGER 这一类型,估计是 Lua 在保证兼容性的前提上为了支持Int64加入了一个补丁——在类型信息之外,内部增加了一个 tag 进行区分,无需影响原有的类型规范。 LUA_TNUMBER, return Int64 or Double according to the lua_isinteger. It is very special because there is no LUA_TINTEGER type, and it might that the Lua addes this patch to support the Int64 after ensuring the compatibility -- except for the type information, a tag is added internal to distinguish without effecting the original type specification.

LUA_TUSERDATA,该对象是 C#之前传入的非数值对象,直接向 C#返回该对象,特别的,如果该对象是委托的包装,返回委托本身。 LUA_TUSERDATA, the non-numeric object transferred by C# previously. It should be directly returned to the C#. Particularly, if the object is a delegate encapsulation, the delegation itself is returned.

LUA_TTABLE,该对象是一个 Lua 表,创建并返回一个与之关联的 LuaTable对象,使得C#能够通过 LuaTable 的 IDictionary 接口直接访问该表。LuaTable对象按照 Lua 规范访问,而不是 IDictionary 规范。例如,IDictionary 重复 Add抛异常,Lua 的重复 Add执行替换;Lua 中 Add nil 值表示删除,所以 LuaTable.Add(key,null)等同于 LuaTable.Remve(key) LUA_TTABLE, a Lua table, create and return the associated LuaTable object. The C# could directly access the table via the IDictionary interface of the LuaTable. Accessing the LuaTable object bases on the Lua specification, not the IDictionary specification. For example, the exception is thrown when IDictionary repeats the Add, and the replacement is executed when the Lua repeats the Add. Adding nil in the Lua means deleating. So LuaTable.Add(key , null) equals LuaTable.Remve(key).

LUA_TFUNCTION,该对象是一个 Lua函数,创建一个与之关联的 LuaObject派生对象,这个对象再使用 LuaFunction委托进行包装,使得 C#能够通过 LuaFunction委托,将函数调用转发给 Lua虚拟机,调用相应的 Lua函数。LUA_TFUNCTION, a Lua function, create an associated object derived from the LuaObject. Then this object uses the LuaFunction delegation to encapsulate so that the C# could forward the function call to the Lua virtual machine to call the responding Lua function.

特别的,对于无返回值的情况,返回 DBNull.Value. 对于多返回值的情况,将各个返回值按照上面的方式转换为相应的 C#对象之后,再包装成 C#对象数组返回。Particularly, the DBNull.Value is returned when there is no return value. If there is multiple return values, the packaged object array of the C# is returned after each return value is converted to the relative C# object according to the previous way.

注意,(int)lua.eval("return 1"); 这样的转换必然导致 System.InvalidCastException,原因在于 lua_Integer 是 64位的,对应 long,返回类型为 System.Int64,这样的装箱类型无法像简单类型 long,特别的,如果类型为委托类型,则获取委托对象的 Invoke 方法作为实际需要关联的对象,这样委托对象就能被正确调用了. 可以在返回对象上调用getType()方法检查实际类型,判断是否符合需求,进而实现正确的转换。 Note: the converison such as (int)lua.eval("return 1"); inevitably causes the System.InvalidCastException, because that the lua_Integer is the 64 bits corresponding to the long and the return type is System.Int64. So this kind of boxing type could not be

241

Page 242: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

directly convert to the int like the simple type long. Particularly, if the type is the delegation type, the Invoke method of the delegation object is obtained as the object that is actually associated to it, so that the delegation object can be correctly called. The getType() method could be called on the returned object to check the actual type, and determine whether it meets the requirement and then process the correct conversion.

规则 3:Lua向 C#传递(构造参数,方法调用参数,委托调用参数,字段设置,属性设置)Rule #3: Lua transfers to C# (parameters of the constructor called, parameters of the method called, parameters of the delegation called, field set, property set)首先按照规则 2,转换为 C#对象,然后根据期待的参数类型在 C#中进行转换,细节参见章节《脚本语言访问 C#规范》First of all, convert to the C# object according to the rule #2, then make the conversion in the C# according to the expected parameter type. Refer to the charpter <The specification that the scripting language accesses C#> for the details.

异常规范 Exception specification

Lua 代码的运行时异常,由 Lua虚拟机捕获,通过 Lua操作对象构造时传入的委托传递给 C#。 The running time execption of the Lua code is caught by the Lua virtual machine and transferred to the C# through the passed delegation when Lua constructs the object.

Lua调用 C#构造函数或者成员函数或者委托时,如果抛出异常,这样的异常将被内部捕获,转换为 C#异常的串格式,再后缀 Lua栈描述,生成错误信息,提交给 Lua虚拟机,最终传递给 C#。这里需要强调一个问题,C#和 Lua互相调用,间接递归的情形下,异常实际上到达不了整个递归栈的最上层,而是打断当前 eval 的执行,传送出异常串,最后 eval返回 DBNull.Value。如果有回滚整个递归栈的需求,可以这样设计:逻辑上避免 eval返回 DBNull.Value,一旦 eval返回 DBNull.Value,在 C#内抛出异常,带上最后一条错误信息。 When the Lua calls the C# construct function, or member function, or delegation, if the exception is thrown, this exception is internally caught and converted to the string format of C# exception, then adds the Lua stack description as the surffix, generates the error information, submitts to the Lua virtual machine, and finally is transferred to the C#. A problem should be emphasized here that under the C# and Lua call each other and is in indirect recursion situation, the exception actually could not reach the top of the entire recursion stack, but interrupt execution of the current eval, send out the exception string, and finally return the DBNull.Value through eval. If there is the requirement to rollback the entire recursion stack, it could design like this: avoid the eval to return DBNull.Value logically, once the eval returns the DBNull.Value, the exception with the last error information is thrown in the C#.

242

Page 243: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

注意事项 Matters need attention:

1. C# 中 的泛型对象不 可 往 Lua 虚拟机中 传递,否则将抛出异常。原因在 于 ,System::Runtime::InteropServices::Marshal::GetNativeVariantForObject ,System::Runtime::InteropServices::Marshal::GetObjectForNativeVariant 这两个关键方法的泛型版本从.NET4.5.1 之后才开始提供,为了兼容较老的.NET版本,该项目使用.NET4.0 开发(.NET3.5 应该也可以使用)。The generic object of the C# could not be transferred to the Lua virtual machine, or there is exception thrown. The reason is that System::Runtime::InteropServices::Marshal::GetNativeVariantForObject, System::Runtime::InteropServices::Marshal::GetObjectForNativeVariant, these two key functions' generic version is provided from the .NET4.5.1, and this project use .NET4.0 to develop (.NET3.5 also works) for the compatibility with the older versions of .NET. 2. LuaObject返回给 C#持有时,将被 C#的 gc控制,gc线程不一定是操作 Lua虚拟机的线程,所以该项目必须按线程安全的方式设计。实现上,通过对 Lua操作对象加锁保证安全,为了在 LuaObject析构时进行锁定,需要将 Lua操作对象的引用传递给 LuaObject 构造器,这样的传递只能通过 lua_State 结构实现,问题是 lua_State 结构缺少一个用户自定义指针的字段,所以实现上挪用了 lua_State.hook指针,这相当于扔掉了 Lua 的 hook 能力,全局空间中的 debug.sethook,debug.gethook 两个方法也被删除掉,避免用户使用,导致错误。如果需要保留 hook 能力,则只能修订 Lua源码,在 lua_State 结构上添加一个用户定义指针,对应修改 clrlua.cpp,重新编译项目。When the LuaObject returns to the C#, it will be controlled by the gc of the C#. The gc thread does not necessarily operate the thread of the Lua virtual machine, so this project must be designed as thread safe project. The implementation ensures the safe through adding lock on the Lua operation object. In order to lock when the LuaObject destructs, it needs to transfer the reference of the Lua operation object to the LuaObject constructor. Such a transfer can only be achieved via lua_State structure. However, the lua_State structure lacks of a field with the user-defined pointer, so the lua_State.hook pointer is used, which is equivalent to throwing away the hook ability of the Lua, and the debug.sethook and debug.gethook two methods are also thrown away to avoid the user to use and cause the error. If it needs to keep the hook ability, it only modifies the Lua source code, add a user-defined pointer on the lua_State structure, modifies the corresponding clrlua.cpp, and recompilies the project. 3. 可以创建多个 Lua操作对象,从一个 Lua操作对象获取的 LuaObject,LuaTable,不能够传入另一个 Lua操作对象,这个显而易见;其它的 C#对象传入多个 Lua操作对象,没有限制,如果不同线程使用不同的 Lua操作对象,这些传入的 C#对象的线程安全性必须应用自己保证。多个 Lua操作对象,如果确有数据交换需求,可以通过 JSON 实现,Limax 自带json.lua,限制是对象中不能存在环。It could create multiple Lua operation objects. The LuaObject and LuaTable obtained from the one Lua operation object could not be transferred to another Lua operation object, which is obvious. There is no limitation for the other C# object to transfer multiple Lua operation objects. If different thread uses the different Lua operation object, the thread safe of these transferred C# objects should be ensured by themself. If the multiple Lua operation objects have the data

243

Page 244: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

exchange requirement, it could be implemented through JSON, and the Limax has its own json.lua. The limitation is that there is no loop between the objects.4. 多线程的情况下通过 LuaTable 访问 Lua虚拟机中的 table 时需要注意,使用 foreach 方式遍历 table前,必须在 lua操作对象或者 LuaTable.SyncRoot 上加锁,这两者实际上指向同一对象。其它访问方法,已经在内部通过加锁保证安全了。It should be noted when accessing the table of the Lua virtual machine through LuaTable in the multiple threads. Before using foreach method to iterate the table, the lock must be added on the lua operate object or LuaTable.SyncRoot, and the two actually points to the same object. The other access methods are safe internally by locking.5. json.lua集成在项目中,全局名字空间中已经存在 JSON.stringify,JSON.parse,可以直接使用。The json.lua is integrated in the project, and the global name space has the JSON.stringify, JSON.parse, which could be directly used.

CLR/Javascript(SpiderMonkey)

Limax提供一个 clrjs 项目,粘合 C#与 Javascript,支持 C#与 Javascript 代码之间的互操作,用于实现 C#脚本模式框架下的 JavaScriptHandle。这一章介绍 clrjs提供的功能。The Limax provides a clrjs project, which combines C# with Javascript, supports the interoperability between the C# and Javascript, to implement the JavaScriptHandle in the framework of the C# script model. This chapter describes the functions provided by the clrjs.

编程接口 Programming interface

limax.script.Js

创建该对象也就创建了一个 javascript虚拟机,使用该对象即可与 javascript虚拟机交互。When creating this object, a javascript virtual machine is also created, and use this object to interact with the javascript virtual machine. 构造函数:Constructor: Js(uint maxbytes)

maxbytes 决定了 javascript虚拟机使用的最大内存字节数。maxbytes determines the maximum memory byte numbers used by the javascript virtual machine.

Js()调用上一个构造函数,默认maxbytes = 8388608Call the previous construct method, and the default maxbytes = 8388608.

成员函数:Method: object eval(string code)执行 code 表示的代码 chunk,返回结果。Execute the code chunk expressed by the code and return the result.

244

Page 245: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

object eval(string codepattern, params object[] parameters)用变长参数列表填充 codepattern 中的占位符<0>,<1>......<n>,然后执行 chunk,返回结果。占位符通过与之对应创建的上下文变量进行关联,在模板代码中应该当作变量名使用,而不能用引号括起来,否则将替换结果将是一个上下文变量名串。返回的结果可能是任何类型,特别的可能返回 JsObject,JsArray,JsFunction 类型。Use the variable-length parameter list to fill the placeholder <0>, <1>......<n> of the codepattern, then execute chunk and return the result. The placeholder is associated with the context variable created by the placeholder, which should be used as the variable name in the template code, but not be enclosed in quation marks, or the replacement result will be a context variable name string.The returned result could be any type, and in particular, it is possible to return JsObject,JsArray, JsFunction type.

Js name(string chunkname)为后续 eval命名,如果运行时错误发生在被命名的 eval 的代码块中,错误信息中会前缀该名字,便于调试。该名字内部使用 UTF8编码,建议不要使用非 ASCII字符。Name the following eval. If the run-time error occurs in the named eval code, the name will be prefixed in the error message for debugging. This name uses UTF8 encoding internally and non-ASCII characters is not recommended.

异常:Exception: ScriptException见异常规范一节。Refer to the exception specification section.

ThreadContextException见线程安全一节。Refer to the thread safety section.

limax.script.JsObject

无法转换为 C#类型的 javascript对象,哪些无法转换在类型映射一节介绍,该对象实现了System.Collections.IDictionary 接口,便于在 C#代码中操纵 javascript对象的属性。The javascript object which could not convert to C# type. Which can not be converted is introduced in the type mapping section. This object implements the System.Collections.IDictionary interface in order to manipulate the property of the javascript object in the C# code.

limax.script.JsArray

派生自 JsObject,对应了 javascript 中的 Array,实现了 IList 接口,便于在 C#代码中操纵javascript 的 Array对象。Derived from the JsObject, corresponding to the Array of the javascript, implement the IList interface to control the Array object of the javascript in the C# code.

limax.script.JsFunction

一个变长参数委托,包装了一个派生自 JsObject,关联到 javascript虚拟机中的 Function 上245

Page 246: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

的对象。在该委托上进行调用,相当于调用了虚拟中对应的 Function。A variable-length argument delegation, encapsulates the object derived from JsObject and associated with Function of the javascript virtual machine. Calling on this delegation is equivalent to calling the corresponding Function of the virtual machine.

注意,通过 limax.script.Js 创建的对象,在之后的文字中命名为 javascript操作对象,区别于JsObject。Attention, the object created through limax.script.js, is named as the javascript operating object in the following content to distinguish from the JsObject.

C#与 Javascript 的互操 作举例 Interoperability example between C# and

Javascript

首先定义 Js js = new Js();First, define Js js = new Js();

C#操纵 Javascript C# controls Javascript

C#操纵 Javascript,通过 eval 方法调用实现。C# controls Javascript through calling eval method to implement. hello world

js.eval("print('hello world')");js.eval("print(<0>)", "hello world");Console.WriteLine(js.eval("'hello world'"));Console.WriteLine(js.eval("<0>", "hello world"));输出:Output:hello worldhello worldhello worldhello world其中,第一行代码无需解释,第二行代码说明了占位符的使用,第三行代码说明了eval 可 以返回 javascript 虚拟机中 持 有 的值,第四行 代 码说明了 一 个值可 以 在C#,Javascript 间反复传递。The first line of code need not be interpreted, the second line of code explains the usage of the placeholder, the third line of code explains that the eval could return the value held in the javascript virtual machine, and the forth line of code explains that an value could be repeatedly transferred between C# and Javascript.

null 与 undefined null and underfinedConsole.WriteLine(js.eval("null") == null);js.eval("print(<0> === null)", null);Console.WriteLine(js.eval("undefined") == DBNull.Value);js.eval("print(<0> === undefined)", DBNull.Value);

246

Page 247: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

输出Output:TruetrueTruetrue这 就证明了 javascript 的 null 与 C# 的 null 完 全等价, javascript 的 undefined 与Js.undefined 完全等价。This proves that javascript's null is completely equivalent to the null of C#, and javascipt's underfined is completely equivalent to the DBNull.Value of C#.

Unicodejs.eval("var 好好学习=<0>", "天天向上");js.eval("print(好好学习)");Console.WriteLine(js.eval("好好学习"));输出:Output:天天向上天天向上这里可以看出,Unicode 可以被正确支持。Seen from here that the Unicode could be correctly supported.

执行代码片段返回 JsObject Execute code and return JsObjectJsObject obj = (JsObject)js.eval("var t = { a: 'A', b :'B' }; t ");foreach (DictionaryEntry e in obj) Console.WriteLine(e.Key + ":" + e.Value);输出:Output:a:Ab:B

继续执行:Continue execute:obj.Remove("a");obj.Add("c", "C");js.eval("print (t.a)");js.eval("print (t.c)");输出:Output:undefinedC这里可以看见,C#中的修改影响到了 javascript对象的属性Seen from here that the modification in the C# affects the property of the javascript object.

继续执行:Continue execute:247

Page 248: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

js.eval("t.d = 100");foreach (DictionaryEntry e in obj) Console.WriteLine(e.Key + ":" + e.Value);输出:Output:b:Bc:Cd:100这里可以看出,javascript内部修改对象属性,也反映到 C#一边。Seen from here that modification of the object’s property in the javascript internal is alos reflected to the C#.

执行代码片断返回 JsArray Execute code and return JsArrayJsArray a = (JsArray)js.eval("var o=[1,2]; o");foreach (var v in a) Console.WriteLine(v);输出:Output:12

继续执行:Continue execute:a.Add(3);js.eval("print(o)");输出:Output:1,2,3这里可以看见,C#中的修改影响到了 javascript 数组的内容Seen from here that the modification in the C# affects the content of the javascript's array.

继续执行:Continue execute:js.eval("o.shift()");foreach (var v in a) Console.WriteLine(v);输出Output23这里可以看出,javascript内部数组操作,也反映到 C#一边。Seen from here that the operate of the arrary in the javascript internal is also reflected to the C#.

继续执行:Continue execute:Console.WriteLine(a.Count);a[5] = 100;js.eval("print(o)");

248

Page 249: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

输出:Output:22,3,,,,100这里可以看出,JsArray 数组上的操作,符合 javascript 数组访问规范,不符合 C#数组访问规范,不会抛出 System.ArgumentOutOfRangeException异常。Seen from here that the operate on the JsArray array complies with the access specification of the javascript array, but does not comply with the access specification of the C# array and does not throw System.ArgumentOutOfRangeException exception.

继续执行:Continue execute:a.Remove(DBNull.Value);js.eval("print(o)");输出:Output:2,3,100这里再次明确,C#的 DBNull.Value 等同于 javascript 的 undefined,不存在的元素在javascript 数组中表示为 undefined。Specifically here again, the DBNull.Value of C# equals to the undefined of javascript, and the non-existed element is expressed as undefined in the javascript array.

继续执行:Continue execute:JsObject o = a;o['c'] = 1000;Console.WriteLine(a.Count);Console.WriteLine(o.Count);输出:Output:34这里需要特别注意,对于 javascript而言,Array 也是 Object,Array 上也可以操作属性,通过 JsObject 或者 JsArray两种方式引用同一对象,导致了不同行为。非特殊情况下,为了避免混乱,数组只用 JsArray 操作更容易理解。It should be particularly noted that for javascript, Array is also an Object, and the property could be manipulated on the Array. Reference to the same object through JsObject or JsArray lead to different behaviour. In non-special case, to avoid the confusion, the array only using JsArray to manipulate is more understandable.

执行代码片断返回 JsFunction Execute code and return JsFunctionJsFunction f = (JsFunction)js.eval("var func = function(a,b){ return a + b;}; func");Console.WriteLine(f(10, 20));输出:Output:30这里可以看出,返回一个 javascript函数在 C#中使用非常容易。Seen from here that returning a javascript function is esay to use in C#.

249

Page 250: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Javascript对象操作 Javascript object operationJsObject obj1 = (JsObject)js.eval("function CLS(n){this.value = n; this.dump = function(){print(this.value);}}; new CLS(200)");((JsFunction)obj1["dump"])();输出:Output:200这里可以看出,javascript 代码用函数 CLS 创建了一个对象,交由 obj1 持有,obj1 上查询到函数属性 dump 进行调用,输出创建出的对象 value值 200。C#的语法无法写成obj1.dump,只好用索引化方式访问。Seen from here that the javascipt code uses CLS function to create an object which is held by the obj1, the obj1 queries the dump property with the function as value to call and outputs the created object's value 200. The C#'s syntax could not be writted as obj1.dump and has to use indexed way to access.

继续执行:Continue execute:JsFunction cls = (JsFunction)js.eval("CLS");JsObject obj2 = JsObject.create(cls, 400);((JsFunction)obj2["dump"])();输出:Output:400第一行返回了前面创建的函数 CLS,第二行使用 JsObject 上的静态方法 create 创建对象obj2,比较之前的代码,可见 JsObject.create等效于 javascript 代码中的 new。The first line returns the previous created CLS function, the second line uses the static method create of the JsObject to create object obj2. Compared with the previous code, the JsObject.create equals to the new in the javascript.

Javascript操纵 C# Javascript controls C#

首先定义一个 C#实验类。First, define a C# experimental class.

public class TestJs{

public TestJs() { Console.WriteLine("Construct TestJs"); acc = add; }public TestJs(int x) { Console.WriteLine("Construct TestJs with " + x); acc = add; }public int add(int a, int b) { return a + b; }public string mystr;public int Prop { get; set; }public int this[int x]{

get { return x + 1; }set { Console.WriteLine("Indexed Property set key = " + x + " value = " + value); }

}

250

Page 251: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

public delegate int ACC(int a, int b);public ACC acc;

}这里应该看到,需要通过 Javascript 访问的对象成员必须声明为 public。Seen from here that the member of object accessed by Javascript must be declared as public. 构造对象 Construct object

js.eval("var t0 = new<0>()", typeof(TestJs));js.eval("var t1 = new<0>(100)", typeof(TestJs));Console.WriteLine(js.eval("t0") is TestJs);输出:Output:Construct TestJsConstruct TestJs with 100True在这里,类 TestJs被传递进 Javascript虚拟机,在类上执行 new操作,也就创建了对象,通过参数个数的控制可以选择不同的构造函数进行调用。第三行把 Javascript 中创建的变量 t0 传回 C#进行验证,果然是 TestJs对象。Here the class TestJs is transferred into Javascript vm, executes the new operation on the class to create the object, and selects the different construct function to call through controlling the parameter number. The third line transferres the variable t0 created in the Javascript to the C# to verify and it is TestJs object.

instanceof测试 instanceof testjs.eval("print(t0 instanceof <0>)", typeof(object));js.eval("print(t0 instanceof <0>)", typeof(TestJs));输出:Output:truetrue在这里,t0 是通过 C#类 TestJs 构造的对象,所以 instanceof操作转发给 C#执行,两行代码都输出 true。Here the t0 is the object constructed through C#' TestJs class. So the instanceof operation is transferred to C# to execute. These two lines output true.

继续执行:Continue execute:js.eval("print('abc' instanceof <0>)", typeof(string));js.eval("print(<0> instanceof <1>)", 'a', typeof(string));js.eval("print(100 instanceof <0>)", typeof(int));js.eval("print(<0> instanceof <1>)", 100, typeof(int));js.eval("print(<0> instanceof <1>)", 100L, typeof(int));js.eval("print(<0> instanceof <1>)", 100L, typeof(double));js.eval("print(<0> instanceof <1>)", UInt32.MaxValue, typeof(uint));js.eval("print(<0> instanceof <1>)", UInt32.MaxValue, typeof(double));js.eval("print(<0> instanceof <1>)", (uint)10, typeof(uint));输出:Output:

251

Page 252: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

truetruetruetruefalsetruefalsetruefalse实际上 instanceof执行时,用第二个参数作为类型来测试第一个参数,所以第一条语句输出 true;第二条语句,字符型进入 javascript转换为串类型,所以测试为 true;第三,第四条语句输出 true显而易见;第五,第六条语句,javascript整数仅支持 int,所以 long 被转换 为 double ;第七,第八条 语 ,既然 uint 为 无符号 类 型 ,那么大 于0x7FFFFFFF 的 uint只能转换为 double,转换为 int将变为负数,完全损失了无符号的意义;第九条语句,尽管第一个参数类型为 uint,但是 javascript内部并不存在这个类型,所以输出 false,实际上,javascript 的数值只有 int,double 两种类型,用之外的 C#类型进行测试永远 false。Actually, when instanceof executes, the second parameter is used as the type to test the first parameter, so the first statement outputs true; for the second statment, the character type is converted to the string type in javascript, so the test is true; it is clear that the third and forth statments output true; for the fifth and sixth statments, the javascript integer only supports int, so long is converted to double; for the seventh and eighth statements, since unit is an unsigned type, the unit greater than 0x7FFFFFFF is converted to double because it will become negative when converted to int and completely lose the meaning of unsigned; for the ninth statement, even though the first paramenter is uint, there is no this type in the javascript and outputs false. Actually, there are only int and double types in the javascrip's values, and test with the other than C# type is always false.

方法调用 Call methodjs.eval("print (t0.add(1,2))");输出:Output:3如果是类的静态方法,同样可以调用。If it is the class's static method, it also could be called.

字段访问 Access fieldjs.eval("print(t0.mystr)");js.eval("t0.mystr='hello world'");Console.WriteLine(js.eval("t0.mystr"));Console.WriteLine(((TestJs)js.eval("t0")).mystr);输出 Output:nullhelloworld

252

Page 253: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

helloworld第一行代码,输出 mystr 中的内容,对象构造时没有初始化该字段,C#中为 null,对应了 Javascript 中的 null,第二行设置 mystr 的字段内容,所以第三第四行访问均正确返回了设置内容。The first line of the code, outputs the content of mystr. This filed does not be initiated when constructing object, and it is null in C# which corresponds to the null of Javascript. The second line sets the content of mystr field, so the third and forth line correctly return the set content when accessing.

普通属性访问 Access normal propertyjs.eval("print(t0.Prop)");js.eval("t0.Prop = t0.Prop + 10");Console.WriteLine(js.eval("t0.Prop"));输出:Output:010第一行代码输出 Prop 的默认初始化值 0,第二行相当于读取该属性,再设置该属性,属性上加上 10,所以第三行输出 10。The first line of the code outputs the default initiation value 0 of Prop. The second line equals to read this property, then set this property with 10 added to it. So the third line outputs 10.

索引化属性访问 Access indexed propertyjs.eval("print(t0[100])");js.eval("t0[200] = 300");输出:Output:101Indexed Property set key = 200 value = 300这里可以看到索引化属性的两个方法均被正确调用。Seen from here that the two methods of the indexed property are correctly called.

委托访问 Access delegationjs.eval("print(t0.acc(100,200))");输出:Output:300对照前面的 C#代码,这里的 acc 实际上是一个委托,指向了 add 方法,所以实际上执行的是 add。Compared with the previous C# code, the acc here is actual a delegation which points to add method. So actually the add is executed.

Javascript 访问 C#的具体细节参见章节《脚本语言访问 C#规范》Refer to the charpter <The specification that the script language assesses C#> for the details about Javascript accessing C#

253

Page 254: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

复杂交互 Complex interaction

首先定义委托 FNFirst, define the delegation FN.public delegate int FN(object f, int a);然后执行下面的代码Then, execute the following code.FN fn = (object f, int a) => (int)js.eval("<1><2?<1>:<0>(<0>,<1>-1)+<0>(<0>,<1>-2);", f, a);js.eval("print(<0>(<0>,<1>))", fn, 9);输出:Output:34事实上,这段程序在 C#和 Javascript 之间间接递归,计算了斐波那契数列的第 9 项。说

明了 C#和 Javascript 进行复杂交互的可能性。Actually, this program is indirectly recursive between C# and Javascript, calculating the ninth term of the Fibonacci sequence. It presents the possibility of the complex interaction between C# and Javascript.

类型映射 Type mapping

C#,Javascript 两种语言的类型系统没有兼容性可言,所以类型映射无法用一张明确的表格进行描述。分 3 种规则讨论。It is not compatible at all in the type of system between C# and Javascript two languages. So type mapping can not be described in a definite form. It is discussed in three rules.

规则 1:C#向 Javascript 传递(C#通过 eval向 Javascript 传递;Javascript 访问 C#时,构造返回对象,方法返回值,委托返回值,读取字段,读取属性)Rule #1: C# transfers to Javascript (C# transfers to Javascript via eval; when Javascript accesses C#, the return object be constructed, return value of the method, return value of the delegation, access fields, and access property) 这种情况下,C#类型信息可以明确获知。In this condition, the type information of C# could be clearly obtained. null,解释为 javascript 的 null null, is interpretted as the null of the Javascript DBNull.Value , 解 释 为 javascript 的 undefined DBNull.Value, is interpretted as the

undefined of the javascript bool 以及装箱类型 Boolean,解释为 javascript 的 boolean 类型 bool and the boxing type

Boolean, are interpretted as the boolean of the javascript byte C# 向 Javascript 传 递 , sbyte , short , ushort , int , 以 及 装 箱 类 型

Byte,SByte,Int16,UInt16,Int32 解释为 javascript 的 int 类型。When C# passes to the Javascript, byte, sbyte, short, ushort, int and boxing type Byte, SByte, Int16, UInt16 and Int32 are interpretted as the int of the Javascript.

float,double,decimal 以及装箱类型 Single,Double,Decimal,解释为 javascript 的double 类 型 。 float, double, decimal and boxing type Single, Double, Decimal are

254

Page 255: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

interpretted as the double of the Javascript. uint 以及装箱类型 UInt32,如果值小于 0x80000000 解释为 javascript 的 int 类型,否则

解释为 javascript 的 double 类型。uint and the boxing type UInt32, if the value is less than 0x80000000, they are interpretted as the int of the javascript, or as the double of the javascript.

char,string 以及装箱类型 Char,String 解释为 javascript 的 string。需要注意,字符串传递给 javascript 之后,已经变成 javascript字符串,不可能调用 C#的字符串方法进行访 问 。 如 果 执 行 js.eval("print(<0>.Length)", "abc"); , 将 输 出 undefined , 改 为js.eval("print(<0>.length)", "abc");,才能获得正确结果 3。语法上比较怪异,小心。 char, string and the boxing type Char, String are interpretted as the string of the Javasript. It should be noted that the character string has changed to the javascript string after it is transferred to the javascript, and can not be accessed through calling the character string method of C#. If executing js.eval("print(<0>.Length)", "abc"); , the undefined will be outputted. It should be changed to js.eval("print(<0>.length)", "abc"); to get the correct result 3. The grammar is a little weird and should be careful.

JsObject,JsArray,JsFunction 之外类型的 C#对象,在 javascript 中创建一个特殊对象与之关联,通过 SetPrivate引用该 C#对象,防止在 C#虚拟机中被 GC,特别的,如果类型为委托类型,则获取委托对象的 Invoke 方法作为实际需要关联的对象,这样委托对象就能被正确调用了。The C# object except for JsObject, JsArray and JsFunction, create a special object in javascript to associate with it, reference to this C# object via SetPrivate to prevent to be GC in the C# virtual machine. Particularly, if the type is delegation type, the Invoke method of the delegation object is obtained as the object that is actually associated to it, so the delegation object can be correctly called.

JsObject,JsArray,JsFunction 是之前返回给 C#的类型,这时取回之前关联的 Javascript对象。JsObject, JsArray and JsFunction are the type returned to the C# previously, and get the previous associated Javascript object at this time.

规则 2:Javascript向 C#返回值(eval 的返回)Rule #2: Javascript return value to C# (the return of eval)这种情况下,只能根据 Javascript当前已知的信息返回,C#获取这样的返回值以后,必须严格检查类型然后使用,不小心就可能导致类型转换异常,System.InvalidCastException。In this condition, only return based on the currently known information of Javascript. After C# gets this return value, it should strictly check the type before using, or it might cause the type conversion exeception, System.InvalidCastException. null,返回 null null, returns null. undefined,返回 DBNull.Value undefined, returns DBNull.Value. Boolean,返回 Boolean Boolean, returns Boolean. string,返回 String string, returns String. int,返回 Int32 int, returns Int32. double,返回 Double double, returns Double. 通过 SetPrivate引用了 C#对象的特殊 javascript对象,返回引用的 C#对象。Reference to

the special javascript object of the C# object via SetPrivate, return the referenced C# object.

255

Page 256: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

其它 javascript对象,创建一个 JsObject对象引用它,防止在 javascript虚拟机中被GC , 该 JsObject 被返回。 The other javascript object, creates a JsObject object to reference to it, to prevent to be GC in the javascript virtual machine. This JsObject is returned.

除数组和函数外的 Javascript对象使用 JsObject返回,JsObject 实现了 IDictionary 接口,通过该接口可以访问对应 javascript对象的属性。JsObject 的按照 Javascript 规范访问,而不是 IDictionary 规范。例如,IDictionary 重复 Add抛异常,Javascript 重复 Add执行替换;JsObject 上 Remove属性,跟在属性上 Add 一个 DBNull.Value效果完全一样。The Javascript objects except for array and function use JsObject to return. The JsObject implements the IDictionary interface not IDictionary specification. For example, the IDictionary throws exception when repeating Add, and Javascript executes replacement when repeating Add; Remove property on JsObject has the same effect as Add a DBNull.Value on property.

Javascript 数组使用 JsArray返回,JsArray继承自 JsObject,实现了 IList 接口,通过该接口可以访问对应的 javascript 数组的成员。JsArray按照 Javascript 规范访问,而不是 IList规范,简单说来,不会生成 System.ArgumentOutOfRangeException异常,即不存在数组越界的问题。JsArray 实现的 IList 接口与继承自 JsObject对象中的 ICollection 接口,存在属性和方法同名的问题,使用了 new 规则 override,所以按照 JsArray 方式与按照JsObject 方式可能导致不同结果,可以回顾之前的例子。 The Javascript arrary use JsArray to return. The JsArray inherits from the JsObject, implements the IList interface, and access the corresponding javascript array's member through this interface. The JsArray accesses according to the Javascript specification, not IList specification. The JsAarry does not generate System.ArgumentOutOfRangeException exception, which means no array out of range issue. The IList interface implemented by JsArray and ICollection interface inherited from the JsObject object, have the problem that the property and method have the same name. Because the new rule is used to override, using the JsArray way and JsObject way might cause different result, and the previous sample could be reviewed.

Javascript函数使用,使用 JsFunction委托返回,该委托包装了一个继承自 JsObject 的对象操作该 Javascript函数。Javascript function uses JsFunction delegation to return. This delegation encapsulates an object inherited from JsObject as the Javascript function.

注意,(int)js.eval("3.14"); 这样的转换必然导致 System.InvalidCastException,原因在于返回的 javascript值是 double 类型,返回类型为 System.Double,这样的装箱类型无法像原生类型 double 一样直接转换为 int,正确的写法应该是(int)(double)js.eval("3.14");一旦出现类型转换异常,可以在返回对象上调用 getType()方法检查实际类型,判断是否符合需求, 进而实 现 正 确 的转换。 It should be noted that the conversion (int)js.eval("3.14"); certainly causes System.InvalidCastException. The reason is that the returned javascript value is double, and the returned type is System.Double. This kind of boxing type can not be directly converted to int as the primitive double type. The correct usage is (int)(double)js.eval("3.14");. Once there is type conversion exception, it could call the getType() method on the returned object to check the real type and determine whether it matches the requirements in order to correctly convert.

Javascript 规范不断更新中,为对象属性规定了很多特定访问控制属性,比如设置为readonly 的 属 性 , 修 改 不 可 能 成 功 。例如 ,执行 代 码 , js.eval("print=100;

256

Page 257: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

print('readonly')");将输出 readonly。这种情况需要小心,不过一般来说,不是系统内建 的 一 些 对象,方法, 类 型 , 不 会存在 这 类 问题。 The Javascript specification is continually updated. A number of specific access control attributes are specified for the object properties, such as the property set as readonly can not be correctly modified. For example, executing code js.eval("print=100; print('readonly')"); will output readonly. This kind of case should be careful. However, in general, not the objects, methods and types built in system have no this issue.

规则 3:Javacript向 C#传递(构造参数,方法调用参数,委托调用参数,字段设置,属性设置)Rule #3: Javascipt passes to C# (construct parameters, method calling parameters, delegation calling parameters, fields setting, properties setting)首先按照规则 2,转换为 C#对象,然后根据期待的参数类型在 C#中进行转换,细节参见章节《脚本语言访问 C#规范》First convert to C# object according to rule #2, then convert in C# accroding to the expected parameter type. Refer to the charpter <The specification that the script language accesses C#> for the details.

异常规范 Exception specification

C#与 Javascript均支持异常框架,两种语言都可以进行异常捕捉。 C# and Javascipt support the exception framework. These two languages could catch the

exception. C#调用 Javascript,Javascript 中抛出了异常,没有捕捉,异常抛出到 C#中。 When C# calles Javascript, if the Javascript throws exception and does not catch, the

exception is thrown to the C#. Javascript调用 C#,C#中抛出了异常,没有捕捉,异常抛出到 Javascript 中。 When Javascript calles C#, if the C# throws exception and does not catch, the exception is

thrown to the Javascript. C#与 Javascript 可以互相嵌套,没有捕捉,异常可以抛出到嵌套的最外层。 C# and Javascript could be nested each other. Without catching, the exception could be

thrown to the outmost nested layer. 异常每进入一次 C#,则用 limax.script.Js.ScriptException 包装一次。 Every time the exception enters C#, the limax.script.Js.ScriptException is used to

encapsulated. ScriptException将 Javascript异常串作为异常 message,C#异常作为内部异常,进行包装。

ScriptException treates the Javascript exception string as the exception message, and the C# exception as the internal exception to encapsulate.

Javascript异常串由文件位置信息,异常 message和 StackTrace共同拼接而成。对于Javascript而言抛出的 Error 才能包含位置信息,和 StackTrace。

The Javascript exception string is concated with the file location information, exception

257

Page 258: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

message and StackTrace. For Javascript, the thrown Error includes the location information and StackTrace.

Javascript 中,捕获的异常对象上 toString,可以获得完整信息,包括来自 C#的异常信息。

In Javascript, calling toString on catched exception object, the complete information could be obtained, including the exception information from C#.

线程安全 Thread safety

SpiderMonkey库不允许虚拟机跨线程运行。javascript操作对象上执行的任何操作,包括 eval , 以及 eval 返回的 JsObject , JsArray , JsFunction 上 的操作, 必 须 在 创 建javascript 操 作 对 象 的 线 程 中 执 行 , 否 则 抛 出 异 常limax.script.Js.ThreadContextException。

The SpiderMonkey library does not allow the virtual machine to run accross threads. Any operation executing on the javascript operating object, including eval, and the operation on the JsObject, JsArray and JsFunction returned by eval, must be executed in the thread creating javascript operating object, or throw the exception limax.script.Js.ThreadContextException.

注意事项 Matters need attention

1. C# 中 的泛型对象不 可 往 javascript 虚拟机中 传递,否则将抛出异常。原因在 于 ,System::Runtime::InteropServices::Marshal::GetNativeVariantForObject ,System::Runtime::InteropServices::Marshal::GetObjectForNativeVariant 这两个关键方法的泛型版本从.NET4.5.1 之后才开始提供,为了兼容较老的.NET版本,该项目使用.NET4.0 开发(.NET3.5 应该也可以使用)。The generic object of C# can not be transferred to Javascript virtual mathine, or throws exception. The reason is that System::Runtime::InteropServices::Marshal::GetNativeVariantForObject and System::Runtime::InteropServices::Marshal::GetObjectForNativeVariant these two key methods' generic version is provided since .NET4.5.1. For compatibility with older .NET versions, this project uses .NET4.0 to develop (.NET3.5 also could be used).2. 可 以 创 建 多 个 Javascript 操 作 对 象 , 从 一 个 Javascript 操 作 对 象 获 取 的JsObject,JsArray,JsFunction 不能够传入另一个 Javascript操作对象,这个显而易见;其它的 C#对象传入多个 Javascript操作对象,没有限制。多个 Javascript操作对象,如果确有数据交换需求,可以通过 JSON 实现,Javascript 本身即提供了完整支持,限制是对象中不能存在环。Could create multiple Javascript operating objects. The JsObject, JsArray and JsFunction obtained from one Javascript object can not be transferred to another Javascript operating object, which is obvious. There is no limitation for the other C# object to transfer multiple Javascript operating objects. If there is data exchange requirment for the multiple Javascript operating objects, it

258

Page 259: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

could be realized through JSON and the Javascript itself provides the complete support with the limitation that there is no ring in the objects.

脚本语言访问 C#规范 The specification that the scripting

language accesses C#

Limax提供 limax.util.ReflectionCache 类,为脚本语言访问 C#对象提供尽量完整的支持。当前由 CLR/Lua,CLR/Javascript 使用。The Limax provides limax.util.ReflectionCache class to give the possiblely complete support for script language to access C# object. Currently it is used by CLR/Lua and CLR/Javascript.

编程接口 Programming interface

构造函数:Constructor: ReflectionCache(ToStringCast toString)

toString, 定义为委托 delegate object ToStringCast(object obj);指令脚本系统执行相应的ToString操作,某些脚本系统对象,可能只是具体实现的包装,该方法提供了解包装再ToString 的机会。toString, is defined as the delegation delegate object ToStringCast(object obj); to instruct the script system to execute corresponding ToString operation. Some script system objects might be the packing with the specific implementation. So this method provides the chance to unpack and then ToString.

成员函数:Method: object GetValue(object obj, object name)

obj,被访问的 C#对象 obj, accessed C# object.name,被访问的 C#对象的成员 name, accessed C# object's member该方法返回了 C#对象成员的值,成员可以是字段,属性,方法。如果返回的是方法,使用 Invokable 接口包装,便于下一步被脚本语言调用。name 的类型为 object,而不是string,这是因为脚本语言访问对象一般两种形式 obj.member,obj[member],第一种形式,member 必然是 string 类型,第二种形式则不然,第二种形式与 C#的索引化属性访问方式类似,所以可以被利用来支持单参数索引属性的访问,这时 name 的含义不再是字段,属性,或者方法,而是索引参数。This method returns the value of the C# object member, and the member could be field, property and method. If the method is returned, the Invokable interface is used to encapsulate so that it is convenient to called by the scripting language in the next step. The type of name is object not string. The reason is that the scripting language has two ways to access the object, obj.member and obj[member]. In the first way, the member must be string type, but the second way is not. The second way is similar with the indexed property access

259

Page 260: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

way of C#, so it could be used to support the access to the single parameter indexed property. At this time, the definition of the name is not field, property, or method, but indexed parameter.

void SetValue(object obj, object name, object value)obj,被访问的 C#对象 obj, accessed C# objectname,被访问的 C#对象的成员 name, accessed C# object's membervalue,期望给成员设置的值 value, the value expected to set to the member该方法为 C#对象成员设置一个值,成员可以是字段,属性,如果 name指定了方法成员,SetValue 的执行没有任何效果。类似 GetValue,该方法同样被索引化属性利用。This method sets an value for the C# object member. The member could be field and property. If the name specifies a method member, the execution of SetValue has no any effect. Similar with GetValue, this method is also used by indexed property.

object Construct(object obj, object[] parameters)obj,类型对象 obj, type objectparameters,构造函数参数 parameters, construct funtion parameter该方法构造并返回类型对象 obj 的一个实例,obj 必须是 Type派生类,否则调用抛出System.InvalidCastException。This method constructs and returns an instance of the type object obj. The obj must be the derived class of the Type, or throws System.InvalidCastException when calling.

接口成员:Interface: interface Invokable

该接口用于包装对象方法,便于被脚本语言调用,包含两个成员:This interface is used to encapsulate the object method so that it is convenient to be called by scripting language. It includes two members:object Invoke(object[] parameters);使用参数 parameters调用包装的对象方法Use parameter parameters to call the encapsulated object method.object GetTarget();见正确处理委托一节。Refer the section "Correctly handle the delegation".

System.DBNull.Value:不存在的值在 C#中用 DBNull.Value 表示。脚本语言在不存在的值的理解上存在差异,

比如 Javascript 用 undefined 表示值不存在;而 Lua 中存在二义性,有时候不存在就是完全没有,占位都不行;有时候又认定为 nil,可以在 Lua控制台程序中做一个实验说明:The non-existed value is represented with DBNull.Value in C#. There is difference in the understanding about the non-existed value for script languages, for example, the Javascript uses undefined to indicate that the value does not exist. There is ambiguity in Lua, sometimes non-exist means nothing at all, and placeholder is also not acceptable; sometimes it means nil, and an experimental description could be used in the Lua console program:

定义函数:function undefined() endDefine function: function undefined() end执行:print(undefined())

Execute: print(undefined())260

Page 261: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

输出是空白,从语句的描述来看,print 有一个参数,那就是 undefined()函数的返回值实际上,返回值根本没有,所以 print执行的时候发现参数数量为 0,输出空白。要证明这个结论,可以用 CLR/Lua做一个实验:The output is blank. From the description of the statement, print has one parameter, which is the return value of the undefined() function. Actually, there is no return value at all, so the print finds that the parameter number is 0 when executing and the output is blank. To prove this conclusion, use CLR/Lua to do an experiment:

Action a = () => Console.WriteLine("empty");lua.eval("function undefined() end\n<0>(undefined())", a);输出 empty output empty回到 Lua控制台程序,继续执行:

Back to Lua console program and continue execute:value = undefined()print(value)输出竟然是 nil !!!

The output is nil . 可以解释为,赋值语句必需执行,但是没有值,只好用 nil 代替。对于这种解释,又必

需提出一个问题,为什么不可以解释成赋值根本没有执行,毕竟 value 还没有初始化过?所以还得继续实验:This can be interpreted as that the assignment statement must be executed but no value, so nil is used to replace. For this interpretation, it is necessary to raise a question: why not explain that the assignment statement is not executed. After all, the value has not been initiated. So continue the experiment:再执行:Then execute:

value = 100print(value)

输出 100 Output 100再执行:Then execute:

value = undefined()print(value)

输出 nil Output nil上面的实验见证了 Lua 不支持不存在的值导致的二义性。CLR/Lua 在实现上保证和 Lua

的特性完全兼容,这些问题同样存在,必须小心。The above experiment proves that the ambiguity is caused becuase Lua does not support the non-existed value. The implementation of CLR/Lua is fully compatible with Lua's feature, and these issues also exist and must be careful.

访问范围:Access scope: 需要提供给脚本语言访问的对象字段,属性,方法,限定为 public The object's fields, properties and methods which need to be provided to scripting language

to access are restricted as public. 对象本身的字段,属性,方法可以被访问,不包括继承的基类成员。 The fields, properties and methods of the object itself can be accessed, but does not include

261

Page 262: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

the inherited base class's members. 以上两点限制很容易突破,不过,从编程规范化的角度看,不建议。对于第一条,C#

本身代码都被限制访问的成员,通过脚本语言反而能访问完全不存在合理性;对于第二条,尽量多使用接口,少使用类继承,是更好的编程方式,没有理由往不好的习惯上引导。

The above two restrictions are easy to break, however, it is not recommended from programming specification. For the first entry, it is not reasonable to access the members which C# code itself is restricted to access through the scripting language. For the second entry, as much as possible to use interface and less use class inheritance is a better programming way. There is no reason to guide the bad hibits.

构造对象 Construct object

调用 Construct 方法,即可构造对象,具体执行流程与方法调用相同,之后介绍。Call Construct method to construct the object. The detailed execution process is same as the method calling and will be introduced later.

字段访问 Field access

读字段 Read fields

GetValue 时,name参数为 string 类型,值与当前对象的字段名匹配,执行字段读取。When GetValue, if the parameter of name is string type and the value matches the current object's filed name, execute the field reading.

写字段 Write fields

SetValue 时,name参数为 string 类型,值与当前对象的字段名匹配,用 value写字段,脚本语言提供的 value 类型可能与字段实际类型不匹配,所以必须先执行类型转换,类型转换有失败抛出异常的可能。When SetValue, if the parameter of name is string type and the value matches the current object's field name, use value to write field. The type of value provided by the scripting language might not match the actual type of the field, so the type conversion must be executed firstly. There is possbile to throw exception when type conversion fails.

属性访问 Property access

读属性 Read properties

GetValue 时,name参数为 string 类型,值与当前对象的属性名匹配,执行属性读取,如果属性不可 get,返回 DBNull.Value。When GetValue, if the parameter of name is string type and the value matches the current

262

Page 263: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

object's property name, execute the property reading. If the property can’t be gotten, return DBNull.Value.

写属性 Write properties

SetValue 时,name参数为 string 类型,值与当前对象的属性名匹配,用 value写属性,脚本语言提供的 value 类型可能与属性实际类型不匹配,所以必须先执行类型转换,类型转换有失败抛出异常的可能。如果属性不可 set,操作被忽略。When SetValue, if the parameter of name is string type and the value matches the current object's property name, use value to write property. The type of value provided by the scripting language might not match the actual type of the property, so the type conversion must be executed firstly. There is possible to throw exception when type conversion fails. If property can’t be set, the operation is ignored.

方法访问 Method access

读方法 Read methods

GetValue 时 , name 参数 为 string 类 型 ,值与当前对象的 方法名匹配 , 使 用 一 个 实 现Invokable 接口的内部对象包装方法集合(方法重载允许同名方法的存在)并返回。When GetValue, if the parameter of name is string type and the value matches the current object's method name, use an internal object which implements the Invokable interface to encapsulate the method set (method override allows the existance of the methods with the same name) and return.

写方法 Write methods

SetValue 时,name参数为 string 类型,值与当前对象的方法名匹配,该操作被忽略。When SetValue, if the parameter of name is string type and the value matches the current object's method name, this operation is ignored.

索引化属性访问 Indexed property access

脚本语言的语法限制了只能访问单参数索引化属性。The syntax of the scripting language limits the access to the single-parameter indexed property only.

读索引化属性 Read indexed properties

GetValue 时,如果 name参数为 string 类型,并且 name 的值不匹配任何可访问的字段名,属性名,方法名,则把 name假设为索引参数,执行后续访问,这样的访问存在二义性 。name参数为其它类型,则直接认定为索引参数,遍历所有单参数可读索引属性,匹配类型进行访问,没有可匹配的情况下返回 DBNull.Value。访问方式与方法调用相同,之后介绍,可能抛出异常。

263

Page 264: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

When GetValue, if the parameter of name is string type and the value of name does not match any accessable field name, property name and method name, assume the name as the index parameter to execute the following access, and such access is ambiguous. If the parameter of name is the other type, directly recognize the name as the index parameter to traverse all single-parameter readable index properties. When matches the type, execute the access; without a match, return DBNull.Value. The access way is the same as the method call and will be introduced later. There might be exception thrown.

写索引化属性 Write indexed properties

SetValue 时,如果 name参数为 string 类型,并且 name 的值不匹配任何可访问字段名,属性名,方法名,则把 name假设为索引参数,执行后续访问,这样的访问存在二义性。name参数为其它类型,则直接认定为索引参数,遍历所有单参数可写索引属性,匹配类型进行访问,没有可匹配的情况下操作被忽略。访问方式与方法调用相同,之后介绍,可能抛出异常。When SetValue, if the parameter of name is string type and the value of name does not match any accessable field name, property name and method name, assume the name as the index parameter to execute the following access, and such access is ambiguous. If the paramter of name is the other type, directly recognize the name as the index parameter to traverse all single-parameter writable index properties. When matches the property, execute the access; without a match, ignore the operation. The access way is the same as the method call and will be introduced. There might be exception thrown.

事件访问 Event access

常见脚本语言没有相应的语法对应,所以不支持。The common scripting languages have no relevant syntax to correspond, so they are not supported.

方法调用流程 Method call flow

读方法获得的 Invokable 接口对象上执行 Invoke,构造对象,读索引属性,写索引属性,这4 种情况下执行方法调用流程。Execute Invoke on the Invokable interface object obtained through GetValue, construct an object, read indexed properties, write indexed properties, in these four cases, the method call flow is executed.

参数匹配 Parameter matching

遍历所有同名方法,首先根据形参实参列表长度进行匹配,然后逐参数匹配,获得适合的方法列表。参数匹配使用一个加权评价算法,得分最高的那些方法被选中,评价值为Int32.MinValue 的方法被直接抛弃,如果全部都是 Int32.MinValue,全部抛弃。方法评价值的计算:

264

Page 265: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Traverse all methods with the same name. First of all, comparing to the length of the formal parameter list and acutal parameter list to match, then parameter-by-parameter match, and get a suitable method list. Parameter matching uses a weighted evaluation algorithm, the methods with the highest score are selected, the methods with the evaluation value as Int32.MinValue are directly discarded, and if all methods are Int32.MinValue, they are all discarded. The calculation of the method evaluation value:1. 遍历形参列表 Traverse formal parameter list1.1. 使用用形参对应位置的实参与形参类型,计算参数评价值

Use the actual parameter with the corresponding position of the formal parameter and the type of the formal parameter to calculate the evaluation value.

1.1.1. 形参类型等于实参类型,表示完全匹配,返回评价值 1If the type of actual parameter equals to the type of formal parameter, it means full matching and returns the value 1.

1.1.2. 实参是形参类型的实例,或者实参为 null并且形参类型为非值类型,表示兼容的转换,返回评价值 0If the actual parameter is the instance of the type of the formal parameter, or the actual parameter is null and the type of the formal parameter is non-value type, it means the compatible conversion and returns value 0.

1.1.3. 如果形参类型为 bool 或者 string,返回评价值-1,这是为了兼容脚本语言特性,可以进行安全的转换。If the type of formal parameter is bool or string, return value -1, which is for compatibility with scripting language feature and can execute safe conversion.

1.1.4. 如果形参实参都是 IConvertible 类型,说明还有机会使用 System.Convert.ChangeType进行转换,数值类型都是 IConvertible,转换损失精度的情况下会抛出异常,这就是接下来参数绑定过程忽略失败转换的主要原因,这种情况下返回评价值-2。If both the formal parameter and actual parameter are IConvertible type, it means there is chance to use System.Convert.ChangeType to convert and numeric types are IConvertible. An exception is thrown when the conversion loses accuracy, which is the main reason why the following parameter binding process ignores the failed conversions, and this case returns value -2.

1.1.5. 其它情况不存在有效的转换,返回 Int32.MinValue。The other cases have no valid conversion and return Int32.MinValue.

1.2. 如果参数评价值为 Int32.MinValue,则方法的评价值直接返回 Int32.MinValue,否则累加到方法评价值中。If the parameter evaluation value is Int32.MinValue, the method evaluation value directly returns Int32.MinValue, otherwise it is added to the method evaluation value.

2. 如果所有实参都被匹配过了,返回方法评价值If all actual arguments are matched, return method evaluation value.

参数绑定 Parameter binding

遍历匹配后获得的方法列表,执行参数类型转换,忽略失败的转换,记录成功的转换,如果转换成功一次以上,抛出异常 System.Reflection.AmbiguousMatchException。遍历形参列

265

Page 266: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

表执行参数绑定,实际上就是按照形参类型对实参进行转换,获得新的参数列表对象。转换方法:Traversing the obtained method list after matching, executes the parameter type conversion, ignores the failed conversions and records the successful conversions. If there is more than one successful conversion, throw the exception System.Reflection.AmbiguousMatchException. Parameter binding, in fact is to execute the conversion for the actual argument according to the formal parameter type and obtain the new parameter list object. Conversion method: 1. 实参是形参类型的实例,或者实参为 null并且形参类型为非值类型,直接返回该实参,

实际上对应了评价值为 1,0 的两种情况。If the actual parameter is the instance of the formal parameter type, or the actual parameter is null and the formal parameter type is non-value type, directly return this argument, which actually corresponds to two cases that the evaluation values are 1 and 0.

2. 形参类型为 bool 的情况下(评价值-1),按照脚本语言的惯例,转换实参:In the case that the formal parameter type is bool (the evaluation value is -1), convert the actual parameter following the conventions of the scripting language:

2.1. 实参为 null 或者 DBNull.Value,转换为 falseIf the actual parameter is null or DBNull.Value, convert to false.

2.2. 实参为 char 类型,等于'\0'为 false,否则 trueIf the actual parameter is char, convert to false when it equals to '\0', otherwise convert to true.

2.3. 实参为 string 类型,长度为 0 为 false,否则 trueIf the actual parameter is string, convert to false when the lenght is 0, otherwise convert to true.

2.4. 实参为各种数值类型,交给 System.Convert.ChangeType执行转换,这些转换不会抛出异常,可以获得符合脚本语言惯例的结果(当然了,这不是 Lua 的惯例,对 Lua而言,if 0 then print('true') else print('false') end 输出 true,这一点跟 Lua 不兼容)。If the argument is numeric type, the System.Convert.ChangeType executes the conversion, and this conversion does not throw exception and obtain the result which is in accordance with the scripting language conventions (Of cause, it is not the Lua's convention. For Lua, if 0 then print('true') else print('false') end outputs true, which is not compatible with Lua).

2.5. 其它情况一律转换为 trueThe other cases are converted to true.

3. 形参类型为 string 的情况下(评价值-1),使用构造 ReflectionCache 时提供的 toString委托,将实参提交给脚本系统进行转换。When the formal parameter type is string (the evaluation value is -1), use the provided toString delegation when constructing ReflectionCache to submit the actual argument to the script system to convert.

4. 其它情况(评价值-2)交给 System.Convert.ChangeType执行转换,可能抛出异常,指示形参列表对应的方法不能被调用,应该忽略。For the other cases (the evaluation value is -2), the System.Convert.ChangeType executes the conversion and might throw the exception, which means the corresponding method of the parameter list can not be called and should be ignored.

266

Page 267: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

方法调用 Method call

如果存在成功的转换,使用转换结果执行对应的方法;如果没有,对于 Invokable对象上的Invoke,抛出异常指示没有合适的方法可用,构造对象的情况下,抛出异常指示没有合适构造函数可用,读索引属性的情况下返回 DBNull.Value,写索引属性忽略该操作。Invokable对象上的 Invoke 的方法如果返回类型为 void,返回 DBNull.Value;If there is successful conversion, use the conversion result to execute the corresponding method; if not, for the Invoke on the Invokable object, throw exception which means no suitable method to use; for the construct object, throw the exception which means no suitable construct function to use; return DBNull.Value when reading indexed property and ignore this operation when writing indexed property. If the return type of the Invoke method on the Invokable object is void, return DBNull.Value.

注意事项 Matters need attention

1. 不支持包含可选参数的方法,也就是说调用可选参数方法时,可选的那些参数不能省略。可选参数方法并不常用,支持可选参数的版本性能会将大大降低,得不偿失。Not support the methods with optional parameters, which means when calling the methods with optional parameters, these optional parameters can not be omitted. The methods with optional parameters are not commonly used, so the performance of the version which supports the optional parameters will sharply reduced and is more harm than good.

2. 参数匹配实际上是为了减少参数绑定失败次数,提高性能,而作的预处理。The parameter matching is the pretreatment actually to reduce the number of parameter binding failure and improve the performance.

3. 实现上,首先按照参数长度进行匹配,如果选择出唯一的方法,则省略参数匹配过程,可以提高性能,所以减少重载的使用才是最优的方法。In implementation, first perform the matching according to the parameter list length. If the unique method is choosen, omit the parameter matching process to improve the performance. So reducing the usage of the overriding is the best way.

4. 参数评价算法中,为了减少参数绑定失败概率,向 string转换的评价值高于数值间转换,某些情况下可能违反脚本使用习惯。 例如,定义方法 int add(int a, int b);与 string add(string a, string b);分别执行数值加法与串连接。脚本语言调用 add(1,2),如果是javascript 来调用返回 3,如果是 Lua 来调用,返回字符串 12,原因在于,Javascript 的整数是 int,可以严格匹配,评价值为 2和-2;Lua 的整数是 long,评价值为-4 与-2,这类情况需要小心。In the parameter evaluation algorithm, in order to to reduce the probability of parameter binding failure, the evaluation value converted to string is higher than an inter-numeric conversion, some cases might break the script usage habit. For example, define the methods int add(int a, int b); and string add(string a, string b); , to respectively perform the numeric addition and string concatenation. The scripting language calls add(1,2), returns 3 if javascript calls, and returns 12 if Lua calls. The reason is that the Javascript's integer is int which could strictly match and the evaluation values are 2 and -2. The Lua's integer is long and the evaluation values are -4 and -2, which should be careful.

5. 参数绑定是个完全动态的过程,比如脚本语言中使用一个变量作为参数调用方法267

Page 268: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

func(short x);如果变量的值在 short 范围内,该方法被调用,否则报告找不到合适的方法。The parameter binding is a fully dynamic process. For example, the scripting language uses a variable as the actual parameter to call the method func(short x);. If the variable value is within the short range, this method is called, otherwise report that could not find a suitable method.

正确处理委托 Properly handle delegation

C#对象进入脚本环境,需要检测该对象是否为委托对象,如果是,则需要进一步在该对象上 GetValue获取 Invoke 方法的 Invokable 包装作为实际被脚本引用的对象,在脚本环境看来,这样的对象与读取对象方法获得的 Invokable 包装没有任何区别。Invokable 包装重新回到 C#就必须进一步判断被包装的对象是否是委托对象,如果是,返回委托对象,否则返回包装本身,这样才能做到进出一致,这也就是 Invokable.GetTarget()方法需要完成的功能。When C# object enters the script environment, it needs to check whether the object is delegation object. If it is, further GetValue on this object to obtain the Invokable encapsulation of the Invoke method as the object which is really referenced by script. From the script environment, this object has no any different with the Invokable encapsulaton obtained when reading the object method. If the Invokable encapsulation returns to C#, it must be further judaged whether the encapsulated object is the delegation object. If it is, return the delegation object, otherwise return the encapsulation itself. This keeps the consistence and is the function that the Invokable.GetTarget() method needs to finish.委托对象必须被包装,原因在于,.Net 实现上,提供了一个 Delegate 基类,delegate

关键字定义的信息,被用来生成一个类,该类继承自 Delegate,生成的 Invoke 方法的参数列表正好就是 delegate 定义的参数列表,有了它才能进行正确的类型转换,然后调用。如果不包装,直接调用 Delegate.DynamicInvoke则缺少类型转换过程,不能保证正确性。The delegation object must be encapsulated. The reason is that the implementation of the .Net provides a Delegate base class, and the information defined by the delegate keyword is used to generate a class and this class inherits from the Delegate. The formal parameter list of the generated Invoke method is the formal parameter list defined by the delegate. With this list, the correct type conversion can be carried and called. If no encapsulation and directly call Delegate.DynamicInvoke, it lacks of the type conversion process and can not guarentee the correctness.

Node.js兼容框架 Node.js compatible framework

Limax 集成原生 java 版本 node.js 兼容框架,使用 JDK8 提供的 Nashorn引擎,依据node.js手册的描述实现,并做了适当扩展,以利于集成到 Limax 框架中,为连接 Limax 外的系统提供便利。The Limax integrates the native Java version's node.js compatible framework, uses the Nashorn

268

Page 269: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

engine provided by the JDK8, is implementted according to the description of the node.js manual and makes the appropriate expansion to integrate to the Limax framework and provide the convenience to connect the system outside of Limax.

Nashorn 支持 ES5.1标准,对于某些 ES6标准的 js 文件,应该首先使用 6to5工具转换,现有 Node.js手册示例都是 ES6 代码,应该转换以后实验。The Nashorn supports the ES5.1 standard. For some js files matching ES6 standard, it should use the 6to5 tool to convert. The existing manual examples are all ES6 code and should be experimentted after conversion.

以下的描述,符合 node.js 6 文档的顺序,便于对比。The below description conforms to the order of the node.js 6 documents, which is convenient to compare.

核心模块 Core module

除了 Buffer 模块比较特殊,放在 包 limax.node.js 内, 其它核心模块均放置 在 包limax.node.js.modules内,一个 .java 文件一个 .js 文件配对,按照 java习惯,首字母大写,require 时全小写。Except that the Buffer module is quite special and put in the limax.node.js package, the other code modules are all put in the limax.node.js.modules package, and make a pair based on one .java file and one .js file, and the first letter is capital and all letters are lowcase when requiring according to the Java custom.

Assert(断言)Assert

功能与标准 node.js 一致。AssertionError 类做了一些信息伪造。实际上,js 中,Error 类无法被有效继承,特别是在 stack 的问题上。The function is consistent with the standard node.js. The AssertionError class makes some information counterfeit. Actually, in js, the Error class can not be effectively inherited, especially on stack issue.

Buffer

不同于其它核心模块的对象,Buffer 不是 js对象,直接导出了 limax.node.js.Buffer对象。原因在于,node.js 文档描述了[]操作,但是 ES5.1标准不支持索引化属性的访问,所以使用了 Nashorn 的一个怪异特性,实现了 List 接口的对象可以用 []方式问,带来的问题是BoundCheck 在 Nashorn 中完成,数组越界会直接抛出异常,这一行为与 js 不一致。最好避免使用[]访问 Buffer。Different from the object of the other core modules, the Buffer is not js object, and directly exports the limax.node.js.Buffer object. The reason is that the node.js document describes the [] operation, however the ES5.1 standard does not support the access to the indexed properties. So a strange feature of Nashorn is used, through which the object that implements the List interface could be used in [] mode. The problem is that the BoundCheck is finished in Nashorn and the exception is directly thrown when the array is out of range, which is inconsistent with js. It is best to avoid using [] to access Buffer.

269

Page 270: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Java插件 Java plugin

不同于 node.js 用 C++实现,使用 C++创建插件。Limax 的 Node插件用 Java 创建,一个插件就是一个 jar,一个插件 jar内部只能有一个 js 文件,同时保证存在一个相应名字的java 类,更多的细节可以参见 limax.node.js.modules 包。Unlike the node.js is implemnets in C++ and uses C++ to create the plugin, the Node plugin of Limax is created in Java. One plugin is a jar, and a plugin can have only one js file inside, while ensuring that there is a corresponding name of the java class. Please refer the limax.node.js.modules package for the details.

例如:For example:

MyTest.javapackage testnodejsmodule;

import limax.node.js.EventLoop;import limax.node.js.Module;

public final class MyTest implements Module {public MyTest(EventLoop eventLoop) {}

public Number add(Number x, Number y) {return x.doubleValue() + y.doubleValue();

}}

MyTest.jsexports.add = function(x, y) {

return java.add(x, y);}

这样两个程序用 eclipse export 成一个 jar,就可以作为插件被引用。将插件命名为 test.jar 放置在主模块目录下,执行主模块console.log(require('./test.jar').add(1,2))即可输出结果 3。So that the two programs with eclipse export into a jar which can be referenced as a plug-in.Name the plugin as test.jar and put it in the main module directory to execute the main module console.log(require('./test.jar').add(1,2)) and output the result 3.

具体细节详见模块一节。Please refer the module section for the details.

270

Page 271: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Child Processes(子进程)Child Processes

使用 java.lang.ProcessBuilder操控子进程,支持进程管道操作。Use java.lang.ProcessBuilder to operate the child processes, and support the process pipe operation.1. 不支持 child_process.fork 方法,Limax 使用线程模型,不需要这个方法,详见模块一节。

Not support child_process.fork method, the Limax uses the thread model and does not need this method. Please refer the module section for details.

2. 类 ChildProcess仅支持 exit 事件(其它事件都与 IPC相关,不需要),在进程结束后触发 。 支 持 child.kill 方法, java.lang.Process 向进 程 发 送 的信号仅支 持 SIGTERM 与SIGKILL , 非 SIGKILL 的 情 形 , 一 律 默 认 SIGTERM 。 支 持child.stdin,child.stdout,child.stderr,child.stdio。The class ChildProcess just supports the exit event (the other events are related to the IPC and do not need) which is triggered after the process ends. Support the child.kill method, and the signal sent to the process by java.lang.Process just supports the SIGTERM and SIGKILL. The non-SIGKILL case is always SIGTERM as default. Support child.stdin, child.stdout, child.stderr and child.stdio.

3. 所有方法的 options参数中,支持 options.cwd,options.env(Node.js手册中声明 spawn方法默认 process.env,exec 方法默认 null,实际测试 exec默认值也是 process.env,所以 默 认process.env),options.shell,options.detached,options.input,options.timeout,options.encoding,options.maxBuffer,options.killSignal(SIGTERM|SIGKILL),options.stdio(只支持 3 个标准 IO对象)。All methods' options parameter support options.cwd, options.env (the Node.js manual declares that spawn method default process.env, exec method default null, and the actual test is that exec default value is proces.env, so default is process.env), options.shell, options.detached, options.input, optios.timeout, options.encoding, options.maxBuffer, options.killSignal (SIGTERM|SIGKILL), options.stdio (only suppot three standard IO objects).

4. child_process.spawnSync 方法返回的对象,没有 pid 成员,因为 java.lang.Process 不提供。The object returned by child_process.spawnSync method, has no pid member because the java.lang.Process does not provide.

Cluster(集群)Cluster

Node.js 使用进程模型,一个进程对应一个 Javascript虚拟机,为了使用 cpu 的多核特性,所以提出了集群概念, fork 出更多的虚拟机,使用 IPC 进行协调。 Limax 中,通过扩充require,允许 require 传递参数,允许 require 创建新的虚拟机实例,完全可以达成同样的目的,而且更节约资源。具体细节详见模块一节。The Node.js uses process module, and one process corresponds to a Javascript virtual machine. In order to use the cpu's multiple core feature, the cluster concept is posed, to fork more virtual machines, using IPC to coordinate. The Limax, through extending require, which allows require to transfer the parameter, and allows require to create the new virtual machine instance, could

271

Page 272: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

totally achieve the same purpose and save more resources.

Please refer the module section for the details.

CLI(命令行)CLI

可以在 shell 中执行,这种情况下 java –jar limax.jar node <module> [args]可以作为 Limax 服务加载,在服务 xml 配置中添加:Execute in the shell. In this condition, the java –jar limax.jar node <module> [args] is loaded as the Limax service. Add the below statement in the xml configuration.<NodeService module=module_path>

<parameter value="p0" />按参数数量,顺序排列

sequencely order according to the parameters </NodeService>一条 NodeService element启动一个 javascript虚拟机线程执行 module。One NodeService element launches a javascript virtual thread to execute module.

Console(控制台)Console

1. console.dir 作为 console.log 的别名,不支持显示颜色之类的选项。The console.dir as the alias for console.log, does not support the options such as the display colors.

2. 内部使用 java.lang.String.format 方法格式化输出字符串,格式参数与 node.js 使用的sprintf格式参数稍有差异,应该注意。如果格式化失败,按照普通字符串对待。Internally use java.lang.String.format method to format the output string. The format parameter is a little different with the sprintf format parameter used by node.js, and should be noted. If the format fails, it should be treated according to the common string.

3. console.timeEnd,用 WeakHashMap 管理,不删除定时器也不会有泄漏。Console.timeEnd, uses WeakHashMap to manager, and does not leak without deleting the timer.

Crypto(加密)Crypto

1. node.js 使用 openssl 实现, limax 中主要使用 jce 实现,支持的算法集合稍有差异。getCiphers(),getCurves(),getHashes(),三个方法可以用来获取具体支持的算法,进行比较。The node.js uses openssl to implement. The Limax mainly uses jce to implement. So the set of supported algorithms is a little different. The getCiphers(), getCurves() and getHashes() three methods could be used to obtain the specific supported algorithm to compare.

2. Cipher 类没有提供 AEAD 的支持,因为 AEAD 的使用流程尚未确定。The Cipher class does not provide AEAD, because the use process of AEAD has not been determined.

3. limax.util.OpenSSLCompat提供了需要的 openssl 兼容性支持。jce只支持自己的 keystore与 PKCS11,PKCS12,这里通过使用 limax.codec.asn1 包,提供 PEM格式的支持,这两

272

Page 273: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

处代码,其它地方可能有用。The limax.util.OpenSSLCompat provides the needed openssl compitable support. The jce only supports its own keystore, PKCS11 and PKCS12. Here through using limax.codec.asn1 package, provide the support to PEM format. These two codes could be used in other case.

Debugger(调试器)Debugger

Nashorn 根本不会生成 js字节码,而是直接生成 java字节码,深度调试应该使用 java 的调试器。The Nashorn does not generate the js bytecode, but directly generates the java bytecode. So deeply debugging should use the java's debugger.

DNS(域名服务器)DNS

node.js 使用 libresolve 实现,limax 使用 jndi 实现,所以不提供 libresolve 规格的错误码。特别注意,lookup 方法的返回结果,node.js 文档描述与实现不一致,文档描述是错误的。The node.js uses the libresolve to implement. The Limax uses the jndi to implement. So it does not provide the error code of the libresolve specifications. Please pay attention to the return result of lookup method, which the document description of the node.js is inconsistent with the implementation and the document descripton is error.

Domain(域)Domain

node.js 文档申明该模块是废弃的,所以不提供。The node.js document declares that this module is obsolete and therefor not available.

Error(错误)Error

错误类由 Narshorn提供,不需要提供。The error class is provied by Narshorn and no need to provide.需要注意的是,Limax环境下,所有通过 callback报告的错误,可能是 js 的 Error,也可能是java 的 Exception,并没有统一成 Error,因为 Exception 可以提供更丰富的栈信息,例如:It should be noted that in the Limax environment, all the errors reported through callbak, might be the js Error, or might be the java Exception. They are not unified into Error because that the Exception could provide more plentiful stack information. For example:

function(err) {if (err) {

if (err instanceof Error) {console.log(err.message, err.stack);

} else {err.printStackTrace();

}return;

}

273

Page 274: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

}这样的处理,可以输出最完整的错误信息。This processing could output more complete error message.

Events(事件)Events

提供了完全的 java 实现,js只是一个简单包装。setMaxListeners 方法实际上被忽略, 不限制 listener 数 量 , getMaxListeners 永远返回Integer.MAX_VALUE。node.js手册的提法是,限制 listener 数量有助于寻找内存泄露,java环境下无需考虑这个问题。Provide the complete java implementation, and js is just a simple package.The setMaxListeners method is actually ignored without limiting the number of the listener, and the getMaxListeners always returns Integer.MAX_VALUE. The instruction of the node.js manual is that limiting the number of the listener is helpful to find the memory leaks. It is no need to consider this issue in Java environment.

File System(文件系统)File System

使用 java.nio.file 包实现,与 node.js 实现有稍许差异。Use java.nio.file package to implement, which is a little different from the implementation of node.js.1. fs.Stats 差异,java 能够获取的信息与 struct stat 有差异,可以用如下代码 dump 具体信息进行比较。The difference of fs.Stats. The information obtained by java is different from the struct stat and could be compared with the datailed information dumped by the below code.fs.stat(file, console.log);

2. java 不提供硬连接能力,所以 fs.link,fs.linkSync 不提供。The Java does not provide the hard link capability, so fs.link and fs.linkSync are not provided.

3. fs.watch ,改为 通 过绝对路径调用 callback ,便于 使 用单一 listener 监听多 个 目 录options.recursive 无效,java 不提供支持,node.js手册对 options.encoding 的描述上下文矛盾。fs.watch, is changed to callback via absolute path, which is convenient to use the single listener to monitor multiple directories. The options.recursive is ignored and java does not provide. The description of options.encoding from the node.js manual is context contradiction.

4. fs.readFile,fs.readFileSync,fs.writeFile,fs.writeFileSync,fs.appendFile,fs.appendFileSync,这6 个方法的 options参数不支持对象,只作为 encoding串进行 Buffer.isEncoding测试,测试失败直接默认为 UTF8。否则逻辑上存在大量矛盾,比如执行 writeFile,提供flag=’r’,纯属自找麻烦。The options parameter of the six methods fs.readFile, fs.readFileSync, fs.writeFile, fs.writeFileSync, fs.appendFile, and fs.appendFileSync does not support object, and only performs the Buffer.isEncoding test as encoding string. The test failure is UTF8 as default directly. Otherwise there is a large number of logical contradictions, for example, when

274

Page 275: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

executing writeFile, provide flag='r', which is ridicuous.5. fs.mkdtemp,fs.mkdtempSync,这两个方法的 options参数看不出任何意义,忽略;

prefix 要求一个不带路径分隔符的串,生成用户临时目录下的文件名,避免创建临时文件出现权限问题。The options parameter of the two methods fs.mkdtemp and fs.mkdtempSync has no any meaning and is ignored. The prefix requires a string with no path delimiter, and generates the file name in the user's temporay directory to avoid the permission issue when creating the temporary files.

6. fs.constants 实际上是一 java对象,只有 R_OK,W_OK,X_OK,F_OK 这 4 个值。The fs.constants is actually a java object, and only has R_OK, W_OK, X_OK, and F_OK four values.

7. java.nio.file并不支持文件描述符,所以 open返回的文件描述符实际上是内部伪造的用来索引 FileChannel 的整数。The java.nio.file does not support the file descriptors. So the file descriptor returned by the open is actually an internal forged integer which is used to index FileChannel.

Global(全局变量)Global

node.js 手册描 述 的 全局变量 全 部 支 持 , require 相关 成员做了扩充,增加了parameters,java,两个模块级全局变量,详见模块一节。The global variables described in the node.js manual are all supported, and the require related members are expanded and add parameters and java two module level global variable. Please refer the module section for the details.

HTTP

1. http.Agent 构造参数 options 中 options.keepAlive被忽略,Agent始终支持 HTTP/1.1 的keep-alive。options.maxSockets 没有明显意义被忽略。options.keepAliveMsecs被重新解释为连接 idle超时,超时以后连接被关闭,默认为 60000ms,多数 NAT环境 TCP IDLE默认 5 分钟,不会存在问题,node.js 文档中默认的 tcp-keepalive=1000ms 的配置,linux上通过设置 tcp选项 TCP_KEEPINTVAL 才能实现,不具有通用性。The options.keepAlive in the constructor parameter options of http.Agent is ignored. The Agent always supports keep-alive of HTTP/1.1. The options.maxSockets has no meaning and is ignored. The options.keepAliveMsec is reinterpreted as the connection idle timeout, and the connection is closed after timeout and the default is 60000ms. In multiple NAT environment, the TCP IDLE is 5 mins as default and has no problem. The default configuration tcp-keepalive=1000ms in the node.js document, is implemented through setting tcp option TCP_KEEPINTVAL in linux and not portable.

2. http.Request 中,http.setSocketKeepAlive第二个参数 initialDelay,被忽略,jdk 不支持这个参数,也不知道哪个协议栈能支持这样的行为。In http.Request, the second parameter initialDelay of http.setSocketKeepAlive is ignored. The jdk does not support this parameter and there is no idea which protocol stack can support this behavior.

275

Page 276: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

HTTPS

Node.js 的设计方式过于冗余,Limax版本直接在 HTTP 模块中实现(实际上不存在 https 这 个模块),只需要简单修订 2 个方法。(TLS 的相关内容详见 TLS/SSL 一节)The design of Node.js is too redundant, the Limax version is directly implemented in the HTTP module (actually, there is no https module), and only simply modifies two methods. (For more information about TLS, please reference the TLS/SSL section )1. http.createServer([requestListener])扩展为 http.createServer([requestListener], [tls]),其中,

tls 为 TLS 配置信息,只要提供了 TLS 配置,则服务器启动为 HTTPS 服务器,如果配置要 求 客 户 端认证, http.IncomingMessage 上 可 以读取属性 socket.tlsPeerPrincipal 与socket.tlsPeerCertificateChain,分别获得客户端证书的主题与证书链。The http.createServer([requestListener]) is extended as http.createServer([requestListener], [tls]). The tls is the TLS configuration information. As long as the TLS configuration is provided, the server is started as the HTTPS server. If the configuration requires the client authentication, the properties socket.tlsPeerPrincipal and socket.tlsPeerCertificateChain can be read on http.IncomingMessage, respectively getting the subject and certificate chain of the client certificate.

2. http.ClientRequest 的构造参数 options 中,增加 options.tls属性,如果设置了该属性,使 用 HTTPS 发 送 客 户 端 请 求 , 如 果 没 有提供该字段,检查 options.protocol 是否为’https:’如果是,使用 HTTPS 发送客户端请求,这种情况下,使用 JDK提供的 cacerts中的受信任根证书验证服务器,指定 SNIHostName 为连接的服务器名,不支持客户端认证,不检查证书撤销状态。Add the options.tls property into the constructor parameter options of http.ClientRequest. If this property is set, the HTTPS is used to send the client request. If this field is not provided, check whether the options.protocol is 'https:'. If yes, the HTTPS is used to send the client request. In this case, the trusted root certificate in the cacerts provided by JDK is used to verify the server, specify the SNIHostName as the server name of connection, do not support the client authentication, and do not check the certificate revocation status.

示例:最简单的 https 客户端:For example: the simplest https client:

var http = require(‘http’)var req = http.get('https://www.alipay.com', function(res) {

res.pipe(process.stdout);});req.once('socket', function(socket) {

socket.once('tlsHandshaked', function() {for (var i = 0; i < arguments.length; i++)

console.log(arguments[i].toString());});

})最后一条语句,通过在 socket 上监听 tlsHandshaked消息可以获得服务器证书的主题以及证

276

Page 277: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

书链。The last statement can get the subject and certificate chain of the server certificate by listening to the tlsHandshaked message on the socket.

Module(模块)Module

在实现 node.js 文档描述功能之外,添加了更多功能。In addition to implementing the function described in the node.js document, more functions have been added.1. require(module[, parameter1[, parameter2,…]])允许向模块传递启动参数,因为模块默认被 cache 了,所以第一次传入的启动参数才有意义。Allow to transfer the launch parameters to the module. Because the module is cached by default, the first input launch parameters are meanful.

2. require.reload(module[, parameter1[, parameter2,…]])重新加载模块,不存在 C++插件可能导致的麻烦,所有模块都允许重新加载。Reload the module. There is no trouble caused by the C++ plugin, so all modules are allowed to reload.

3. require.launch(module[, parameter1[, parameter2,…]])在新线程中启动新的 Nashorn虚拟机,加载模块,被加载的模块是 main 模块,这个方法可以实现 Cluster 描述的行为。Launch the new Nashorn virtual machine in the new thread, and load the module. The loaded module is main module and this method could implement the behavior described by Cluster.

4. 使用不包含路径前缀(./,../,/)的名字加载模块,按核心模块名搜索失败之后,按Node 模块加载之前,插入一个 java 模块加载步骤,如果成功,直接返回该模块。例如,当前 ClassLoader内存在一个模块类 app.Jmodule,require(‘app.Jmodule’),即可加载该模块,优先于 Node 模块加载。Load the module with the name which does not contain the path prefix (./, ../, /). After searching fails by core module name, insert a java module loading step before loading according to Node module. If success, directly return this module. For example, there is a module class app.Jmodule in current ClassLoader, using require('app.Jmodule') to load this module before loading Node module.

5. 全局目录加载位置不包括$PREIFX/lib/node,因为没有合适地方配置 node_prefix。The global directory loading location does not include $PREIFX/lib/node, because there is no suitable place to configure node_prefix.

6. 加载模块时的添加的外层 wrapper,添加了 parameters,java 两个参数。The added outer wrapper when loading module, addes parameters and java two parameter.(function (exports, require, module, __filename, __dirname, parameters[, java]) {

// your module code});parameters 为 require 传入的参数 Array。

277

Page 278: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

The parameters is the parameter Array passed by require.实现 java插件时,java变量为关联的 java对象实例,js 通过该变量访问对应的 java对象;其他模块类型,java变量为 undefined。When implementing java plugin, the java variable is the associated java object instance, and the js accesses the corresponding java object via this object. For the other module types, the java object is undefined.

7. require.extensions,尽管被 node.js 文档废弃了,实际上用户可以用来扩展更强的模块支 持 能力,所以保留, 该对象中 key 为 文件扩展名, value 为 function(path, parameters);这里的 path 为模块文件绝对路径,parameters 为 require 传入的参数。废弃这样一个扩展性,不知道设计者怎么考虑的。The require.extensions, despite being discarded by node.js document, actually could be used to extend the stronger module support abilities by user, so it is kept. The key in this object is the file extension and value is function(path, parameters). Here the path is the absolute path of the module file, and parameters is the parameter passed by require. We have no idea why the designer discards this extension.

8. node.js 经典示例的 Cluster版本实现:example.jsThe implementation of Cluster version for the node.js classic example. example.js:

if (--parameters[0] > 0)require.launch(__filename, parameters[0]);

var http = require('http');var hostname = '127.0.0.1';var port = 3000;var Thread = Java.type('java.lang.Thread');var server = http.createServer(function(req, res) {

res.statusCode = 200;res.setHeader('Content-Type', 'text/plain');res.end('Hello World ' + Thread.currentThread() + '\n');

});server.listen(port, hostname, function() {

console.log('server launch');});

用 java –jar limax.jar node example.js 3 启动服务器,可以看见启动了 3 个服务器实例,关键是前两行,require.launch 与 require参数配合即可提供 Cluster 支持。用浏览器访问http://127.0.0.1:3000/可以看见服务线程名,刷新浏览器,服务线程名没有改变,体现了Keep-Alive 特性,重启浏览器访问,可以看见线程名发生了改变,体现了 Cluster 特性。Use java –jar limax.jar node example.js 3 to launch the server, three server instances have been launched. The key is the first two lines, and require.launch coordinates with require parameter to provide Cluster support. The service thread name could be seen when using browser to access http://127.0.0.1:3000/. When refreshing the browser, the service thread name has not changed, wihch reflects the Keep-Alive feature. When restarting the browser access, the thread name has changed, which reflects the Cluster feature.

278

Page 279: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

8. 虚拟机线程间通讯,是实现 Cluster 的基础,可以参考 limax.node.js.module.Net 的实现,基本方法就是,实现自己的插件,通过类静态成员进行数据交换,正确使用同步即可。The communication between the virtual machine threads is the basis to implement the Cluster, which could refer the implementation of limax.node.js.module.Net. The basic method is implementing the own plugin, exchanging the data through class staic members, and correctly synchronizing.

Net(网络)Net

该模块使用 java.nio.channels 中的异步 IO 支持类实现,与 js 的异步特性正好对应。This module uses the asychronous IO support class of the java.nio.channels to implement, which is corresponding to the js asychronous feature.1. java 本身只支持 TCP 服务,不支持 LocalDomain 服务。

The java itself only supports the TCP service, and does not support the LocalDomain service.2. server.listen 端口 bind失败会直接报告异常,node.js 不会,反复重试,应该是为了支持

Cluster达成的妥协。The server.listen port-binding failure will directly report the exception, however the node.js does not report but repeatly retry, which should be the compromise to support the Cluster.

3. node.js 文档要求 server.listen 可以执行多次,应该也是为了支持 Cluster达成的一种妥协,这里不允许。The node.js document requires that the server.listen could be executed mutliple times, which should be the compromise to support the Cluster, but it is not allowed here.

4. node.js 文档中提及 server.maxConnections 在 Cluster环境下没有意义, limax 实现重新找回了这个成员的意义。ServerChannel 实际上全局管理的,accept获得的 channel向各个虚拟机轮转投递,某个虚拟机中的 maxConnections限制超出以后,就向下一个虚拟机投递,所有虚拟机都拒绝接受该 channel 才关闭该 channel,所以配置这个选项和Cluster 数量配合,可以实现更合理的负载均衡。The server.maxConnections mentioned in the node.js document has no meaning in the Cluster environment, but the limax re-finds the meaning of this member. The ServerChannel is actually globally managed. The channel obtained by accept rotationly deliveries to each virtual machine. When the maxConnections limit in a virtual machine is exceeded, the channel is deliveried to the next virtual machine. This channel will be closed when all the virtual machines reject accepting it. So configuring this option coornidating with the number of Cluster could achieve the more reasonable load balance.

5. socket.KeepAlive 方法的第二个参数 initialDelay,仍不明确。The second parameter initialDelay of the method socket.KeepAlive is still not clear.

6. node.js 文档中提供的 ECHO 服务器与客户端,尽管多数时候都能正确运行,实际上并不合理,任何时候都必须明确 TCP 是流,不应该假设一个报文就能承载所有数据,即便数据量很小。The ECHO server and client provided in the node.js document, although most of time can be run correctly, actually is not reasonable. It must be clear at any time that the TCP is a stream, so it should not assume that a packet is able to carry all the data, even though the amount of data is very small.

279

Page 280: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Os(操作系统)OS

理论上,既然应用运行在 java虚拟机上,就不应该再考虑操作系统层面的问题,该模块仅提 供 java 虚 拟 机 能 提 供 的 信 息 , 包 括os.arch(),os.endianess(),os.homedir(),os.hostname(),os.networkInterfaces(),os.platform(),os.release(),os.tmpdir(),os.type(),os.userInfo(),某些方法返回的信息与 node.js 文档提到的可能有少许差异,比如 x64,在这里可能是 amd64。In theory, since the application runs on the java virtual machine, the issue of operating system level should not be considered, and this module just provides the information provided by java virtual machine, including os.arch(), os.endianess(), os.homedir(), os.hostname(), os.networkInterfaces(), os.platform(), os.release(), os.tmpdir(), os.type(), and os.userInfo(). The information returned by some methods might have some difference to that mentioned in the node.js document, such as that x64 might be the amd64 here.

Path(路径)Path

与 node.js 文档基本一致,除了 path.win32等同于 path.posix,应该说都等同于 path.java,不需要过多考虑系统相关问题,java虚拟机层面解释即可。Basically consistent with node.js document, except that path.win32 equals to path.posix, it should be said that both are equivalent to path.java. It is no need too much to consider the system-related issue and the explanation from the java virtual machine level is acceptable.

Process(进程)Process

java 虚 拟 机 中 不 需 要 太 多 进 程 相 关 特 性 , 所 以 这 个 模 块 仅 提 供process.chdir(),process.cwd(),process.env,process.exit(),process.exitCode,process.hrtime(),process.memoryUsage(),process.nextTick(),process.platform,process.stderr,process.stdin,process.stdout,其中 process.memoryUsage()返回的内容是 Java虚拟机的内存使用情况,与node.js 描述完全不同,process.nextTick()完全用 setImmediate()实现。The java virtual machine does not require too many process-related features, so this module just provides process.chdir(), process.cwd(), process.env, process.exit(), process.exitCode, process.hrtime(), process.memoryUsage(), process.nextTick(), process.platform, process.stderr, process.stdin, and process.stdout. Among them, process.memoryUsage() returns the memory usage of java virtual machine. Different from the description in node.js, process.nextTick() is completely implemented with setImmediate().

Punycode

既然 node.js 模块自己都废弃了,不实现。Since the node.js module itself discards it, it is not implemented.

Query String(查询字符串) Query String

纯 js 代码,完整实现。280

Page 281: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Pure js code, is completely implemented.

Readline(逐行读取)Readline

太多的终端相关行为,用得不多,暂不实现。Too many terminal-related behaviors, rarely use, and is not yet implemented.

REPL(交互式解释器)REPL

用得不多,暂不实现。Rarely use, and not yet implemented.

Stream(流)Stream

核心中的核心,纯 js 代码,完整实现,如同 node.js 文档所说,应该尽量使用 flow 模型。Core of the core, pure js code, is completely implemented. As described in the node.js document, flow model is prefered.

String Decoder(字符串解码器)String Decoder

使用 java.nio.charset.CharsetDecoder 实现。Use java.nio.charset.CharsetDecoder to implement.

Timer(定时器)Timer

用处很多的工具,完整实现。The tools with many usefulness is completely implemented.

TLS/SSL

Node.js 的设计方式过于冗余,Limax版本直接在 Net 模块中实现。(实际上不存在 tls 这个 模块),Limax版本的实现不但支持 Node.js 的 TLSSocket 方式,也支持某些实际网络协议使用的 STARTTLS 模式,允许在非安全连接上启停 TLS。The design of Node.js is too redundant, so the Limax version directly implements it in the Net module. (Actually, there is no tls module.) The implementation of the Limax version not only supports the TLSSocket way of Node.js, but also supports some actual network protocols using the STARTTLS way and allowing to start and stop TLS on non-secure connection.

Net 模块的扩充 The expansion of Net module1. net.Server 类 net.Server class1.1. 构造参数 options 中增加了 options.tls属性,如果设置了该属性,服务器按 TLSSocket

服务器方式启动。(http 模块的使用的方式)The options.tls property is added into the constructor parameter options. If this property is set, the server is launched according to the TLSSocket server way. (the using way of http module)

2. net.Socket 类 net.Socket class2.1. 构造参数 options 中增加了 options.tls属性,如果设置了该属性,socket.connect 时客

281

Page 282: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

户端按照 TLSSocket 客户端方式启动。(http 模块使用的方式)The options.tls property is added into the constructor parameter options. If this property is set, the client is launched according to the TLSSocket client way. (the using way of the http module)

2.2. 方法 socket.starttls(tls),在非安全连接上使用 tls 配置信息启动 TLS。starttls 之后,允许立即发送数据,实际上数据将在 tls握手完成以后被发送。The method socket.starttls(tls) uses the tls configuration information to launch TLS on the non-secure connection. After starttls, the data is allowd to be sent immediately. In fact, the data will be sent after the tls handshake is completed.

2.3. 方法 socket.stoptls([function()]),结束 TLS会话,回到非安全会话状态。如果传入一个回调函数,该函数在 tlsDown消息触发时被调用,详见 tlsDown消息的说明。一般来说,如果需要设计 STARTTLS 类应用,必须正确设计 STOPTLS 协商机制,TLS 结束握手完成以后再执行非安全连接状态下的会话。Limax 实现上,保证不丢失任何数据,由于一些异步特性,如果 stoptls 之后立刻发送数据,这些数据可能在安全会话中发送,也可能在安全会话结束以后发送。The method socket.stoptls([function()]) ends the TLS session and returns to the non-secure session state. If a callback function is passed in, this function is called when the tlsDown message is triggered as described in the tlsDown message. In general, if it needs to design a STARTTLS style application, it must correctly design the STOPTLS negotiation mechanism and perform a non-secure connection session after the TLS ends handshake. The Limax implementation ensures that no data is lost. Due to some asynchronous features, if the data is sent immediately after stoptls, the data might be sent in the secure session, or sent after the secure session ends.

2.4. 方法 socket.tlsrenegotiate(),启动 TLS 重协商,该方法仅设置一个标记,指示在下一次 socket活动时重新握手,所以不会返回任何错误。The method socket. tlsrenegotiate() starts the TLS renegotiation. This method only sets a mark to indicate the handshake again in the next socket activity, so there is no any error to return.

2.5. 消息 tlsHandshaked,握手完成后,该消息被触发,参数模式为 function(principal, cert0, cert1 …) , 也 可 以 在 触 发 后 直 接 读 取 属 性 socket.tlsPeerPrincipal 与socket.tlsPeerCertificateChain , 获 得 对 方 的 证 书 主 题 与 证 书 链 , 其 中socket.tlsPeerPrincipal 为 javax.security.auth.x500.X500Principal 类 型 ,socket.tlsPeerCertificateChain 为 一 js 数 组 , 成 员 类 型 为java.security.cert.X509Certificate。The event tlsHandshakes is triggered after the handshake is completed and the parameter prototype is function(principal, cert0, cert1 …). The properties socket.tlsPeerprincipal and socket.tlsPeerCertificateChain might be directly read after the message is triggered to obtain the certificate and certificate chain of the subject, among them the socket.tlsPeerPrincipal is instance of javax.security.auth.x500.X500Principal, socket.tlsPeerCertificateChain is js array and the member is instance of java.security.cert.X509Certificate.

2.6. 消息 tlsDown,TLS会话结束后该消息被触发,参数模式为 function(err),if(err)成立,表示会话由某些异常导致,err.printStackTrace()可以获取异常信息,发生异常以后,

282

Page 283: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

用户应该自己 socket.destroy(err),关闭网络连接。(可以参考 http 模块的实现)The event tlsDown is triggered after the TLS session ends and the parameter prototype is function(err). If if(err) is true, which means the session is ended by some exception and the err.printStackTrace() may get the exception information. If tlsDown is triggered with the exception, the user should call socket.destroy(err) to close the network connection. (Please reference the implementation of the http module.)

3. net.createTLS(function(c)),创建 TLS 配置,参数为一个 callback函数,c 为一个 java对象,类型为 limax.node.js.modules.tls.TLSConfig,所有的配置必须在该 callback 中调用 c上的相应方法执行,这样可以最小化异常传播。The net.createTLS(function(c)) creates the TLS configuration. The parameter is a callback function and c is a java object instanceof limax.node.js.modules.tls.TLSConfig. All the configuration must be called on the corresponding method of the c called by this callback, which could minimize the exception propagation.

3.1. 信任检测类配置,客户端必须配置,服务器如果需要验证客户端,也必须配置。The configuration of trust check class must be set on the client. If the server needs to verify the client, it also must be set.

3.1.1. c.addAllCA(),将 jdk 的 cacerts 文件中的受信任证书全部加入。The c.addAllCA() addes all the trusted certificates in the cacerts file of jdk.

3.1.2. c.addTrustCertificate(data) ,加入信任证书, data 的 类 型 可 以 是 String , 可 以 是Buffer,内容可以是文件路径,也可以是实际内容,实际内容的格式可以是 PEM,可以是 DER,可以有一个证书,也可以有多个证书,也可以是 PKCS7证书。The c.addTrustCertificate(data) addes the trusted certificate, the type of the data may be the String or Buffer, and the content may be the file path or actual content. The format of the actual content may be the PEM or DER. There may be one certificate, or multiple certificates, or PKCS7 certificate.

3.1.3. c.addCRL(data),加入 CRL列表,data 的解释同上。The c.addCRL(data) addes the CRL list. The description of the data is the same as above.

3.1.4. c.setRevocationEnabled(revocationEnabled) , revocationEnabled 为 boolean 类型 ,该方法配置是否允许 CRL测试,如果不允许,通过上面的 c.addCRL加入的 CRL列表也没 有 意 义 。 如 果 允 许 , 则 必 须 在 启 动 虚 拟 机 时 加 入 虚 拟 机 参 数com.sun.security.enableCRLDP=true,否则需要通过网络进行的测试(比如 OCSP)将会失败,CRL测试多数情况下极其费时,默认为 false。The c.setRevocationEnabled(revocationEnabled) method configures whether to allow the CRL test and the revocationEnabled is boolean type. If it is not allow, the CRL list added through c.addCRL has no meaning. If it is allowd, the virtual machine parameter com.sun.security.enableCRLDP=true must be added when launching the virtual machine, otherwise the test (such as OCSP) which need to be performed over the network will fail. In most cases, the CRL test is extremely time consuming and the default is false.

3.1.5. c.setTrustChecker(function(chain, exception)) , chain 为 java 数组表示的证书链,exception 为使用上面的配置检测失败以后抛出的异常,该方法返回 true,表示接受证书链,TLS握手过程可以继续。通常,浏览器访问 https 网站时,发现某些证书异常,提示用户是否继续,就是使用这样的方式实现。复杂的证书链检测,应该将chain和 exception转发给用户自己实现的 java 模块。

283

Page 284: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

c.setTrustChecker(function(chain, exception)), the chain is the certificate chain represented with java array, and the exception is the exception thrown after the above configuration check fails. This method returns true, indicating that the certificate chain is accepted and the TLS handshake process can continue. Usually, this implementation is used when the browser accesses https site, finds some certificates exception and prompts the user whether to continue. The complex certificate chain check should forward the chain and exception to java module implementd by the user itself.

3.1.6. c.setPositiveTrustChecker(function(chain, exception)),执行积极的证书链检测,参数解释同上,使用该方法配置,将忽略上面配置的检测,直接使用设置的检测器,上面配置的检测能力不符合要求的情况下可以使用该方法。另外,使用该方法设置永远返回 true 的检测器,可以辅助进行一些证书配置上调试。The c.setPositiveTrustChecker(function(chain, exception)) performs the positive certificate chain check. The description of the paramter is the same as above. Using this method to configure will ingore the check to the above configuration and directly use the set checker. This method can be used when the check ability of the above configuration does not match the requirement. In addition, using this method to set the checker which always returns true could assist in some certificates to debug on the configuration.

3.2. 服务器证书私钥配置,需要客户端验证的情况下,客户证书私钥的配置。The configuration of the server certificate private key, and the configuration of the client certificate private key in the condition that requires the client verification.

3.2.1. c.addPKCS12(data, pass),加入 PKCS12证书包,data 的类型可以是 String,可以是Buffer,内容可以是文件路径,可以是 PKCS12证书包的实际内容;pass 的类型可以是 String,可以是 Buffer,内容为 PKCS12证书包的密码。The method c.addPKCS12(data, pass) addes the PKCS12 certificate package. The type of data may be the String or Buffer, the content may be the file path or the actual content of the PKCS12 certificate package, the type of pass may be the String or Buffer and the content may be the password of the PKCS12 certificate package.

3.2.2. c.addPrivateKeyAndCertificatePack(pkey, cert, pass),加入私钥和相应的证书链,实际上就是生成 PKCS12证书包需要的信息。所有参数都允许是 String 或者 Buffer,特别的,pkey 关联的实际内容要求 PEM格式表示的各种私钥格式,RSA,DSA,EC 或者PKCS8,pass提供了私钥密码,如果私钥没有设置密码,pass被忽略,cert 的要求与前面的 c.addTrustCertificate(data)相同。The method c.addPrivateKeyAndCertificatePack(pkey, cert, pass) addes the private key and corresponding certificate chain, in fact, which is the information required when generating the PKCS12 certificate package. All parameters are allowed to be String or Buffer, in particular, the actual content related to pkey requires a variety of private format represened by PEM format, RSA, DSA, EC or PKCS8. The pass provides the password of private key. If the private key does not set the password, the pass is ignored. The requirement of the cert is the same as the previous c.addTrustCertificate(data).

3.3. TLS引擎配置 TLS engine configuration3.3.1. c.setProtocol(protocol) , protocol 为 String 类 型 , 允 许

SSLv3,TLSv1,TLSv1.1,TLSv1.2,默认 TLSv1.2。c.setProtocol(protocol), protocol is the String type, and allows SSLv3, TLSv1, TLSv1.1,

284

Page 285: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

TLSv1.2, and default is TLSv1.2.3.3.2. c.setNeedClientAuth(enable),enable 为 boolean 类型,默认为 false,设置为 true,

将强制客户端提供证书进行验证。c.setNeedClientAuth(enable), enable is boolean type and default is false. When it is set as true, the client is forced to provide the certificate to verify.

3.4. 虚拟服务器相关配置 Virtual server related configuration3.4.1. c.setSNIHostName(hostname),hostname 为 String 类型或者 Buffer 类型,客户端调用

该方法,设置请求的虚拟服务器名。c.setSNIHostName(hostname), hostname is String type or Buffer. The client calles this method to set the required virtual server name.

3.4.2. c.addSNIServerName(type, name),type 为 int 类型,name 为 Buffer 类型,客户端调用该方法,按类型添加请求的虚拟服务器名, c.setSNIHostName,实际上提供了type=0,所有这些 type 不能重复,否则抛出异常。c.addSNIServerName(type, name), type is int type, and name is Buffer type. The client calles this method to add the required virtual server name accroding to the type. c.setSNIHostName actually provides type=0. All these type can not be repeated, otherwise there is exception thrown.

3.4.3. 服务器没有使用 JDK提供的 SNIMatcher机制实现,采用的方法是,如果服务器端配置加入了多个私钥证书包,则启动虚拟服务器方式, SNIServerName 使用证书Subject 中 CN 设定的域名模板和 SubjectAlternativeNames 中的 dNSName 类型的域名模板进行模式匹配,选择正确的证书链和私钥,如果所有的匹配都不成功,由 JDK的 pkixKeyManager选择一对证书链和私钥。之所以这样实现,是因为 JSSE提供的方 案 并 不 完 备 , 存 在 逻 辑bug。javax.net.ssl.ExtendedSSLSession.getRequestedServerNames()方法可以获取对方请求的服务器名,问题在于握手阶段选择证书时,该方法返回空集,直到握手完成,才返回客户端请求的证书。The server does not use the SNIMatcher mechanism provided by JDK to implement. The method it used is that if the server-side configuration addes multiple private key certificate package, it launches in the virtual server mode. The SNIServerName uses the domain name template set by CN in the certificate Subject and domain name template of dNSName type in SubjectAlternativeNames for pattern matching and selecting the correct certificate chain and private key. If all the matching is not successful, the pkixKeyManager of JDK selects a pair of certificate chain and private key. The reason for this implementation is that the program provided by JSSE is not complete and has logic bug. The method javax.net.ssl.ExtendedSSLSession.getRequestedServerNames() could get the server name required by peer. The problem is that when selecting the certificate in the handshake phase, this method returns empty set and retruns the certificate required by the client until the handshake completes.

示例:For example:

服务器 testtlsserver.js server testtlsserver.js285

Page 286: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

var net = require('net')var tls = net.createTLS(function(c) {

c.addPKCS12('/work/js.test/testtls.p12', '123456');c.addTrustCertificate('/work/js.test/testtls.cer');c.setNeedClientAuth(true);

});var server = net.createServer(function(c) {

c.on('error', function(e){console.log('error', e.stack)e.printStackTrace();

});c.once('tlsHandshaked', function() {

console.log('tlsHandshaked:');for (var i = 0; i < arguments.length; i++)

console.log(arguments[i].toString());});c.once('tlsDown', function(err) {

console.log('tlsDown'); if (err)

client.destroy(err)})c.on('end', function() {

console.log('client disconnected');});c.starttls(tls);c.write('hello\r\n');c.pipe(c);c.pipe(process.stdout)

});server.on('error', function(err) {

throw err;});server.listen(8124, function() {

console.log('server bound');});

客户端 testtlsclient.js Client testtlsclient.jsvar net = require('net')var tls = net.createTLS(function(c) {

c.addPKCS12('/work/js.test/testtls.p12', '123456');c.addTrustCertificate('/work/js.test/testtls.cer');

});var client = net.createConnection({

286

Page 287: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

port : 8124}, function() {

client.starttls(tls);console.log('connected to server!');client.write('world!\r\n', function() {

print('send done')client.stoptls(function() {

print('stopped');client.write('wwww?');

});});

});client.on('tlsHandshaked', function() {

console.log('tlsHandshaked:');for (var i = 0; i < arguments.length; i++)

console.log(arguments[i].toString());});client.on('tlsDown', function(err) {

console.log('tlsDown')if (err)

client.destroy(err)});client.on('data', function(data) {

data = data.toString();console.log(data);if (data[data.length - 1] == '?')

client.end();});client.on('close', function() {

console.log('disconnected from server');});client.on('error', function(e) {

console.log('error', e.stack)e.printStackTrace();

})

1. 该例子通过对 ECHO 功能的服务器客户端进行 TLS改造而来,要求客户端提供认证,客户端服务器简单使用同样的私钥和证书,testtls.p12,testtls.cer,可以按 Node.js 文档示例的方式制作。This example makes the TLS modification on the server and client with ECHO function, which requires the client to provide the authentication. The client and server simply uses the same private key and certificate, testtls.p12 and tetstls.cer, which could be created according to the example in the Node.js document.

287

Page 288: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

2. 客户端 STARTTLS 后,立即向服务器发送”world\r\n”,可以明确该字符串在 TLS会话中发送,发送完成后立即 STOPTLS,停止完成后,发送字符串”wwww?”,这里可以明确该字符串在非安全会话中发送,这两个串最终都会被服务器 ECHO回来。The client immediately sends the "world\r\n" to the server after STARTTLS. This string can be defined to send in the TLS session. The client immediately STOPTLS after the sending completes. When stop is completed, the client sends the string "wwww?" and defines this string to send in the non-secure session. These two strings are finally ECHO back by the server.

3. 服务器 accept 客户端连接以后,立即在该连接上 STARTTLS,随后向客户端发送’hello\r\n’,可以明确该字符串在 TLS会话中发送,收到”world\r\n”,向客户端 ECHO ”world\r\n” , 这 里注意到 , 服 务 器并不 需 要 关心客 户 端 是否 STOPTLS ,切换由底层 完成,”wwww?”的 ECHO,肯定在非安全会话中完成。逻辑上讲,尽管”world\r\n”在TLS会话中收到,ECHO回去的数据是否在 TLS会话中发送,不应该作假设,毕竟客户端发送数据之后立即 STOPTLS。这里可以看到设计 STARTTLS 类协议,应用层面必须仔细考虑 STOPTLS 的握手机制。After the server accept the client connection, it immediately STARTTLS on this connection, and then sends "hello\r\n" to the client, defining that this string is sent in the TLS session. After the server receives the "world\r\n", it ECHO "world\r\n" to the client. It should be noted here that the server does not need to concern whether the client STOPTLS. The switch is completed by the bottom level and the ECHO of "wwww?" is completed in the non-secure session. Logically, even though "world\r\n" is received in the TLS session, it should not assume whether the data returned by ECHO is sent in the TLS session, because the client is immediately STOPTLS after sending data. Seen from here that the application level must carefully consider the handshake mechanism of the STOPTLS when designing STARTTLS-style protocol.

4. 运行例子可以观察 tlsHandshaked,tlsDown 两个消息的效果。Run the example and observer the effect of the tlsHandshaked and tlsDown two events.

TTY(终端)TTY

终端使用很少,不实现。The terminal is rarely used and not yet implemented.

UDP/Datagram

1. UDP 服务器理论上不能提供 Cluster 能力,如果用 Cluster 方式启动,后续服务器 bind时会产生地址已经使用异常。目前 UDP 最大的用途还是局域网环境内进行组播实现服务器间协同,不需要承载太重的负载。重负荷的情况可以设计为一个虚拟机收发报文,通过线程间通讯将任务分发给服务 Cluster。In theory, the UDP server can not provide the Cluster ability. If launching with Cluster mode, the exception that address has been used will be generated when the following server binds. Currently, the largest usage of UDP is multicast in the LAN environment to achieve inter-server collaboration, and does not need to carry too heavy load. In heavy load

288

Page 289: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

condition, it could be designed as a virtual machine to send and receive message, distributing the tasks to service Cluster through inter-thead communication.

2. socket.addMembership(multicastAddress[, multicastInterface])的实现与 node.js 描述有差异,Limax 在实现上,缺省multicastInterface 的行为是查找所有已经 UP并且有组播能力的接口进行绑定。不同系统接口名差异太大,选择正确的名字都是问题,很难保证正确性;现在的计算机网络接口太多,让操作系统自己选择一个接口也是不可靠行为,谁知道能选中一个自己需要的?所以 java.nio.channels.MulticastChannel 接口也不提供自动选择能力。The implementation of socket.addMembership(multicastAddress[, multicastInterface]) is different with the description of node.js. In the Limax implementation, the default multicastInterface behavior is to search all interfaces which are UP and have the multicast ability to bind. The interface name of different system is too different, and it is problem how to choose the correct name and is difficult to ensure correctness. Now, the computer network interface is too much, so that it is not reliable to select an interface by operating system itself because it can not ensure that the selected one is the wanted. So the java.nio.channels.MulticastChannel interface does not provide the automatic select ability.

3. socket.setTTL(ttl)与 socket.setMulticastTTL(ttl) 等同,因为 DatagramChannel 上仅提供一个 TTL 设置方法。The socket.setTTL(ttl) is equivalent to the socket.setMulticastTTL(ttl), because only one TTL setting method is provided on DatagramChannel.

URL

完整实现,仔细研究 node.js 文档可以发现问题,HTTP 模块会用到这个 URL 解析器,这个解析器返回的 urlObject.host并不是 HTTP 模块需要的 host,HTTP 模块文档需要的 host 实际上是这里的 urlObject.hostname,基本数据字典都没有统一,不知道怎么规划的?Completely implement. There is problem after carefully reading node.js document that the HTTP module uses this URL parser. The urlObject.host returned by this parser is not the host required by HTTP module, and the host required by HTTP module document is actually urlObject.hostname here. The basic data dictionary is not unified and not clear how to plan.

Util(实用工具)Util

提供 util.format() , util.inherits() , util.inspect() 三个 方法 。 其 中 , util.format() 使 用java.lang.String.format格式化参数,格式参数与 sprintf格式参数有稍许差异;inspect 用于递归展示对象数据,不支持显示颜色之类的特性。实际上 console.log 输出的串就是 inspect展开的。Provide util.format(), util.inherits() and util.inspect() three methods. The util.format() uses java.lang.String.format to format the parameters, and the format parameter is a little different with the sprintf format parameter. The inspect() is used to recursively display the object data and does not support the feature such as displaying color. Actually, the output string of console.log is extracted by inspect.

289

Page 290: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

V8

Nashorn,没 V8啥事。Nashorn, not related to V8.

VM(虚拟机)VM

1. 所有方法的 options,都是 V8相关的,忽略。All the methods' options are related to V8 and ignored.

2. DebugContext 是 V8 特性,所以 vm.runInDebugContext(),作为 vm.runInNewContext()的别名实现。The DebugContext is V8 feature, so the vm.runInDebugContext() is implemented as the alias of vm.runInNewContext().

3. 不建议使用已有 sandbox 创建 Context,最好 sandbox = vm.createContext(),然后初始化sandbox,有利于提高性能。这是 Nashorn 的限制,非全局对象设置到 Context 上访问,会在该对象上添加一个 nashorn.global 成员作为真正的全局对象执行访问,为了保证正确性,执行完成之后必须执行一次拷贝。Not suggest to use the existing sandbox to create Context. First sandbox = vm.createContext() then initiate the sandbox, which helps to improve the performance. This is the limit of Nashorn. When the non-global object is setting on the Context to access, a nashorn.global member will be added on this object as the real global object to access. In order to ensure the correctness, a copy must be executed after the completion of the implementation.

ZLIB(压缩)ZLIB

node.js 使用 zlib 实现,limax 使用 java.util.zip 实现The node.js uses zlib to implement, and the limax uses java.util.zip to implement.1. java.util.zip 没有提供太多配置用常量,多数情况不需要使用 options,默认实现即可。

The java.util.zip does not provide too many constants for configuration. In most cases, the options are not required and the default implemention is enough.

2. node.js 文档中时间推送服务器的例子,flush 的使用与文档本身定义的原型不符,原型中 callback参数必选,例子没有。In the example about time push server in node.js document, the usage of flush does not match the prototype defined by the document itself. The callback parameter in the prototype is mandatory, but not supply in the example.

3. 实 验 时 间 推 送 服 务 器 例 子 , 最 好 把 gzip 算 法 改 为deflate,java.util.zip.GZIPOutputStream 的 cache太大,直接的结果是感觉服务器长时间不响应。When experimenting the time push server example, it is best to change the gzip algorithm to deflate. Because the cache of java.util.zip.GZIPOutputStream is too big, the result is that the server does not respond for a long time.

290

Page 291: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

增强模块 Enhancement module

长期以来,node.js被诟病最多问题的就是缺乏持久化能力与管理能力。Java 能够轻松解决这些问题,所以扩充两个模块 sql和 edb,分别提供关系数据库,键值对数据库访问能力,扩充一个简单 JMX查询模块monitor。For a long time, the most challenging issue of node.js is that it lacks of the ability of the persistence and management. The Java could easily solve these issues, so it expands two modules sql and edb, which seperately provides the access ability of the relational database and key-value database, and expands a simple JMX query module monitor.

Sql

sql.Connection 类 sql.Connection class ‘close’ 事件 'close' event connection.execute(statement [,parameter1[,parameter2…]], callback) connection.destroy()

sql.createConnection(url)

Sql.Connection 类 Sql.Connection class提供数据库连接,内部实现为连接池,不活动的连接 1 分钟淡出。Provide the database connection, internally implement as the connection pool, and inactive connection keep-alive 1 mintue before fade out.

var url = 'jdbc:mysql://192.168.1.3:3306/test?user=root&password=admin&autoReconnect=true'var sql = require("sql");var conn = sql.createConnection(url);conn.once('close', function() {

console.log('closed')});conn.execute("select * from a where id > ?", 1, function(err) {

if (err) {console.log(err);return;

}var s = '!';for (var i = 1; i < arguments.length; i++)

s += arguments[i] + ' ';console.log(s)

});conn.destroy();

‘close’ 事件 'close' event

291

Page 292: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

执行 destroy,关闭数据库连接,但是关闭时可能还有活动连接存在,所有的活动连接结束以后触发该事件,表示数据库连接彻底关闭。Execute destroy to close the database connection. But there might be active connection existing when closing, this event is trigger after all the active connections finish, indicating that the database connection is completely closed.

connection.execute(statement [,parameter1[,parameter2…]], callback)statement <String>符合 jdbc 规范的 sql 语句,用?占位parameter <Object> 该参数的个数必须和 statement 中的占位符个数相等callback <Function> 结果回调statement<String>, the sql statement which matches the jdbc specification, uses ? as placeholder.parameter<Object>, the number of these parameters must be equivalent to the number of placehoder(s) in the statement.callback<Function>, callback while the result return

parameter 通过 PreparedStatement.setObject()方法设置到查询请求中,能接受的类型取决于应用数据库的 JDBC 连接库,一般来说 int 类型 string 类型没有问题,其它类型必须通过实际测试决定,小心 long,js 的 long只有 53位有效位。最坏情况下,可以重新设计 sql 语句,支持 string参数,使用数据库自身的 convert函数将 string转换为目标类型。The parameter is setted on the query request via PreparedStatement.setObject(). The acceptable type is determined by the JDBC connection pool of the application database. In general, the int and string type have no problem, the other type must be determined by the actual test, and it should take care the long because the long of js has only 53 effective bits. In the worst case, the sql statement could be redesigned to support string parameter, using the database own convert function to convert the string to the target type.

callback第一个参数是 err,这一点与 node.js习惯一致。SELECT 使用的 callback参数数量取决于查询语句本身返回的列数,查询语句返回 N列,那么使用 N+1 个参数进行回调。查询语句可能返回空集,所以为了表示查询结束,最后一次 callback 传入 0 个参数,可以通过arguments.length = 0检测查询结束,所以上面的例子,最后一行输出总是单字符”!”。返回结果的类型思考与 paramter 一致,如果返回了不可识别的类型,考虑修改 sql 语句使用数据库自身的 convert转换为 string。UPDATE,DELETE,INSERT,这一类操作用 2 个参数调用 callback 一次,第二个参数为影响的行数。The first parameter of callback is err, which is consistent with node.js. The parameter number of callback used by SELECT is determined by the number of the columns returned by the query statement itself. If the query statement returns N columns, the N+1 parameters are used to callback. The query statement might return empty set, so last callback passes 0 parameter in order to indicate that the query is done. The arguments.length = 0 could be used to check whether the query is done, so in the above example, the last line always outputs the single character "!". The type of the returned result is considerated to be consistent with the parameter. If the unrecongnized type is returned, modify the sql statement to use the database own convert function to convert to string. The UPDATE, DELETE and INSERT, this kind of operation uses two parameters to callback once, and the second parameter is the row number affected.

292

Page 293: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

connection.destroy()结束数据库连接,destroy 之后的 execute调用直接用连接已经关闭的错误回调。Call execute function following destroy, callback with exception of connection has been closed directly.

sql.createConnection(url)url <String> 数据库连接 urlurl<String> datbase connection url

创建数据库连接的便捷方法。A convenient way to create database connection.

Edb

edb.Instance 类 edb.Instance class ‘close’ 事件 'close' event instance.addTable(table1, [,table2 [,table3 …]], callback) instance.removeTable(table1, [,table2 [,table3 …]], callback) instance.insert(table, key, value, callback) instance.replace(table, key, value, callback) instance.remove(table, key, callback) instance.find(table, key, callback) instance.exist(table, key, callback) instance.walk(table, [,from], callback) instance.destroy()

edb.createEdb(path)

edb.Instance 类 edb.Instance classedb对象实例,提供键值对数据库访问操作。edb object instance, provides the access operation to the key-value database.首先,必须创建目录’/work/js.test/dbhome’,Edb 不会自动创建。First, the directory ‘/work/js.test/dbhome’ must be created, The Edb does not create it automatically.

var edb = require('edb').createEdb('/work/js.test/dbhome');edb.once('close', function() { console.log('close')});edb.addTable('a', function() {

var n = 3;function insert3() {

if (--n > 0)return;

edb.find('a', '1', console.log);

293

Page 294: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

edb.walk('a', '0', console.log);edb.destroy();

}edb.insert('a', '0', 'AA', insert3);edb.insert('a', '1', 'b', insert3);edb.insert('a', '3','',insert3);

});注意一下这段代码的并行特性,3 个 insert操作并行,完成以后 find 与 walk操作并行,之后 destroy 关闭数据库,禁止后续查询操作。Be careful, the parallel feature of the code, 3 insert operations is parallel, followed by find and walk which is parallel, then close edb by destroy, forbid any operation succeed.

‘close’ 事件 'close' event执行 destroy,关闭 Edb 实例,但是关闭时可能还有操作正在执行,所有的操作执行结束以后触发该事件,表示 Edb 实例彻底关闭。Execute destroy, close Edb instance. However there might be operations in progress when closing, this event is triggered when all operations end, which indicates that the Edb instance is completely closed.

instance.addTable(name1, [,name2 [,name3 …]] , callback)name <String> 往数据库中添加的表名 table name added into databasecallback <Function>

一次可以添加多个表,执行完成 callback被调用,如果有错误第一个参数为 Exception信息。数据库中已有该表,操作忽略。Multiple tables can be added once, and callback is called after execution finishes. If there is error, the first parameter is Exception message. If there is table existed in the database, this operation is ignored.

instance.removeTable(name1, [,name2 [,name3 …]] , callback)name <String> 从数据库中删除的表名 table name deleted from databasecallback <Function>

一次可以删除多个表,执行完成 callback被调用,如果有错误第一个参数为 Exception信息。数据库中该表不存在,操作忽略。Multiple tables can be deleted once, and callback is called after execution finishes. If there is error, the first parameter is Exception message. If the table does not exist in the database, this operation is ignored.

instance.insert(table, key, value, callback)table <String> 表名 table namekey <Buffer|String>value <Buffer|String>

294

Page 295: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

callback <Function>

插入一个键值对,key 或者 value 如果为 String,在内部用 utf8编码为 Buffer 后执行。callback(err, succeed);如果 key 在表中已经存在 succeed == false,插入失败。When inserting a pair of key-value, if key or value is String, code to Buffer with utf8 in the internal then excute.callback(err, succeed), if the key exists in the table, succeed == false, insert fails.

instance.replace(table, key, value, callback)table <String> 表名 table namekey <Buffer|String>value <Buffer|String>callback <Function>

替换一个键值对,key 或者 value 如果为 String,在内部用 utf8编码为 Buffer 后执行。callback(err)。key 在表中不存在执行插入,存在则替换。Replace a pair of key-value, if key or value is String, code to Buffer with utf8 in the internal then execute.callback(err), if the key does not exist in the table, execute inserting, or execute replacing.

instance.remove(table, key, callback)table <String> 表名 table namekey <Buffer|String>callback <Function>

删除一个键值对,key 如果为 String,在内部用 utf8编码为 Buffer 后执行。callback(err)。key 在表中不存在,操作忽略,不作为错误。Delete a pair of key-value, if key is String, code to Buffer with utf8 in the internal then execute.callback(err), if the key does not exist in the table, the operation is ignored, not as wrong.

instance.find(table, key, callback)table <String> 表名 table namekey <Buffer|String>callback <Function>

执行查找,key 如果为 String,在内部用 utf8编码为 Buffer 后执行。callback(err, value)。key 在表中不存在,value == null,value 为 Buffer 类型Execute finding, if key is String, code to Buffer with utf8 in the internal then execute.callback(err, value), if the key does not exist in the table, value == null, the value is the Buffer type.

instance.exist(table, key, callback)table <String> 表名 table name

295

Page 296: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

key <Buffer|String>callback <Function>

key 如果为 String,在内部用 utf8编码为 Buffer 后执行。callback(err, value)。key 在表中不存在,value == falseIf key is String, code to Buffer with utf8 in the internal then execute.callback(err, value), if the key does not exist in the table, value == false.

instance.walk(table, [,from], callback)table <String> 表名 table namefrom <Buffer|String>callback <Function>

from 如果为 String,在内部用 utf8编码为 Buffer 后执行。遍历表,如果有 from参数,从 from 的下一个键值对开始遍历。callback(err, key, value)。key, value均为 Buffer 类型If from is String, code to Buffer with utf8 in the internal then execute.Traverse the table, if there is from parameter, begin the traverse from the next pair of key-value from from.callback(err, key, value), the key and value are Buffer type.

instance.destroy()关闭 Edb 实例连接,destroy 之后执行的操作直接用实例已经关闭的错误回调。Close the connection of Edb instance, the operation executed after destroy. The callback with exception message is directly closed by instance.

edb.createEdb(path)创建 Edb 实例的便捷方法,应用必须保证 path 目录存在,否则会抛出异常,结束当前代码片段的执行。An easy way to create Edb instance. The application must ensure that the path directory exists, or throws exception and ends the execution of current code fragment.

Monitor

mointor.Host 类 monitor.Host class ‘close’ 事件 'close' event host.query(objectName, callback) host.destroy() host.ref() host.unref()

monitor.connect (host, serverPort, rmiPort[, username, password])例如,启动 switcher 服务器(配置了本机 jmx 服务,见 service-switcher.xml)For example, launch switcher server (configure the machine jmx service, refer service-

296

Page 297: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

switcher.xml)

var host = require('monitor').connect('localhost', 10003, 10002);setInterval(function(){

host.query('java.lang:type=Threading', console.log);}, 10000);

运行该程序,即可 10秒间隔,查询线程状态。Run this program, and can query the thread status in 10 seconds interval.

monitor.Host 类 monitor.Host class描述了一个 JMX 连接Describe a JMX connection.

‘close’事件 'close' event执行 destroy,关闭 Host对象,但是关闭时可能还有操作正在执行,所有的操作执行结束以后触发该事件,表示 Host彻底关闭。Execute destroy, close Host object. However there might be operation in progress when closing, so this event is triggered after all operations finish, which indicates that the Host is completely closed.

host.query(objectName, callback)objectName<String> 需要查询的 jmx对象名 the jmx object name required to querycallback <Function>

查询完成使用 callback(err, obj) 回调。Use callback(err, obj) to callback after query finishes.

host.destroy()关闭 Host对象连接,destroy 之后执行的操作直接用实例已经关闭的错误回调。Close Host object connection, the operation executed after destroy. The callback with exception message is directly closed by instance.

host.ref()Opposite of unref, calling ref on a previously unrefd host will not let the program exit if it's the only host left (the default behavior). If the host is refd calling ref again will have no effect.

Returns server.

host.unref()Calling unref on a host will allow the program to exit if this is the only active host in the event system. If the host is already unrefd calling unref again will have no effect.

Returns server.297

Page 298: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

monitor.connect (host, serverPort, rmiPort[, username, password])host<String>serverPort<Number> IntegerrmiPort<Number> Integerusername<String>password<String>

提供连接参数,创建 Host对象。Provide connection parameter, and create Host object.

PKIX支持 PKIX support

为了建立跨服务器、跨项目、跨组织机构的信任关系,Limax集成 PKIX 模块,提供创建CA,签署,回收 X509证书的能力,为系统间安全互操作提供基础支持。同时,提供便利的管理工具,在确保安全的基础上,最大限度简化 CA 系统管理负担。In order to establish a trust relationship across servers, across projects and across organizations, the Limax integrates the PKIX module to provide the ability to create CA, sign and revoke the X509 certificates, and provide the basic support for the inter-system security interoperability. At the same time, the Limax provides the convenient management tool and maximum simplifies the CA system management burden on the basis of ensuring the security.证书以及 CRL 的相关概念可以参考 RFC5280,http://www.ietf.org/rfc/rfc5280.txtCerficates and CRL related concepts can refer to RFC5280, http://www.ietf.org/rfc/rfc5280.txt.OCSP 服务相关概念可以参考 RFC2560,http://www.ietf.org/rfc/rfc2560.txtOCSP service related concepts can refer to RFC2560, http://www.ietf.org/rfc/rfc2560.txt

设计原则 Design principles

ROOTCA 离线管 理 , 使 用命令行工具操作, 规避网 络安全风险。 The ROOTCA offline

management, uses command line tool to operate to avoid the network security risks.

CA 服务实现为网络服务,集成 3 个服务器。 The CA service is implemented as netwok

service, and integrates 3 servers.

1 签署,回收证书采用近线方式,实现为 HTTP 服务,绑定在服务器的 localhost:8080 接口上,如果需要远程操作,必须架设自己的反向代理,所有的操作必须通过 TOTP 方式提供授权码。最大限度限制未授权访问。Signing and recycling certificates uses the near-line mode and is implementes as the HTTP service, which is bound on the localhost:8080 interface of server. If the remote operation is

298

Page 299: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

required, the reverse proxy must be set up and all operations must provide the authorization code through the TOTP way to minimize unauthorized access.

2 通过 HTTPS 方式向外提供自动 Renew 服务,允许签署短期证书,证书超出 80%寿命之后,向服务器发起 Renew 请求,更换证书。使用短期证书,可以有效减少回收列表长度,降低回收管理负担;一旦证书不再使用,快速过期,减少盗用风险,降低管理负担。Provide the Renew service through the HTTPS way, and allow signing the short-term certificates. When the certificate exceeds the 80% of life, the Renew request is issued to the server and the certificate is replaced. Using the short-term certificate can effectively reduce the length of the revoke list and reduce the burden of the revoke management. Once the certificate is no longer used, quick expiration decreases the risk of malversation and reduces the management burden.

3 通过 HTTP 方式对外提供 OCSP 服务,同时自动发布 CRL。Provide the OCSP service through the HTTP way, and automatically publish the CRL at the same time.

CA存在过期的问题,所以提供相应的解决方案,确保 Renew 的新证书由新 CA签署,避免重新线下申请。 CA has the expiration problem, so the appropriate solution is provided to

ensure that new certificate of Renew is signed by the new CA to avoid the re-application

offline.

通过重新解释证书策略的方式,以 CPS 进行名字空间命名,为多个 ROOTCA 间互操作,提供基础支持。 By re-interpreting the certificate policy, name the namespace with CPS, and

provide the basic support for the interoperation between the multiple ROOTCA.

允许使用支持 PKCS11 的智能卡设备,为 Key安全提供进一步的保障。Allow using the

smart card device which supports the PKCS11 to provide further protection for Key security.

应用端模块,自动维护私钥、证书链,执行 Renew操作,隐藏实现复杂性,提供简单的接口, 简化开 发 。 Application-side module, automatically maintains the private key and

certificate chain, executes the Renew operation, hides the complexity of implementation and

provides the simple interface to simplify the development.

Location

Location对应了 java.security.KeyStore.PrivateKeyEntry,是使用指定私钥及其证书链的基础。Location 用 opaque URI 方式描述,scheme:[alias@]path[#algorithm]Location corresponds to java.security.KeyStore.PrivateKeyEntry, which is the basis for using the specified private key and its certificate chain.Location is described in opague URL way, scheme:[alias@]path[#algorithm].

299

Page 300: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

scheme

支持的 scheme 为 FILE,PKCS11,PKCS12,JKS,JCEKS。约定了私钥、证书链的存放位置。The supported schemes are FILE, PKCS11, PKCS12, JKS and JCEKS. The storage location of private key and certificate chain is appointed.

alias

alias 与 KeyStore 定义的 alias 的含义相同。一个私钥、证书链容器,允许存放多份私钥、证书链条目,用 alias索引。除了 FILE,PKCS11 不允许缺省 alias 外,其它 scheme均可缺省,缺省的情况下通过 KeyStore.aliases()方法查找第一个 alias,作为隐含 alias。Alias has the same meanging as the alias defined by KeyStore. A container with private key and certificate chains allows storing multiple private keys and certificate chains, with the alias as the index. Except that FILE and PKCS11 do not allow the default alias, the other scheme can be default, and find the first alias through KeyStore.aliases() method in the default case as implicit alias.注意:JDK 在 alias 的管理上没有明确的规范进行约定,实现上非常混乱。例如, PKCS12 可以使用空串 alias,但是 javax.net.ssl.KeyManagerFactory,却搜索不出这样的 alias,行为与JKS,JCEKS 不一致。同样,PKCS11 类型的 KeyStore,可以通过 KeyStore.aliases()搜索出这样的 alias,KeyStore.loadKey()的时候却又失败。所以,尽量避免缺省 alias。Note: JDK has no clear norms on the alias management and the implement is very confusing. For example, PKCS12 can use empty string alias, but javax.net.ssl.KeyManagerFactory can not search this alias, so the behavior is inconsistent with JKS and JCEKS. Similarly, the KeyStore of PKCS11 can search this alias by KeyStore.aliases(), but fails by KeyStore.loadKey(). So, try to avoid the default alias.

path

path 是一个文件系统路径,在不同的 scheme 下有不同的解释。Path is a file system path, and has different interpretation under different scheme.1. FILE 的情况下,私钥、证书链分别存储为.key 文件与.p7b 文件。path 是一个目录名。

例如 :” file:abc@/work” ,指定 了私钥文件 /work/abc.key ,证书链文件 /work/abc.p7b。The private key and certificate chain are separately stored as .key file and .p7b file in the FILE case. The path is a directory name. For example, “file:abc@/work”, specifies the private file /work/abc.key and certificate chain file /work/abc.p7b.

2. PKCS11 的情况下,path指定了 pkcs11 配置文件,例如,对于 epass2003 这样一个支持PKCS11 的 TOKEN,可以找到 PKCS11驱动,建立配置文件:The path specifies the pkcs11 configuration file in the PKCS11 case. For example, for epass2003 such a TOKEN supporting PKCS11, PKCS11 driver can be found and configuration file can be created./work/epass2003-pkcs11.cfg:name = epass2003library = c:/work/eps2003csp11.dll那么,”abc@/work/epass2003-pkcs11.cfg” 这样一个 location,指明了 pkcs11 容器中

300

Page 301: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

abc alias对应的私钥、证书链。Then, “abc@/work/epass2003-pkcs11.cfg” such a location, indicates the private key and certificate chain corresponding to the abc alias in the pkcs11 container.

3. PKCS12、JKS、JCEKS,的情况下,path 为相应 KeyStore 文件的路径。例如”pkcs12:/work/client.p12”,指明了使用 client.p12 文件里面的第一个 alias对应的私钥、证书链。特别的,因为 CA 可以签发 PKCS12私钥证书包,这样的证书包只放置一对私钥、证书链,这样的证书包默认使用空串 alias,也只有这种情况下,使用缺省 alias 是合理的。In the PKCS12, JKS and JCEKS cases, the path is the path corresponding to the KeyStore file. For example, “pkcs12:/work/client.p12” indicates using the private key and certificate chain corresponding to the first alias in the client.p12 file. In particular, because CA can issue the PKCS12 private key certificate package, such a certificate package only places a pair of private key and certificate chain. Such a certificate package default uses empty string alias, and only in this case, using default alias is reasonable.

algorithm

证书相关算法描述,构造为 PublicKeyAlgorithm/PublicKeyLength[/SignatureLength]Certificate related algorithm, constructed as PublicKeyAlgorithm/PublicKeyLength[/SignatureLength].PublicKeyAlgorithm 可取的值为 RSA,DSA,EC。the preferred value is RSA, DSA and EC.PublicKeyLength,与相应的算法相关。corresponds to the relative algorithm.SignatureLength,可取的值为 256,384,512。The preferred value is 256, 384 and 512.除了初始化 ROOTCA,签署 CA证书之外,algorithm 几乎都可以缺省,之后的命令行操作详细介绍。Except for initializing ROOTCA and issuing CA certificate, the algorighm can almost all be absent, and the next command line operations will be detailed introduced.

开发工具 Development tools

limax.pkix 包提供了 Key 管理工具,证书服务开发工具,证书服务客户端工具。The limax.pkix package provides the Key management tools, certificate service development tools and certificate service client-side tools.

Key 管理工具 Key management tools

limax.pkix.KeyInfo所有相关工具的基础,解释 Location,获取,保存相应的私钥、证书链,同时提供私钥、证书链的更新支持,更新过程保证事务完整性,PKCS11 的情况下,有稍许安全性降低。更新的私钥、证书链首先会暂存到本地存储(当然,私钥是被启动时提供的密码加密的),然后执行一个拷贝过程,最后删除本地暂存数据。其它 scheme 下,必须保证 Location指向的位置可写。另外,直接支持在 KeyInfo 的基础上创建 SSLContext。The basis of all the relevant tools, explains the Location, gets and saves the corresponding private key and certificate chain, while providing the update support to private key and certificate chain. The update process ensures transaction integrity. In the PKCS11 case, there is a little security

301

Page 302: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

reduction. The updated private key and certificate chain will first be temporarily stored to the local storage (the private key is encrypted by the password provided when activating), then execute a copy process, and finally delete the local temporary data. In other scheme cases, it must ensure that the position pointed to by Location is writable. In addition, it directly supports to create SSLContext on the basis of KeyInfo.

证书服务开发工具 Certificate service development tools

limax.pkix.CAService通过实现 X509*Parameter 系列接口,描述 ROOTCA证书,CA证书,EndEntity证书,CRL 请求,Renew 请求,执行相应的签署操作。具有相同 Subject 的 CAService允许进行组合,使用过期时间最迟的 CA证书签署新证书;使用被回收证书的发布 CA签署 CRL。这种方式可以确保提供可持续服务。By implementing the X509*Parameter series interfaces, describes the ROOTCA certificate, CA certificate, EndEntity certificate, CRL request and Renew request, and performs the corresponding signing operation. The CAService with the same Subject allows for a combination, and signs the new certificate with CA certificate with the latest expiration time; signs the CRL issued by the CA with revoked certificate. This way ensures the provision of sustainable services.

证书服务客户端工具 Certificate service client-side tools

limax.pkix.SSLContextAllocator使用 Location指定的私钥、证书链,分配对应的 SSLContext,提供给应用作进一步开发。短连接应用的情况下,每次获取 SSLContext 直接使用即可,长连接的情况下,可以注册一个Listener获取 renew消息,适时更换新的 SSLContext。Uses the private key and certificate chain specified by Location, and assignes the corresponding SSLContext to application for further development. In the short connection application case, the obtained SSLContext can be directly used each time. In the long connection case, a Listener can be registered to obtain the renew message, and timely replace the new SSLContext.

管理命令 Management command

limax.pkix.tool 包,提供了 pkix相关管理工具的实现。一共 9 条命令。以下命令,如果涉及到私钥访问的,都会提示输入私钥使用密码,涉及到私钥创建的,会 2次确认私钥创建密码。The limax.pkix.tool package provides the implementation of the pkix related management tools. There are total 9 commands. The following commands, if it involves the private key access, will be prompted to enter the password of the private key. If it involves the private key creation, it will confirm the creation password of private key twice.

java -jar limax.jar pkix algo

显示支持的算法列表。302

Page 303: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Display the supported algorithm list.

java -jar limax.jar pkix keygen <algo> <PathPrefix>

请求证书签名有两种方式,一是客户端生成私钥,提交公钥给 CA,签署证书,生成证书链;二是 CA 直接生成公钥私钥对,签署证书,将私钥、证书链打包为 PKCS12。第一种形式,私钥不会在网络上传输,具有更高的安全性。该命令为第一种方式提供支持。例 如 , 执 行 java –jar limax.jar pkix keygen “rsa/1024” “/work/client” 生 成 私 钥 文件/work/client.key,公钥文件 /work/client.pub。将 client.pub,提交给 CA签署证书。这种情况下,CA 发还一个.p7b证书链文件,可以命名为 client.p7b,放置在/work 目录下,之后即可通过 Location: ”file:client@/work”使用。There are two ways to request a certificate signature: the first is that the client generates the private key, submits the public key to CA, signs the certificate, and generates the certificate chain; the second is that the CA directly generates the public key and private key pair, signs the certificate, and packages the private key and certificate chain as PKCS12. For the first way, the private key does not be transmitted on the network, with higher security. This command provides the support for the first way.For example, execute java –jar limax.jar pkix keygen “rsa/1024” “/work/client” to genereate the private key file /work/client.key, and public key file /work/client.pub. Submit the client.pub to CA to sign the certificate. In this case, the CA sends back a .p7b certificate chain file. This file can be named as client.p7b and placed in the /work directory and used through Location:”file:client@/work”.

java -jar limax.jar pkix copy <locationSRC> <locationDST>

Location转换工具,scheme 之间进行拷贝,可能覆盖 locationDST相应容器中的 alias 条目。例如,java –jar limax.jar pkix copy “file:client@/work” “pkcs12:client@/work/client.p12” 将 file方式描述的私钥、证书链转换为 PKCS12 方式。注意,PKCS11 不可能作为拷贝的源,因为 PKCS11 规范要求,私钥不可导出,这样的操作必然失败。Location conversion tool, performes copy between scheme, and may cover the alias item in the container corresponding to locationDST.For example, java –jar limax.jar pkix copy “file:client@/work” “pkcs12:client@/work/client.p12” command converts the private key and certificate chain described by file way to PKCS12 way.Note: the PKCS11 can not be used as the source of the copy, because the PKCS11 specification requires that the private key can not be exported, such an operation must fail.

java -jar limax.jar pkix initroot <locationROOT> <subject> <yyyyMMdd> <yyyyMMdd>

初始化 ROOTCA,例如:Initiate ROOTCA, for example:java -jar limax.jar pkix initroot "file:ca@/work/pkix/root#rsa/2048/256" "dc=limax-project,dc=org" "20100101" "20500101"以"dc=limax-project,dc=org"为 subject签署有效期从 2010-01-01 到 2050-01-01 的自签名证书,使用算法 rsa/2048,256位签名。在这里算法参数不可缺省。

303

Page 304: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Sign the self-signed certificate from 2010-01-01 to 2050-01-01 with "dc=limax-project,dc=org" as subject, using the algorithm rsa/2048, 256 bits signature. Here the algorithm parameters can not be absent.

java -jar limax.jar pkix initca <locationROOT> (<locationCA>|<keygen/.pub>) <subject>

<yyyyMMdd> <yyyyMMdd> <OcspDomainOfRoot>

使用 ROOTCA证书,签署一个 CA证书,例如:Use ROOTCA certificate to sign a CA certificate, for example:java -jar limax.jar pkix initca "file:ca@/work/pkix/root" "pkcs12:/work/pkix/ca/ca0.p12#ec/256" "dc=ca,dc=limax-project,dc=org" "20150101" "20200101" "root.limax-project.org"使用上面签署的 ROOTCA证书,签署 subject 为"dc=ca,dc=limax-project,dc=org"的 CA证书。在这里, locationCA 使用 ec/256算法。允许缺省算法参数,缺省的情况下,直接继承ROOTCA 的算法参数。使用 ROOTCA 时也可以使用”file:ca@/work/pkix/root#rsa/1024/512”这样的描述形式,在这里因为证书已经签署,所以 rsa/1024 没有意义,被忽略,512指定了签名长度,覆盖了 CA证书签署时使用的 256长度。“root.limax-project.org” 指定 ROOTCA 关联的 OcspServer 的域名。证书签署方法使用从该域名的派生的 OCSP 服务信息与 CRL 分发点信息填写证书,PKIX 应用系统需要访问这些 URL获得证书回收状态。Use the above signed ROOTCA certificate to sign the CA certificate with "dc=ca,dc=limax-project,dc=org" as subject.Here the locationCA uses ec/256 algorithm. The default algorithm parameters are allowed, and the algorithm parameters of ROOTCA are directly inherited in default case. When using ROOTCA, such description ”file:ca@/work/pkix/root#rsa/1024/512” can be used. Because the certificate has been signed, the rsa/1024 has no meaning and is ignored. Here 512 specify the length of the signature, which override the 256 length used when the CA certificate is signed.“root.limax-project.org” specifies the domain name of OcspServer corresponding to ROOTCA. The certificate signing method uses the OCSP service information derived from this domain and CRL distribution point information to fill the certificate. The PKIX application system needs to access this URL to obtain the certificate status.

java -jar limax.jar pkix initocsp <location> <locationOcsp> <subject> <yyyyMMdd>

<yyyyMMdd>

签署一份 OCSP 服务专用证书,通常情况下这里的 location 就是 locationROOT,ROOTCA 规划为离线访问,需要运行一个关联的 OcspServer,提供下级 CA 的回收状态,该证书即是提供给 OcspServer 用来签名状态响应的。Sign an OCSP service dedicated certificate, usually the location here is locationROOT. The ROOTCA is planned as offline access, and a running associated OcspServer is needed, to provide subordinate CA status. This certificate is provided to the OcspServer used to sign the response.

304

Page 305: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

java -jar limax.jar pkix gencrl <location> <crlfile> <nextUpdate(yyyyMMdd)> [[-](CertDir|

CertFile)]

签署 CRL , 通常情况下 这 里 的 location 就 是 locationROOT , ROOTCA 规划为离线访 问 ,ROOTCA 的下级 CA相关 CRL 必须离线生成,然后提供相应下载。nextUpdate,指出下一次 CRL 更新的预期时间。CertDir|CertFile参数缺省时,如果 crlfile 文件不存在,生成一份包含空列表的 crlfile;如果存在则更新 crlfile 的 nextUpdate信息。CertFile,可以是 X509证书格式,也可以是 PKCS7格式,所有由 location指定的 CA签署的证书被提取出来,回收掉。CertDir 目录扫描一层,扫描内部所有 CertFile执行回收。如果 CertFile 或者 CertDir 之前前缀了”-”,表示执行 recall操作,从 CRL列表中移除相应证书。Sign the CRL, usually the location here is locationROOT. The ROOTCA is planned as offline access, and the related CRL of subordinate CA of the ROOTCA must be generated offline and then provides the download accordingly.nextUpdate, indicates the expected time for next CRL update.CertDir|CertFile, when the parameter is absent, if the crlfile does not exist, generate a crlfile containing the blank list; if exists, update the nextUpdate information of the crlfile.CertFile, can be the X509 certificate format, also can be the PKCS7 format. All the certificates signed by CA assigned by the location are extracted and revoked.CertDir, the only one level directory is scanned, and all the internal CertFile are scanned and revoked.If CertFile or CertDir is prefixed with “-“, the recall operation is indicated to execute, and the corresponding certificate is removed from the CRL list.

java -jar limax.jar pkix ca [path to caserver.xml]

根据 caserver.xml 的配置运行 CA 服务器。后文详细介绍。Run the CA server according to the configuration of the caserver.xml. Please refer the following for the details.

java -jar limax.jar pkix ocsp [path to ocspserver.xml]

根据 ocspserver.xml 的配置运行独立 OcspServer,一般提供给 ROOTCA 使用。后文详细介绍。Independently run the OcspServer according to the configuration of the ocspserver.xml, which usually is provided to the ROOTCA to use. Please refer the following for the details.

CAServer

参考 caserver.xml,进行说明。Refer the caserver.xml for the details.

CAServer顶层元素 CAServer top level element

<CAServer archive="/work/pkix/ca/certs" authCode="123456" domain="ca.limax-project.org">

305

Page 306: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

…………………………………….</CAServer>3 个属性:3 attributes:domain:指定了该 CAServer 运行的服务器域名,由该 CAServer签发的所有证书的 OCSP 服务信息,CRL 分发点信息,均通过该域名派生出来。Domain: specify the server domain name run by this CAServer. The OCSP service information of all the certificated issued by this CAServer, and CRL distribution point information are all derived from this domain.archive:该 CAServer签发的所有证书的存档目录,包括 Renew 过程签发的证书。目录内文件以”certSerialNumber”.cer 方式命名,certSerialNumber 为证书序列号,文件格式为 DER。Archive: the archive directory for all the certificated issued by this CAServer, including the certificates issued in the Renew process. The files in the directory are named with “certSerialNumer”.cer way, and the certSerialNumber is the certificate serial number and the file format is DER.authCode:授权码种子,实际运营时,不应该填写该属性,而应该在服务器启动时输入。这个种子用来生成 TOTP授权码程序,签署证书时使用该程序生成当时的授权码,填写证书服务页面的授权码字段。authCode: authorization code seed, in actual operation, this attribute should not be written here and should be entered during the server launching. This seed is used to generate the TOTP authorization code program. When signing the certificate, the authorization code generated by this program is used through filling in the authorization code field in the certificate service page.CAServer 运行后,当前目录下将生成 authcode.jar,需要填写授权码时,可以 java –jar authcode.jar 运行该程序。When CAServer runs, the authcode.jar will be generated in the current directory. If it needs to fill the authorization code, this program can be executed by java –jar authcode.jar.

在这个界面下,输入这一次运行 CAServer提供的授权码种子,然后回车,切换为如下界面。In this window, enter the authorization code seed provided by CAServer which is running this time, and then press Enter to switch to the below page.

点击该界面的 authCode按钮,生成的 TOTP密钥的将被生成到系统剪贴板,直接粘贴到相应页面即可。Click the authCode button in this window, the generated TOTP key will be generated to the

306

Page 307: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

system clipboard, directly paste to the corresponding page.注意,每次启动 CAServer都会生成新的 authcode.jar,之前的失效,即便使用了同样的授权码种子。Note: each time, when launching the CAServer, the new authcode.jar is generated, and the previous one is invalid, even if the same authorization code seed is used.

CAService元素 CAService element

<CAService location="pkcs12:/work/pkix/ca/ca0.p12" passphrase="123456"/>2 个属性:2 attributes:location:ROOTCA 为 CA签发的私钥,证书链 location。passphrase:location 的私钥启用密码,实际运营时,不应该填写该属性,而应该在服务器启动时输入。location: the location of the private key and certificate chain issued by the ROOTCA for CA.passphrase: the password to enable the private key of the location. In actual operation, this attribute should not be used and should be entered when server launches.

该元素允许多条,所有 location指向的 CA证书必须具有相同的 Subject,有效期不同,整体快要过期时(由最后过期的 CA证书的过期时间减去该 CAServer签发证书的最长有效期决定),应该向 ROOTCA 发起申请,添加配置。已经过期的 CA,可以从配置中剔除。Multiple CAService elements are allowed. All the CA certificated pointed to by location must have the same Subject with different validity period. When the overall elements will expire (which is determined by the expiration time of the last expired CA certificate minus the maximum validity period of certificate issud by this CAServer), the application should request new CA from ROOTCA and add the configuration. The expired CA can be deleted from the configuration.

CertServer元素 CertServer element

<CertServer port="8080" publicKeyAlgorithmForGeneratePKCS12="rsa/1024">………………………………</CertServer>两个属性:2 attributes:port:CertServer 的 HTTP 服务运行端口 8080。CertServer 服务绑定在 localhost 接口上。publicKeyAlgorithmForGeneratePKCS12 : CAServer 支 持 CertServer 帮助生 成私钥, 打 包 成PKCS12 文件发送给申请者的形式,该属性配置了密钥对生成算法。CertServer内部元素,与页面元素的配置选项相对应,可以根据具体需求调整。特别的,subject 输入必须是合法的 X500 Distinguished Name。若有进一步的约定,可以配置 Subject元素的 pattern相关属性,使用 java.util.regex.Pattern 支持的模式,校验 subject 合法性,template属性提供 subject字段初始模板,简化页面输入。NotAfter参数 period 系列属性单位为天。其它元素内mandatory属性,指定相应页面元素是否 disable。非法输入在相应位置用红框提示。port: the HTTP service of the CertServer running on the port 8080. The CertServer service is

307

Page 308: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

bound on the localhost interface.publicKeyAlgorithmForGeneratePKCS12: CAServer supports CertServer to generate the private key, and package into the PKCS12 file to send to the applicant. This attributes configus the private key pair generation algorithm.CertServer internal element, corresponding to the configuration options of the page elements, can be adjusted according to the detailed requirement.In particular, the subject input must be the legal X500 Distinguished Name. If there is a further convention, the pattern related properties of the Subject element can be configured, using the pattern supported by java.util.regex.Pattern, and verifying the legality of the Subject. The template property provides initialization template of the subject field, and simplify the page input.The NotAfter parameter is the period serial attributes and the unit is day.The mandatory attribute within the other elements specifies whether the corresponding page element is disabled.The illegal input is prompted in the corresponding position with a red box.

访问 http://localhost:8080/可以看到如下页面:Access http://localhost:8080/ and enter the below web page:

该页面使用申请方提供的公钥(java –jar limax.jar genkey 生成的.pub 文件)生成.p7b证书链文件。This page uses the public key provided by the applicant (the .pub file generated by java –jar limax.jar genkey) to generate the .p7b certificate chain file.点击 publicKey,页面切换为:Click the publicKey and the page is switched to:

308

Page 309: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

由 CAServer 生成私钥,输出.p12 文件,这里设定的 PKCS12Passphrase(至少 6位),必须选择独立渠道提供给申请方。Generate the private key by CAServer, and export the .p12 file. Here the specified PKCS12Passphrase (at least 6 characeters) must choose the independent channel available to the applicant.

该页面用来回收证书,输入内容可以是独立的证书文件,也可以是之前签发的.p7b 文件。This page is used to revoke the certificate, and the input content can be the independent certificate file, or the previous signed .p7b file.

309

Page 310: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

该页面用来撤销回收,输入内容同上。This page is used to undo the revoke, and the input content is same as the above.

OcspServer元素 OcspServer element

7 个属性:7 attributes:port:指定 OCSPServer 的 HTTP 服务运行端口 80,该服务绑定在所有网络接口上。Port: specifies the HTTP service running port 80 of the OCSPServer, which is bound to all network interfaces.nextUpdateDelay:下一次更新延迟的天数,该属性既用来设置 Ocsp响应中的 nextUpdate信息,也决定了 CRL列表生成周期。nextUpdateDelay: the number of days for next update. This attribute is used to set the nextUpdate information in the Ocsp response, and also determine the generation cycle of CRL list.certificateAlgorithm:CAServer 自动签署 Ocsp签名证书时使用的公钥算法。certficateAlgorithm: the public key algorithm used by CAServer when automatically signing the Ocsp signature certificate.certificateLifetime:CAServer 自动签署的 Ocsp签名证书的以天为单位的寿命。certificateLifetime: the life with the day as unit used by CAServer when automatically signing the Ocsp signature certificate.signatureBits:Ocsp响应签名位数,合法值为 256,384,512。signatureBits: the bit number of the Ocsp response signature, and the legal values are 256, 384, and 512.responseCacheCapacity:每一个 Ocsp响应都需要使用 Ocsp证书签名,为了提高性能,必须提供 cache,cache 设计为 32 个分区的 LruCache,该参数配置了响应 cache总容量。responseCacheCapacity: each Ocsp response requires the Ocsp certificate signature. In order to improve the performance, the cache must be provided. The cache is designed as LruCache with 32 partitions, and this parameter configures the total capacity to response cache.ocspStore:配置 ocsp存储目录路径。

310

Page 311: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

ocspStore: configures the path of ocsp storage directory.ocspStore 管理方式:ocspStore management:1. ocspStore 目录下,按 CA 的 serialNumber区分 CA子目录,每个子目录内的证书命名方

式为:”certSerialNumber”.”certNotAfter”.”revocationTime”.cer,其中,certSerialNumber为被回收证书序列号,certNotAfter 为被回收证书过期时间,revocationTime 为回收时间。In the ocspStore directory, the CA subdirectory is distinguished by the CA’s serialNumer, and the certificate in each subdirectory is named as ”certSerialNumber”.”certNotAfter”.”revocationTime”.cer, where the certSerialNumber is the serial number of the revoked certificate, and the certNotAfter is the expiration time of the revoked certificate, and the revocationTime is the revoke time.

2. 过期的被回收证书自动从相应的 CA子目录内删除,CA 过期,整个子目录都被自动删除。The expired revoked certificate is automatically deleted from the corresponding CA subdirectory. If the CA is expired, the entire subdirectory is automatically deleted.

3. ocspStore 目录本身被监控,如果目录中发现了新文件,则该文件被当作需要回收的证书文件进行识别,识别成功执行回收,然后删除,否则直接删除。需要注意,期望被回收的证书文件首先应该在同一文件系统内其它位置创建出来,然后 MOVE 到ocspStore,而不要 COPY,COPY存在一个写入过程,文件创建出来的时候立刻被监视到,识别操作执行时写入过程可能没有完成,导致识别失败,直接删除,回收操作被忽略。The ocspStore directory itself is monitored. If there is new file in the directory, this file is recognized as a certificate which needs to be revoked to be distinguished. The file will be revoked and then deleted if success, or directly deleted. Note: the certificate which is expected to be revoked should first be created in other location within the same file system, and then MOVE to ocspStore, but not COPY because COPY has a writing process. The file is immediately monitored when creating. When the recognization operation is performed, the writing process may not complete, which results in the recogniation failure. The file is directly deleted and the revoke operation is ignored.

4. ocspStore 目录下的 CA子目录被监控,如果某个回收的证书文件被删除,则自动执行Recall操作。The CA subdirectory in the ocspStore directory is monitored. If a revoked certificate file is deleted, the Recall operation is automatically performed.

5. 监控目录的管理方式主要提供给独立 OcspServer 使用,在 CAServer内,还是应该通过管理页面执行 Revoke,Recall操作。The management of the monitored directory is mainly provided to independent OcspServer to use. In the CAServer, the management page is used to perform the Revoke and Recall operations.

CAServer启动时,自动为该服务器签署 Ocsp签名证书,证书即将到期时,自动更新。When CAServer launches, it automatically issues the Ocsp signature certificate for this service. And when the certificate will be expired, it will be automatically renew.

311

Page 312: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

CertUpdateServer元素 CertUpdateServer element

4 个属性:4 attributes:port:指定 CertUpdateServer 的 HTTPS 服务运行端口 443,该服务绑定在所有网络接口上。Port: specifies the HTTPS service running port 443 of CertUpdateServer, which is bound on all network interfaces.renewLifespanPercent:证书寿命过去了该百分比才执行 Renew操作,避免客户端频繁renew增加服务器负荷。renewLifespanPercent: after the certificate life passes this percentage, the Renew operation will be performed, to avoid the client side frequent renew which increases the server load.certificateAlgorithm:CAServer 自动签署 HTTPS 服务器证书时使用的公钥算法。certificateAlgorithm: the public key algorithm used by CAServer when automatically signing the HTTPS server certificate.certificateLifetime:CAServer 自动签署的 HTTPS 服务器证书的以天为单位的寿命。certificateLifetime: the life with the day as unit used by CAServer when automatically signing the HTTPS server certificate.

CAServer启动时,自动为该服务器签署 HTTPS 服务器证书,证书即将到期时,自动更新、重启服务。该服务器仅接受来自同一 CAServer签署的证书的 Renew 请求。被 Revoke 的证书,通不过验证,无法 Renew。When CAServer is launched, it automatically signs the HTTPS server certificate for this server. When the certificate will expire, the server automatically renews the certificate and restarts the service. This server only accepts the Renew request from the certificates signed by the same CAServer. The Revoked certificate can not be Renewed because of failed verification.

Trace元素 Trace element

与其它 Limax 服务配置方式相同,见手册正文,运行管理配置一节。Same as the other Limax service configuration. Please refer the Operation Management Configuration section in the manual.

OcspServer

独立运行的 OcspServer。ROOTCA离线运行,所以必须启用独立的 OcspServer 发布由该 ROOTCA签发的 CA 状态。Independent running OcspServer.The ROOTCA runs offline, so the independent OcspServer must be launched to issue the CA status signed by this ROOTCA.

OcspServer顶层元素 OcspServer top layer element

9 个属性:9 attributes:

312

Page 313: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

domain:指定该 OcspServer 运行的服务器域名。Domain: specifies the server domain name of this OcspServer running.location:Ocsp签名证书的 locationlocation:the location of Ocsp signature certificationpassphrase:location 的私钥启用密码,实际运营时,不应该填写该属性,而应该在服务器启动时输入。Passphrase: the password to enable the private key of location. In actual operation, this property should not be used and should be input during the server launching.cRLFile:ROOTCA签发的 CRL 文件的路径。cRLFile: the path of CRL file signed by ROOTCA.port,nextUpdateDelay,signatureBits,responseCacheCapacity,ocspStore:这 5 个属性的描述见 CAServer 一节中 OcspServer元素的说明。Port, nextUpdateDelay, signatureBits, responseCacheCapacity, ocspStore: the descriptions of these 5 attributes are described in the OcspServer element of the CAServer section.

Trace元素 Trace element

与其它 Limax 服务配置方式相同,见手册正文,运行管理配置一节。Same as the other Limax service configuration. Please refer the Operation Management Configuration section in the manual.

与测试配置相对应的命令举例 Example of commands corresponding to the

test configuration

所有密码提示下都输入 123456。Enter 123456 in all password prompts.

创建并运行 ROOTCA Create and run ROOTCA

1. 签署 ROOTCA Sign ROOTCAjava -jar limax.jar pkix initroot "file:ca@/work/pkix/root#rsa/2048/256" "dc=limax-

project,dc=org" "20100101" "20500101"2. 签署 ROOTCA 的 Ocsp签名证书 Sign the Ocsp signature certificate issued by ROOTCA

java -jar limax.jar pkix initocsp "file:ca@/work/pkix/root" "pkcs12:/work/pkix/root/ocsp.p12#rsa/2048" "cn=OCSP Responder,dc=limax-project,dc=org" "20170101" "20200101"3. 初始化 ROOTCA 的 CRL Initiate CRL of ROOTCA

java -jar limax.jar pkix gencrl "file:ca@/work/pkix/root" /work/pkix/root/ca.crl 201710014. 配置域名服务器,将 root.limax-project.org 解析到运行独立 OcspServer 的机器的 IP 上,然后 运 行 OcspServer 。 Configure the domain name server, resolve the root.limax-project.org to the IP of the machine which runs the independent OcspServer, and then run OcspServer.

313

Page 314: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

java -jar limax.jar pkix ocsp ocspserver.xml

签署并运行 CAServer(可以签署第一个 CA 使用几次以后,签署第二个 CA)Sign and run

CAServer (sign the first CA and use it several times, then sign the second CA)

1. 使用 ROOTCA签署第一个 CA Use ROOTCA to sign the first CAjava -jar limax.jar pkix initca "file:ca@/work/pkix/root"

"pkcs12:/work/pkix/ca/ca0.p12#ec/256" "dc=ca,dc=limax-project,dc=org" "20150101" "20200101" "root.limax-project.org"2. 使用 ROOTCA 签署第二 个 CA(subject 相同)Use ROOTCA to sign the second CA (the

same as subject)java -jar limax.jar pkix initca "file:ca@/work/pkix/root"

"pkcs12:/work/pkix/ca/ca1.p12#rsa/2048" "dc=ca,dc=limax-project,dc=org" "20170101" "20300101" "root.limax-project.org"3. 配 置域名服 务 器 ,将 ca.limax-project.org 解析到 运 行 CAServer 的机器 的 IP 上 。

Configure the domain name server, and resolve ca.limax-project.org to the IP of the machine which runs CAServer.java –jar limax.jar pkix ca caserver.xml

4. 拷贝出当前目录下生成的 authcode.jar,在证书管理页面中使用。Copy the generated authcode.jar in current directory and use it in the certificate management page.

现实应用的考虑 Consideration of actual application

规划的考虑 Consideration of plan

1. 公钥系统仅仅是技术规范,一个基础设施。The public key system is just a technical specification, an infrastructure.

2. 如果要达成系统间相互信任的目标,必须在系统间达成相应的协议,所有参与者必须遵从协议的约束。If the goal of mutual trust between systems needs to be achieved, the corresponding agreement between systems must be reached and all participants must follow the constraints of the agreement.

3. 通常情况下,设计审计规范,监督协议的执行是必要的。Usually, it is necessary to design the audit specification and supervise the implementation of the agreement.

4. 例如,公共域 CA签发一份网站证书,向用户证明网站的真实性。一旦这样的 CA 为域名劫持者签发同样的证书,用户必然被欺骗。所以,公共域 CA 必须是可信第三方,麻烦在于可信第三方不可自证,所有的公共域 CA 都提供审计,剩下的问题只能扔给法律。For example, the public domain CA signs a website certificate to prove the authenticity of the site to the user. Once such a CA issues the same certificate to the domain name hijackers, the user must be deceived. Therefore, the public domain CA must be a trusted third party. The trouble is that the trusted third party is not self-certified, so all public domain CA provide the audit and the remains issues belong to the law.

314

Page 315: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

5. 可以这样理解,公钥系统提供的信任,是现实中的信任向电子化系统的延伸,现实中的信任才是真正的基础。It can be understood that the trust provided by the public key system is the extension of the reality trust to the electronic system. The reality trust is the real basis.

6. 确定现实信任关系的系统之间,可以自己约定证书签署字段的模式,自己解释相应的字段。Limax 重新解释证书策略,提供枚举 limax.pkix.X509CertificatePolicyCPS,需要的情况下可以继承该枚举,为私有的信任系统提供一个最基本的划分。Determine that the systems with the reality trust relationship can arrange the signed field model of the certificate by themselves and explain the corresponding fields by themselves. The Limax reinterprets the certificate policy, provides enumerated limax.pkix.X509CertificatePolicyCPS. This enumeration can be inherited if necessary, providing a basic partition for the private trust system.

运营的考虑 Consideration from operation

1 ROOTCA1.1 有效期内私钥必须安全保存,可以选择各种线下方式,PKCS11智能卡可以作为

一个选择,但不一定是最好的,硬件失效也应该考虑In the period of validity, the private key must be safely saved. A varity of offline ways can be chosen. The PKCS11 smart card can be used as a choice, but not is the best one because the hardware failure should be considerated.

1.2 注意 Ocsp签名证书过期问题,一旦 Ocsp签名证书过期,OcspServer 自动退出。The Ocsp signature certificate exipration issue should be noted. Once the Ocsp signature certificate is expired, the OcspServer automatically exits.

1.3 Ocsp签名证书的实际过期时间由证书过期时间减去 nextUpdateDelay 决定,因为签名证书没有理由为自己有效期范围外的行为背书。The actual expiration time of Ocsp signature certificate is determined by the certificate expiration time minus nextUpdateDelay, bacause the signature certificate has no reason to endorse the behavior beyond its validity period.

1.4 ROOTCA 如 果 要回收 CA 证书, 应 该执行 2 步操作,首先将 CA 证书上 传 到OcspServer ,移动 到 ocspStore 目 录 ; 接 下 来 使 用 gencrl 签署 CRL , 上 传 到OcspServer cRLFile指定位置。If the ROOTCA wants to revoke the CA certificate, the two steps should be performed. First, upload the CA certificate to OcspServer and move to ocspStore directory; then, use gencrl to issue CRL, and upload to the location specified by OcspServer cRLFile.

1.5 ROOTCA 有责任根据上一次签署 CRL 时设置的 nextUpdate日期更新 CRL并上传到OcspServer 服务器主机。The ROOTCA is responsible for updating CRL and uploading it to OcspServer server host according to the nextUpdate date set when signing CRL last time.

2 CAServer2.1 安全的服务器主机(网络安全,物理安全都必须考虑),毕竟 CA私钥存储在上

面,可以考虑使用 PKCS11智能卡。The safe server host (network security and physical security must be considered).

315

Page 316: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

After all, the CA private key is stored on it, and the PKCS11 smart card can be considered.

2.2 证书管理服务绑定在 localhost 上,实际上,操作者不应该直接登录 CAServer操作管理证书,必须架设反向代理,这里故意增加了一个可审计环节。The certificate management service is bound on localhost. Actually, the operator should not directly login CAServer to operate certificate management, and must set up a reverse proxy. Here a auditable link is purposely added.

2.3 启动 CAServer 生成的 authcode.jar,虽然可以随意分发,但也只是在 authCode 种子足够安全的前提下。如果怀疑出现了泄密问题,可以重新启动 CAServer,重新设置种子生成新的 authcode.jar。Start the authcode.jar generated by CAServer. Although it can be freely distributed, it is only in the pre-condition that the authCode seed is enough safe. If a leak problem is doubtable, it could re-start CAServer and re-set seed to generate the new authcode.jar.

2.4 执行证书管理服务的机器,防木马是必须的。The anti-Trojan is necessary for the machine to run the certificate management service.

2.5 注意 CA证书过期问题,提前向 ROOTCA 发出新的申请。The CA certificate expiration issue should be noted and send the new application to ROOTCA in advance.

2.6 设计定时执行脚本,收集存档目录内的证书,根据设计规划管理所有已签发证书,收集之后清理存档目录。Design timing execution script to collect the certificate within the archive directory, manage all issued certificated according to the design plan, and clean up the archive directory after collection.

3 服务器内存许可的前提下,尽量配置大的 Ocsp响应 cache。In the pre-condition that the server memory allows, try to configure a large Ocsp response cache.

4 对外提供的服务,使用 HTTP,HTTPS 协议,可以架设相应的正向代理。The external service, using HTTP and HTTPS protocols, can set up the corresponding forward proxy.

5 运行 ntp 服务,同步时钟。Run ntp service to synchronize the timer.

6 选择抗攻击,安全的域名服务。Choose anti-attack and safe domain name service.

7 为了达成抗攻击、负载均衡的目标,OcspServer 与 CAServer均可以部署多份。这种情况下,签署证书使用任意一台 CAServer即可;REVOKE,RECALL操作可以考虑在服务器端定时执行简单脚本,到特定位置获取操作数据,在 ocspStore 目录内直接操作。In order to achive the anti-attack and load balance goals, the OcspServer and CAServer can be deployed multiple copies, and any a CAServer can be used to sign certificate. The REVOKE and RECALL operations can be considered to timing execute the simple script on the server-side, obtaining the operation data on the specified position and directly operating in the ocspStore directory.

316

Page 317: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

开发的考虑 Consideration from development

1 规划阶段设计的证书字段、策略解释方式必须在开发的时候正确执行。The certificate field designed in the plan stage, and strategy explaination way must be correctly executed in the development.

2 CAServer签发的证书链,自带了签发 CAServer 的 ROOTCA证书,简单情况下的验证是足够的。如果 ROOTCA 本身过期更新了,或者需要验证别的 ROOTCA派生出来的证书,就 必 须 把 更 新 的 、 别 的 ROOTCA 加 入 信 任 , 例 如 ,limax.pkix.SSLContextAllocator.addTrust(),提供了这样的方法。The certificate chain signed by CAServer contains the ROOTCA certificate which issued CAServer. The verification in the simple case is enough. If ROOTCA itself is overdue and renewed, or it is necessary to verify the certificate derived from the other ROOTCA, the renewed or other ROOTCA must be added to the trust. For example, limax.pkix.SSLContextAllocator.addTrust(), provides such method.

3 ROOTCA证书必须通过安全渠道分发,现实中,Windows 更新,JDK 更新,都会更新公共域上的 ROOTCA证书,这一类通过网络的更新,事实上都没有办法保证避免欺骗,也许假设欺骗不会持续发生这一前提,有机会提供某些事后策略。The ROOTCA certificate must be distributed through a secure channel. In reality, the Windows update and JDK update will update the ROOTCA certificate on the public domain. Such type of updates through network, have no way to avoid fraud in fact. Perhaps in the pre-condition that assuming that fraud will not continue, there is chance to provid some strategy afterwards.

4 事实上 Limax提供的工具并没有支持 DSA算法,DSA算法设计之初就是搭配 SHA1 用来签名,2017 以后 SHA1逐渐废弃。DSA1024/SHA256,DSA2048/SHA256,这些算法 JDK能够支持,但是 Windows并不能支持,为了兼容性,Limax工具仅提供了测试证明兼容的算法。EC算法使用的一些特定曲线,由权威机构发布,兼容性没有问题,怀疑论者担心植入后门,RSA算法还是常用选择。In fact, the tools provided by Limax do not support DSA algorithm. In the beginning of design, the DSA algorithm with SHA1 is used to sign. After 2017, the SHA1 is gradually abandoned. DSA1024/SHA256, DSA2048/SHA256, these algorithm are supported by JDK, but not by Windows. So for compatibility, the Limax tools just provide the algorithm to test and prove the compatibility. The EC alogorithm uses some specific curves which are published by authority and have no compatibility issue. The skeptics worry about implantating the back door, so the RSA algorithm is the common choice.

Key分发系统 Key distribution system

为了建立跨服务器、跨项目、跨组织机构的信任关系,Limax集成 Key 分发系统,在 PKIX框架的基础上,实现安全可靠的 Key 分发。获得的 Key 可以用来签名,加密,确保信任系统间安全共享信息。

317

Page 318: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

In order to establish a cross-server, cross-project, and cross-organization trust relationship, the Limax integrates the Key distribution system, which implements a safe and reliable Key distribution based on the PKIX framework. The obtained Key can be used to sign and encrypt, to ensure the safe sharing of the information between the trusted systems.

设计原则 Design principles

基本原理 Fundamental

1. 服务器维护一个定时生成的随机数 masterKey 的数组,使用 timestamp索引。The server maintains a masterKey array of timed-generated random numbers, using timestamp index.

2. 客户端生成 nonce,连同信任域名字 group,发送给服务器。The client side generates a nonce, along with the trusted domain name group to send to the server.

3. 服务器执行 key = Hash(masterKey, nonce, group)。timestamp和 key 发还客户端。The server executes key = Hash(masterKey, nonce, group). The timestamp and key are sent back to client side.

4. 客户端组合 timestamp,nonce,group形成 keyIdent。The client side combines the timestamp, nonce and group to form the keyIdent.

5. 客户端使用 key签名或者加密消息,连同 keyIdent 发送给接收客户端。The client side uses key to sign or encrypt the message, along with the keyIdent to send to the receiving client.

6. 接收客户端使用 keyIdent向服务器请求对应的 key,用来验证或者解密消息。The receving client uses keyIdent to request the corresponding key from the server, to verify or decrypt the message.

信任关系设计 Trust relationship design

1. 通过约定证书签署方式,建立信任关系。Establish the trust relationship through agreeing the certificate sign mode.

2. 用 KDSName约定 Key 分发系统名,必须使用有效的域名。Use KDSName to agree the Key distribution sytem name, which must uses a valid domain name.

3. 服务器证书的 Subject 中的 RDN CommonName指定了 KDSName,其它 RDN 不作约定。The RDN CommonName in the Subject of the server certificate specifies KDSName, and the other RDN has no agreement.

4. 同一 Key 分发系统中的所有服务器证书必须由同一 CA签署。All the server certificates in the same Key distribution system must be signed by the same CA.

5. 客户端证书的 SubjectAltName 中必须有一个 DNS 条目,指定 KDSName。The SubjectAltName in the client certificate must have a DNS entry, which specifies the KDSName.

318

Page 319: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

6. 客户端证书的 SubjectAltName 中可以有多个 URI 条目,指定 group.The SubjectAltName in the client certificate can have multiple URI entries, which specify the group.

7. 同一 CA签名的具有相同 KDSName,group 的客户端证书,在 Key 分发系统中相互信任。例如,同一 CA 为系统 A,B,C,签署客户端证书,使用相同 KDSName,其中 A 包含group G0,G1,B 包含 group G0,G1,C 包含 group G0。在 G0内,A,B,C 可以互相通讯,G1内 A,B 可以互相通讯。The client certificates signed by the same CA have the same KDSName and group, and trust each other in the Key distribution system. For example, the same CA signs the client certificate for the system A, B, and C, using the same KDSName, where A contains the group G0 and G1, B contains the group G0 and G1, and C contains the group G0. In the G0, A, B and C can communicate with each other, and in the G1, A and B can communicate with each other.

8. 签发客户端的 CA 不一定需要与签发服务器的 CA相同。不同 CA签署的具有相同KDSName,group 的客户端证书没有信任关系(实现上生成 Key 的时候客户端证书Issuer同时参与 HASH 计算)。The CA which issues the client certificate does not necessarily need to be the same as the CA which issues the server certificate. The client certificates signed by the different CA with the same KDSName and group have no trust relationship. (When generating the Key on the implementation, the client certificate’s issuer also participate the HASH calculation at the same time.)

服务器设计要点 Server design essential

1. 同一 CA签署的具有同一 KDSName 的服务器证书,唯一决定了一个 Key 服务网络。The server certificate signed by the same CA has the same KDSName, and uniquely determines a Key service network.

2. 定期生成新的 masterKey,为消息发送客户端分配 key,老的 masterKey保留一定时间,保证消息接收客户端能够用 keyIdent 还原之前的 key。Regularly generate the new masterKey, and distribute the key for the message sending client. The old masterKey should retain a certain period of time, to ensure that message receiving client can use keyIdent to restore the previous key.

3. Key 服务网络设计成基于 DHT 的 P2P 网络,masterKey 数组在服务网络中分发。P2P 方式下服务网络规模不受限制,最大限度保证系统健壮性。The Key service network is designed as a P2P network based on DHT, and the masterKey arrary is distributed on the service network. In the P2P mode, the size of the service network has no limit to ensure the system robustness in the maximum.

客户端设计要点 Client design essential

1. nonce 用当前时间生成,有机会 cache 服务器响应,最大限度降低服务器负荷,减少客户端阻塞的机会。The nonce is generated with the current time, and cache the server response if possible to minimize the server load and reduce the chance of the client block.

319

Page 320: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

2. nonce 的生成精度可调,可以在签署证书时直接约定,group 使用 URI 表示,利用 URI中 fragment 部 分 。例如 , G0#10s ,约定 了 使 用 group G0 时 , 以 10s 为单位生 成nonce,许可的单位为 s,m,h,分别为秒,分,小时,如果不带单位,默认为毫秒。fragment 不存在或者解析失败使用系统默认参数,fragment 不作为 group 一部分。The generation accuracy of nonce is adjustable, and can be directly agreed when signing the certificate. The group is represented with URI, using the fragment part of URI. For example, G0#10s, agrees that when using group G0, generates the nonce with 10s as the unit. The permitted units are s, m and h, respectively as second, minute and hour. If there is no unit, the default is milliseconds. If the fragment does not exist or parse fails, the system default parameters are used, and fragment is not part of group.

3. 客户端发起请求前,预先根据自己的证书检查 group 合法性,避免增加不必要的网络开销。当然,这不妨碍服务器执行检查。Before initiating a request, the client checks the group legitimacy with its own certificate in advance, to avoid increasing the uncessary network spending. Certainly, this does not prevent the server performing the check.

服务器运营 Server operating

签署证书 Sign certificate

这样的签署的服务器证书约定了 Key 分发系统 key.limax-project.orgThis signed server certificate agrees the Key distribution system key.limax-project.org.

服务器配置(keyserver.xml)Server configuration (keyserver.xml)

顶层 KeyServer元素,8 个属性Top-level KeyServer elements, 8 properties.location:服务器证书的 location the location of server certificate.passphrase:location 的私钥启用密码,实际运营时,不应该填写该属性,而应该在服务器启动 时 输入。 the enable password of the location private key. In the actual operation, this property should not be filled and should be entered when launching the server.trustsPath:信任证书路径,location 中已经包含了派生该证书的 ROOTCA,如果要信任其它ROOTCA(或者当前 ROOTCA即将过期,应该将新的 ROOTCA放置在这里),则需要配置

320

Page 321: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

trustsPath,trustsPath 可以是一个文件,也可以是一个目录,如果是目录,遍历一层,获取文件,文件允许任何证书格式,包括 CER,DER,PKCS7,PKCS12,KeyStore,从中获取ROOTCA ,忽略所有错误。 the trusted certificate path. The location includes the ROOTCA derivated from this certificate. If it needs to trust the other ROOTCA (or the current ROOTCA will expire soon, the new ROOTCA should be placed here), it should configure the trustsPath. The trustsPath can be a file or a directory. If it is a directory, traverse one level, and get the file. The file allows any certificate format, including CER, DER, PKCS7, PKCS12, and KeyStore, from which the ROOTCA is obtained, ignoring all errors.revocationCheckerOptions:服务器检测对方证书回收状态的选项,可选的值为 DISABLE, NO_FALLBACK, ONLY_END_ENTITY, PREFER_CRLS, SOFT_FAIL,大小写不敏感,后 4 个参数的解释见 java.security.cert.PKIXRevocationChecker.Option,DISABLE 具有最高优先级,如果配置了DISABLE,其它配置无意义。the option that the server checks the certificate recovery status of the peer. The optional values are DISABLE, NO_FALLBACK, ONLY_END_ENTITY, PREFER_CRLS, and SOFT_FAIL. Case is not sensitive. The definitions of the last four parameters refer to java.security.cert.PKIXRevocationChecker.Option. The DISABLE has the highest priority. If the DISABLE is configured, the other configuration has no meaning.algorithm:指定了 HASH算法,HASH算法决定了服务器返回客户端的 Key长度,例如 SHA1返回 160位 Key,SHA-256返回 256位 Key。specifies the HASH algorithm. The HASH algorithm determines the Key length returned to the client by server, for example, SHA1 returns 160-bit Key, and SHA-256 returns 256-bit Key.master:指定了 P2P 网络的入口服务器列表,逗号分隔,可以是域名,可以是 IP。specifies a portal server list of P2P network, with comma to separate. It can be domain name, or IP.keyLifespan:masterKey 生存周期,单位为天,超出生存周期的 masterKey被服务器删除。the life cycle of masterKey, with day as unit. The masterKey which exceeds the life cyle will be deleted by server.publishPeriod:masterKey 发布周期,单位为天,一个新的周期开始时,服务器向网络中发布一个新的 masterKey。没有配置该属性的服务器,不参与发布。理论上,一个服务网络内部只能有一个发布者,现实中可以配置多个保证冗余性,详见高级话题。 the release cycle of masterKey, with day as unit. When a new publish period begins, the server releases a new masterKey to the network. The server without configuring this property does not participate in this publication. In theory, only a publisher can be existed within a service network. In reality, multiple publishers can be configured to guarantee the redundancy, referring to the advanced topic.

服务器运行 Server running

java –jar limax.jar keyserver [path to keyserver.xml]keyserver.xml 目录下会生成一个 keyserver.dht 文件,该文件定时更新,保存了 P2P 网络中DHT邻居信息,有助于重启服务器时,快速建立邻居关系,不要删除。A keyserver.dht file will be generated in the keyserver.xml directory. This file is updated regularly, and saves the DHT neighbor information in the P2P network, which is helpful to quickly establish the neighbor relationship when restarting the server and should not be deleted.

321

Page 322: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

客户端开发 Client development

签署证书 Sign certificate

这样签署的客户端证书约定了客户端使用 Key 分发系统 key.limax-project.org,具有两个分组prjA和 prjB,其中 prjA 使用 10秒的 nonce 生成精度。This signed client certificate specifies that the client uses Key distribution system key.limax-project.org, with two groups of prjA and prjB, where prjA uses 10 seconds nonce to generate precision.

编程接口 Programming interface

1 limax.key.KeyDesc,Key 描述类,提供 2 个关键方法。limax.key.KeyDesc, Key description class, provides two key methods.1.1 byte[] getIdent();,获取 Key标识。发送端将标识,合并到签名或者加密的消息中

发送。接收端使用 Key标识向服务器请求对应的 Key。byte[] getIdent(); obtains the Key identity. The sender combines the identity to signature or encrypted message to send. The receiver uses the Key identity to request the corresponding Key to the server.

1.2 byte[] getKey(); 获取 Key。发送端用 Key签名或者加密消息,接收端使用 Key验证或者解密。byte[] getKey(); obtains the Key. The sender uses Key to sign or encrypt message. The receiver uses Key to verify or decrypt.

2 KeyException,使用 KeyException.Type getType()返回相关异常。KeyException, uses KeyException.Type getType() to return correspondind exception.2.1 SubjectAltNameWithoutDNSName,证书签署错误,SubjectAltName 中没有包含

DNS 类型条目。SubjectAltNameWithoutDNSName , certificate signing error, SubjectAltName does not contain DNS type entry.

2.2 SubjectAltNameWithoutURI,证书签署错误,SubjectAltName 中没有包含 URI 条目。

322

Page 323: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

SubjectAltNameWithoutURI, certificate signing error, SubjectAltName does not contain URI entry.

2.3 MalformedKeyIdent,key标识解码异常,通常是传输过程中被篡改,消息不可信。MalformedKeyIdent, key identity decoding exception, usually is tampered during transmission and the message is not trusted.

2.4 UnsupportedURI,当前客户端证书不支持指定的 group。UnsupportedURI, the current client certificate does not support specified group.

2.5 ServerRekeyed,服务器无法使用指定 timestamp 生成 Key。通常是 timestamp对应的 masterKey 过期了,这种情况下,信息获取方应该向发送方重新请求消息。ServerRekeyed, the server can not generate a Key with specified timestamp. Usually the masterKey corresponding to timestamp expires, in which case the message receiver should re-request the message from the sender.

3 limax.key.KeyAllocator,Key 分配器类limax.key.KeyAllocator, Key allocator class3.1 KeyAllocator(SSLContextAllocator sslContextAllocator) , 使 用 sslContextAllocator 构

造 一 个 Key 分 配 器 , cache 容 量 1024 。 可 能 抛 出 异 常 类 型SubjectAltNameWithoutDNSName,SubjectAltNameWithoutURI。KeyAllocator(SSLContextAllocator sslContextAllocator), uses sslContextAllocator to construct a Key allocator, and the cache capacity is 1024. The possible thrown exception types are SubjectAltNameWithoutDNSName and SubjectAltNameWithoutURI.

3.2 KeyAllocator(SSLContextAllocator sslContextAllocator, int cacheCapacity) , 使 用sslContextAllocator 构造一个 Key 分配器,指定 cache 容量。可能抛出异常类型SubjectAltNameWithoutDNSName,SubjectAltNameWithoutURI。KeyAllocator(SSLContextAllocator sslContextAllocator, int cacheCapacity), uses sslContextAllocator to construct a Key allocator, with specified cache capacity. The possible thrown exception types are SubjectAltNameWithoutDNSName and SubjectAltNameWithoutURI.

3.3 KeyDesc createKeyDesc(java.net.URI uri),信息发送方,请求一个 KeyDesc,使用uri 指定 的 group , uri 的 fragment 部 分被忽略。 可 能抛出异常类 型UnsupportedURI。如果抛出 KeyException 之外的异常,通常是网络错误,可以重试。KeyDesc createKeyDesc(java.net.URI uri), the message sender, requests a KeyDesc, using the group specified by uri, and the fragment part of uri is ignored. The possible thrown exception type is UnsupportedURI. If the exception other than KeyException is thrown, it is usually a network error that can be re-tried.

3.4 KeyDesc createKeyDesc(byte[] ident),信息接收方,使用收到的 Key标识,请求KeyDesc 。 可 能 抛 出 异 常 类 型MalformedKeyIdent,UnsupportedURI,ServerRekeyed。如果抛出 KeyException 之外的异常,通常是网络错误,可以重试。KeyDesc createKeyDesc(byte[] ident), the message receiver, uses the received Key identity to request KeyDesc. The possible thrown exception types are MalformedKeyIdent , UnsupportedURI and ServerRekeyed. If the exception other

323

Page 324: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

than KeyException is thrown, it is usually a network error that can be re-tried.3.5 String getDNSName(),获取客户端证书中签署的 DNSName。

String getDNSName(), gets the signed DNSName of the client certificate.3.6 Map<URI,Long> getURIs(),获取客户端证书中签署的 group信息,Map 的 Value

为 nonce精度,单位为毫秒。Map<URI,Long> getURIs(), gets the signed group information of the client certificate. The Value of Map is nonce precision, with milliseconds as unit.

3.7 void setHost(java.lang.String httpsHost),指定访问的 Key 服务网络中的部分 Key 服务器的域名或者 IP地址,如果没有使用该方法设置服务器地址,则默认使用getDNSName()返回的域名。如果域名解析出多个 IP地址,这些 IP地址被一一访问,直到没有发生网络错误,或者全部发生错误,抛出网络异常。void setHost(java.lang.String httpsHost), specifies the domain name or IP address of partial Key server in the accessed Key service network. If this method is not used to configure the server address, the default is the domain name returned by getDNSName(). If the domain name resolves multiple IP addresses, these IP addresses are accessed one by one until no network error occurs or all errors occur and throw network exception.

3.8 String getHost(),返回当前正在使用的 Key 服务器域名或者 IP。String getHost(), returns the domain name or IP of current used Key server.

4 系统属性 System properties4.1 limax.key.KeyAllocator.DEFAULT_PRECISION , nonce 精度 ,单位毫秒,默认

3600000。limax.key.KeyAllocator.DEFAULT_PRECISION, nonce precision, with milliseconds as unit, and the default is 3600000.

4.2 limax.key.KeyAllocator.TIMEOUT,网络访问超时,单位毫秒,默认 3000。limax.key.KeyAllocator.TIMEOUT, network access timeout, with milliseconds as unit, and the default is 3000.

应用实现要点 Application implementation essential

1 如果服务提供者提供了新的 ROOTCA,通过 SSLContextAllocator.addTrust加入。If the service provider provides a new ROOTCA, add in via SSLContextAllocator.addTrust.

2 根 据 服 务 提 供 者 的 建 议 决 定 是 否 需 要SSLContextAllocator.setRecovationCheckerOptions。Determine whether the SSLContextAllocator.setRecovationCheckerOptions is needed according to the suggestion from the service provider.

3 根据服务提供者的提供的配置决定 KeyAllocator.setHost。Determine the KeyAllocator.setHost according to the configuration from the service provider.

4 KeyAllocator.createKeyDesc,正确进行异常分类,KeyException 类型属于不可恢复异常,可能需要检查证书配置,或者请求消息发送方重发。其它 Exception属于超时之类的网络异常(这样的异常应该非常少,详见高级话题),应该优先考虑重试操作,唯一例外是证书过期导致连接被 reset,需要确认日志中的“Certificate renew fail”警告。KeyAllocator.createKeyDesc, correctly classifies the exception. The KeyException type is an

324

Page 325: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

unrecoverable exception, and may need to check the certificate configuration, or request the message sender to re-send. Other Exception is network exception like timeout (this kind of exception should be very little and refer to advanced topic), and the re-try operation should be priorly considered. The only exception is that the certificate expiration causes the connection to be reset, and the warning “Certificate renew fail” in the log should be confirmed.

5 应该为消息设计自己的 expire机制,masterKey寿命仅仅应该理解为 expire 的最大值。Should design their own expire mechanism for the message. The masterKey life should only be understood as the maximum value of expire.

高级话题 Advanced topic

时钟同步的考虑 Clock synchronization considerations

既然服务本身依赖了时钟,不论服务器,客户端,都应该使用 ntp同步时钟。这里讨论时钟误差的容忍限度。Since the service itself depends on the clock, the server and client should use the ntp to synchronize clock. Here we discuss the tolerance limit for clock error.1 服务器时钟不同步,理论上,masterKey寿命变为配置寿命减去机器间时钟差异。也就

是机器间 masterKey寿命区间的交集。非常长的配置寿命有助于容忍时钟的不同步。The server clock does not synchronize. In theory, the masterKey lifetime changes to the difference that the configuration life minus the inter-machine clock. That is the intersection interregional the masterKey life between the machines. The very long configuration life helps to tolerate the unsynchronization of the clock.

2 客 户 端 时 钟 不 同 步 , nonce 由 系 统 时 间 除 以 配 置 精 度 取 整 获 得 。(timestamp,nonce,group)组合而成的 keyIdent作为客户端 cache 的 key,不同步的结果即是 cache 中(timestamp,nonce)的项变多,换言之,客户端时钟不同步将导致客户端cache 使用量变大,网络请求变多。The client clock is not synchronized, and nonce is obtained by dividing the system time by the configuration precision. The keyIdent combined by timestamp, nonce and group is the key of client cache. The result of the out-of-synchronization is that the entry of timestamp and nonce in the cache is increased, in other words, the out-of-synchronization of the client clock will cause the usage amount of the client cache to be larger and the request of network to be more.

masterKey 的分发 The distribution of masterKey

masterKey 在 P2P 网络中分发。因为应用环境的不同,所以采用修改过的 Kademlia算法。因为没有 Kademlia参数的现实评估结果,所以这里涉及到许多可以配置的系统参数。The masterKey is distributed in the P2P network. Because of the difference of the application environments, the modified Kademlia algorithm is applied. Because there is no the realistic evaluation result of the Kademlia parameter, there are a lot of configurable system parameters.1 使用 HTTPS 代替 UDP,保证服务器间的严格认证。

325

Page 326: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Use HTTPS to replace UDP to ensure the strict authentication between the servers.2 DHT查找过程中的协议交互,附带交换服务器间 masterKey。查找发起服务器发送自己

节点信息的同时发送已知的 timestamp集合。接收服务器返回邻居信息的同时,通过diff操作,附带返回对方没有的 masterKey集合,以及自己缺少的 timestamp。查找发起服务器解析返回的邻居信息,merge返回的 masterKey集合,向对方 upload缺少的mastreKey集合。The alternation of the protocols in the process of the DHT searching exchanges the masterKey between the servers incidentally. The searching originated server sends the known timestamp set while sending its own node information. The receiving server returns the masterKey set which the other side has none and its lack of timestamp through diff operation, while returning the neighbor information. The searching originated server resolves the returned neighbor information, merges the returned masterKey set, and uploads the lack masterKey set.

3 Node地址长度通过 limax.p2p.DHTAddress.HASH 决定,默认配置 SHA-256.The length of the Node address is decided by limax.p2p.DHTAddress.HASH, and the default is SHA-256.

4 单个 k-Bucket尺寸通过 limax.p2p.Neighbors.BUCKET_SIZE 决定,参考 BT,默认配置为8。The size of the single k-Bucket is decided by limax.p2p.Neighbors.BUCKET_SIZE, refers to BT, and the default is 8.

5 k-Bucket 更新方法不同于 Kademlia,以距离优先为标准,因为服务器不需要过多考虑偶尔在线的问题。新加入 k-Bucket 的 node,设置 PING 定时器,如果 node 已经存在,cancel 之前的定时器。k-Bucket溢出,删除最后一个条目,cancel对应的定时器。PING采 用 连 接 HTTPS 端 口 的 方 式 实 现 , PING 定 时 器 周 期 由limax.p2p.Neighbors.ENTRY_AGE_MAX 决 定 , 默 认 20 分 钟 。 PING 超 时 由limax.key.KeyServer.NETWORK_TIMEOUT 决定,默认 3秒。The update method of k-Bucket is different from that of Kademlia, with the standard that the distance is prior, because the server does not need to too much consider the occasional online problems. The new added node of k-Bucket sets PING timer. If the node already exists, cancel the previous timer. If k-Bucket overflows, delete the last entry, and cacel the corresponding timer. PING is implemented through connecting HTTPS port. The life cycle of PING timer is decided by limax.p2p.Neighbors.ENTRY_AGE_MAX, and the default is 20 minutes. The timeout of PING is decided by limax.key.KeyServer.NETWORK_TIMEOUT, and the default is 3 seconds.

6 Refresh 做 2 次 查 找 , 查 找 自 己 , 查 找 一 个 随 机 地 址 。 Refresh 周 期 由limax.key.KeyServer.NEIGHBORS_REFRESH_PERIOD 决定,默认配置 20 分钟。The Refresh does twice searching, searching itself, and searching a random address. The life cycle of Refresh is decided by limax.key.KeyServer.NEIGHBORS_REFRESH_PERIOD, and the default is 20 minutes.

7 查找动作使用的本地初始集合大小由 limax.key.P2pHandler.BASE_LIMIT 决定,默认 8。被查找的服务器返回集合大小由 limax.key.P2pHandler.REPORT_SIZE 决定,默认 16。查找并行度由 limax.key.P2pHandler.CONCURRENCY_LEVEL 决定,默认 64。期望的查找结果集最大尺寸由 limax.key.P2pHandler.ANTICIPANTION 决定,默认 8。

326

Page 327: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

The size of local initialization set used by searching operation is decided by limax.key.P2pHandler.BASE_LIMIT, and the default is 8. The size of set returned by searched server is decided by limax.key.P2pHandler.REPORT_SIZE, and the default is 16. The parallelism of searching is decided by limax.key.P2pHandler.CONCURRENCY_LEVEL, and the default is 64. The maximum size of expected searching result set is decided by limax.key.P2pHandler.ANTICIPANTION, and the default is 8.

8 如果查找结果数量无法满足预期,则从配置文件中 master属性配置的服务器开始再执行一轮查找。这些配置指定服务器类似于 BT环境中的超级种子,随着邻居表逐渐增大,可以预期这些超级种子被访问的频率逐渐降低。If the number of searching results does not meet the anticipation, a round of searching is performed from the server configured with master attribute in the configuration file. These server specified by configuration are similar with the super seed in the BT environment. As the neighbor table gradually grows, it is expected that the frequency of access to these super seeds will gradually decrease.

9 发布服务器向邻居表中所有地址,所有 master 配置属性指定的地址分发 masterKey。The publish server distributes the masterKey to all addresses in the neighbor table, and all addresses specified by master configuration attribute.

10 既然 refresh频率为 20 分钟,每次 refresh 在本地选择 8 个最近 node,也就是不到 4 层,算作 3 层(Kademlia 使用基于地址前缀的二叉树划分地址空间),地址算法使用 SHA-256,总共 256 层,256 / 3 * 20 / 60 / 24 = 1.19 天,作最保守的估计 masterKey同步到最远端需要 2 天,配置文件中 publishPeriod属性配置为 3 天。正因为如此,服务器优先使用第二新的 masterKey提供服务,严格来说,整个网络的启动至少需要提供 2 天的初始静默期。实际运营,通过指定合适的 master 配置,这个问题就不再重要了。Since the frequency of refresh is 20 minutes, each refresh locally selects 8 nearest node, which is less than 4 layers and is counted as 3 layers (the Kademlia uses binary tree bases on address prefix to divide the address space). The address algorithm uses SHA-256, total 256 layers, 256 / 3 * 20 / 60 / 24 = 1.19 days. It takes 2 days that the masterKey is synchronized to the far end as the most conservative estimate, and the publishPeriod attribute in the configuration file is set as 3 days. So the server priorly uses the second new maserKey to provide the service, strictly speaking, the lauching of the entire network at least needs 2 days for initiation silence period. In the actual operating, this problem is no longer important through specifying the proper master configuration.

11 正 确 处 理 NAT 。 实 现 上 DHTAddress 关 联 InetSocketAddress 表 示 为NetworkID,InetSocketAddress作为查找时的 secondaryKey。NAT内部服务器如果没有分配外部 IP,应该适当提高 Refresh频率,配置较多的 master。Correctly handle NAT. In implementation, the DHTAddress associates with InetSocketAddress and is represented as NetworkID, and InetSocketAddress as the secondaryKey when searching. If there is no allocated external IP in the NAT internal server, the Refresh frequency should be appropriately improved, and more master is configured.

多个 masterKey 分发服务器 Multiple masterKey distribution servers

1 理论上分发服务器只能有一个。327

Page 328: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

In theory, only one distribution server should be accepted.2 实现上,分发服务器启动时,向邻居服务器同步所有 masterKey,记录最新masterKey

的 timestamp,然后启动分发定时器。定时器到期时检查当前最新 timestamp 是否与之前的记录相等, 如 果相等,执行 分 发 ; 不相等, 不执行 , 最 后依据当前最新timestamp启动下一轮定时。In implementation, when the distribution server lauches, it synchronizes all masterKey to neighbor server, records the timestamp of the latest masterKey, and then lauches the distribution timer. When the timer expires, check whether the current latest timestamp is equals to the previous record. If it is equal, execute the distribution; if not, do not execute, and finally launch the next round timing according to the latest timestamp.

3 从实现来看,启用多分发服务器需要满足两个条件。其一,分发服务器间网络通讯良好,分发服务器集共享 master 配置,包括所有的分发服务器地址,这样就保证了最新生成的 masterKey 能立即同步。其二,分发服务器的启动设定一个时间差,保证其中一台服务器的分发动作,发送到其它服务器,设置它们的最新 timestamp 之前,它们的分发定时器没有到期。From the implementation, it needs to meet two conditions to enable multiple distribution servers. First, the network communication between the distribution servers is good. The distribution server set shares the master configuration, including the addresses of all distribution servers, which ensures that the latest generated masterKey can be immediately synchronized. Second, the launch of the distribution server sets a time difference, to ensure that before the distribution operation of one of the server is sent to the other servers and set their latest timestamp, the distribution timer of them is not expired.

4 实际上,timestamp 的精度为毫秒,就算两台服务器同时分发,生成的 timestamp碰撞的概率也非常小,顶多让所有服务器多记录一个 masterKey。Actually, the precision of timestamp is milliseconds. Even though two servers are distributed at the same time, the probability of generated timestamp collision is very small, and at most all servers record one more masterKey.

不使用 P2P 的资源存取模型 Resource access model of P2P is not used

1 理论上,可以通过 timestamp 在 P2P 网络中搜索对应的 masterKey,而不必等待一个较长的发布周期。事实上这不可行。In theory, search the corresponding masterKey in the P2P network via timestamp, without waiting a longer masterKey distribute period. Actually, it is infeasible.

2 搜索是一个高开销行为,恶意客户端可以伪造 timestamp向服务器发起请求,如果提供搜索就给这些恶意客户端提供了攻击 P2P 网络的机会。Searching is a high spending behavior. A malicious client can forge timestamp to send request to the server. If the searching is provided, P2P network attacking become possible.

3 如果要避免攻击,就只能给 timestamp签名,服务器在发起搜索操作之前作一次过滤,这样做需要一个签名 Key,整个服务网络本身就是为了提供 Key而存在,这个问题就变成了一个循环。To avoid such attacking, the timestamp signature is required. The server does a filtering before initiating a searching operation, which needs a signature Key, and the entire service

328

Page 329: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

network itself is to provide Key. So this problem becomes a loop.

孤立服务器,孤立网络 Isolated server, isolated network

1 脱离 P2P 网络的服务器称为孤立服务器,与分发服务器网络断开的网络称为孤立网络。The server separated from P2P network is called isolated server, and the network disconnected from the distribution server network is called isolated network.

2 孤立服务器,或者孤立网络中的服务器可以为客户端生成 key,但有可能不能还原客户端访问非孤立网络中服务器后生成的 keyIdent,原因很简单,新的 timestamp 关联的masterKey 没有收到。An isolated server, or server in the isolated network can generate a key for the client, but it is possible that the keyIdent generated after the client accesses the server in the non-isolated network can not be restored. The reason is simple that the new masterKey associated with timestamp is not received.

3 孤立服务器可以通过日志确认,“Neighbors save NNN entries”记录条目中 NNN = 0,应该怀疑服务器已经孤立。The isolated server can be confirmed by the log. If there is the entry NNN = 0 in the log “Neighbors save NNN entries”, it should suspect that the server has been isolated.

4 现实环境中,证书不能及时 renew,过期之后可能导致服务器孤立。事实上就算签署30 天短期服务器证书,80%寿命开始 renew,也就是说有 6 天的时间可以 renew,每小时重试一次,一共可以重试 144次,过期的情况几乎不可能发生,除非 CA 6 天不在线。In the actual environment, the certificate can not be renewed in time, and the server can be isolated after expiration. Actually, even if the 30 days short-term server certificate is signed, 80% life begins to renew, which means that ther is 6 days time to renew, re-try once an hour and total 144 times. The expiration is almost impossible, unless that CA has been offline for 6 days.

5 服务器证书被 CA回收,这种情况下,应该关闭服务器。The server certificate is recoveryed by CA. In this condition, the server should be closed.

6 孤立网络可以通过日志确认,“MasterKeyContainer CurrentDigester”记录了masterKey 更新事件,两条这样的日志之间的时间差大致等于发布周期,如果该超出 2倍发布周期的该记录还没有出现,意味着该服务器及其邻居都处于孤立网络内。The isolated network can be confirmed by the log. The “ MasterKeyContainer CurrentDigester ” records the masterKey update event. The time difference between two such logs is approximately equal to the public period. If this record beyond twice public periods has not yet appeared, it means that this server and its neighbor are all in isolated network.

7 现实环境中,除非大范围网络故障,孤立网络不会存在,配置文件中使用比较大的publishPeriod 可以增加网络故障耐受容限。In the actual environment, unless a wide range of network failure, the isolated network does not exist. A larger publishPeriod in the configuration file can increase the torlerance limitation of network failure.

329

Page 330: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

客户端的高可用性 High availability of client

1 较大的 nonce 设置可以有效利用 cache,大大减少网络访问。The larger nonce setting can effectively use cache and greatly reduce the network access.

2 尽管客户端不加入服务器的 P2P 网络,但是也提供同样级别的高可用实现,有效减少网络失败。Although the client does not join the P2P network of server, the same level of high availablity is also provided to effectively reduce the network failure.

3 设定网络访问优先级。证书提供的 DNSName地址具有低优先级;KeyAllocator.setHost设置的地址具有中优先级;每次向服务器发起 Key 请求,服务器会连带返回一个随机的 服 务 器 地 址 列 表 , 该 列 表 具 有 高 优 先 级 , 客 户 端 测 量 这 些 地 址 的RTT(limax.key.KeyAllocator.TIMEOUT 决定的超时限度内,超过该限度的服务器直接放弃)。后续 Key 请求按 RTT排序的高优先级列表,中优先级列表,低优先级列表,逐一访问,直到成功,失败的 IP从高优先级列表中删除。Set the network acees priority. The DNSName address provided by certificate has a low priority; the address set by KeyAllocator.setHost has a medium priority; each time a Key request is originated from the server, the server will return a random server address list, and this list has a high priority. The client measures the RTT of these addresses (in the timeout limitation decided by limax.key.KeyAllocator.TIMEOUT, the server exceeding this limitation is directly discarded). The subsequent Key requests are accessed one by one according to the high priority list sorted by RTT, medium priority list and low priority list until success. The failed IP is deleted from the high priority list.

4 证书提供的 DNSName 与 KeyAllocator.setHost提供的域名,映射的 IP地址可能改变,客户端定期解析这些地址,limax.key.ServerEvaluate.DOMAIN_UPDATE 设置了解析频率,默认 5 分钟。The DNSName provided by certificate, domain name and resolved IP address provided by KeyAllocator.setHost may change. The client periodically resolves these addresses. The limax.key.ServerEvaluate.DOMAIN_UPDATE sets the resolution frequency, and the default is 5 minutes.

5 服务器返回的地址列表,每次都可能更新,实现上选择 RTT 最小的 8 个 IP提供使用,可以通过系统属性 limax.key.ServerEvaluate.DYNAMIC_SERVERS修改。The address list returned by server can be updated each time. The implementation selects 8 IP with minimum RTT to use, through system attribute limax.key.ServerEvaluate.DYNAMIC_SERVERS to modify.

6 服务器提供给客户端的最大随机地址列表个数由 limax.key.P2pHandler.ANTICIPANTION决定,默认 8 个。The maximum number of random address list provided by server is decided by limax.key.P2pHandler.ANTICIPANTION, and the default is 8.

7 limax.key.KeyAllocator.ISOLATED_SERVER_THRESHOLD默认为 0,该属性提供了孤立服务器过滤能力,如果服务器返回的随机列表个数小于该值,则认为该服务器不可靠,继续访问下一 IP。小心配置该值,绝不能大于 limax.key.P2pHandler.ANTICIPANTION。The default of limax.key.KeyAllocator.ISOLATED_SERVER_THRESHOLD is 0. This attribute provides the filtering ability of isolated server. If the number of random list returned by

330

Page 331: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

server is smaller than this value, this server is considered unreliable and continues to access the next IP. This value should be carefully configured, and should not bigger than limax.key.P2pHandler.ANTICIPANTION.

安全考虑 Security consideration

1 服务器使用 HTTPS 交互,接收请求一方验证对方的证书的 Subject 中 CN 是否与自己证书相同,同时验证 Issuer名字是否相同。(这里不能比较 CA证书,因为 CA 自身可能renew,CA 的 renew 规则要求 Subject 不能改变,见上一章《PKIX 支持》)The server uses HTTPS interaction to receive the request party to verify whether the CN of Subject in the other party’s certificate is the same as its own certificate, and verify whether the Issuer name is same at the same time. (Here the CA certificate can not be compared, because the CA itself may renew. The renew rule of CA requires that the Subject can not change, and refers to the previous chapter <PKIX support>)

2 每一台接入网络的服务器都必须保证私钥安全,任何一台服务器私钥被盗都可能导致masterKey被盗,危害整个网络,可以考虑采用硬件方式,足够的冗余保证了硬件失效可接受。Each server connecting to the network must ensure the security of private key. The private key of any one of server may cause the mastrKey stolen and harm the entire network. The PKCS11 hardware should be considered, and the enough redundancy ensures that the failure of hardware is acceptable.

3 配置文件中 revocationCheckerOptions属性不要选择 DISABLE,CA 正常运转的情况下,至少考虑使用 SOFT_FAIL。The attribute revocationCheckerOptions in the configuration file does not select DISABLE. In the condition that CA is in the normal operation, the SOFT_FAIL is considered to use at least.

运营维护 Operation and maintenance

1 Key 分发网络应该看作是与 CA同级别的基础设施,直接由 CA 维护是最合理的选择。The Key distribution network should be regarded as the infrastructure as the same level as CA and the most reasonable choice is to be directly maintained by CA.

2 配置文件中,master属性可以配置成层次结构或者网状结构(环不是问题),这样做的实质是把 P2P 网络从动态网络,局部调整为静态,可以进一步提高健壮性。(见《多个 masterKey 分发服务器》一节)In the configuration file, the master attribute may be configured as a hierarchical structure or network structure (loop is not a problem). The essence is to adjust the P2P network from the dynamic network to static to further improve the robustness. (Refer to the section <Mulitiple masterKey distribution servers>)

3 关注服务器孤立,网络孤立的情况。Concern about the sever isolation and network isolation issue.

331

Page 332: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Key 分 发 系 统 本质是 key 的离线协商 Key distribution system is essentially off-line

negotiation of key

1 Key 协商是系统间安全通讯的基础。Key negotiation is the basis for secure communication between systems.

2 常见安全协议例如 SSL,必须在通讯双方同时在线的情况下完成 key 协商。Common security protocols, such as SSL, must complete the key negotiation when both communication parties involved are online at the same time.

3 在线协商不可能覆盖所有应用场景。Online negotiation can not cover all application scenarios.

4 Limax提供的 Key 分发系统通过提供一个能够保证持续在线的第三方机构来实现客户之间的离线协商。The Key distribution system provided by Limax implements the offline negotiation between the clients by providing a third-party organization that ensures continuous online presence.

5 从建立信任的角度看,可信第三方是保障安全的前提,这一点和 CA 完全一致。From the perspective of confidence-building, the credible third parties are the prerequisites for security, which is exactly the same as the CA.

外部数据 External data

来自其它系统的数据就是相对于当前系统的外部数据,简称外部数据。通常情况下,外部数据需要完整性或者机密性的保证。信任系统间的数据交换就是外部数据的交换。Key 分发系统建立了系统间信任关系,分配的 key,可以用来签名或者加密应用数据。这里提供一个外部数据表示模块,完成这样的任务。The data from the other systems is the external data relative to the current system, which is called for short as external data. In general, the external data needs the guaranty of the integrity or confidentiality. Data exchange between the trusted systems is the exchange of external data. The key distribution system estabilishes a trust relationship between systems, and the assigned key can be used to sign or encrypt the application data. Here an external data representation module is provided to complete such a task.

编程接口 Programming interface

limax.key.ed.Transformer

数据转换对象,编码解码外部数据。Data conversion object encodes and decodes the external data.构造函数:Constructor: Transformer(KeyAllocator keyAllocator)

Key 分配器的基础上创建 Transformer对象。332

Page 333: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Create the Transformer object based on Key allocator. Transformer(Map<URI, byte[]> keyAllocator)将Map对象 keyAllocator 看作是一个 Key 分配器,创建 Transformer对象。提供这样的方法可以简化调试,也可以满足极其简单的环境下的应用需求。这里的 Map对象不作拷贝,直接引用,所以创建 Transformer对象之后,也可以修改Map,这种情况下建议使用 ConcurrentHashMap,避免并发异常。The keyAllocator of Map object is regarded as a Key allocator, creating the Transformer object. Providing such a method can simply debugging, and also meets the application requirement in an extremely simple environment. The Map object here is not a copy but direct reference. So after creating Transformer object, it can modify the Map. In this case, it is recommended to use ConcurrentHashMap to avoid concurrency exception.特别的,这里的 URI允许设置 fragment 部分配置外部数据超时,例如,G0#10s,约定了使用 group G0编码的外部数据 10s超时,许可的单位为 s,m,h,分别为秒,分,小时,如果不带单位,默认为毫秒。 fragment 不存在或者解析失败则不作超时检测fragment 不 作 为 group 一 部 分 。 解 码 外 部 数 据 时 检 测 到 超 时 抛 出limax.key.KeyException,类型为 ServerRekeyed。In particular, the URI here allows that the external data of fragment configuration is set as timeout. For example, G0#10s, which promises that the external data 10s timeout of group G0 encoding is used, and the permitted units are s, m, h, respectively, seconds, minutes, hours. If there is no unit, the default is milliseconds. If the fragment does not exist or the parsing fails, the timeout is not detected. Fragment is not part of the group. A timeout was detected when decoding external data, throwing limax.key.KeyException with type ServerRekeyed.

成员函数:Member function: ByteArrayCodecFunction getEncoder(URI uri, KeyProtector keyProtector, Compressor

compressor)获得一个编码函数对象,使用这个对象执行编码操作。编码原始数据 data,首先使用compressor 指定的算法执行压缩,然后使用 uri指定的 group 分配 key,最后使用keyProtector 指定 的算法加密或 者签名。执行 过 程 中 的 任 何异常都转换 为CodecException抛出。Obtain an encoding function object and use this object to perform the encoding operation. To encode the original data, first perform compression using the algorithm specified by the compressor, then assign the key using the group specified by uri, and finally encrypt or sign using the algorithm specified by keyProtector. Any exceptions during execution are converted to CodecException to throw.

ByteArrayCodecFunction getDecoder()获得一个解码函数对象,使用这个对象执行解码操作。uri,keyProtector,compressor相关信息在编码过程中已经打包在编码结果中了,所以获取解码器不需要任何参数。执行过程中的任何异常都转换为 CodecException抛出。Obtain a decoding function object and use this object to perform the decoding operation. The related information of uri, keyProtector, and compressor has been packaged in the encoding result during the encoding process, so no parameters are required to obtain the

333

Page 334: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

decoder. Any exceptions during execution are converted to CodecException to throw.

limax.key.ed.Compressor

枚举类型,指定压缩方法。如果压缩之后的数据尺寸没有减小,自动输出为不压缩的格式。Enum type specifies the compression method. If the data size after compression does not decrease, automatically output the uncompressed format. NONE

不压缩Not compressed.

RFC2118使用 limax.codec.RFC2118Encode,limax.codec.RFC2118Decode,执行压缩解压。Use limax.codec.RFC2118Encode and limax.codec.RFC2118Decode to execute the compression and decompression.

ZIP使用 java.util.zip.Deflater,limax.util.zip.Inflater,执行压缩解压。Use java.util.zip.Deflater and limax.util.zip.Inflater to execute the compression and decompression.

limax.key.ed.KeyProtector

枚举类型,指定签名或者加密方法。Enum type specifies the signature or encryption method. HMACSHA224 HMACSHA256 HMACSHA384 HMACSHA512 TripleDESCBC AESCBC128解码时,如果检测到签名失败,抛出 CodecException,其中包裹了 SignatureException。When decoding, if a signature is detected as failed, a CodecException is thrown, which wraps SignatureException.

应用考虑 Application considerations

1 多数系统共享数据时,需要标识数据来源。按照安全系统的设计惯例可以将证书链打包传输,提供来源标识。基于下述原因,这里并不提供证书链打包支持。When most systems share data, they need to identify the source of data. According to the design practice of the safety system, the certificate chain can be packaged and transmitted, providing the source identification. The certificate chain packaging support is not provided here for the following reasons.1.1 一份数据一份证书链,空间开销过大。

A piece of data has a certificate chain, which causes the large space spending.

334

Page 335: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

1.2 即便提供证书链也无法保证来源真实性,假如信任系统内,存在一个恶意系统,这个恶意系统至少可以将任何的数据来源替换为自己。(如果简单附带证书链,可以替换为任意证书链,如果设计上能够提供一个随机串的 Challenge机制,使用私钥签名随机串,则只能替换为自己)Even if the certificate chain is provided, it can not guarantee the authenticity of the source. If there is a malicious system in the trusted system, at least this malicious system can replace any data source with itself. (If a simple certificate chain is included, it can be replaced with any certificate chain. If a Challenge mechanism with random string is provided in design, the private key is used to sign the random string to replace itself.)

1.3 从上一条叙述可以看出,实际上证书链提供的标识,在仅有 2 个参与者的信任系统中才是不可伪造的。Seen from the previous statement, in fact, the identification provided by certificate chain is unforgeable in the trust system with only two participates.

2 信任系统应该根据具体需求,仔细定义交换的数据格式,提供诸如来源标识,时戳,有效期之类的信息。The trust system should carefully define the data format of the exchange according to the specific requirement; provide such information as source identification, timestamp, and expiration date.

3 为了简单,KeyProtector 中,签名与加密实现为二选一。现实应用,例如 IPSEC,尽管AH/ESP 支持同时使用,绝大多数时候没人这样使用。For simplicity, signature and encryption are implemented as two options in KeyProtector. In realistic application such as IPSEC, although AH/ESP supports simultaneous use, no one uses this way most of the time.

ProviderLogin

Limax 框架中,Provider 为应用提供服务。某些应用场景,存在 Endpoint切换 Provider 的需求,从源 Provider携带特定的 Session 数据,传送到目的 Provider,本质就是 Session迁移。使用 ProviderLogin机制,源 Session 数据在目的 Provider 创建 Session 之前安全传送到目的Provider,可以大大简化 Session迁移类应用的开发。In the Limax framework, the Provider provides the service for the applications. In some application scenarios, there is a requirement that Endpoint switches Provider, carring the specific Session data from the source Provider and delivering to the destiontion Provider, which essentially is Session migration. Using the ProviderLogin mechanism, the source Session data is safely transferred to the destiontion Provider before the destiontion Provider creates the Session, which greatly simplifies the development of Session migration applications.

335

Page 336: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

基本原理 Fundamental

流程考虑 Process considerations

通常的登录流程为The usual login process is:1. Endpoint 连接 Switcher,发起登录请求,登录请求包含所有需要访问的 Provider。

The Endpoint connects to the Switcher, and initiates a login request, which contains all the Providers that needs to be accessed.

2. Switcher向 Auany转发登录请求。The Switcher forwards the login request to Auany.

3. Switcher获得 Auany 的授权之后,向请求的所有 Provider 发起上线通告。After obtaining the Auany authorization, the Switcher issues an online notification to all the requesting Providers.

4. 收到上线通告的 Provider,成功初始化 Session 之后,向 Switcher 发出 OK 应答。The Provider which receives the online notification sends an OK reponse to Switcher after successfully initiating the Session.

5. Switcher 收集 Provider 发来的应答,如果全部 OK,向 Endpoint 发送上线通告。The Switcher collects the response sent from the Provider, and sends the online notification to the Endpoint if all the response is OK.

ProviderLogin机制扩展了登录流程的第 3步。Switcher获得 Auany 的授权之后,逐一判断请求 的 Provider 是否需 要 LoginData , 如 果 需 要 ,向 Endpoint 查询 LoginData , 接 下 来向Provider 发起上线通告,附带 LoginData。The ProviderLogin mechanism extends the step 3 of the login process. After obtaining the Auany authorization, the Switcher determines whether the requesting Provider needs the LoginData one by one. If necessary, the Switcher queries LoginData from Endpoint, then sends the online notification to Provider, attaching the LoginData.

安全考虑 Safety considerations

存在两种形式的 LoginData,安全的和不安全的。There are two forms of LoginData, safe and unsafe.1. 来自同一信任域的 Provider 发送的隧道数据可以用来创建安全 LoginData,包含了 label

与 data,详见《Provider 间数据交换》。The tunnel data sent from the same trusted Provider can be used to create safe LoginData, including label and data. Please refer “Data exchange between Providers” for the detail.

2. 应用使用其它方式指定的数据只能创建不安全的 LoginData。The data specified by the application by other means can only create unsafe LoginData.

336

Page 337: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

服务器开发 Server development

Provider 实 现 limax.provider.ProviderListener 时 , 并 列 实 现limax.provider.LoginDataSupport 接口。When implementing limax.provider.ProviderListener, the Provider implements

limax.provider.LoginDataSupport interface at the same time.

public interface LoginDataSupport {}

LoginData 接口仅为 一标签接口, 不 需 要 实 现 任 何 方法。 实 现 该 接口的 Provider 连 接Switcher 时,将报告 Switcher,该 Provider 需要 LoginData。The LoginData interface is only a label interface, and does not need to implement any method. When connecting to the Switcher, the Provider which implements this interface will report the Switcher that this Provider requires LoginData.

ProviderListener.onTransportAdded 方法的实现代码中,可从 Transport 中获取 LoginData。In the source code of ProviderListener.onTransportAdded method, the LoginData can be obtained from Transport.

@Overridepublic void onTransportAdded(Transport transport) throws Exception {

ProviderTransport pt = (ProviderTransport) transport;LoginData loginData = pt.getLoginData();String message;if (loginData != null) {

if (loginData.isSafe())message = "SAFE LoginData " + loginData.getLabel() + " " +

Helper.toHexString(loginData.getData().getBytes());else

message = "UNSAFE LoginData " + Helper.toHexString(loginData.getData().getBytes());

} elsemessage = "NO LoginData";

System.out.println(message);}这里可以看到,如果 Provider 需要 LoginData,客户端没有提供,那么返回的 loginData 为null,是否作为错误,应用自己决定,抛出异常即可关闭客户端的网络连接。 需要注意,onTransportAdded 之后,Transport 上存储的 LoginData 即被清除,这是考虑到 LoginData 数据量可能较大,如果应用忘记清除,浪费内存空间。It should be decided by application itself whether it is error that the returned LoginData is null, if the Provider needs LoginData and the client side does not provide, and the network connection

337

Page 338: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

of client can be closed through throwing exception. It should be noted that after onTransportAdded, the LoginData stored on Transport is cleared immediately; the reason is that the potentially large amount of LoginData data will waste the memory space if the application forgets to clear.

客户端开发 Client development

HTML5 之外的所有客户端 All clients except HTML5

使用 limax.endpoint.ProviderLoginDataManager组织 LoginData,提供 3 个 add 方法。Use limax.endpoint.ProviderLoginDataManager to organize LoginData, and provide 3 add methods.

void add(int pvid, Octets unsafedata);void add(int pvid, int label, Octets data);void add(int pvid, int label, String data);

其中,不使用 label 的方法,表明提供的是不安全 LoginData,带 String参数的 add 方法用于构造 LoginData 的隧道数据来自脚本系统的情况。The meothod without using label indicates that the unsafe LoginData is provided. The add method with a String parameter is used to create the tunnel data of LoginData which comes from script system.如果期望使用字符串作为不安全 LoginData,可以编码成 UTF8格式的 Octets再 add,同时服务器端使用 UTF8 解码,有助于获得最好的兼容性。If the string is expected as unsafe LoginData, the Octets can be encoded as UTF8 format and then add, and the server uses UTF8 to decode, which is helpful to get the best compatibility.

LoginConfig 中选用带 ProviderLoginDataManager参数的静态方法,创建登录信息。Use static create-method with ProviderLoginDataManager parameter of LoginConfig to create login info.

HTML5 客户端 HTML5 client

按照如下模式定义 login参数对象The login parameter object is defined as follows.var login = {

scheme : 'ws',host : '127.0.0.1:10001',username : 'testabc',token : '123456',platflag : 'test',

338

Page 339: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

pvids : [100],logindatas : {

100 : {data : 'unsafedata'

},101 : {

label : 0,data : <base64tunneldatastring>

}}

}var connector = WebSocketConnector(limax, login);

LimaxKey

LimaxKey 简称 Lmk,支持证书方式的客户端登录。客户端登录过程使用约定方式签署的私钥证书包。证书内容中包含了签署者信息,可以非常容易地追溯用户来源,事实上Lmk提供了去中心化的第三方登录机制。LimaxKey is called Lmk for short, and supports certificate-based client login. The client login process uses a private key certificate package that is signed by convention. The signer information is included in the certificate content, and the user source can be traced very easily. In fact, Lmk provides a decentralized third-party login mechanism.

设计要点 Design essential

1. 为了简化客户端实现,提供 Lmk私钥证书包格式 LmkBundle,LmkBundle当前只支持RSA。提供 LmkBundle格式和 PKCS12等常用格式的互相转换工具。To simplify the implementation of the client, the Lmk private key certificate package format LmkBundle is provided, and LmkBundle currently only supports RSA. The conversion tool between LmkBundle format and common formats such as PKCS12 is provided.

2. 提供 Lmk签署服务的实现。支持大型第三方机构,允许 ROOTCA签署特定第三方 CA证书 LmkCA,LmkCA只能为自己的用户签署 LmkBundle。支持小微第三方机构,允许通用 CA 为任何第三方机构签署属于自己的用户的 LmkBundle。The implementation of the Lmk signing service is provided. The large-scale third-party organizations are supported, allowing ROOTCA to sign a specific third-party CA certificate LmkCA. LmkCA can only sign LmkBundles for its own users. The small and micro third-party agencies are supported, allowing generic CAs to sign their own users’ LmkBundles for any third party organization.

3. Switcher,Auany帮助客户端更新证书。客户端登录之后,如果 Switcher,Auany检测到客户端使用 Lmk 方式登录,并且达到证书更新时限,Switcher,Auany 后台请求相应的 Lmk签署服务获得新的 LmkBundle 存储在 Auany 上,客户端下一次登录时,将

339

Page 340: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

LmkBundle推送到客户端保存。(当然,客户端也可以定期到自己的签署机构获取新的 LmkBundle)。Switcher and Auany help clients to renew certificates. After the client logs in, if Switcher or Auany detects that the client logs in using Lmk mode and reaches the certificate renew deadline, Switcher and Auany request the corresponding Lmk signing service to obtain a new LmkBundle and stores it on Auany. When the client logs in next time, LmkBundle is pushed to the client to save. (Of course, the client can also regularly get a new LmkBundle from its signing institution).

4. 特定情况下,允许通过配置,忽略证书过期检测。In certain circumstances, configuration is allowed to ignore certificate expiration detection.

5. LmkBundle 可以用来绑定登录凭证,这样的凭证使用证书更新时限决定有效期。凭证过期,将导致客 户 端 使 用 Lmk 方 式 重新绑定凭证, 这 时 Auany 帮助客 户 端 更新LmkBundle,下一次使用凭证登录的时候,更新的 LmkBundle被推送到客户端保存。LmkBundle can be used to bind login credentials. Such credentials use the certificate update time limit to determine the validity period. Expiration of the certificate will cause the client to re-bind the certificate by using Lmk. At this time, Auany helps the client to update the LmkBundle. When the next time the credentials are used to log in, the updated LmkBundle is also pushed to the client to save.

6. 纯 HTML5 方式暂不支持 Lmk登录,原因在于各种浏览器,HTTPS 客户端证书密码提示的方式不能兼容。保存自动推送私钥证书包也要受到不同操作系统的安装限制。The pure HTML5 mode does not support Lmk login temporarily. The reason is that various browsers and HTTPS client certificate password prompts are not compatible. Saving automatic push private key certificate packages also suffers the installation restrictions of different operating systems.

客户端开发(以 java 客户端为例)Client development (using the java client

as example)

相关类与接口 Relative classes and interfaces

1. limax.endpoint.LmkBundle LmkBundle LmkBundle.createInstance(Octets lmkdata, Octets passphrase);创建 LmkBundle,lmkdata 为保存在客户端的第三方机构为用户签发的私钥证书包文件数据,passphrase 为文件密码。Create an LmkBundle. lmkdata is the private key certificate package file data signed for the users by the third-party organization that is stored on the client. Passphrase is the file password.Octets LmkBundle.save(Octets passphrase);允许客户端自己更改 LmkBundle密码,返回新的文件数据。Allows the client itself to change the LmkBundle password and return new file data.

2. limax.endpoint.LmkUpdater (C#中为委托,C++中为函数对象)(delegate in C#, function

340

Page 341: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

object in C++)void LmkUpdater.update(Octets lmkdata, Runnable done) throws Exception;实现该接口,保存 Switcher推送回来的更新的 LmkBundle 数据,lmkdata存储完成后执行 done。Implement this interface and save the updated LmkBundle data pushed back by Switcher. After lmkdata saved run done.

3. limax.endpoint.LoginConfig其中,4 个版本的 LoginConfig.lmkLogin 方法使用 LmkBundle 创建登录对象。Among them, four versions of the LoginConfig.lmkLogin method use LmkBundle to create a login object.LoginConfig.setLmkUpdater(LmkUpdater lmkUpdater);安装 LmkUpdater对象。Install LmkUpdate object.

客户端实现样式代码 Client implementation style code

LoginConfig loginConfig = LoginConfig.lmkLogin(LmkBundle.createInstance(Octets.wrap(Files.readAllBytes(Paths.get("/work/xxx.lmk"))),Octets.wrap("123456".getBytes())));

loginConfig.setLmkUpdater((lmkdata, done) -> {Files.write(Paths.get("/work/xxx.lmk"), lmkdata.getBytes()); done.run();

});

注意事项 Matters need attention

1. 如果使用 Lmk 方式创建的凭证,那么通过 LoginConfig.credentialLogin 创建登录对象之后,同样需要执行 loginConfig.setLmkUpdater安装 LmkUpdater对象。If the credential created by using Lmk mode is used, it needs to execute LoginConfig.setLmkUpdater to install the LmkUpdater object after creating the login object through LoginConfig.credentialLogin.

2. 从安全角度看,LmkUpdater 应该设计为 LmkUpdater.update(LmkBundle lmkBundle),用户存储 LmkBundle 时自己设置一个密码。这种方式,需要使用 LmkBundle.save 重新设置密码,加密 LmkBundle 后再执行存储,用户通常难以接受。实际实现时,passphrase在登录过程中被上传到服务器,如果需要更新 LmkBundle,即用该 passphrase加密,推送回客户端。所以 LmkBundle.save 方法应该少用,避免冲突。For the security, LmkUpdater should be designed as LmkUpdater.update(LmkBundle lmkBundle). When the user stores the LmkBundle, it sets a password. In this way, it needs to use LmkBundle.save to reset the password, encrypt the LmkBundle and then perform the storage, which is usually difficult for users to accept. In actual implementation, the passphrase is uploaded to the server during the login process. If the LmkBundle needs to be updated, it is encrypted by the passphrase and pushed back to the client. So the

341

Page 342: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

LmkBundle.save method should be used sparingly to avoid conflicts.3. 服务器实现上保证了 LmkBundle 更新事务的完整性,整体完整性的保证必须有客户端的参与 。 具 体 实 现 上 , 如 果登录 过 程 中检测到 Auany 已 经存储了 更新的LmkBundle , Switcher 返 回 给 Endpoint 的 上 线 通 告 中 就 提 供 了 这 个LmkBundle,Endpoint 收到上线通告立即触发 LmkUpdater,lmkdata存储完成后执行done,通告 Auany清除存储的 LmkBundle。The server implementation guarantees the integrity of the LmkBundle update transaction, and the entire integrity of the guarantee must have the client's participation. Specifically, if Auany is detected that an updated LmkBundle has been stored during login, the LmkBundle is provided in the online notification returned by Switcher to the Endpoint. Upon receiving the online notification, the Endpoint immediately triggers LmkUpdater, after lmkdata saved run done, announce Auany to clear the stored LmkBundle.

4. 参见《PKIX 支持》,Location 一节,其中 scheme增加了 LMK 类型,使用上与 PKCS12类似,可以通过 java -jar limax.jar pkix copy <locationSRC> <locationDST>执行格式转换,非 RSA 的情况下执行失败,另外,为了节省空间,Lmk签署服务发行的 LmkBundle 中不包含根证书,向其它格式转换也会失败。Refer to “PKIX Support”, Location section, where scheme adds LMK type. Similar to PKCS12, java -jar limax.jar pkix copy <locationSRC> <locationDST> is used to perform format conversion. Non-RSA execution fails. In addition, in order to save space, the LmkBundle signed by the Lmk signing service does not include the root certificate, and conversion to other formats also fails.

签署 LmkBundle Sign LmkBundle

设计原则 Design Principles

1. Limax 框架提供了 Lmk签署服务,支持签署与回收 LmkBundle。The Limax framework provides Lmk signing service that supports the signing and recycling of LmkBundles.

2. 有能力部署 Lmk签署服务的大型三方机构,可以使用自己的 LmkCA证书运行签署服务,为自己的用户发行 LmkBundle。Large trird-party organizations that have the ability to deploy Lmk signing service can use their own LmkCA certificates to run signing service and distribute LmkBundle for their own users.

3. 没有能力部署 Lmk签署服务的小微三方机构,可以请求自己的客户端证书,访问通用CA证书上运行的签署服务,为自己的用户发行 LmkBundle。The small and micro trird-party institution that has no the ability to deploy Lmk signing service can request its own client certificate, access the signing service running on the generic CA certificate, and issue LmkBundle for its own users.

创建 LmkCA Create LmkCA

java -jar limax.jar pkix initlmkca <locationROOT> (<locationCA>|<keygen/.pub>) <subject>

342

Page 343: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

<domain> <yyyyMMdd> <yyyyMMdd> <OcspDomainOfRoot>使用 ROOTCA证书,签署一个三方机构的 LmkCA,与签署通用 CA证书(参见《PKIX 支持》)的区别在于这里多了一个 domain参数,该参数与 Auany 配置中平台名等价,允许和平台名重名,这意味着这个第三方机构,既支持通常的三方登录方式,也可以支持 Lmk 方式。The difference between using a ROOTCA certificate to sign a trird-party institution's LmkCA and the signing of a generic CA certificate (see PKIX Support) is a more domain parameter that is equivalent to the platform name in Auany's configuration, which allows using the same name as the platform name. This means that this third-party organization supports both the usual third-party login mode and the Lmk mode.例如:For example:java -jar limax.jar pkix initlmkca "file:ca@/work/pkix/root" "file:lmkca@/work/pkix/ca/#rsa/2048" "dc=lmkca,dc=limax-project,dc=org" "lmkca.limax-project.org" "20170101" "20300101" "root.limax-project.org"

签署 Lmk 服务请求证书 Sign Lmk service request certificate

注意,这里的 dNSName,即是 Auany 中对应的平台名。It should be noted that the dNSName here corresponding to platform name in Auany.

运行 Lmk签署服务 Run Lmk Signing Service

java -jar limax.jar pkix lmk [path to lmkserver.xml]参考 lmkserver.xml,进行配置说明Refer to lmcserver.xml for configuration instructions

LmkServer顶层元素LmkServer top-level element7 个属性:Seven attributes:certificateAlgorithm:LmkServer 自动签署 HTTPS 服务器证书时使用的公钥算法。certificateAlgorithm: the public key algorithm used by LmkServer to automatically sign the HTTPS server certificate.

343

Page 344: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

certificateLifetime:LmkServer 自动签署的 HTTPS 服务器证书以天为单位的寿命。certificateLifetime: the lifetime in days that LmkServer automatically signs HTTPS server certificate.constraintNameLength:用户名长度限制。constraintNameLength: the length limit of the user name.domain:LmkServer 的 HTTPS 服务器域名。domain: the HTTPS server domain name of the LmkServer.port:HTTPS 服务器运行端口,默认 443。port: HTTPS server running port, the default is 443.revocationCheckerOptions,trustsPath参见《Key 分发系统》服务器运营一节。Please refer to the "Key distribution system" server operation section for

revocationCheckerOptions and trustsPath.

LmkBundle元素LmkBundle element:2 个属性:Two attributes:certificateLifetime:签署的 LmkBundle 以天为单位的寿命。certificateLifetime: the lifetime in days of the signed LmkBundle.rsaBits:LmkBundle 的 RSA私钥长度。rsaBits: RSA private key length of LmkBundle.

其它元素的配置与 caserver.xml相同,参见《PKIX 支持》The configuration of other elements is the same as that of the caserver.xml, please refer to "PKIX Support".其 中 CAService 的 location 可 以指向 LmkCA , 也 可 以指向通 用 CA , 具 体 的区别在 请 求LmkBundle 时体现。The location of CAService can point to LmkCA, and can also point to a generic CA. The specific difference is reflected in requesting LmkBundle.

请求 LmkBundle Request LmkBundle

使用 HTTP/GET 方法请求需要客户端认证的 HTTPS 服务,使用 JSON组织查询参数,JSON编码之后再 URL编码即可。Use the HTTP/GET method to request the HTTPS service that requires client authentication. Use JSON to organize the query parameters. After the JSON encoding, URL encoding is required.JSON 请求样式为:{ ”u”:”username”, “p”:”passphrase” }The JSON request style is: {"u":"username","p":"passphrase" }.其中:Among them:u 为用户名,必须为非空串,长度小于等于 Lmk签署服务配置的 constraintNameLength,用户名转换为小写签署 LmkBundle。u is the user name. It must be a non-empty string. The length is less than or equal to the

344

Page 345: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

constraintNameLength of the Lmk signature service configuration, and the user name is converted to a lowercase to sign LmkBundle.p 为 LmkBundle 的加密密码。p is the encrypted password of LmkBundle.Limax 框架提供了 java 实现:The Limax framework provides a java implementation:limax.pkix. LmkRequest.createReqeustor(SSLContextAllocator sslContextAllocator, String host);初始化一个 requestor 之后,即可 requestor.fetch(“user”); 为后续用户请求 LmkBundle 了。After initializing a requestor, calling requestor.fetch("user"); can request LmkBundle for subsequent users.

更新 LmkBundle Renew LmkBundle

通常情况下使用 Switcher , Auany 帮助客户端更新 LmkBundle 即可。 Switcher and Auany are usually used to help the client renew the LmkBundle . Limax 框架提供了 java 实现,直接向 Lmk签署服务请求更新:The Limax framework provides a java implementation to renew LmkBundle from LmkServer directly:limax.pkix.LmkBundleUtils.renew(Path path, char[] passphrase);LmkBundle 更新实现流程与请求 LmkBundle稍有不同,其中:The LmkBundle renew implementation flow is a little different from the request LmkBundle, where:JSON 请求样式为:{“p”:”passphrase”},只需要提供密码。The JSON request style is: {"p":"passphrase"}, and only the password is required.使用 LmkBundle 自己的证书执行 HTTPS 客户端认证,如果证书有效期未过半,更新失败。Use the LmkBundle's own certificate to perform HTTPS client authentication. If the certificate is not valid for more than half, the update fails.

注意事项 Matters need attention

1. Switcher,Auany 也会访问 Lmk签署服务更新证书,服务端口必须对 Switcher,Auany打开。Switcher and Auany will also visit LmkSignServer to renew certificate. The service port must be open to Switcher and Auany.

2. Lmk签署服务器,必须运行 NTP 服务,保证时钟同步,避免签署无效的 LmkBundle。LmkServer must run NTP service to guarantee the clock synchronization, and avoid signing invalid LmkBundle.

Limax 服务运营 Limax service operation

配置 Configuration

Switcher免配置,所有配置信息由 Auany推送。Auany 配置中增加一个 lmk元素。345

Page 346: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Switcher is exempt from configuration. All configuration information is pushed by Auany. Add an lmk element to the Auany configuration.5 个属性:Five attributes:location:CA 为当前 limax 运营环境签署的证书的 location。location: the location of the certificate that the CA signs for the current limax operation environment.passphrase:location 的私钥启用密码,实际运营时,不应该填写该属性,而应该在服务器启动时输入。passphrase: the enable password of the private key of the location. In actual operating, this attribute should not be filled in, but should be entered when the server starts.revocationCheckerOptions,trustsPath参见《Key 分发系统》服务器运营一节。Please refer to the "Key distribution system" server operation section for

revocationCheckerOptions and trustsPath.validateDate:配置了当前运营环境是否需要检测 LmkBundle 过期状态。validateDate: configures whether the current operation environment needs to detect the expiration state of the LmkBundle.

虚拟机参数 Virtual machine parameters

limax.auany.LmkManager.defaultLifetime,如果当前运营环境关闭了 LmkBundle 过期状态检测,通过 LmkBundle 创建的凭证使用该参数决定过期时间,默认为 8640000000,即 100 天。只要使用了 LmkBundle 创建凭证,必须设置过期时间,无论当前环境是否检测过期。因为关闭了的过期状态也可能被重新打开。limax.auany.LmkManager.defaultLifetime, if the LmkBundle expiration state detection is turned off in the current operation environment, the credentials created through LmkBundle use this parameter to determine the expiration time. The default value is 8640000000, that is, 100 days. As long as the LmkBundle is used to create the credential, the expiration time must be set regardless of whether the current environment detects expiration. The closed expiration state may also be reopened.limax.auany.LmkManager.renewConcurrency,该参数决定了单个的 Auany,Switcher 服务器发起 LmkBundle 更新请求使用的线程数量,默认 16。limax.auany.LmkManager.renewConcurrency, this parameter determines the number of threads used by the individual Auany and Switcher servers to initiate LmkBundle renew requests. The default is 16.

注意事项 Matters need attention

1. 如果当前运营环境配置为不检测 LmkBundle 过期状态,那么也不会帮助客户端执行证书更新,同一 LmkBundle 用于多个 Limax 运营环境时必须注意到这个问题。If the current operation environment is configured to not detect LmkBundle expiration state, it will not help the client to perform certificate renew. This problem must be noticed when the same LmkBundle is used in multiple Limax operation environments.

2. 启用了过期检测的环境,Switcher,Auany 必须运行 NTP 服务,保证时钟同步。346

Page 347: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

In an environment where expiration detection is enabled, Switcher and Auany must run NTP services to ensure clock synchronization.

动态 XBean Dynamic XBean

支持动态 XBean 的 Zdb 数据库,允许增删,修改 XBean字段的定义,无须在原先的Zdb 数据库上执行数据格式转换,减少维护代价。详见《运行管理》数据转换一节。The Zdb database that supports dynamic XBean allows adding, deleting, and modifying the definition of the XBean field without the need to perform data format conversions on the original Zdb database and reduce maintenance costs. Please refer the "Operation Management" data conversion section for details.

基本原理 Fundamental

1. 动态 XBean由静态字段和动态字段组成,静态字段的调整需要执行数据格式转换,动态字段的调整则不需要。Dynamic XBean consists of static fields and dynamic fields. Adjustment of static fields requires data format conversion, and adjustment of dynamic fields is not required.

2. 不论是静态字段,还是动态字段,在编程访问的方式上没有任何区别。Whether it is a static field or a dynamic field, there is no difference in the way programming access.

3. 动态 XBean将所有动态字段组织为一个 Map,这个 Map 定义为动态数据,在编码上进行了扩展。编码规则为,首先按定义顺序编码所有静态字段,然后编码动态数据数量,随后编码动态数据条目。其中,动态数据条目的 key 为的 serial,value 为动态字段单独编码后获得的字节数组。A dynamic XBean organizes all dynamic fields into a Map. This Map is defined as dynamic data and is extended in code. The encoding rule is to first encode all static fields in the defined order, then encode the number of dynamic data, and then encode the dynamic data entry. Among them, the key of dynamic data entry is serial and value is the byte array obtained after the dynamic field is encoded separately.

4. serial 是实现动态 XBean 的关键,serial 在 XBean 上进行分配,任何一次修改调整必须递增 serial。serial反映了 XBean 的修改历史。Serial is the key to implementing dynamic XBean. Serial is allocated on XBean and any modification must steadly increase serial. Serial reflects the modification history of the XBean.

5. XBean 中读出了当前运行代码不知道的 serial,意味着该 serial对应的字段需要删除,这样的 serial对应的数据在解码过程中即被忽略。The XBean reads the serial that is not known by the current running code, which means that the field corresponding to the serial needs to be deleted, and the data corresponding to this serial is ignored in the decoding process.

347

Page 348: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

6. 新增动态字段,必然分配了所有已存储 XBean 都不知道的最新的 serial,解码过程获得的 XBean 动态数据不可能更新这一字段的值,所以用户看到的就是构造 XBean 时初始化的默认值(除非字段描述中定义了 script,详见后面的讨论)。Adding dynamic fields will inevitably allocate all the latest serials that are not known to the stored XBean. The XBean dynamic data obtained from the decoding process cannot update the value of this field, so what the user sees is the default value initialized when the XBean is constructed ( Unless the script is defined in the field description, please refer the discussion below for the detail).

7. 同一动态字段,如果需要修改定义,那么最新的定义具有最新的 serial,之前的定义必须保留,这些定义反映了当前数据中的历史。设计上,允许提供脚本描述,定义历史serial向新 serial 的转换,解码过程中如果解码出历史 serial,则调用转换过程,将字段转换为最新的版本,更新 XBean对应字段,提供给用户访问,编码存储 XBean 时最新版本被写入数据库。The same dynamic field, if the definition needs to be modified, the latest definition has the latest serial, the previous definition must be retained, and these definitions reflect the history of the current data. Design allows providing a script description, defining the previous serial conversion to the new serial, in the decoding process if the previous serial is decoded, calling the conversion process to convert the field to the latest version, update XBean corresponding field, and provide to the user access. When encoding stored XBean, the latest version is written to the database.

动态 XBean 的描述 Description of dynamic XBean

描述样式 Description style

<?xml version="1.0" encoding="utf-8"?><zdb dynamic="true">

<xbean name="MyXbean" nextserial="1"><variable name="var0" type="int" /><variable name="var1" dynamic="true">

<variable serial="0" type="int"/> </variable>

</xbean><table name="mytable" key="long" value="MyXbean" autoIncrement="true" />

</zdb>

1. zdb元素 dynamic属性决定了该 zdb 中允许动态 XBean。一旦这样定义 zdb,该 zdb 中所有的 XBean 都是动态的,不论是否定义了动态字段。The zdb element dynamic attribute determines that the dynamic XBean is allowed in this zdb. Once zdb is defined in this way, all XBeans in the zdb are dynamic, whether or not dynamic fields are defined.

2. xbean元素的 nextserial属性,决定了下一次修订动态字段定义时应该使用的 serial,这348

Page 349: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

样的 serial被使用之后,需要将 nextserial加一。该属性用于代码生成时的检查,避免错误分配 serial,导致运行时出现错误。nextserial缺省为 0,没有定义动态字段 XBean允许缺省。The nextserial attribute of the xbean element determines the serial that should be used when the dynamic field definition is next modified. After the serial is used, the nextserial must be incremented. This attribute is used for code generation checks to avoid the wrong allocation of serial, which results in runtime errors. Default nextserial is 0, and XBean allows default when no dynamic fields are defined.

3. 字段 var0 为静态字段。Field var0 is a static field.

4. 字段 var1 为动态字段,当前的 serial 为 0,类型为 int。The field var1 is a dynamic field, the current serial is 0, and the type is int.

增加动态字段 Add dynamic field

例如,增加一个 string 类型的字段 var2,添加一个描述片段即可For example, adding a string field var2, add a description fragment

<variable name="var2" dynamic="true"> <variable serial="1" type="string"/>

</variable>这里使用了 xbean属性 nextserial指定的 serial 1,所以应该将 nextserial修改为 2。This uses the serial 1 specified by the xbean attribute nextserial, so the nextserial should be modified to 2.

删除动态字段 Delete dynamic field

例如,删除 var2,删除上述描述片段即可,xbean属性的 nextserial 不修改。For example, deleting var2, delete the above description fragment. The nextserial attribute of the xbean is not modified.

修改动态字段 Modify dynamic field

例如,修改动态字段 var1,新类型为 double,值为之前的值乘以 3.14。For example, modify the dynamic field var1, where the new type is double, and the value is the previous value multiplied by 3.14.

<variable name="var1" dynamic="true"> <variable serial="0" type="int"/> <variable serial="2" type="double" script="$0 * 3.14"/>

</variable>这里定义了 var1 的更新类型,serial 为 2,记得将 xbean 的 nextserial 更新为 3。Here defines the var1 update type, and serial is 2. It should be remembered to update the xbean's nextserial to 3.动态字段定义中,具有最大 serial 的元素描述的信息为该字段的当前描述信息,其它元素提供了历史描述信息。In the dynamic field definition, the information described by the element with the largest serial is

349

Page 350: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

the current description information of the field, and other elements provide history description information.script属性可以填写一个表达式,这里的$0 表示需要依赖 serial="0"的历史值,动态字段的解码基本过程为:The script attribute can fill in an expression, where $0 represents the history value that depends on serial="0", and the basic process of decoding the dynamic field is:1. 如果最新版本 serial对应的数据已经存在,直接解码即可。

If the data corresponding to the latest version of serial already exists, decode it directly.2. 如果不存在,根据 script 描述的依赖关系递归解析依赖值,计算结果作为当前动态字段的值。If it does not exist, the dependency value is recursively parsed according to the dependency described by the script, and the result is taken as the value of the current dynamic field.

3. 递归解析过程中任何一个依赖值不存在,则使用字段构造时初始化的默认值。If any dependency value does not exist during recursive parsing, the default value initialized at the time of field construction is used.

从当前的例子来看,如果读取的 XBean 动态数据中,存在 serial="2"的值,直接解码,初始化 var1,否则检查 serial="0"的值,如果存在,则按 int 类型解码该值,乘以 3.14,获得的结果初始化 var1,如果 serial="0"的值不存在,使用 var1 构造时的默认值,即 0.0。From the current example, if there is a serial="2" value in the read XBean dynamic data, directly decode and initialize var1, otherwise check the value of serial="0", if it exists, then decode it as an int type, which is multiplied by 3.14 and get the result to initializs var1, and if the value of serial="0" does not exist, the default value when using the var1 construct is 0.0.

历史描述信息没有被 XBean 定义范围内的任何 script引用,意味着历史值可能被丢弃,生成代码时将产生警告。在这里,如果 serial="2"对应的 script 没有定义,则会警告 serial=”0”没有使用。The history description information is not referenced by any script within the scope defined by the XBean, meaning that historical values may be discarded and a warning will be generated when the code is generated. Here, if the script corresponding to serial="2" is not defined, it will warn that serial="0" is not used.

script 的设计原则为:The design principle of script is:1. 为了灵活性,允许使用$引用 XBean 中的任何字段,不限于当前动态字段的历史描述。

For flexibility, it is allowed to use $ to refer to any field in the XBean, not limited to the historical description of the current dynamic field.

2. $number 的形式引用了历史动态字段,其中 number 必须小于自己的 serial,并且存在于当前 XBean 定义中,否则生成过程中报错。The form of $number refers to the history dynamic field, where number must be smaller than its own serial and exists in the definition of current XBean, otherwise an error occurs during generation.

3. $word 的形式引用了静态字段,word 必须为有效的静态字段名,否则生成过程中报错。350

Page 351: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

The form of $word refers to a static field, and word must be a valid static field name, otherwise an error occurs during generation.

4. 生成代码只检查$引用的合法性,并不执行语法检查,所以生成之后应该检查生成代码是否有错,如果有错,修订 script 描述,重新生成。The generated code only checks the legitimacy of the $ reference and does not perform syntax checks. Therefore, after generation, should check whether the generated code is wrong. If there is any error, modify the script description and regenerate it.

5. script只允许填写一个合法表达式,如果需要执行复杂操作,可以首先定义一个生成的XBean 可见的静态函数,执行转换。例如 script=”mypkg.MyApp.transform($0)”.A script can only fill in a valid expression. If need to perform complex operations, can first define a static function that is visible to the generated XBean and perform the conversion. For example script=”mypkg.MyApp.transform($0)”.

6. 特别注意,一个动态字段最小 serial元素中定义的 script 表达式计算结果并不能理解为该字段初始值。该值仅仅是解码 XBean 时使用的默认值,并不是构造 XBean 的默认值这一点需要特别注意,通常最小 serial元素中最好不要定义 script 属性,这样就能保证解码默认值与构造默认值一致。In particular, the result of a script expression defined in the smallest serial element of a dynamic field cannot be interpreted as the initial value of the field. This value is just the default value used when decoding XBean. It is not the default value for constructing XBean. This requires special attention. Usually, it is better not to define the script attribute in the smallest serial element. This ensures that the decoding default value is the same as the construction default value.

7. 修改动态字段,设计 script 的时候,正确区分解码默认值与构造默认值对于 insert新记录时,如何正确填写相应字段的值非常重要。When modifing the dynamic field and designing the script, correctly distinguishing the decoding default value and the construction default value is very important for insert new records, how to correctly fill in the value of the corresponding field.

数据格式转换 Data format conversion

使用动态 XBean 可以减少修改 Zdb 描述带来的数据格式转换代价。至少有两种情况需要转换,其一,非动态 XBean 系统向动态 XBean 系统的转换;其二,Zdb 描述修订太多次,开发人员感觉难以维护。Using dynamic XBean can reduce the cost of data format conversions that result from modifying the Zdb description. There are at least two situations that need to be converted. First, it is the conversion from a non-dynamic XBean system to a dynamic XBean system. Second, Zdb describes revisions too often, and developers feel difficult to maintain.

数据格式转换的操作与《运行管理》数据转换一节,完全相同,应该首先熟悉操作过程。The operation of data format conversion is exactly the same as that of the "Operation Management" data conversion section. The developers should first familiarize themselves with

351

Page 352: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

the operation process.

静态转换到动态 Static to dynamic

<?xml version="1.0" encoding="utf-8"?><zdb>

<xbean name="MyXbean"><variable name="var0" type="int" />

</xbean><table name="mytable" key="long" value="MyXbean" autoIncrement="true" />

</zdb>假设原本的 Zdb 这样的描述,已经运行一段时间了。现在需要修订为前面例子定义的形式,加入动态字段 var1。修订 Zdb 描述之后生成代码,运行程序。则会报告错误” convert needed to cast to dynamic”.Assume that the original description of Zdb has been running for some time. Now it needs to modify the form defined for the previous example and add the dynamic field var1. After modifing the Zdb description, generate the code and run the program. It will report the error "convert needed to cast to dynamic".

java -cp ../limax/bin/limax.jar;bin limax.zdb.tool.DBTool -e "convert zdb zdbcov"当前开发环境下,参考上面的命令执行,生成转换代码。In the current development environment, refer to the above command execution to generate the conversion code.编译代码之后,再执行一次上述命令,(注意创建 zdbcov 目录),数据被转换到 zdbcov 中。After compiling the code, execute the above command again (note that the zdbcov directory is created) and the data is converted to zdbcov.备份 zdb 目录,zdbcov 重命名为 zdb,程序即可正常执行。Back up the zdb directory and rename zdbcov to zdb. The program will execute normally.特别注意,动态不可重新转换回静态。In particular, the dynamics cannot be converted back to static.

维护性转换 Maintenance conversion

Zdb 描述中动态字段修订太多次,开发人员感觉难以维护,可以考虑生成代码执行维护性转换,维护性转换的实质就是将所有 XBean读取存储一次,更新到最新版本,清除所有历史数据。继而可以清理 Zdb 描述,删除所有动态字段的历史描述,只保留 serial 最大的元素。The dynamic field in the Zdb description has been modified too many times, and developers feel difficult to maintain, so can consider generating code to perform maintenance conversion. The nature of maintenance conversion is to read all stored XBean once, update to the latest version, and clear all historical data. Then can clean up the Zdb description, delete the history description of all dynamic fields, only keep the largest element of serial.

java -cp ../limax/bin/limax.jar;bin limax.zdb.tool.DBTool -e "convert zdb zdbcov"执行维护性转换同样需要运行这样的命令,结果是为所有以 XBean 为 value 的 table 生成转

352

Page 353: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

换代码,编译代码后重复运行上述命令执行转换。Performing a maintenance conversion also requires running such a command. The result is to generate conversion code for all tables that have an XBean value, and to repeat the above command to perform the conversion after compiling the code.

这里需要注意,即便 XBean 没有描述任何动态字段,也会生成转换代码,因为转换工具不能确定动态字段从来都不存在,还是仅仅在当前的描述中被删除了。所以,开发人员可以根据实际情况清理相应的生成代码,基本原则有两条,其一,确实从来没有定义动态字段其二,动态字段一直只有一条定义,从来没有修改过,能够确信数据库中没有历史信息。最简单的策略是根本不清理,转换时间长一些而已。It should be noted here that even if the XBean does not describe any dynamic fields, the conversion code is generated because the conversion tool cannot determine whether the dynamic field never existed or was simply deleted in the current description. Therefore, developers can clean up the corresponding generated code according to the actual situation. There are two basic principles, first, really never define the dynamic field; second, the dynamic field has only a definition, has never been modified, and can be sure that there is no database history information. The simplest strategy is not to clean up at all, and the conversion time is longer.

如果静态字段的定义被修改了,运行程序自然会报告需要转换,这种情况参考《运行管理》数据转换一节即可。If the definition of a static field is modified, the running program will naturally report that it needs to be converted. Refer to the "Operation Management" data conversion section.

动态还是静态的讨论 Dynamic or static discussion

基本比较 Basic comparison

动态 静态性能 编解码过程较复杂,性能较差 好修改描述的影响 影响小,动态字段的修订,无需

执行数据转换影响大,每次修改都需要转换

安全性 低,动态字段的修订必须非常小心,数据库上执行错误的代码将会造成不可挽回的影响。

高,既然每次修改都需要转换,转换的源可以作为备份,一旦出现问题容易挽回。

Dynamic StaticPerformance The coding and decoding process is

complicated, and performance is poor

Good

Impact of modifying the description

Small impact, dynamic field revisions have no need to perform data conversion

Big impact, need to be converted every time

353

Page 354: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

Safety Low, modification of dynamic field must be very careful, and executing the wrong code on the database will have an irreparable impact.

High, since each modification needs to be converted, the source of the conversion can be used as a backup. Once the problem occurs, it is easy to recover.

修改描述的讨论 Discussion of the modifying description

1. 数据库设计必须仔细斟酌,不到万不得已不应该修改。Database design must be carefully considered and should not be modified if not must.

2. 动态方式不是随意修改设计的理由,通常动态系统都会教坏设计者。Dynamic mode is not a reason to arbitrarily modify the design; usually the dynamic system will take the bad influence on the designer.

3. 通常的修改都是需求变化带来的,讨论好需求,预期一些可能的变化,事先准备,是比较好的方式。The usual modifications are brought about by changes in demand. It is better to discuss the demand, anticipate some possible changes, and prepare in advance.

推荐的方式 Recommended way

1. 项目开始投入使用,推荐使用静态方式。The project starts to be put into use, and static methods are recommended.

2. 维护时,使用转换工具转换数据库,备份原始库,是非常安全的方式。During maintenance, it is a very safe method to use the conversion tool to convert the database and back up the original database.

3. 随着记录数量的增加,转换时间快要超出容忍限度的时候,可以考虑转换为动态方式,降低之后的转换频率。As the number of records increases, the conversion time will soon exceed the tolerance limitation; it can consider converting to a dynamic mode, reducing the subsequent conversion frequency.

4. 一旦使用动态方式需要特别小心,应该在备份的数据上,执行严格的测试,再应用于实际系统,避免造成运营事故。Once the use of dynamic methods requires special care, strict testing should be performed on the backed up data and then applied to the actual system to avoid operational accidents.

QR Code

Limax参考 ISO/IEC 18004-2006提供QR Code编解码支持(Micro QR Code除外)。所有语言版本都实现在单个文件中,没有任何语言标准库之外的依赖,便于引用。Limax provides QR Code codec support (except Micro QR Code) with reference to ISO/IEC 18004-2006.

354

Page 355: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

All language versions are implemented in a single file, without any dependencies except for the language standard library for easy reference.

编码器 Encoder

原型 Prototype

Java QrCode limax.codec.QrCode.encode(byte[] data, int ecl)C# QrCode limax.codec.QrCode.encode(byte[] data, int ecl)C99 QrCode qr_encode(void *data, int size, int ecl)Javascript QrCode encode(data, ecl)

1 参数 Parameter1.1 data,字节数组,特别的,对于 Javascript,提供方法 QrCode.toUTF8,可以将浏

览器中输入的字符串转换为 UTF8字节数组。(可以参考 javascript 目录下的qrcode.html)data, byte array, in particular, for Javascript, the method QrCode.toUTF8 is provided, which can convert the string input in the browser into a UTF8 byte array. (Please refer to qrcode.html in the javascript directory)

1.2 ecl,各语言定义的常数 ECL_L,ECL_M,ECL_Q,ECL_H 决定纠错级别。ecl, the constants ECL_L, ECL_M, ECL_Q, and ECL_H defined by each language determine the error correction level.

2 返回值 Return value2.1 QrCode对象,输入数据过长以至于 VERSION 40 的 QR Code 都不能表示,返回

null。特别的,对于 C99,QrCode对象需要通过 qrcode->release(qrcode);的方式释放。QrCode object, the input data is too long to be represented by the QR Code of VERSION 40, and returns null. In particular, for C99, the QrCode object needs to be released by qrcode->release(qrcode).

2.2 所有 QrCode对象都提供方法 toSvgXML 方法,返回 SvgXML字符串,输出到文件便于浏览器直接显示。All QrCode objects provide the method toSvgXML, return the SvgXML string, and output to the file, which is convenient for the browser to display directly.

2.3 Java , C# 的 QrCode 对象提供方法 getModules() ,返回表示 QrCode 模块的boolean 数组,数组长度开平方取整,即可获得尺寸,C99 中直接访问 QrCode 的成员modules,size即可,通过模块数组可以方便地生成其它形式的表示,比如图片(可以参考 limax.executable.QrCodeTool.java 的实现)The QrCode object of Java and C# provides the method getModules(), returns the boolean array representing the QrCode module, and the length of the array is squared to obtain the size. The C99 directly accesses the QrCode member modules and size. It is easy to generate other forms of representation through the module array, such as images (refer to the implementation of limax.executable.QrCodeTool.java).

355

Page 356: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

ISO/IEC 18004-2006相容性 The compatibility of ISO/IEC 18004-2006

1 只支持单一的数字模式,字母数字模式,字节模式编码,不使用混合模式。输入数据全部符合数字模式选择数字模式,全部符合字母数字模式选择字母数字模式,否则使用字节模式。Only single digital mode, alphanumeric mode, byte mode encoding are supported, and without mixed mode. If all the input data conform to the digital mode, the digital mode is selected. If all the input data conform to the alphanumeric mode, the alphanumeric mode is selected, otherwise the byte mode is used.

2 不支持 ECI,不支持 FNC1,不支持 KANJI,不支持结构链。ECI, FNC1, KANJI, and Structured Append are not supported.

应用讨论 Application discussion

1 通常输入数据应该考虑使用 UTF8编码。Usually input data should be considered using UTF8 encoding.

2 通过 QR Code 交换数据,如果要求生成尽量小的 QR Code,使用纯数字最有效,数字模式生成短编码,加入大写字母,字母数字模式生成较短编码。避免使用小写字母。To exchange data through QR Code, if it is required to generate a QR Code as small as possible, it is most effective to use pure numbers. The digital mode generates short codes; adds uppercase letters; and alphanumeric mode generates shorter codes. Avoid using lowercase letters.

3 如果 QR Code 中间需要嵌入图片,选择较高纠错级别,比如,ECL_Q,ECL_H。If it needs to embed images in the QR Code, a higher error correction level is chosen, such as ECL_Q, ECL_H.

解码器(解码一张黑白图片中的 QR Code)Decoder (decode a QR Code in a

black and white picture)

原型 Prototype

Java

QrCode.Info limax.codec.QrCode.decode(byte[] image_1bit, int width, int height, int sample_granularity);

C99 QrCodeInfo qr_decode(char *image_1bit, int width, int height, int sample_granularity);

1 参数 Parameter1.1 image_1bit,输入应该为 1bit黑白图片,一个字节表示一个像素,0 为黑。

image_1bit, the input should be a 1bit black and white picture, one byte for one pixel and 0 for black.

1.2 width,图片宽度width, image width.

1.3 height,图片高度height, image height.

1.4 sample_granularity,采样粒度,1 为逐点采样,较大的图片选择较大采样粒度可356

Page 357: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

以获得更好的性能,但是可能导致解码失败。sample_granularity, sample granularity, 1 is point-by-point sampling. Larger pictures can choose better sampling granularity for better performance, but may result in decoding failure.

2 返回值 Return value2.1 无论解码成功失败,都返回 QrCode.Info,或者 QrCodeInfo对象,C99 中的返回

对象 info 应该通过 info->release(info)方式释放。成功失败由其中的 status字段表示。C99 中对象字段直接访问即可,Java 中通过 get 方式获取。Whether the decoding succeeds or not, it returns QrCode.Info, or QrCodeInfo object. The return object info in C99 should be released by info->release(info). Either success or failure is indicated by the status field. The object fields in C99 can be accessed directly, and it is obtained by the get methods in Java.

2.2 status , 状 态字段。 Java 中 的 enum QrCode.Info.Status , C99 中 的 , enum scan_status。有多种返回状态值。status, status field. enum QrCode.Info.Status in Java, enum scan_status in C99. There are multiple return status values.

2.2.1 SCAN_OK,扫描成功,可以获取正确的解码数据。SCAN_OK, if the scan is successful, the correct decoded data can be obtained.

2.2.2 ERR_POSITIONING,图片中的 QR Code 定位失败。ERR_POSITIONING, positioning the QR Code in the image fails.

2.2.3 ERR_VERSION_INFO,获取版本信息失败。ERR_VERSION_INFO, failed to get version information.

2.2.4 ERR_ALIGNMENTS,定位QR Code 中的 ALIGNMENT PATTERNS失败。ERR_ALIGNMENTS, failed to locate ALIGNMENT PATTERNS in QR Code.

2.2.5 ERR_FORMAT_INFO,获取格式信息(纠错级别和掩码)失败。ERR_FORMAT_INFO, failed to get format information (error correction level and mask).

2.2.6 ERR_UNRECOVERABLE_CODEWORDS,码字错误数量超过纠错容量。ERR_UNRECOVERABLE_CODEWORDS, the number of codeword errors exceeds the error correction capacity.

2.2.7 UNSUPPORTED_ENCODE_MODE,不支持的编码模式,这种情况下从 Info 中获取的数据是纠错完成的未解码数据,应用如果有能力应该自己解码。UNSUPPORTED_ENCODE_MODE, an unsupported encoding mode. In this case, the data obtained from Info is the undecoded data of the error correction completion, and the application should decode itself if it has the ability.

2.3 data,length,返回数据的字节数组表示,C99 中不可理解为 NULL终止串。data, length, the byte array representation of the returned data, which cannot be interpreted as a NULL termination string in C99.

2.4 reverse,图片是否是反转的。reverse, whether the image is inverted.

2.5 mirror,图片是否是镜像的。mirror, whether the image is mirrored.

2.6 version,版本357

Page 358: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

version, version.2.7 ecl,纠错级别

ecl, error correction level.2.8 mask,掩码号

mask, mask number.

ISO/IEC 18004-2006相容性 The compatibility of ISO/IEC 18004-2006

1 不支持 Micro QR Code 解码Not support Micro QR Code decoding.

2 支持 QR Code 的反转和镜像Support QR Code reversal and mirroring

3 支持混合模式,数字模式,字母数字模式,字节模式,KANJI 模式(KANJI 模式编码的日文字符转换为 UTF8 输出)Supports mixed mode, digital mode, alphanumeric mode, byte mode, KANJI mode (KANJI mode encoded Japanese characters are converted to UTF8 output)

4 支 持 ECI 模 式 , 两 种 FNC1 模 式 , 使 用 ISO/IEC 15424 指定 的符号标识方 式 输 出 。(ECI,FNC1 能否交错,如何交错,两个标准都没有提及,实现为允许随意交错)Supports ECI mode, two FNC1 modes, which are output using the symbol identification method specified by ISO/IEC 15424. (Whether ECI, FNC1 can be interlaced, and how to interlace, are not mentioned by neither standard. It is implemented to allow random interleaving)

5 不支持结构链模式Structured Append mode is not supported

示例工具 Sample tool

Javascript版本 Javascript version

javascript 目录下的 qrcode.html,可以根据输入串,生成纠错级别为 H 的 QR Code。The qrcode.html in the javascript directory can generate a QR Code with an error correction level of H based on the input string.

Java版本 Java version

java -jar limax.jar qrcode encode <filename> <(L|M|Q|H)> <text>为 text 文字串生成 QR Code,输出到 filename指定的文件中。文件内容由文件名后缀决定,如果是 svg,生成 SvgXML,除此之外生成图形文件,文件名后缀必须为 ImageIO.write 方法支持的文件格式。Generate a QR Code for the text string and output it to the file specified by filename. The file content is determined by the file name suffix. If it is svg, SvgXML is generated. In addition, the graphic file is generated, and the file name suffix must be the file format supported by the ImageIO.write method.

358

Page 359: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

java -jar limax.jar qrcode decode <filename> [sample_granularity=1] [bwthreshold=64] [meanfilter=2]解码图片,可以使用一张包含QR Code 的图片执行解码。sample_granularity 为采样粒度,bwthreshold 为图片转换为灰度图之后进一步转换为黑白图的阈值(这里存在一个比较奇怪的问题, jdk 的灰度转换结果不符合 Y = 0.299R + 0.587G + 0.114B 的灰度转换公式,明显偏暗,不得已选择了 bwthreshold=64),meanfilter 决定了均值过滤器尺寸,使用周边(N*2+1)^2 个像素参与中心像素均值计算,均值过滤器过滤掉可能影响解码的图像噪点。To decode a picture, a picture containing a QR Code is used to perform decoding.sample_granularity is the sampling granularity, bwthreshold is the threshold for further conversion to black and white after the image is converted to grayscale (there is a strange problem, the grayscale conversion result of jdk does not meet the conversion formula of Y = 0.299R + 0.587G + 0.114B, which is obviously dark, and choose bwthreshold=64 here), meanfilter determines the mean filter size, uses the surrounding (N*2+1)^2 pixels to participate in the central pixel mean calculation. The mean filter filters the image noise which may affect decoding.默认参数比较适合解码照片,如果要解码屏幕截图,这种参数选择可能导致失败。The default parameters are more suitable for decoding photos. If the screenshot is needs to be decoded, this parameter selection may lead to failure.

java –jar limax.jar qrcode decode capture.jpg 1 128 0也许是合适的选择,屏幕分辨率通常比照片小得多,meanfilter=2 有可能把截图中的 QR Code 模块破坏掉,另外,屏幕截图通常不存在噪点问题,0 才是更合适的选择。Maybe it is the right choice. The screen resolution is usually much smaller than the photo and meanfilter=2 may destroy the QR Code module in the screenshot. In addition, the screenshot usually has no noise problems, and 0 is a more appropriate choice.

高级话题 Advanced topic

解码性能 Decoding performance

1. 显而易见,越小的图片解码性能越高,图片越小细节越少,解码高版本 QR Code 可能存在问题,缩小原始图像或者截取部分原始图像属于工程手段,不作重点讨论。Obviously, the smaller the picture, the higher the decoding performance is. The smaller the picture, the less the detail is, which may be problem to decode the high version QR Code. It is an engineering method to zoom out the original image or intercept part of the original image, and no discussion is focused.

2. 解码操作属于内存密集型操作, java版本性能终归比不上 C99版本,对性能有要求的应用建议使用 C99版本。The decoding operation is memory intensive. The performance of the java version is not comparable to the C99 version. The application that requires performance is recommended to use the C99 version.

3. 彩色图像到黑白图像的预处理过程比解码操作本身开销更大,可以考虑使用能够充分359

Page 360: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

利用 GPU 能力的图像处理库,demo 中使用了 opencv。pc 上实测,比 java版本可以有10倍以上提升。The preprocessing process from color image to black and white image is more expensive than the decoding operation itself. An image processing library that can fully utilize GPU capabilities can be considered to use. Opencv is used in the demo. Actual measurement on pc can be more than 10 times higher than the java version.

4. 上述手段都用上了,还不能满足要求,可以选择较大的 sample_granularity 解码参数。If all of the above methods are used and the requirements are not met, a larger sample_granularity decoding parameter can be chosen.

5. 更加极端的需求,就只能对解码器进行并行优化了,并行优化的运行环境相关性太大,这里不提供,源码中 scanFinder 方法就是最大的性能瓶颈,可以重点考虑。For more extreme requirements, the decoder can be optimized in parallel. The parallel optimization of the running environment is too relevant. It is not provided here. The scanFinder method in the source code is the biggest performance bottleneck and can be considered.

图像质量 Image Quality

1. 图像质量是能否成功解码的先决条件,受各种因素影响。Image quality is a prerequisite for successful decoding and is affected by various factors.

2. ISO/IEC 18004-2006对 QR Code图像的规格,印刷规则有明确指导。现实中确有很多不合规 QR Code存在,例如,Border明确要求至少 4倍模块宽度,却有不少 QR Code 在Border 范围内印刷文字。ISO/IEC 18004-2006 has clear guidelines for the specification of QR Code images and printing rules. In reality, there are a lot of non-compliant QR Codes. For example, Border explicitly requires at least 4 times width of the module, but there are many QR Codes that print text in the Border range.

3. QR Code 使用 RS纠错算法,对于 QR Code内图像污染有一定的抵抗能力,但是无法解决三个角上的 FinderPattern 的污染。此外,3 个 FinderPattern并不足以进行透视矫正,当前实现通过扫描底边和右边确定 QR Code 的右下角,所以对底部空白,右边空白有一定的要求,对于扫描范围内的污染没有太好的抵抗能力。QR Code uses the RS error correction algorithm and has some resistance to image contamination in the QR Code, but it cannot solve the FinderPattern pollution at the three corners. In addition, the three FinderPatterns are not enough for perspective correction. The current implementation determines the bottom right corner of the QR Code by scanning the bottom and right edges, so there is a certain requirement for the bottom blank and the right blank, and there is no good resistance ability to the contamination in the scan range.

4. 版本越高的 QR Code对于对焦的要求越高,对焦不好,边缘模糊的 QR Code图像滤波之后黑模块与白模块的宽度比例会出现明显偏差,FinderPattern 不能确定下来,直接导致解码失败。The higher the version of the QR Code, the higher the focus requirements are. The poor focus, the edge ratio of the blurred QR Code image filtering, and the ratio of the width of the black module to the white module will be significantly deviated. The FinderPattern can not

360

Page 361: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

be determined, and directly leads to decoding failure.5. 照度不足将导致相机选择更高感光度,产生更多图像噪点,图像滤波可以解决一定的

问题。如何选择更好的滤波方案只能根据具体应用环境实测。Insufficient illumination will cause the camera to choose a higher sensitivity, resulting in more image noise, and image filtering can solve certain problems. How to choose a better filtering solution can only be measured according to the specific application environment.

6. 不支持同一图片上多个 QR Code,多 QR Code 可能导致 FinderPattern选择错误。PC屏幕上某些选中的 Radio Button 具有明显的 FinderPattern 特征,实际环境中同样可能存在这类干扰。当前实现,以面积最大为标准选择 3 个 FinderPattern,不作方差过滤(较重 的透视变形使 得远端 FinderPattern 明显偏小 , 方差过滤可 能滤掉这 样 的FinderPattern)。面积最大的标准,决定了 QR Code面积在图片中占比越大,解码成功率越高,扫描明显很小 QR Code 可能需要进行图像截取,或者局部放大。Multiple QR Code on the same image is not supported, and multiple QR Code may cause FinderPattern selection errors. Some selected Radio Button on the PC screen has obvious FinderPattern characteristic, and such interruption may also exist in the actual environment. In terms of current implementation, three FinderPattern are selected according to the largest area, and no variance filtering is performed (the bigger perspective distortion makes the far-end FinderPattern significantly smaller, and the variance filtering may filter such FinderPattern). The largest area standard determines the larger the QR Code area in the picture, the higher the decoding success rate is. Scaning the significantly small QR Code may require image cutting or partial magnification.

7. 不可能完全支持非标准的创意 QR Code,某些创意 QR Code贴合特定解码器的参数条件进行设计,为这样 QR Code调试参数没有太大意义,除非把兼容这些 QR Code作为应用的设计目标。It is impossible to fully support the non-standard creative QR Code. Some creative QR Code is designed to fit the parameter conditions of a particular decoder. It is not very meaningful to measure parameters for such QR Code unless the compatibility of such QR Code is design goal.

Demo

1. demo/qrdecode 目录下提供了 pc 与 android 两个版本的 demo,编译这些版本均需要下载 opencv库,进行相应配置。The demo/qrdecode directory provides two versions of the demo for pc and android. To compile these versions, it needs to download the opencv library and configure it accordingly.

2. pc版本与 java版本功能相同,用来解码照片中的 QR Code,有极大的性能提升。The pc version has the same function as the java version, and is used to decode the QR Code in the photo, which has a great performance improvement.

3. pc版本与 android版本的关键代码完全相同,可以辅助设计特定场景下的 android 应用,试验和选择图像滤波算法以及参数。The pc version is identical to the android version of the key code, which can assist in the design of android applications in specific scenarios, experiment and select image filtering algorithms and parameters.

361

Page 362: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

4. android版本利用相机直接实现一个通用的实时解码器,显示QR Code信息。The android version uses a camera to directly implement a generic real-time decoder that displays QR Code information.

5. http://www.limax-project.org/download/QR-Demo.apk 提供下载。http://www.limax-project.org/download/QR-Demo.apk provides download.

JDBC连接池 JDBC Connection pool

常见的 c3p0 之类的连接池,直接向应用分配自己管理的连接。绝大多数 JDBC 方法,都会抛出 SQLException,要求每一处 SQL操作都能正确处理连接意外并不现实。典型的,数据库重启之后,初次 SQL操作往往会失败,接下来连接池才会恢复所有连接。 Mysql 的Connector/J,抛出 CommunicationsException 的问题并不少见。所以 Limax提供一个简单的连接池实现,通过区分 SQLException 决定是否重启操作,避免把可以通过重启操作解决的问题推给应用。The common connection pools such as c3p0 directly assign their own managed connections to the application. Most JDBC methods will throw a SQLException, and it is not realistic to require that every SQL operation handle the connection accident correctly. Typically, after the database is restarted, the initial SQL operation will often fail, and then the connection pool will recover all connections. The issue that Mysql's Connector/J throws a CommunicationsException is not uncommon. So Limax provides a simple connection pool implementation that determines whether to restart the operation by distinguishing SQLException, and avoids propagating issues that can be resolved by the restart operation to the application.

基本原理 Fundamental

1. 连接通过 consumer 的方式提供给应用, SQL操作集合在一个特定范围内完成,这样所有的 SQLException 才有可能被连接池者监管,consumer 才有机会被重启。这里提供两个范围支持,函数范围和线程范围(跨线程转移连接不支持,这也不必要,即便使用常规连接池,线程也是从连接池请求新连接,而不至于私下交换)。The connection is provided to the application through the consumer. The SQL operation set is completed within a specific scope, so that all SQLException may be supervised by the connection pool, and the consumer has a chance to be restarted. Here two scope support are provided, function scope and thread scope (cross-thread transfer connection is not supported, which is not necessary. Even if a regular connection pool is used, the thread requests a new connection from the connection pool, not private exchange).

2. 通过重启 consumer 也顺便解决死锁,超时之类的异常。By restarting the consumer, it also solves the exceptions such as the deadlock and timeout.

362

Page 363: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

编程接口 Programming interface

接口 limax.sql.SQLConnectionConsumer Interface limax.sql.SQLConnectionConsumer

应用实现这个接口打包 SQL操作集合Application implements this interface to package SQL operation collection.

接口 limax.sql.SQLExecutor limax.sql.SQLExecutor interface

定义方法 void execute(SQLConnectionConsumer consumer) throws Exception Define method void execute(SQLConnectionConsumer consumer) throws Exception定义常量 COMMIT和 ROLLBACK,专用于线程范围连接池。Define constants COMMIT and ROLLBACK, which are dedicated to thread-scope connection pool.execute(SQLExecutor.COMMIT) 提交事务。execute(SQLExecutor.COMMIT) commit transaction.execute(SQLExecutor.ROLLBACK)回滚事务。execute(SQLExecutor.ROLLBACK) rollback transaction.

类 limax.sql.SQLPooledExecutor Class limax.sql.SQLPooledExecutor

构造函数:Constructor:SQLPooledExecutor(String url, int size, boolean threadAffinity, Consumer<Exception> logger)url 为数据库连接 url,size 决定了连接池尺寸,threadAffinity 决定了该连接池是函数范围还是线程范围,logger 用于记录导致 consumer 重启的那些异常,便于应用排查重启原因,改进设计。The url is the database connection url; the size determines the connection pool size; the threadAffinity determines whether the connection pool is a function scope or a thread scope; the logger is used to record the exceptions that cause the consumer to restart, which is convenient for the application to check the restart reason and improve the design.

SQLPooledExecutor(String url, int size, Consumer<java.lang.Exception> logger) SQLPooledExecutor(String url, int size, boolean threadAffinity) SQLPooledExecutor(String url, int size)简化版本的构造函数,其中不含 threadAffinity 参数的构造函数,默认 threadAffinity 为false。The simplified version of the constructor does not contain threadAffinity parameter. The default threadAffinity is false.

构造函数返回时,size 个连接已经建立,这样可以确保数据库认可连接 url,并且允许建立size 个连接,如果构造函数迟迟不返回,应该检查数据库服务器是否启动,这种情况下logger 也会不断输出导致重连的异常。When the constructor returns, the size connections have been established, which ensures that

363

Page 364: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

the database recognizes the connection url and allows size connections to be established. If the constructor does not return, it should check if the database server is started. In this case, the logger will continue to output the exception that causes reconnection.

成员函数:Member function:void execute(SQLConnectionConsumer consumer) throws Exception执行 consumer 中 应 用提供的 SQL 操作集合 , consumer 不 必 catch 异常, 特别是SQLException,应该在 execute 外 catch异常,保证连接池能够检查所有异常决定是否重启,导致重启的异常通过 logger记录,不会导致的重启的异常通过 execute 往外抛。。Execute the SQL operation set provided by the application in the consumer, the consumer does not need to catch the exception, especially the SQLException. The exception should be caught (catch) outside execute, to ensure that the connection pool can check all the exceptions to decide whether to restart. The exception causing the restart is recorded by logger, and the exception without causing the restart is thrown out by execute.

void shutdown()关闭连接池,拒绝新的执行请求。Close the connection pool and reject the new execution request.

类 limax.sql.RestartTransactionException Class imax.sql.RestartTransactionException

线程范围连接池使用这个异常来指明事务需要被重启。The thread-scope connection pool uses this exception to indicate that the transaction needs to be restarted.

函数范围连接池 Function Scope connection pool

一次 execute 就能完成整个事务,使用函数范围连接池。这种类型的连接池,绝大多数情况够用了。An execute can complete the entire transaction, using a function-scope connection pool. This type of connection pool is sufficient in most cases.

特性 Characteristic

1 连接初始化为 auto-commit 方式。The connection is initialized to auto-commit mode.

2 一次 execute 之后无论成败,连接重新设置为 auto-commit 方式。After an execute, regardless of success or failure, the connection is reset to auto-commit mode.

3 执行多语句事务,首先应该将 auto-commit 设置为 false。To execute a multi-statement transaction, auto-commit should first be set to false.

364

Page 365: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

示例 Example

public final class MysqlTest {public static void main(String[] args) throws Exception {

SQLPooledExecutor executor = new SQLPooledExecutor("jdbc:mysql://192.168.1.3:3306/test?user=root&password=admin&characterEncoding=utf8", 3, e -> e.printStackTrace());

Thread.sleep(10000);executor.execute(conn -> {

conn.setAutoCommit(false);try (PreparedStatement ps = conn.prepareStatement("INSERT INTO test(name) VALUES(?)")) {

ps.setString(1, "A");ps.executeUpdate();ps.setString(1, "B");ps.executeUpdate();

}});executor.shutdown();

}}在 sleep 的 10秒期间,重启数据库,观察输出,执行结束后两条记录均被插入数据库。During the 10 seconds of sleep, restart the database and observe the output. After the execution, both records are inserted into the database.

线程范围连接池 Thread scope connection pool

线程范围连接池跨越多个 execute操作,除非复杂应用,这种类型的连接池很少用到,最典型的应用是 limax.zdb.LoggerMysql.java 中的 writer 连接池,一次 checkpoint操作发起的全部修改动作打包为一个事务。Thread-scope connection pool spans multiple execute operations. Unless a complex application, this type of connection pool is rarely used. The most typical application is the writer connection pool in limax.zdb.LoggerMysql.java, all modifications initiated by a checkpoint operation is assembled as a transaction.

特性 Characteristic

1 连接初始化为非 auto-commit 方式,consumer禁止设置 auto-commit。The connection is initialized to non-auto-commit mode, and the consumer is prohibited from setting auto-commit.

2 线程首次调用 execute,连接分配给线程,意味着事务的开始。The thread calls execute for the first time, and the connection is assigned to the thread, which means the beginning of the transaction.

365

Page 366: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

3 任何一次 execute抛出异常或者以 SQLExecutor.COMMIT,SQLExecutor.ROLLBACK作为consumer调用 execute,连接将归还连接池,事务结束。Any once an execute throws an exception or calls execute with SQLExecutor.COMMIT or SQLExecutor.ROLLBACK as the consumer, the connection will be reclaimed by the connection pool, and the transaction ends.

4 任何一次 execute抛出异常,事务回滚并结束。其中,RestartTransactionException指示应用可以重启事务。Any once an execute throws an exception, the transaction rolls back and ends. Among them, RestartTransactionException indicates that the application can restart the transaction.

示例 Example

public final class MysqlTest {private static SQLPooledExecutor executor = new SQLPooledExecutor("jdbc:mysql://192.168.1.3:3306/test?

user=root&password=admin&characterEncoding=utf8", 3, true);

private static void insert(String s) throws Exception {System.out.println("insert " + s);executor.execute(conn -> {

try (PreparedStatement ps = conn.prepareStatement("INSERT INTO test(name) VALUES(?)")) {

ps.setString(1, s);ps.executeUpdate();

}});

}

public static void main(String[] args) throws Exception {while (true) {

try {insert("A");Thread.sleep(5000);insert("B");Thread.sleep(5000);insert("C");executor.execute(SQLExecutor.COMMIT);Thread.sleep(5000);

} catch (RestartTransactionException e) {System.out.println("restart");

}}

}

366

Page 367: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

}

程序启动之后,任何时间点重启数据库,可以看到类似如下的结果。After the program starts, restart the database at any time point, the following results can be gotten.insert Ainsert Binsert Crestartinsert Arestartinsert Arestartinsert Ainsert B发生了 3次 restart,原因在于,连接池 size = 3,轮转分配,首次 restart 之后的两次 insert A,触发了后续连接的恢复。Three restarts occurred because the connection pool size = 3 and round-robin allocated, which means that the two insert A after the first restart triggered the recovery of subsequent connections.可以看出,无论以任何方式重启数据库,ABC插入数据库的事务完整性总能得到保证。It can be seen that the transaction integrity of the ABC insert database is always guaranteed regardless of restarting the database in any way.特别注意,executor.execute(SQLExecutor.COMMIT);跟其它 SQL操作在同一个 try块内,千万不能习惯性放到 finally块,这个操作同样有可能抛出异常。It should be noticed that executor.execute(SQLExecutor.COMMIT); should be in the same try block as other SQL operations, and should not be put into the finally block, because this operation may also throw an exception.

高级话题 Advanced topic

1 SQLConnectionConsumer约束了应用实现,同时也就约束了更好的事务完整性。The SQLConnectionConsumer constrains the application implementation and also constrains better transaction integrity.

2 既然操作可能重启,那么必须设计可重启的代码,比如某些参数多次执行不可改变。Since the operation may restart, the restartable code must be designed, such as some parameters can not be changed while multiple executed.

3 一些大型结果集的 SELECT操作,也许设计允许失败,这种情况下,应该判断操作是否重启,作出相应决策。The SELECT operations of some large result sets perhaps allow the failure in design. In this case, it should be judged whether the operation is restarted and the corresponding decision is made.

367

Page 368: Limax Projectlimax-project.org/download/Limax Project.en.docx · Web viewThe Limax uses the concept of the view to replace the traditional protocol model, presents the MVC in the

4 如果数据库服务器长时间维护,应用也许会表现为不响应,碰到这种情况应该检查logger 输出,检查连接状态。If the database server is maintained for a long time, the application may appear to be unresponsive. In this case, the logger output should be reviewed or check the connection status.

5 RestartTransactionException从 RuntimeException派生,而不是 SQLException,这是应用复 杂 性 决 定 的 , 也 许调用栈内大 多 数 方法都跟 SQL 毫无 关 系 , 可 以参考limax.zdb.Checkpoint.java 中的使用。RestartTransactionException is derived from RuntimeException, not SQLException, which is determined by the application complexity. Perhaps most of the methods in the calling stack has nothing to do with SQL. Please refer to the use in limax.zdb.Checkpoint.java.

368