IM推送Android客戶端之重連策略

話不多直接進入主題

所謂重連就是Android客戶端額度TCP連接斷開之後自動進行連接,但是有時候會遇到一直重連不上的情況,那麼就不能一直頻繁的進行重連操作,畢竟在手機休眠的時候重連操作可能會有網絡數據通信,手機會被喚醒,等重連操作完成後手機纔會再進入休眠,所以頻繁的重連會導致手機頻繁的喚醒,耗電量妥妥的就上來了。

重連策略

  • 重連分兩種,一種是連接斷開重連,另一種是連接成功後秒斷重連,連接成功秒斷重連的意思是,重連成功,但是連接維持很小一段時間就又斷開了,這時候也要有遞增的時間間隔策略
  • 重連之前先判斷手機是否有網絡連接,如果沒有則不繼續進行重連操作
  • 如果一直重連失敗,那麼下一次的重連時間和之前的重連時間之間的間隔要不斷的遞增,最終穩定在一個較大的區間之內,如果仍然連接不上,那麼就一直以這個較大的重連間隔時間去進行重連
  • 考慮到秒斷情況,如果重連操作能成功連接上,但是連接不久之後又再次斷開,之後要再次進行重連,斷開和再次重連之間的時間間隔也要做限制,否則會導致重連成功,之前累計的時間間隔清零,連接又斷開,然後連接間隔又從0開始計時

連接斷開的重連時間間隔的隨機算法

private AtomicInteger connectPeriodTag = new AtomicInteger(0);

private float connectTaskPeriod;

private void randomConnectPeriod() {
        int start = (int) (Math.pow(2, connectPeriodTag.get()) * 1000);
        int end = (int) (Math.pow(2, connectPeriodTag.get() + 1) * 1000);
        connectTaskPeriod = RandomUtil.getRandom(start, end);
        if (connectTaskPeriod >= 20 * 60 * 1000) {
            connectTaskPeriod = 20 * 60 * 1000;
        } else {
            connectPeriodTag.incrementAndGet();
            SyncLogUtil.i("to increment the connectPeriodTag:" + connectPeriodTag.get() + ",connectTaskPeriod:" + connectTaskPeriod);
        }
        SyncLogUtil.i("connect task period is [" + TimeFormatUtil.format((long) connectTaskPeriod) + "]");
    }

在連接成功後就把connectPeriodTag 重新設置成0,這樣假如將來連接又斷開了,連接間隔的計算又從0開始,保證在正常情況下連接斷開後能及時重連,如果這次沒有重連成功,那麼connectPeriodTag 遞增1,然後再計算下次重連間隔,最大的穩定時間間隔爲20分鐘,也就是說如果一直重連失敗,那程序最後就會每隔20分鐘再進行一次重連

連接秒斷的重連時間間隔隨機算法

隨機算法和上面的是一樣的,具體做法是連接成功後記錄下連接成功的時間點,然後下次再進行重連的時候首先判斷一下此時的時間和上一次連接成功的時間間隔是否在一個較小的時間段內,如果是則計算秒斷時間間隔算法,如果不是就把秒斷時間間隔算法裏面的tag設置成0,演示代碼如下:

private void reconnect() {
        SyncLogUtil.d("reconnect...");
        long lastConnectPeriod = System.currentTimeMillis() - lastConnectTime;
        SyncLogUtil.i("lastConnectPeriod:" + lastConnectPeriod);
        // 避免連接連接成功後又斷開,然後又進行重連導致短時間內重連頻率太高而給服務器帶來的併發壓力
        if (lastConnectPeriod < reconnectPeriodLimit) {
            // 用鬧鐘來喚醒重連
            startReconnectAlarm(hostInfo);
        } else {
            reconnectPeriodTag.set(0);
            connect(hostname(), port(), true, null);
        }
    }

之所以選擇隨機算法的原因是儘量避開多臺設備在同一時間點觸發重連,因爲每次運行隨機算法所產生的時間間隔都不同,所以在多臺設備上也都會產生不同的重連時間間隔,降低了同一時刻因爲多臺設備觸發重連給服務器造成的併發壓力,上述的隨機算法運行的效果如下:
第一次運行:
1451.5292毫秒,00:00:01
2339.7693毫秒,00:00:02
6769.3306毫秒,00:00:06
15956.557毫秒,00:00:15
17409.965毫秒,00:00:17
47527.496毫秒,00:00:47
96241.05毫秒,00:01:36
195035.5毫秒,00:03:15
270967.44毫秒,00:04:30
685830.0毫秒,00:11:25
1200000.0毫秒,00:20:00
1179013.6毫秒,00:19:39
1200000.0毫秒,00:20:00
1200000.0毫秒,00:20:00
1200000.0毫秒,00:20:00

第二次運行:
1578.5051毫秒,00:00:01
3575.9648毫秒,00:00:03
4001.7532毫秒,00:00:04
10672.303毫秒,00:00:10
21160.8毫秒,00:00:21
43281.56毫秒,00:00:43
98480.76毫秒,00:01:38
163748.23毫秒,00:02:43
279018.8毫秒,00:04:39
835901.8毫秒,00:13:55
1200000.0毫秒,00:20:00
1200000.0毫秒,00:20:00
1200000.0毫秒,00:20:00
1200000.0毫秒,00:20:00
1180800.1毫秒,00:19:40

第三次運行:
1965.0104毫秒,00:00:01
3987.2378毫秒,00:00:03
6056.092毫秒,00:00:06
15700.957毫秒,00:00:15
27048.209毫秒,00:00:27
40336.637毫秒,00:00:40
106045.37毫秒,00:01:46
249301.33毫秒,00:04:09
458808.22毫秒,00:07:38
992412.1毫秒,00:16:32
1200000.0毫秒,00:20:00
1200000.0毫秒,00:20:00
1200000.0毫秒,00:20:00
1140937.8毫秒,00:19:00
1200000.0毫秒,00:20:00
1200000.0毫秒,00:20:00
1200000.0毫秒,00:20:00
從上面的運行效果來看是隨着重連次數的增加,時間間隔會不斷的變大,而且變大的幅度也增加了,最終會穩定在20分鐘

測試運行的代碼:

public class ReconnectPeriodTest {

    private static AtomicInteger connectPeriodTag = new AtomicInteger(0);

    private static float connectTaskPeriod;

    public static void main(String[] args) {
        while(true) {
            randomConnectPeriod();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    private static void randomConnectPeriod() {
        int start = (int) (Math.pow(2, connectPeriodTag.get()) * 1000);
        int end = (int) (Math.pow(2, connectPeriodTag.get() + 1) * 1000);
        connectTaskPeriod = getRandom(start, end);
        if (connectTaskPeriod >= 20 * 60 * 1000) {
            connectTaskPeriod = 20 * 60 * 1000;
        } else {
            connectPeriodTag.incrementAndGet();
        }
        System.out.println(connectTaskPeriod + "毫秒," + format((long) connectTaskPeriod));
    }

    public static float getRandom(int min, int max) {
        Random random = new Random();
        return (float) (random.nextDouble() * (max - min) + min);
    }

    public static String format(long milliseconds) {
        if (milliseconds < 1000) {
            milliseconds = 1000;
        }
        Date date = new Date(milliseconds - 8 * 3600 * 1000);
        return new SimpleDateFormat("HH:mm:ss").format(date);
    }
}
發佈了52 篇原創文章 · 獲贊 35 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章