我有一个场景,像是车场道闸那种
1.gateway-worker作为服务端,然后道闸系统是客户端,与服务端建立tcp长连接。(客户端会向服务端发送心跳,5s/次)
2.用户请求服务端,获取道闸上的金额,进行支付
假设道闸客户端连接到gateway-worker的client_id是001,车场的客户端编号是A,那在道闸连接上服务端的时候,会进行client_id 001和编号A的绑定Gateway::bindUid(001, A)
。那后续有用户发起http请求获取金额的时候,请求中也会传递车场编号A,那服务端会向客户端A发送tcp请求获取金额。
现在有一种情况,就是道闸系统有时候网络不好,老是断网,断网情况下,道闸客户端就没办法向服务端及时发送心跳包,服务端也不知道这个客户端A离线了,这时候如果有http请求进来,服务端会继续向客户端A发送请求获取金额,这时候就会导致进程阻塞,导致http请求一直在请求中,直接影响到后续的http请求也进不来
想问下,这种情况应该如何处理呢?
1、在向客户端A发送请求前,检测客户端A是否离线
使用Gateway::isOnline(string $client_id)
先检测客户端A状态。但是这种方法建立在client_id触发了onClose回调,像断网这种情况,客户端是没办法触发onClose回调
2、服务端向客户端发送心跳包检测客户端状态
我看官方文档可以通过服务端向客户端发送心跳包检测客户端状态,假设我设置心跳包3s/次,但是这种也会存在心跳包刚检测客户端正常,过了1s客户端因为断网离线了,这时候有http请求进来,服务端还不知道客户端连接不上了,还是会出现上面我所说的情况
想问下大家有更合适的方案吗?
http请求可以直接用workerman或者webman自定义进程来做,做成非阻塞http服务,包括服务端会继续向客户端A发送请求获取金额这个过程做成非阻塞的请求。
如果你不会做,也可以把服务端会继续向客户端A发送请求获取金额这个过程设置个超时时间,比如1秒。
请问设置超时的这个方案,gateway-worker内有现成的解决方案吗?
等下给你写个方案
好的 谢谢大哥
准备工作
安装channel
代码
start.php
Events.php
启动
php start.php start
测试
浏览器打开页面
http://127.0.0.1:1234/?device_id=d2
开启一个终端,输入
浏览器显示 800
原理
启动一个channel server作为多进程或者跨服务器通讯组件
businessWorker的Events里onWorkerStart连接channelserver并监听http进程发来的get_amount请求(请求中包含设备id)
启动一个httpworker作为http接口,onWorkerStart里连接channelserver并监听get_amount_result结果(结果中包含设备id)
浏览器向httpworker发起请求,httpworker获得要查询的设备id,并将连接保存到connection_maps中,然后通过channel发布一个get_amount事件给businessWorke的Events.php
设备返回金额后在Events里的onMessage里通过channel发送get_amount_result事件通知http进程对应的设备返回了金额
httpworker获得金额后查找本地连接里($connection_maps)是否有查询对应设备金额的连接,有的话返回金额
整个过程是非阻塞的,可以承受很大的设备在线数
谢谢大佬,受教了~ 我研究一下,谢谢~
Events.php中“ // 只需要在0号进程上开启get_amount监听 if ($worker->id !== 0) { ” 应该是 $worker->id == 0吧。
如果设备不在线,http请求会不会一直等待响应?
对,应该是 $worker->id === 0 会。设备不在线会一直等,服务端可以设置定时器去返回数据,也可以客户端设置超时
学习了