在做公众号开发-被动回复消息的过程中,官方要求如下:

假如服务器无法保证在五秒内处理并回复,必须做出下述回复,这样微信服务器才不会对此作任何处理,并且不会发起重试(这种情况下,可以使用客服消息接口进行异步回复),否则,将出现严重的错误提示。详见下面说明:

1、直接回复success(推荐方式) 2、直接回复空串(指字节长度为0的空字符串,而不是XML结构体中content字段的内容为空)

一旦遇到以下情况,微信都会在公众号会话中,向用户下发系统提示“该公众号暂时无法提供服务,请稍后再试”:

1、开发者在5秒内未回复任何内容 2、开发者回复了异常数据,比如JSON数据等

但图像识别接口,数据获取的过程就非常复杂,首先要获取accessToken,然后通过媒体接口去下载用户发来的图片,然后再调用本地复杂的识别逻辑,图片一旦过大根本无法保证5秒内处理完毕,所以我想了个简单做法,如果处理时间超过3秒,就直接结束掉处理线程回复一个success。但是我有点犯难,python内置库压根没有直接可以用的类或方法啊。

回忆起java,要实现这样的效果多简单:

python如何实现任务超时处理?_公众号

用线程池创建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框架对接公众号了。