Giter VIP home page Giter VIP logo

Comments (37)

miranquil avatar miranquil commented on July 21, 2024 1

三种解决方案都能够解决问题。

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

miranquil avatar miranquil commented on July 21, 2024

另外刚刚见到了没见过的新错误:
定时任务运行时抛出异常,之后机器人如上述一样失效。

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.

stdrc avatar stdrc commented on July 21, 2024

你遇到这个 TimeoutError 的时候(或者之前),插件的日志那边有看到相应的调用的 API 的日志吗(最好把 log_level 配置成 debug 来运行一下看看)

from nonebot.

stdrc avatar stdrc commented on July 21, 2024

可以看下你定时任务的代码吗

from nonebot.

miranquil avatar miranquil commented on July 21, 2024
@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.

stdrc avatar stdrc commented on July 21, 2024

看你这个感觉初步判断应该是:每分钟执行了一次定时任务,然后调用了 await bot.send_group_msg(group_id=group_id, message=message),然后这玩意,API 连接断了,没发送成功,NoneBot 一直等待,60 秒超时,时间累积久了之后,APScheduler 报错说超过最大任务数量

from nonebot.

stdrc avatar stdrc commented on July 21, 2024

应该还是因为插件的反向 WebSocket 不够稳定,先换 HTTP 试试

from nonebot.

miranquil avatar miranquil commented on July 21, 2024

看你这个感觉初步判断应该是.....

嗯是这样的没错。

改HTTP的话,直接往post_urlhttp://IP:PORT么?

from nonebot.

stdrc avatar stdrc commented on July 21, 2024

直接往post_urlhttp://IP:PORT

对,然后 NoneBot 这边,配置 API_ROOT

from nonebot.

stdrc avatar stdrc commented on July 21, 2024

这个 issue 先不关,我有空研究下看看,你再发一下你现在的具体运行环境

from nonebot.

miranquil avatar miranquil commented on July 21, 2024

阿里云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.

miranquil avatar miranquil commented on July 21, 2024

用HTTP一直很稳定了,只是WebSocket强迫症发作中……

from nonebot.

stdrc avatar stdrc commented on July 21, 2024

对了,可否换成反向 WebSocket 复现一下(配置插件 log_leveldebug),然后把插件的日志(app/io.github.richardchien.coolqhttpapi/log/ 里面)发来看看,看插件有没有实际收到那个请求

另外:

其中get_alerts()是在其他模块中的方法,功能为读取某HTTP API的内容(调用requests库)。

NoneBot 里面不建议用 requests,因为它同步 IO,会阻塞的,可以用 asyncio 的 run_in_executor 包装下,或者用 aiohttp

然后你现在 Docker 的启动命令也发来看看

from nonebot.

miranquil avatar miranquil commented on July 21, 2024

我先复现再改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.

stdrc avatar stdrc commented on July 21, 2024

反向 WS 的地址是通过公网 IP 访问的吗

from nonebot.

miranquil avatar miranquil commented on July 21, 2024

是的,不过酷Q和后台服务在同一服务器。

顺便,这个复现看脸……由于那个定时任务,放太久不管导致出错的几率更低了。

from nonebot.

stdrc avatar stdrc commented on July 21, 2024

那大概原因可能是阿里云的防火墙在一段时间没通信之后掐掉了连接,尝试换 172.17.0.1 看看,Docker 默认的网桥的宿主机地址,具体有可能不一样,在宿主机用 docker inspect bridge 可以看到

from nonebot.

miranquil avatar miranquil commented on July 21, 2024

emm我试试

from nonebot.

miranquil avatar miranquil commented on July 21, 2024

现在就放着看一会吧,顺便这个和心跳包有影响么?我还是开了。

from nonebot.

stdrc avatar stdrc commented on July 21, 2024

