# light-gateway **Repository Path**: lianzt/light-gateway ## Basic Information - **Project Name**: light-gateway - **Description**: 光闸通讯网关 - **Primary Language**: NodeJS - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 11 - **Forks**: 6 - **Created**: 2018-06-11 - **Last Updated**: 2025-09-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 光闸通讯系统 ### 设备说明 [百度百科](https://baike.baidu.com/item/%E5%85%89%E9%97%B8/7135429?fr=aladdin) ### 核心思路 light-gateway 项目是根据光闸的工作原理开发,把光闸两端系统之间的通讯方式由文件交换改变为网络连接的中间件系统,以下简称为光闸网关或网关。 光闸网关与内外网各个系统之间的交互都是 http 协议,根据信息的发起与接收,将通讯双方定义为:信息发起方(以下简称发起方)与信息接收方(以下简称为接收方)。以前使用网闸通讯时只能由外网发起,内网接收,现在使用光闸没有这个限制,内外网都即可以是发起端也可以是接收端。 发起方向接收方发送数据时,需要将请求发送给网关,由网关将网络请求转为文件摆渡到另一端,再将文件转为网络请求发送到接收端,然后将接收端的响应数据按原路返回给发起端,发起端可以选择两种方式接收响应数据:同步接收与异步接收。 网关将接收端的数据请求概括为了三个大类:get、post与json,本质json也属于post方式。(以后如果需要,还可以再增加webservice方式等) 理论上发起端只需要告诉光闸如何使用http协议将数据发送给接收端即可,一个http数据包可解析为以下4个要素: * url:请求地址 * mehtod:get|post * header:http头信息 * body:http报文体,请求数据部分 > cookies 也可以认为是一个要素,目前网关只考虑无状态请求。 然后网关需要把接收端的响应数据,也使用http协议回传给发起端,所以发起端除上述内容外,还需要告诉网关,如何把响应数据回传给自己,即回调地址。 网关有统一的回调规范,接入的系统照此规范实现回调接口,即可接收到数据了。 ### 内外网数据交互系统示意图 ![内外网数据交互系统示意图](docs/image/system.jpg) ### 接口说明 #### rest API 异步请求 由发起端请求网关的 rest api 发起通讯请求,网关收到后就会断开链接,在收到接收端响应后会把响应数据回调给发起端。 > url地址:/api/request 报文格式为: ``` { method: get|post|json|other plus, //发送给接收端的请求使用的发送插件,非空 request_url: url..., //接收端url,非空 request_header: {}, //请求的http header,json格式,可空 request_content: {}, //请求的body,当插件为get|post时,不支持嵌套格式,可空 request_timeout: 20000, //网关的超时时间,默认20000ms,无需考虑光闸摆渡时间,可空 back_url: url... //请求完成后的回调地址,用于通知发起请求的系统,非空 } ``` 网关会给请求端一个同步响应报文,该报文表示已接收了请求,不表示已转发到光闸另一端: ``` { header:{ code: 'success', //success为成功,其它为失败 message: '成功' }, sequence: 1..n, //该请求分配的序号 stack: '' //网关接收时如果出现异常,表示异常堆栈信息 } ``` #### notice异步通知 网关收到接收端的响应后,进入 notice 通知回调功能,把数据反馈给发起端,通知的url就是 back_url,通知使用 http 协议 application/json 格式,报文格式为: ``` { header:{ //表示网关与接收端的通讯是否成功 code: 'success', message: '成功' }, content: '', //接收端的响应内容,method=get|post时为string, method=json时为json对象 sequence: 1..n //发起请求时分配到的序列号,一次请求序号都是一致的。 } ``` 发起端在收到通知后,需要给出成功的响应,这样网关会认为发起端成功收到了通知,目前不管什么响应都认为成功,以后根据需要可能会增加通知重发机制。响应的格式为: ``` { header:{ code: 'success' } } ``` 为了兼容老系统,以下报文也认为通知成功 ``` { head:{ response_code: '000000' } } ``` #### rest API 同步请求(慎用) 由发起端请求网关的 rest api 发起通讯请求,网关收到请求后会保持链接,等待收到接收端报文后,直接响应给发起端。 这种请求方式发起端的线程会被挂起,光闸摆渡时间通常是10s左右,即请求端会挂起10s以上,如果系统访问量较大,很容易出现线程不够的情况。 大并发量的数据请求,禁止使用该方式! 网关为防止内存溢出,会每5分钟检查未完成的同步请求链接数,如果超过10万,会将超过的数量强制断开,通常是按照时间顺序将前n个链接断开。 > url地址:/api/request-sync 报文格式为: ``` { method: get|post|json|other plus, //发送给接收端的请求使用的发送插件,非空 request_url: url..., //接收端url,非空 request_header: {}, //请求的http header,json格式,可空 request_content: {}, //请求的body,当插件为get|post时,不支持嵌套格式,可空 request_timeout: 20000, //网关的超时时间,默认20000ms,无需考虑光闸摆渡时间,可空 } ``` 网关会给出一个同步的响应,格式为json: ``` { header:{ //表示网关与接收端的通讯是否成功 code: 'success', message: '成功' }, content: '', //接收端的响应内容,method=get|post时为string, method=json时为json对象 } ``` ### 性能监控 api rest api 还可以扩展到性能监控方面,监控系统可使用这些API调取系统的运行情况,可定义如下接口: * 文件损坏率 * 请求送达率 * 请求数统计 * 请求响应时间分布 * 等等 (目前还未实现) ### 网关内部数据交换说明 > 以下内容为网关的工作原理说明,使用网关 rest api 时无需关心工作原理,阅读网关源码时可参考下文帮助理解。 #### 系统结构 ![系统结构图](docs/image/struct.jpg) 光闸两端都需要部署一个 light-gateway 项目,两个系统的 request与response目录正好相反。 系统的两个入口:rest api 与 response file #### 文件格式 文件分为两种,保存请求的 request 文件,保存响应的 response 文件,文件的内容都是json字符串,文件名规范为: * request:{timestamp}-{md5}.request * response:{timestamp}-{md5}.response > 其中 timestamp 为时间戳,格式为yyyyMMddHHmmss,用于防止文件名重复; > > md5为文件签名,用于检验文件是否在传输过程中受到损坏。 ##### request 文件 保存请求数据,即 /api/request 接收到的数据直接保存到文件,格式为: ``` [ { sequence: 1..n, //请求分配的序号 data: { method: get|post|json|other plus, //发送到光闸另一端请求的发送插件 request_url: url..., //发送到光闸另一端请求的url request_header: {}, //请求的http header,json格式,可空 request_content: {}, //请求的body,当插件为get|post时,不支持嵌套格式,可空 request_timeout: 20000, //光闸另一端请求的超时时间,默认20000ms back_url: url... //请求完成后的回调地址,用于通知发起请求的系统 } },...... ] ``` 为减少光闸摆渡的文件数量,将以前一个请求一个文件的方式,合并为一段时间的请求为一个文件,这样可以减少光闸设备与服务器磁盘的压力,默认保存周期为1秒。 ##### response 文件 保存请求对应的响应数据,格式为: ``` [ { sequence: 1..n, //请求时分配的序号 data: { header:{ code: 'success', //success为成功,其它为失败,表示light-gateway把数据转发到目标系统过程中是否出现异常 message: '成功' }, content: string | json, //业务系统的返回数据,method=get|post时为string, method=json时为json对象 back_url: '' //发起请求端调用 /api/request 时传入的 back_url } }, ] ``` 为减少光闸摆渡的文件数量,将以前一个请求一个文件的方式,合并为一段时间的请求为一个文件,这样可以减少光闸设备与服务器磁盘的压力,默认保存周期为1秒。 ### 测试代码 > 目录:test * light-imitator.js 光闸设备模拟程序,运行后模拟两个光闸通道。 * send-\*.js 发送请求测试 * send-sync.js 发送同步请求测试 * recv.js 接收端测试,可接收get|post|json请求,也可接收网关通知。 * 其它文件无关紧要 可在本地搭建虚拟环境,需将light-gateway项目拷贝一份(在windows下最好不要拷贝 node_modules 目录,拷贝源文件后再使用 npm install 安装依赖),模拟光闸两端的两个网关程序。 然后运行 light-imitator.js 模拟光闸设备,需修改配置文件,保证两个网关项目与光闸模拟程序目录一致。 可以使用 recv.js 模拟接收数据,测试虚拟环境是否成功,需修改 recv.js(接收端) 与 send-\*.js(发起端) 文件的 url 地址。 测试同步接口时,发起端需要修改 send-sync.js 文件,接收端需运行 recv.js 文件。 测试异步接口时,发起端可使用 send-get|post|json.js 测试,并运行 recv.js 文件接收网关通知,接收端需运行 recv.js 接收请求。 ### 扩展 系统中的 classifier 层可以扩展出多种请求方式,一个method对应一个forward插件。 ### 周边系统设计 在 系统结构图 中,除 light-gateway 之外的所有项目,都为周边系统,可以是为用户提供服务的具体业务系统,也可以提供接口的数据服务系统,周边系统接入 light-gateway 只需实现一个回调的 notice 通知接口即可。 另外周边系统在需求设计时,就应该把光闸的传输特性考虑进去,过光闸的请求都无法同步、实时地获取响应数据,因此需要在用户交互上给出提示。 ##### 实时场景1: 例如用户查询车辆违章,查询违章操作需要过光闸到内网中查询数据,如何让用户感觉不到这个过程? 可以提前把用户的违章数据准备好放在缓存中,用户发起查询操作时直接从缓存中返回。当用户登录到系统时,就可以开始为用户准备数据了。 ##### 实时场景2: 当无法预判用户行为时,无法提前为用户准备数据,可以在页面中建立 websocket 长链接,在系统收到回调通知后,通过 websocket 通知用户浏览器,再由js显示查询结果。 ##### 非实时场景3: 比如用户违章缴费后,违章的处理对实时性要求不高,这样就可以放到后台慢慢执行,等待执行完成后更新数据库即可,界面中可以给用户提示:系统将在三个工作日内处理完成。 ### 运行 >SET DEBUG=light-gateway:* & npm start ### change log --- #### 2017-6-20 ##### 增加文件损坏重传机制 文件损坏通常发生在过光闸传输的过程中,示例图如下: ![文件损坏重传机制](./docs/image/file_broken.jpg) 入口为 request 目录 watch 事件,收到一个被损坏的报文。 需要说明一下 request 目录下的 unimas_ 开关的几个目录,这几个是由光闸自动创建,用于保存传输后文件的目录: * unimas_back 传输成功的文件 * unimas_unpass 传输失败的文件 * unimas_... 可能还会有其它目录,暂时还未发现 重传机制就是从这几个目录是寻找源文件,然后再移动到共享目录重新传输,即使文件被转移到传输成功目录,也是有可能在传输过程中被损坏的。 用于通知发起方文件被损坏的报文为: ``` { header: { code: 'file_broken', message: '文件被损坏' }, content: { filename: '1-xxxxxxxxxxxxxxxxxxxx.request' } } ``` request 与 response 类型的文件处理方式是一样的,因此重传机制这两种文件都可以支持。 ##### 增加已传输文件定时删除机制 在系统运行过程中, unimas 目录中的文件会越来越多,因此需要有定时删除机制, file_controller.js 文件控制器模块增加了一个定时器,根据配置每隔一段时间删除 unimas 目录中的文件,默认配置为10分钟。 由于文件损坏重传机制的存在,文件定时删除时只删除修改时间为本次周期之前的文件(本例中为10分钟),保证不会由于周期性删除文件导致数据丢失。 文件重传机制移动文件时使用的是rename操作(类似mv操作),不会改变文件修改时间,光闸传输后移动文件到 unimas 目录也不会改变文件修改时间,因此可防止某文件的源文件被损坏时,一直被重传的情况,最晚在下一个周期该文件就会被删除。 定时删除机制只处理 request 与 response 类型的文件,保证光闸同时传输的其它类型文件不受影响。 此外正常业务流程中产生的 request 与 response 文件,偶尔会由于文件占用问题无法正常删除,这部分无法删除的文件都统一记录在 util.js/delQueue 队列中,通常一段时间后再次删除就可以了,因此每个周期都集中处理一次这些删除失败的文件。 > 文件无法删除的问题问题产生的原因没有深入研究,问题不太好重现,操作系统、光闸、用户、程序都会影响,跟node.js异步特性也有一定关系。 ##### 修改为了 async/await 异步模式,使提高码可读性 修改了所有异步调用的代码,使用 async/await 运行异步代码,要求 node.js 版本必须在 v7.0 以上,可使用 node -v 查看当前版本号, linux与mac 环境可使用 n 模块升级node.js,windows 可重新安装。 ``` npm install -g n n v8.1.2 ``` ##### 2017-10-12 14:31:38 合并请求文件,增加同步请求的 rest api 接口,增加测试脚本与虚拟光闸 修改了说明文档整体结构,新增内容见文档。 ---