Comments (37)
三种解决方案都能够解决问题。
-
config中添加API_ROOT项,并在酷Q配置文件中改为HTTP通信方式。
-
在ws配置链接中,不使用主机公网IP或
127.0.0.1
,而使用docker inspect bridge
中获取的IPAM-Config-Gateway
网桥IP。 -
添加模拟心跳包事件,用
bot
对象定时调用API,如bot.get_status()
,我测试的是每分钟调用一次。
from nonebot.
另外刚刚见到了没见过的新错误:
定时任务运行时抛出异常,之后机器人如上述一样失效。
Execution of job "_ (trigger: cron[minute='*'], next run at: 2018-12-14 20:45:00 CST)" skipped: maximum number of running instances reached (1)
Job "_ (trigger: cron[minute='*'], next run at: 2018-12-14 20:46:00 CST)" raised an exception
Traceback (most recent call last):
File "/root/anaconda3/lib/python3.6/site-packages/aiocqhttp/api.py", line 101, in fetch
return await asyncio.wait_for(future, 60) # wait for only 60 secs
File "/root/anaconda3/lib/python3.6/asyncio/tasks.py", line 362, in wait_for
raise futures.TimeoutError()
concurrent.futures._base.TimeoutError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/root/anaconda3/lib/python3.6/site-packages/apscheduler/executors/base_py3.py", line 29, in run_coroutine_job
retval = await job.func(*job.args, **job.kwargs)
File "/root/yotsuyu-bot/plugins/warframe_world_state/__init__.py", line 189, in _
await bot.send_group_msg(group_id=group_id, message=message.strip())
File "/root/anaconda3/lib/python3.6/site-packages/aiocqhttp/api.py", line 170, in call_action
action, **params)
File "/root/anaconda3/lib/python3.6/site-packages/aiocqhttp/api.py", line 140, in call_action
return _handle_api_result(await ResultStore.fetch(seq))
File "/root/anaconda3/lib/python3.6/site-packages/aiocqhttp/api.py", line 105, in fetch
raise NetworkError('WebSocket API call timeout')
aiocqhttp.exceptions.NetworkError: WebSocket API call timeout
另外一旦这种情况发生,单纯重启后台服务无意义,必须在noVNC内重启酷Q。
from nonebot.
你遇到这个 TimeoutError
的时候(或者之前),插件的日志那边有看到相应的调用的 API 的日志吗(最好把 log_level
配置成 debug
来运行一下看看)
from nonebot.
可以看下你定时任务的代码吗
from nonebot.
@none.scheduler.scheduled_job('cron', minute='*')
async def _():
alert_list = get_alerts()
alert_to_send = []
for alert in alert_list:
alert_id = alert['_id']['$oid']
if not alert_recorded(alert_id):
record_alert(alert_id)
alert_to_send.append(alert)
if get_alert_record_size() > 100:
clear_alert_record()
for alert in alert_to_send:
record_alert(alert)
message = ''
if len(alert_to_send) == 0:
return
for alert in alert_to_send:
second_remaining = calculate_remain_seconds(alert['Expiry']['$date']['$numberLong'])
format_hour_remaining = second_remaining // 3600
second_remaining %= 3600
format_minute_remaining = second_remaining // 60
second_remaining %= 60
format_second_remaining = second_remaining
mission_info = alert['MissionInfo']
mission_type = DE_DICT.get(mission_info['missionType'])
if not mission_type:
mission_type = mission_info['missionType']
faction = DE_DICT.get(mission_info['faction'])
if not faction:
faction = mission_info['faction']
location = DE_DICT.get(mission_info['location'])
if not location:
location = mission_info['location']
enemy_level = '{}-{}'.format(mission_info['minEnemyLevel'], mission_info['maxEnemyLevel'])
mission_reward = mission_info['missionReward']
reward_credit = mission_reward.get('credits')
reward_item_list = []
if mission_reward.get('items'):
for item in mission_reward['items']:
item_dict_name = DE_DICT.get(item)
if not item_dict_name:
item_dict_name = item
reward_item_list.append(item_dict_name)
message += f'{location} {mission_type}\n派系:{faction}\n等级:{enemy_level}\n报酬:'
if reward_credit:
message += f'{reward_credit}现金 '
for item in reward_item_list:
message += f'{item} '
message += '\n剩余时间:'
if format_hour_remaining > 0:
message += f'{format_hour_remaining}小时'
if format_minute_remaining > 0:
message += f'{format_minute_remaining}分钟'
if format_second_remaining > 0:
message += f'{format_second_remaining}秒'
message += '\n\n'
message = message.strip()
bot = none.get_bot()
broadcast_group_list = get_alert_broadcast_group()
for group_id in broadcast_group_list:
await bot.send_group_msg(group_id=group_id, message=message)
其中get_alerts()
是在其他模块中的方法,功能为读取某HTTP API的内容(调用requests库)。
get_alert_broadcast_group()
为读取redis中的群列表。
这两个方法都是非async方法,难道是这个原因?
from nonebot.
看你这个感觉初步判断应该是:每分钟执行了一次定时任务,然后调用了 await bot.send_group_msg(group_id=group_id, message=message)
,然后这玩意,API 连接断了,没发送成功,NoneBot 一直等待,60 秒超时,时间累积久了之后,APScheduler 报错说超过最大任务数量
from nonebot.
应该还是因为插件的反向 WebSocket 不够稳定,先换 HTTP 试试
from nonebot.
看你这个感觉初步判断应该是.....
嗯是这样的没错。
改HTTP的话,直接往post_url
填http://IP:PORT
么?
from nonebot.
直接往
post_url
填http://IP:PORT
么
对,然后 NoneBot 这边,配置 API_ROOT
from nonebot.
这个 issue 先不关,我有空研究下看看,你再发一下你现在的具体运行环境
from nonebot.
阿里云ECS CentOS 7 x64
酷Q Pro 最新版(Docker)
Python 3.6.5(Anaconda)
下面是pip的包
aiocqhttp 0.6.5
aiofiles 0.4.0
aiohttp 3.4.4
aioredis 1.2.0
cqhttp 1.2.1
docker 3.5.1
none-bot 0.4.1
Quart 0.6.10
from nonebot.
用HTTP一直很稳定了,只是WebSocket强迫症发作中……
from nonebot.
对了,可否换成反向 WebSocket 复现一下(配置插件 log_level
为 debug
),然后把插件的日志(app/io.github.richardchien.coolqhttpapi/log/
里面)发来看看,看插件有没有实际收到那个请求
另外:
其中
get_alerts()
是在其他模块中的方法,功能为读取某HTTP API的内容(调用requests库)。
NoneBot 里面不建议用 requests,因为它同步 IO,会阻塞的,可以用 asyncio 的 run_in_executor
包装下,或者用 aiohttp
然后你现在 Docker 的启动命令也发来看看
from nonebot.
我先复现再改get_alert好了。
Docker命令是
docker run --name=coolq --rm -d -p PORT:PORT -p 5700:5700 -v /root/coolq/data:/home/user/coolq -e COOLQ_URL=http://47.95.202.161/static/cq/CQP-tuling.zip -e VNC_PASSWD=PASSWORD -e COOLQ_ACCOUNT=QQ_ID coolq/wine-coolq
from nonebot.
反向 WS 的地址是通过公网 IP 访问的吗
from nonebot.
是的,不过酷Q和后台服务在同一服务器。
顺便,这个复现看脸……由于那个定时任务,放太久不管导致出错的几率更低了。
from nonebot.
那大概原因可能是阿里云的防火墙在一段时间没通信之后掐掉了连接,尝试换 172.17.0.1
看看,Docker 默认的网桥的宿主机地址,具体有可能不一样,在宿主机用 docker inspect bridge
可以看到
from nonebot.
emm我试试
from nonebot.
现在就放着看一会吧,顺便这个和心跳包有影响么?我还是开了。
from nonebot.
当然开了心跳包是最好,因为心跳包就是因为之前有人说连接一段时间会断开而加的(你不说我都忘了
from nonebot.
233我一直开着,应该不是这原因。
等等,如果说有心跳包存在,那上面
阿里云的防火墙在一段时间没通信之后掐掉了连接
这个还可能会发生么?
想起来之前的问题是event正常但是api关了,或许就是因为心跳包才让event活下来了?
from nonebot.
等下,应该是这样的,心跳包是可以维护住 event 连接,但 api 连接还是会断
from nonebot.
反正首先最好是用 docker 的网桥来访问宿主机,不要用公网 ip
from nonebot.
要不你给心跳包加个维护api连接(斜眼笑
from nonebot.
api 你可以自行心跳呀其实,定时请求 get_status
接口就可以了
from nonebot.
啊,说到这个,上次加status功能时候报错了,我等等吃完东西再试试。
from nonebot.
好,感谢分享!
from nonebot.
代码重构时候想到一个小相关的问题,还是发在这里吧。
我看了API文档中有一段:
下面列出的所有 API 都可以通过附加后缀 _async 来进行异步调用,例如/send_private_msg_async、/send_msg_async、/clean_data_dir_async。
那如果在async函数中调用这些API,并且await返回结果,使用async后缀有无区别?
如await bot.send_group_msg(group_id, message)
和await bot.send_group_msg_async(group_id, message)
。
或者不使用await
,直接bot.send_group_msg_async(group_id, message)
。
这几种方式有何差异?方便的话可否讲解下,谢谢。
from nonebot.
下面列出的所有 API 都可以通过附加后缀 _async 来进行异步调用,例如/send_private_msg_async、/send_msg_async、/clean_data_dir_async。
这里的 _async
后缀是在插件中的异步,也就是说,这个请求进入插件之后,不会立即执行,而是进入线程池的任务队列,然后返回一个 status
为 async
的响应;插件返回后,NoneBot 这边 await
会等待完成,但这个时候插件可能还没有执行那个异步操作。而 NoneBot 这边 await bot.send_group_msg
(不带 _async
)是 Python 这边异步,插件那边还是会执行完才返回结果。
from nonebot.
那这么看来似乎是用async后缀和await 不带async效果一样?
from nonebot.
还是不一样的,看你需求,如果你需要知道这条消息有没有发成功,或者要获取群成员信息之类的,用不带 _async
的,如果只是要发送这条消息,不关心成不成功,可以用带 _async
的。
但 Python 这边无论如何都要 await
,因为是基于 asyncio 的
from nonebot.
但 Python 这边无论如何都要
await
,因为是基于 asyncio 的
除非你故意让他在后台运行
from nonebot.
明白了,另外如果不带async是不是就会产生线程池任务队列积累过多最后报错的隐患?
from nonebot.
应该是带 _async
会有这种隐患吧,应该不至于报错,他会排队的,线程池满了会阻塞,最多会 WebSocket 消息处理卡时间很长然后超时吧
from nonebot.
emm看来我理解错了,是不是应该是这样:
你之前说的
如果只是要发送这条消息,不关心成不成功,可以用带 _async 的。
其实带不带async最终都会成功执行,但是带上async可能不会在调用时立即执行,导致可能需要返回信息的API利用失败,而不带async就会保证第一时间执行对应的API并返回信息?我这样理解有问题么。
from nonebot.
其实带不带async_最终_都会成功执行,但是带上async可能不会在调用时立即执行,导致可能需要返回信息的API利用失败,而不带async就会保证第一时间执行对应的API并返回信息?我这样理解有问题么。
对的,是这个意思,也就是说,你需要 API 的返回结果,那就不能带 _async
,你不关心结果,就可以带
from nonebot.
from nonebot.
Related Issues (20)
- 请问如何在只是User is calling me的情况下保持会话? HOT 2
- scheduler与message_preprocessor不在同一个eventloop导致两者之间很难进行同步 HOT 6
- nonebot+cqhttp反向websocket 时出现错误: websocket: bad handshake HOT 8
- [提问]如何关闭heartbeat的logging HOT 6
- aget 无法发送prompt HOT 3
- 1.8.3移除`CommandSession.args`导致类 Shell 参数解析Command出错 HOT 2
- 以 node-onebot 为后端无法正确设置 `ctx['to_me']` HOT 5
- 加載“失敗”的插件注冊的命令仍然可用 HOT 1
- 考慮移除 sched HOT 1
- NLPSession无法处理被两个qq表情夹着的关键词 HOT 11
- 运行`nb run`命令后直接抛出`cannot import name 'WebSocketSetup' from 'nonebot.drivers' ` HOT 1
- say 和 echo失踪 HOT 3
- NLPSession在较长的句子中不能识别到关键词 HOT 2
- 1
- 关于Pyinstaller打包后的exe为什么会报错的解决方案【建议调整一部分代码】
- 启动时遇到ImportError HOT 2
- m1 运行报错cannot import name 'escape' from 'jinja2' HOT 9
- ImportError: cannot import name 'overrides' from 'nonebot.typing' HOT 1
- 无法正确识别“回复”类型消息中的指令 HOT 2
- 按组一同注册命令的关键词 内容 权限 HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from nonebot.