当然开了心跳包是最好,因为心跳包就是因为之前有人说连接一段时间会断开而加的(你不说我都忘了

from nonebot.

miranquil avatar miranquil commented on July 21, 2024

233我一直开着,应该不是这原因。

等等,如果说有心跳包存在,那上面

阿里云的防火墙在一段时间没通信之后掐掉了连接

这个还可能会发生么?


想起来之前的问题是event正常但是api关了,或许就是因为心跳包才让event活下来了?

from nonebot.

stdrc avatar stdrc commented on July 21, 2024

等下,应该是这样的,心跳包是可以维护住 event 连接,但 api 连接还是会断

from nonebot.

stdrc avatar stdrc commented on July 21, 2024

反正首先最好是用 docker 的网桥来访问宿主机,不要用公网 ip

from nonebot.

miranquil avatar miranquil commented on July 21, 2024

要不你给心跳包加个维护api连接(斜眼笑

from nonebot.

stdrc avatar stdrc commented on July 21, 2024

api 你可以自行心跳呀其实,定时请求 get_status 接口就可以了

from nonebot.

miranquil avatar miranquil commented on July 21, 2024

啊,说到这个,上次加status功能时候报错了,我等等吃完东西再试试。

from nonebot.

stdrc avatar stdrc commented on July 21, 2024

好,感谢分享!

from nonebot.

miranquil avatar miranquil commented on July 21, 2024

代码重构时候想到一个小相关的问题,还是发在这里吧。
我看了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.

stdrc avatar stdrc commented on July 21, 2024

下面列出的所有 API 都可以通过附加后缀 _async 来进行异步调用,例如/send_private_msg_async、/send_msg_async、/clean_data_dir_async。

这里的 _async 后缀是在插件中的异步,也就是说,这个请求进入插件之后,不会立即执行,而是进入线程池的任务队列,然后返回一个 statusasync 的响应;插件返回后,NoneBot 这边 await 会等待完成,但这个时候插件可能还没有执行那个异步操作。而 NoneBot 这边 await bot.send_group_msg(不带 _async)是 Python 这边异步,插件那边还是会执行完才返回结果。

from nonebot.

miranquil avatar miranquil commented on July 21, 2024

那这么看来似乎是用async后缀和await 不带async效果一样?

from nonebot.

stdrc avatar stdrc commented on July 21, 2024

还是不一样的,看你需求,如果你需要知道这条消息有没有发成功,或者要获取群成员信息之类的,用不带 _async 的,如果只是要发送这条消息,不关心成不成功,可以用带 _async 的。

但 Python 这边无论如何都要 await,因为是基于 asyncio 的

from nonebot.

stdrc avatar stdrc commented on July 21, 2024

但 Python 这边无论如何都要 await,因为是基于 asyncio 的

除非你故意让他在后台运行

from nonebot.

miranquil avatar miranquil commented on July 21, 2024

明白了,另外如果不带async是不是就会产生线程池任务队列积累过多最后报错的隐患?

from nonebot.

stdrc avatar stdrc commented on July 21, 2024

应该是带 _async 会有这种隐患吧,应该不至于报错,他会排队的,线程池满了会阻塞,最多会 WebSocket 消息处理卡时间很长然后超时吧

from nonebot.

miranquil avatar miranquil commented on July 21, 2024

emm看来我理解错了,是不是应该是这样:

你之前说的

如果只是要发送这条消息,不关心成不成功,可以用带 _async 的。

其实带不带async最终都会成功执行,但是带上async可能不会在调用时立即执行,导致可能需要返回信息的API利用失败,而不带async就会保证第一时间执行对应的API并返回信息?我这样理解有问题么。

from nonebot.

stdrc avatar stdrc commented on July 21, 2024

其实带不带async_最终_都会成功执行,但是带上async可能不会在调用时立即执行,导致可能需要返回信息的API利用失败,而不带async就会保证第一时间执行对应的API并返回信息?我这样理解有问题么。

对的,是这个意思,也就是说,你需要 API 的返回结果,那就不能带 _async,你不关心结果,就可以带

from nonebot.

miranquil avatar miranquil commented on July 21, 2024

from nonebot.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.