1. IPv6定位優(yōu)化

        共 7057字,需瀏覽 15分鐘

         ·

        2021-03-27 14:41

        背景

        隨著 IPv6的推進(jìn),我們發(fā)現(xiàn)線(xiàn)上需要使用 IPv6 定位的流量已經(jīng)達(dá)到了 8000 QPS。此前我們并未對(duì) IPv6 定位做任何緩存或者其它優(yōu)化,這部分流量會(huì)直接請(qǐng)求定位服務(wù),隨著流量進(jìn)一步提升可能觸發(fā)調(diào)用量報(bào)警以及流控。另外由于此前已經(jīng)對(duì) IPv4 進(jìn)行了緩存,如果 IPv6 不做相應(yīng)的優(yōu)化,因?yàn)槎嗔艘淮?RPC 請(qǐng)求,服務(wù)的響應(yīng)時(shí)間會(huì)隨著 IPv6 流量占比提升而變長(zhǎng)。

        調(diào)研

        通過(guò)和定位服務(wù)負(fù)責(zé)人溝通,我們獲取到如下有用信息:

        1. IPv6 定位數(shù)據(jù)是從外部采購(gòu),數(shù)據(jù)量大概是幾十萬(wàn)條
        2. 和 IPv4 類(lèi)似,前綴相同的地址定位到相同的地域,但是不像 IPv4 使用固定的前3段,具體多少位不確定,由另外一個(gè)參數(shù)決定
        3. 數(shù)據(jù)每天更新一次,每次變更量不大,幾百條左右
        4. 除了提供定位服務(wù)之外,還會(huì)將定位數(shù)據(jù)在 HDFS 上存儲(chǔ)一份

        定位數(shù)據(jù)示例如下:

        2001:250:200::/48        中國(guó)        北京市        北京
        2001:250:201::/48        中國(guó)        北京市        北京
        2001:250:202::/48        中國(guó)        北京市        北京
        2001:250:203::/48        中國(guó)        北京市        北京
        2001:250:204::/48        中國(guó)        北京市        北京

        使用第一行來(lái)進(jìn)行一個(gè)說(shuō)明,如果地址的前 48 位和 2001:250:200:: 的前 48 位一致,則定位到北京。另外需要注意的是雖然示例數(shù)據(jù)中都是根據(jù)前 48 位來(lái)進(jìn)行定位,但是這個(gè)值可能是不同的。

        方案

        從定位數(shù)據(jù)的格式以及定位邏輯來(lái)看,比較適合的數(shù)據(jù)結(jié)構(gòu)是前綴樹(shù),這樣可以很好的實(shí)現(xiàn)根據(jù)前 xx 位進(jìn)行定位。IPv6 共有 128位,即 128 個(gè) 0 和 1,由于值要么是 0 要么是 1,所以構(gòu)建出來(lái)的是一顆二叉樹(shù),數(shù)據(jù)結(jié)構(gòu)相關(guān)的代碼如下:

        private Node root = new Node();

        public class Node {
            
            private Node[] children = new Node[2];
            private Integer localId = null;

            public Integer getLocalId() {
                return localId;
            }

            public void setLocalId(Integer localId) {
                this.localId = localId;
            }

            public Node getChild(int pos) {
                return children[pos];
            }

            public void setChild(int pos, Node child) {
                children[pos] = child;
            }
        }

        比如數(shù)據(jù) 2001:250:200::/48 只需要使用到前 48 位即可,并且在第 48 位上標(biāo)記地域信息,構(gòu)建前綴樹(shù)的代碼如下:

        public void put(Inet6Address inet6Address, Integer mask, Integer localId) {
            if (inet6Address == null || localId == null || localId <= 0 || mask == null || mask <= 0 || mask > 128) {
                return;
            }
            Address address = new Address(inet6Address);
            Node node = root;
            for (int i = 0; i < mask; i++) {
                int pos = address.valueAt(i);
                Node child = node.getChild(pos);
                if (child == null) {
                    child = new Node();
                    node.setChild(pos, child);
                }
                node = child;
            }
            node.setLocalId(localId);
        }

        public static class Address {

            private static int[] temp = new int[]{0x800x400x200x100x080x040x020x1};

            private byte[] bytes;

            public Address(Inet6Address address) {
                this.bytes = address.getAddress();
            }

            public byte valueAt(int pos) {
                if (pos < 0 || pos >= 128) {
                    throw new IllegalArgumentException("pos 的取值范圍是 [0, 128)");
                }
                int num = pos / 8;
                int p = pos % 8;
                return (byte) ((bytes[num] & temp[p]) >>> (7 - p));
            }
        }

        其中 Address 類(lèi)是對(duì) JDK 內(nèi)置類(lèi) Inet6Address 的一個(gè)封裝,提供了便捷的取指定位的方法 valueAt。

        通過(guò)上述代碼使用定位數(shù)據(jù)的每一行調(diào)用 put 方法即可完成前綴樹(shù)的構(gòu)建,下邊看下構(gòu)建好的前綴樹(shù)如何進(jìn)行查找:

        public Integer get(Inet6Address inet6Address) {
            if (inet6Address == null) {
                return null;
            }
            Address address = new Address(inet6Address);
            Node node = root;
            for (int i = 0; i < 128; i++) {
                int pos = address.valueAt(i);
                Node child = node.getChild(pos);
                if (child == null) {
                    return null;
                }
                Integer localId = child.getLocalId();
                if (localId != null) {
                    return localId;
                }
                node = child;
            }
            return null;
        }

        通過(guò)上述代碼已經(jīng)可以實(shí)現(xiàn)對(duì)前綴樹(shù)進(jìn)行構(gòu)建以及查找,需要注意這顆前綴樹(shù)是不能進(jìn)行修改的,只能新增和查找。

        服務(wù)啟動(dòng)的時(shí)候需要進(jìn)行前綴樹(shù)的初始化,此時(shí)會(huì)請(qǐng)求 HDFS 拉取定位數(shù)據(jù),由于網(wǎng)絡(luò)請(qǐng)求不總是靠譜的,增加了三次重試,另外在鏡像中放置一份數(shù)據(jù)(更新頻率更低)來(lái)進(jìn)行降級(jí),避免服務(wù)啟動(dòng)失敗。

        對(duì)于數(shù)據(jù)更新,使用了一個(gè)定時(shí)任務(wù)每天 8 點(diǎn)更新,因?yàn)槎ㄎ粩?shù)據(jù)每天變更的量很小,這個(gè)更新是允許失敗的,更新的時(shí)候會(huì)根據(jù)新的定位數(shù)據(jù)構(gòu)建一顆前綴樹(shù),如果更新成功則替換之前的前綴樹(shù),更新結(jié)果會(huì)記錄一條日志,后續(xù)只需要關(guān)注下相關(guān)日志即可,不再做一個(gè)很復(fù)雜的方案來(lái)保證更新成功。

        通過(guò)上述方案即可處理好 IPv6 的定位,同時(shí)由于不使用 RPC 調(diào)用,也會(huì)給性能和響應(yīng)時(shí)間帶來(lái)一定的提升。

        - END -


        瀏覽 36
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 少妇2做爰未删减版 | 久久国产亚洲精品 | 日皮91 | 日韩欧美一级黄色片 | 插插插插噜噜噜噜 |