微信服務器發送三次重複的排重問題

問題來源: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();
		}
	}

總結:

第一種方法用着思維挺簡單,操作也挺簡單,但是要求必須是企業號或者是服務號,要求不高的話可以用這個方法,操作簡單並且基本上不需要考慮多線程的問題

第二種方法比較好,適用於各種情況,不僅僅是在微信上的,但是需要考慮到請求的時間問題,所以比較複雜

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章