問題來源:http://www.zhihu.com/question/22685171
微信服務器在五秒內收不到響應會斷掉連接,並且重新發起請求,總共重試三次
由於微信官方的服務器在處理消息時候的特殊方式,導致了這種問題的發生
微信的本意是爲了在丟包稍微嚴重的情況下服務依舊能正常運行,但是這樣給開發者確提升了難度
一般是每隔5s發送一次請求的,但是如果我們的處理是耗時操作怎麼辦呢,我們在第10s才能結束操作,這怎麼辦>>>>>>>>>>>>>>>>>>
--------僅用於企業號和服務號
1.就和回答中的Aloong兄弟那樣說的,我們可以先給微信服務器反饋一個空白或者不空白的數據(XML或者非XML都行),然後調用微信的高級接口,給用戶發送數據即可(理由:我們反饋了一個任意數據,微信服務器認爲他的工作完成了,就不會再重複發送數據了,此時我們再調用高級接口發送數據也就不會有干擾了-即使是用戶短時間內多次發送請求)
流程:
a.得到數據response.getWriter();
b.得到數據request下的所有數據--->寫入一個HashMap中
c.使用response的writer返回一個空白,並且關閉writer,注意如果不關閉的話,那麼這個空白消息是不會被傳給微信服務器的
d.使用之前的HashMap的值做我們的超時處理
--------通用方法
2.和李一峯兄弟說的那樣,建立一個簡單的list緩衝-相當於消息隊列,來了一個消息之後就和list比對(有msgid的消息使用msgid排重。事件類型消息推薦使用FromUserName + CreateTime[重複發送相同消息這個值是一樣的] 排重。),遍歷list發現如果在list中存在那麼就不加入list,直接return,否則的話加入list,然後繼續執行操作,執行完成後反饋即可---必須使用AsyncContext這種異步操作
流程:
a.新建一個靜態全局ArrayList<XXX>,添加一個類XXX{msgid, FromUserName, CreateTime}
b.遍歷list如果在list中,那麼直接return || 如果不在list中,list.add就行了(return之後由於Async的存在連接不會斷開,所以request和response不會釋放,也就不會斷開本次回話,如果不使用Async的話,微信的結束自己的任務,不會重複發送,問題是我們也發不出數據回去了)
c.耗時操作,並反饋XML數據回去
模擬:只在第三次的時候返回數據,其他時候掛起
Servlet中
int coutn = 1;
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
final AsyncContext asyncContext = request.startAsync(request, response);
asyncContext.setTimeout(5000);
// asyncContext.addListener(new AsyncListener()); 這裏需要自己添加
asyncContext.start(new Deal_Thread(asyncContext)); //新建一個自己的Thread類,類中執行應盡的操作
}
DealThread中@Override
public void run() {
Date nDate = new Date(System.currentTimeMillis());
PrintWriter pw = null;
Map<String, String> userSendMap = null;
try {
pw = ((HttpServletResponse)this.context.getResponse()).getWriter();
userSendMap = new dealXML().toMap((HttpServletRequest)this.context.getRequest());
// if(is_exist(CoreServlet.list, (HashMap<String, String>)userSendMap))
if(CoreServlet.coutn == 3) {
Message_Text message_Text = new EasyFunctions().genaText(userSendMap.get("FromUserName"), userSendMap.get("ToUserName"), "wocao");
pw.print(message_Text.toXMLString_And_enCode());
}else{
CoreServlet.coutn++;
return;
}
pw.flush();
pw.close();
} catch (Exception e1) {
e1.printStackTrace();
}
}
總結:
第一種方法用着思維挺簡單,操作也挺簡單,但是要求必須是企業號或者是服務號,要求不高的話可以用這個方法,操作簡單並且基本上不需要考慮多線程的問題
第二種方法比較好,適用於各種情況,不僅僅是在微信上的,但是需要考慮到請求的時間問題,所以比較複雜