Skip to content

Commit 2811103

Browse files
committed
fix 修复 websocket 的 onMessage 未定义 Request 对象问题
Request 对象只有在 onOpen 时有效,在onOpen时做将请求的信息与fd绑定,将绑定信息存到 redis 的存储容器中,onMessage/onClose 时再根据 fd 读取出来
1 parent cd89a7c commit 2811103

File tree

2 files changed

+102
-65
lines changed

2 files changed

+102
-65
lines changed

admin/src/views/api_excel/index.vue

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -342,13 +342,21 @@ export default {
342342
})
343343
},
344344
initWebSocket() { // 初始化 weosocket
345+
const that = this
345346
if ('WebSocket' in window) {
346347
const url = process.env.VUE_APP_WEBSOCKET + '?action=api_excel&token=' + getToken() + '&page=' + this.currentpage + '&perPage=' + this.perpage
347348
this.websock = new WebSocket(url)
348349
this.websock.onmessage = this.onmessage
349350
this.websock.onopen = this.onopen
350351
this.websock.onerror = this.onerror
351352
this.websock.onclose = this.close
353+
354+
setInterval(function() {
355+
if (that.websock && that.websock.readyState === that.websock.OPEN) {
356+
// console.log('inSend')
357+
that.send('{"page":1}')
358+
}
359+
}, 5000)
352360
} else {
353361
this.fetchData()
354362
// 浏览器不支持 WebSocket,使用 ajax 轮询
@@ -359,34 +367,36 @@ export default {
359367
// const actions = { 'id': '7' }
360368
// const rs = this.send(JSON.stringify(actions))
361369
// console.log(rs)
370+
setTimeout(() => {
371+
this.reload = false
372+
this.reload_name = '刷新'
373+
}, 800)
362374
},
363375
onerror() {
364376
// 连接建立失败, 发送 http 请求获取数据
365377
// this.initWebSocket()
366378
this.fetchData()
367379
},
368380
onmessage(e) { // 数据接收
369-
// console.log(e.data)
370381
const data = JSON.parse(e.data)
371-
// this.list[2].rate = parseInt(data.data.rate)
372-
// console.log(this.list[2].rate)
373-
// console.log(data)
374-
// websocket 返回的数据
375-
this.list = data.data.data
376-
this.listLoading = false
377-
this.total = data.data.total
378-
this.url = data.data.appUrl
379-
// console.log('type', Object.prototype.toString.call(this.list))
380-
setTimeout(() => {
381-
this.reload = false
382-
this.reload_name = '刷新'
383-
}, 800)
382+
// console.log(data.data)
383+
if (data && data.data) {
384+
// this.list[2].rate = parseInt(data.data.rate)
385+
// console.log(this.list[2].rate)
386+
// console.log(data)
387+
// websocket 返回的数据
388+
this.list = data.data.data
389+
this.listLoading = false
390+
this.total = data.data.total
391+
this.url = data.data.appUrl
392+
// console.log('type', Object.prototype.toString.call(this.list))
393+
}
384394
},
385395
send(Data) {
386396
this.websock.send(Data)
387397
},
388398
close() { // 关闭
389-
// console.log('断开连接')
399+
console.log('断开连接')
390400
},
391401
download(index, row) {
392402
window.location.href = this.url + row.finish_url

laravel/app/Services/WebSocketService.php

Lines changed: 77 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -36,66 +36,80 @@ public function onOpen(Server $server, Request $request)
3636
$userInfo = auth('api')->user();
3737
if (empty($userInfo)) {
3838
$data = $this->outJson(1200, [], '用户未登录或登录超时');
39-
return $server->push($request->fd, $data);
39+
$server->push($request->fd, $data);
40+
} else {
41+
// 存储用户信息
42+
$redis = $this->getRedis();
43+
$key = 'websocket_fd:'.$request->fd;
44+
$redis->setex($key, 7200, json_encode($userInfo->toArray()));
45+
// 在触发 onOpen 事件之前 Laravel 的生命周期已经完结,所以 Laravel 的 Request 是可读的,Session 是可读写的
46+
// \Log::info('New WebSocket connection', [$request->fd, request()->all(), session()->getId(), session('xxx'), session(['yyy' => time()])]);
47+
// 1. 根据 api_excel 的 id 查询总数,
48+
$req = $request->get;
49+
$this->perPage = isset($req['perPage']) ? intval($req['perPage']) : 10;
50+
51+
$action = $req['action'] ?? '';
52+
switch ($action) {
53+
case 'api_excel': // api_excel 列表完成率
54+
$user_id = $userInfo['id'];
55+
$res = $this->apiExcel($user_id);
56+
$server->push($request->fd, $res);
57+
}
4058
}
41-
// 在触发 onOpen 事件之前 Laravel 的生命周期已经完结,所以 Laravel 的 Request 是可读的,Session 是可读写的
42-
// \Log::info('New WebSocket connection', [$request->fd, request()->all(), session()->getId(), session('xxx'), session(['yyy' => time()])]);
43-
// 1. 根据 api_excel 的 id 查询总数,
44-
$req = $request->get;
45-
$this->perPage = isset($req['perPage']) ? intval($req['perPage']) : 10;
46-
47-
$action = $req['action'] ?? '';
48-
switch ($action) {
49-
case 'api_excel': // api_excel 列表完成率
50-
$user_id = $userInfo['id'];
51-
$server->push($request->fd, $this->apiExcel($user_id));
52-
// while (true) {
53-
// sleep(5);
54-
// $state = ApiExcel::where('state', 1)->first();
55-
// if (!$state) {
56-
// $server->push($request->fd, $this->apiExcel($user_id));
57-
// break;
58-
// }
59-
// // 每个用户 fd 限制请求次数
60-
// $redisKey = 'websocket_fd_'.$request->fd;
61-
// if (empty($this->redis)) {
62-
// $this->redis = Redis::connection();
63-
// }
64-
// // 如果获取不到 redis 实例,使用总计数次数
65-
// if ($this->redis) {
66-
// $count = $this->redis->incr($redisKey);
67-
// if ($count == 1) {
68-
// // 设置过期时间
69-
// $this->redis->expire($redisKey, 6000);
70-
// }
71-
// if ($count > 20000) { // 防止刷单的安全拦截
72-
// break; // 超出就跳出循环
73-
// }
74-
// } else {
75-
// $count_fd = 'count_'.$request->fd;
76-
// $this->incrKey($count_fd);
77-
// // 单fd超过 1000 次跳出循环
78-
// if ($this->$count_fd > 1000) {
79-
// unset($this->$count_fd);
80-
// break;
81-
// }
82-
// }
83-
// }
84-
}
85-
return '';
59+
// return '';
8660

8761
// throw new \Exception('an exception');// 此时抛出的异常上层会忽略,并记录到Swoole日志,需要开发者try/catch捕获处理
8862
}
8963

9064
public function onMessage(Server $server, Frame $frame)
9165
{
66+
$key = 'websocket_fd:'.$frame->fd;
67+
$userInfo = $this->getRedis()->get($key);
68+
if (!$userInfo) {
69+
$data = $this->outJson(1200, [], '用户未登录或登录超时');
70+
$server->push($frame->fd, $data);
71+
} else {
72+
$redis = $this->getRedis();
73+
$redis->expire($key, 7200);
74+
75+
$userInfo = json_decode($userInfo, true);
76+
$state = ApiExcel::where('state', 1)->first();
77+
if (!$state) {
78+
$res = $this->apiExcel($userInfo['id']);
79+
$server->push($frame->fd, $res);
80+
} else {
81+
// 每个用户 fd 限制请求次数
82+
$redisKey = 'websocket_fd_'.$frame->fd;
83+
// 如果获取不到 redis 实例,使用总计数次数
84+
if ($redis) {
85+
$count = $redis->incr($redisKey);
86+
if ($count == 1) {
87+
// 设置过期时间
88+
$redis->expire($redisKey, 6000);
89+
}
90+
if ($count > 20000) { // 防止刷单的安全拦截
91+
return; // 超出就跳出循环
92+
}
93+
} else {
94+
$count_fd = 'count_'.$frame->fd;
95+
$this->incrKey($count_fd);
96+
// 单fd超过 1000 次跳出循环
97+
if ($this->$count_fd > 1000) {
98+
unset($this->$count_fd);
99+
}
100+
}
101+
}
102+
}
92103
// \Log::info('Received message', [$frame->fd, $frame->data, $frame->opcode, $frame->finish]);
93-
$server->push($frame->fd, date('Y-m-d H:i:s'));
104+
// $server->push($frame->fd, date('Y-m-d H:i:s'));
94105
// throw new \Exception('an exception');// 此时抛出的异常上层会忽略,并记录到Swoole日志,需要开发者try/catch捕获处理
95106
}
96107

97108
public function onClose(Server $server, $fd, $reactorId)
98109
{
110+
// 删除存储的用户信息
111+
$key = 'websocket_fd:'.$fd;
112+
$this->getRedis()->del([$key]);
99113
// throw new \Exception('an exception');// 此时抛出的异常上层会忽略,并记录到Swoole日志,需要开发者try/catch捕获处理
100114
}
101115

@@ -149,13 +163,14 @@ private function apiExcel($user_id)
149163
if ($user_id != 1) {
150164
$where = ['uid' => $user_id];
151165
}
152-
$list = ApiExcel::with('apiParam')->where($where)->orderBy('id', 'desc')->paginate($this->perPage);
166+
$list = ApiExcel::with('apiParam')->where($where)->orderBy('id', 'desc')->limit($this->perPage)->get();
153167
// 获取完成进度情况
154168
$list = ApiRepository::getInstent()->workProgress($list);
169+
$total = ApiExcel::where($where)->count();
155170

156171
$appUrl = env('APP_URL') ?? '';
157-
$collect = collect(['appUrl' => $appUrl]);
158-
$items = $collect->merge($list);
172+
$items = collect(['appUrl' => $appUrl, 'total' => $total, 'data' => $list]);
173+
// $items = $collect->merge($list);
159174

160175
return $this->outJson(200, $items);
161176
}
@@ -167,4 +182,16 @@ private function incrKey($key)
167182
}
168183
$this->$key++;
169184
}
185+
186+
/**
187+
* 获取 redis 连接
188+
* @return \Illuminate\Redis\Connections\Connection
189+
*/
190+
private function getRedis()
191+
{
192+
if (empty($this->redis)) {
193+
$this->redis = Redis::connection();
194+
}
195+
return $this->redis;
196+
}
170197
}

0 commit comments

Comments
 (0)