這是因爲不和諧客戶端模塊需要每分鐘左右控制一次。
這意味着任何竊取超過一定時間的控件的函數都會導致不和諧的客戶端進入無效狀態(稍後可能會在客戶端的下一個方法調用時顯示爲異常)。
爲了確保不一致模塊客戶端可以ping不和服務器,您應該使用真正的多線程解決方案。
一個解決方案是將所有繁重處理卸載到單獨的進程上(單獨的線程不會這樣做,因爲Python具有全局解釋器鎖定)並將不和諧bot用作負責填充工作隊列的薄層。
相關閱讀: https://discordpy.readthedocs.io/en/latest/faq.html#what-does-blocking-mean
例的解決方案......這是遠遠超出了問題的範圍,但我已經有大部分的代碼編寫。如果我有更多的時間,我會寫一個短的解決方案:)
2份,不和諧的交互和處理服務器:
這是不和諧的聽衆。
import discord
import re
import asyncio
import traceback
import websockets
import json
# Call a function on other server
async def call(methodName, *args, **kwargs):
async with websockets.connect('ws://localhost:9001/meow') as websocket:
payload = json.dumps({"method":methodName, "args":args, "kwargs": kwargs})
await websocket.send(payload)
#...
resp = await websocket.recv()
#...
return resp
client = discord.Client()
tok = open("token.dat").read()
@client.event
async def on_ready():
print('Logged in as')
print(client.user.name)
print(client.user.id)
print('------')
@client.event
async def on_error(event, *args, **kwargs):
print("Error?")
@client.event
async def on_message(message):
try:
if message.author.id == client.user.id:
return
m = re.match("(\w+) for (\d+).*?", message.content)
if m:
g = m.groups(1)
methodName = g[0]
someNumber = int(g[1])
response = await call(methodName, someNumber)
if response:
await client.send_message(message.channel, response[0:2000])
except Exception as e:
print (e)
print (traceback.format_exc())
client.run(tok)
這是用於處理大量請求的工作服務器。您可以使這部分同步或異步。
我選擇使用一種叫做websocket的魔法來將數據從一個python進程發送到另一個。但是你可以使用任何你想要的東西。例如,您可以讓一個腳本將文件寫入目錄,另一個腳本可以讀取文件並處理它們。
import tornado
import tornado.websocket
import tornado.httpserver
import json
import asyncio
import inspect
import time
class Handler:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def consume(self, text):
return "You said {0} and I say hiya".format(text)
async def sweeps(self, len):
await asyncio.sleep(len)
return "Slept for {0} seconds asynchronously!".format(len)
def sleeps(self, len):
time.sleep(len)
return "Slept for {0} seconds synchronously!".format(len)
class MyService(Handler, tornado.websocket.WebSocketHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def stop(self):
Handler.server.stop()
def open(self):
print("WebSocket opened")
def on_message(self, message):
print (message)
j = json.loads(message)
methodName = j["method"]
args = j.get("args",())
method = getattr(self, methodName)
if inspect.iscoroutinefunction(method):
loop = asyncio.get_event_loop()
task = loop.create_task(method(*args))
task.add_done_callback(lambda res: self.write_message(res.result()))
future = asyncio.ensure_future(task)
elif method:
resp = method(*args)
self.write_message(resp)
def on_close(self):
print("WebSocket closed")
application = tornado.web.Application([
(r'/meow', MyService),
])
if __name__ == "__main__":
from tornado.platform.asyncio import AsyncIOMainLoop
AsyncIOMainLoop().install()
http_server = tornado.httpserver.HTTPServer(application)
Handler.server = http_server
http_server.listen(9001)
asyncio.get_event_loop().run_forever()
現在,如果你運行在單獨的Python腳本這兩個進程,並告訴你的機器人「睡眠100」,它會睡100秒愉快! asyncio的功能是作爲一個轉換工作隊列,你可以通過將它們作爲單獨的python腳本運行來正確地將監聽器與後端處理分開。
現在,無論您的函數在'服務器'部分運行多久,客戶端部分都不會被阻止對不和服務器執行ping操作。
圖片未能上傳,但是...無論如何,這是如何告訴機器人睡覺並回復...注意睡眠是同步的。 http://i.imgur.com/N4ZPPbB.png
謝謝你在不和諧運作中的這種見解。不幸的是,我不是線程和異步編程方面的專家。你能解釋一下這個「薄層」是什麼以及如何實現它? – shrx
有很多方法,這個討論超出了這個問題的範圍。我會瀏覽我自己的個人代碼(這很糟糕...因爲我寫了ot),看看我是否可以爲你提供一些片段和想法:) – JamEnergy
謝謝你用例子進行徹底的解釋。 – shrx