在做公衆號開發-被動回覆消息的過程中,官方要求如下:
假如服務器無法保證在五秒內處理並回復,必須做出下述回覆,這樣微信服務器纔不會對此作任何處理,並且不會發起重試(這種情況下,可以使用客服消息接口進行異步回覆),否則,將出現嚴重的錯誤提示。詳見下面說明:
1、直接回復success(推薦方式) 2、直接回復空串(指字節長度爲0的空字符串,而不是XML結構體中content字段的內容爲空)
一旦遇到以下情況,微信都會在公衆號會話中,向用戶下發系統提示“該公衆號暫時無法提供服務,請稍後再試”:
1、開發者在5秒內未回覆任何內容 2、開發者回覆了異常數據,比如JSON數據等
但圖像識別接口,數據獲取的過程就非常複雜,首先要獲取accessToken,然後通過媒體接口去下載用戶發來的圖片,然後再調用本地複雜的識別邏輯,圖片一旦過大根本無法保證5秒內處理完畢,所以我想了個簡單做法,如果處理時間超過3秒,就直接結束掉處理線程回覆一個success。但是我有點犯難,python內置庫壓根沒有直接可以用的類或方法啊。
回憶起java,要實現這樣的效果多簡單:
用線程池創建Future直接就可以實現了,代碼如下:
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.*;
public class Test {
public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
final ExecutorService exec = Executors.newFixedThreadPool(1);
final Random random = new Random();
Callable<String> call = () -> {
//開始執行耗時操作
int millis = random.nextInt(7000);
System.out.println("預計處理耗時" + millis + "毫秒");
Thread.sleep(millis);
return "順利識別完圖片,執行完畢";
};
while (true) {
Future<String> future = exec.submit(call);
try {
String result = future.get(1000 * 3, TimeUnit.MILLISECONDS); //任務處理超時時間設爲 3 秒
System.out.println("三秒內已經"+result);
} catch (TimeoutException ex) {
System.out.println("已超時,請重試....");
// ex.printStackTrace();
} catch (Exception e) {
System.out.println("處理失敗.");
e.printStackTrace();
} finally {
future.cancel(true);
}
}
// 關閉線程池
// exec.shutdown();
}
}
結果如下:
預計處理耗時4102毫秒
已超時,請重試....
預計處理耗時2831毫秒
三秒內已經順利識別完圖片,執行完畢
預計處理耗時4106毫秒
已超時,請重試....
預計處理耗時5341毫秒
已超時,請重試....
預計處理耗時5705毫秒
已超時,請重試....
預計處理耗時535毫秒
三秒內已經順利識別完圖片,執行完畢
預計處理耗時1441毫秒
三秒內已經順利識別完圖片,執行完畢
預計處理耗時5463毫秒
已超時,請重試....
預計處理耗時5192毫秒
已超時,請重試....
預計處理耗時441毫秒
三秒內已經順利識別完圖片,執行完畢
預計處理耗時2147毫秒
三秒內已經順利識別完圖片,執行完畢
...
可是python如何實現這樣的效果呢?我踩了無數的坑,試驗了無數的方法,發現eventlet特別好使,寫出來的代碼比java更簡單。
當然使用前,可能需要先安裝:
pip install eventlet
然後編寫的代碼如下:
import random
import time
import eventlet # 導入eventlet這個模塊
eventlet.monkey_patch() # 必須加這條代碼
while 1:
t = eventlet.Timeout(3, False) # 設定超時時間爲3秒
try:
randtime = random.random() * 7
print(f"預計處理耗時:{randtime}秒")
time.sleep(randtime)
print('3秒內順利識別完圖片,執行完畢')
except eventlet.timeout.Timeout as e:
print('已超時,請重試...')
finally:
t.cancel()
結果:
預計處理耗時:4.658229865957732秒
已超時,請重試...
預計處理耗時:6.228621136519976秒
已超時,請重試...
預計處理耗時:5.377029668612019秒
已超時,請重試...
預計處理耗時:2.019114138389199秒
3秒內順利識別完圖片,執行完畢
預計處理耗時:6.853365361191674秒
已超時,請重試...
預計處理耗時:2.7689501896254516秒
3秒內順利識別完圖片,執行完畢
預計處理耗時:1.2264810150796943秒
3秒內順利識別完圖片,執行完畢
預計處理耗時:0.7978596675396795秒
3秒內順利識別完圖片,執行完畢
預計處理耗時:1.682507234965843秒
3秒內順利識別完圖片,執行完畢
預計處理耗時:6.754677994247869秒
已超時,請重試...
預計處理耗時:1.9340315674113921秒
3秒內順利識別完圖片,執行完畢
...
哈哈,順利實現了這個效果,然後我就把類似的邏輯引入到web框架對接公衆號了。