人妖在线一区,国产日韩欧美一区二区综合在线,国产啪精品视频网站免费,欧美内射深插日本少妇

新聞動(dòng)態(tài)

nginx內(nèi)存池源碼解析

發(fā)布日期:2021-12-13 00:25 | 文章來(lái)源:CSDN

內(nèi)存池概述

內(nèi)存池是在真正使用內(nèi)存之前,預(yù)先申請(qǐng)分配一定數(shù)量的、大小相等(一般情況下)的內(nèi)存塊留作備用。當(dāng)有新的內(nèi)存需求時(shí),就從內(nèi)存池中分出一部分內(nèi)存塊,若內(nèi)存塊不夠用時(shí),再繼續(xù)申請(qǐng)新的內(nèi)存。

內(nèi)存池的好處有減少向系統(tǒng)申請(qǐng)和釋放內(nèi)存的時(shí)間開(kāi)銷,解決內(nèi)存頻繁分配產(chǎn)生的碎片,提示程序性能,減少程序員在編寫(xiě)代碼中對(duì)內(nèi)存的關(guān)注等

目前一些常見(jiàn)的內(nèi)存池實(shí)現(xiàn)方案有STL中的內(nèi)存分配區(qū),boost中的object_pool,nginx中的ngx_pool_t,google的開(kāi)源項(xiàng)目TCMalloc等。

為了自身使用的方便,Nginx封裝了很多有用的數(shù)據(jù)結(jié)構(gòu),比如ngx_str_t ,ngx_array_t, ngx_pool_t 等等,對(duì)于內(nèi)存池,nginx設(shè)計(jì)的十分精煉,值得我們學(xué)習(xí),本文重點(diǎn)給大家介紹nginx內(nèi)存池源碼,并用一個(gè)實(shí)際的代碼例子作了進(jìn)一步的講解。

一、nginx數(shù)據(jù)結(jié)構(gòu)

// SGI STL小塊和大塊內(nèi)存的分界點(diǎn):128B
// nginx(給HTTP服務(wù)器所有的模塊分配內(nèi)存)小塊和大塊內(nèi)存的分界點(diǎn):4096B
#define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1) 
// 內(nèi)存池默認(rèn)大小
#define NGX_DEFAULT_POOL_SIZE    (16 * 1024)
// 內(nèi)存池字節(jié)對(duì)齊,SGI STL對(duì)其是8B
#define NGX_POOL_ALIGNMENT       16
#define NGX_MIN_POOL_SIZE        ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)), \
		                         NGX_POOL_ALIGNMENT)
// 將開(kāi)辟的內(nèi)存調(diào)整到16的整數(shù)倍
#define ngx_align(d, a)          (((d) + (a - 1)) & ~(a - 1))
typedef struct ngx_pool_s ngx_pool_t;
typedef struct {
    u_char               *last;   // 指向可用內(nèi)存的起始地址
    u_char               *end;    // 指向可用內(nèi)存的末尾地址
    ngx_pool_t           *next;   // 指向下一個(gè)內(nèi)存塊  
    ngx_uint_t            failed; // 當(dāng)前內(nèi)存塊分配空間失敗的次數(shù)
} ngx_pool_data_t;
// 內(nèi)存池塊的類型
struct ngx_pool_s {
    ngx_pool_data_t       d;          // 內(nèi)存池塊頭信息
    size_t                max;	
    ngx_pool_t           *current;    // 指向可用于分配空間的內(nèi)存塊(failed < 4)的起始地址
    ngx_chain_t          *chain;      // 連接所有的內(nèi)存池塊
    ngx_pool_large_t     *large;	  // 大塊內(nèi)存的入口指針
    ngx_pool_cleanup_t   *cleanup;    // 內(nèi)存池塊的清理操作,用戶可設(shè)置回調(diào)函數(shù),在內(nèi)存池塊釋放之前執(zhí)行清理操作
    ngx_log_t            *log;        // 日志
};

二、nginx向OS申請(qǐng)空間ngx_create_pool

// 根據(jù)size進(jìn)行內(nèi)存開(kāi)辟
ngx_pool_t * ngx_create_pool(size_t size, ngx_log_t *log){
    ngx_pool_t  *p;
	// 根據(jù)系統(tǒng)平臺(tái)定義的宏以及用戶執(zhí)行的size,調(diào)用不同平臺(tái)的API開(kāi)辟內(nèi)存池
    p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);
    if (p == NULL) {
        return NULL;
    }
    p->d.last = (u_char *) p + sizeof(ngx_pool_t);  // 指向可用內(nèi)存的起始地址
    p->d.end = (u_char *) p + size;                 // 指向可用內(nèi)存的末尾地址
    p->d.next = NULL;   // 指向下一個(gè)內(nèi)存塊,當(dāng)前剛申請(qǐng)內(nèi)存塊,所以置空              
    p->d.failed = 0;    // 內(nèi)存塊是否開(kāi)辟成功
    size = size - sizeof(ngx_pool_t);              // 能使用的空間 = 總空間 - 頭信息
    // 指定的大小若大于一個(gè)頁(yè)面就用一個(gè)頁(yè)面,否則用指定的大小
    // max = min(size, 4096),max指的是除開(kāi)頭信息以外的內(nèi)存塊的大小
    p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
    p->current = p;         // 指向可用于分配空間的內(nèi)存塊的起始地址
    p->chain = NULL;
    p->large = NULL;        // 小塊內(nèi)存直接在內(nèi)存塊開(kāi)辟,大塊內(nèi)存在large指向的內(nèi)存開(kāi)辟
    p->cleanup = NULL;
    p->log = log;
    return p;
}

三、nginx向內(nèi)存池申請(qǐng)空間

void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
    if (size <= pool->max) {
    	// 當(dāng)前分配的空間小于max,小塊內(nèi)存的分配
        return ngx_palloc_small(pool, size, 1);   // 考慮內(nèi)存對(duì)齊
    }
#endif
    return ngx_palloc_large(pool, size);
}
void *
ngx_pnalloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
    if (size <= pool->max) {
        return ngx_palloc_small(pool, size, 0);  // 不考慮內(nèi)存對(duì)齊
    }
#endif
    return ngx_palloc_large(pool, size);
}
void* ngx_pcalloc(ngx_pool_t *pool, size_t size){
    void *p;
    p = ngx_palloc(pool, size); // 考慮內(nèi)存對(duì)齊
    if (p) {
        ngx_memzero(p, size);   // 可以初始化內(nèi)存為0
    }
    return p;
}

ngx_palloc_small 分配效率高,只做了指針的偏移

static ngx_inline void *
ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align)
{
    u_char      *m;
    ngx_pool_t  *p;
	// 從第一個(gè)內(nèi)存塊的current指針指向的內(nèi)存池進(jìn)行分配
    p = pool->current;
    do {
        m = p->d.last;  // m指向可分配內(nèi)存的起始地址
        if (align) {
        	// 把m調(diào)整為NGX_ALIGNMENT整數(shù)倍
            m = ngx_align_ptr(m, NGX_ALIGNMENT);
        }
		// 內(nèi)存池分配內(nèi)存的核心代碼
        if ((size_t) (p->d.end - m) >= size) {
        	// 若可分配空間 >= 申請(qǐng)的空間
        	// 偏移d.last指針,記錄空閑空間的首地址
            p->d.last = m + size;
            return m;
        }
        // 當(dāng)前內(nèi)存塊的空閑空間不夠分配,若有下一個(gè)內(nèi)存塊則轉(zhuǎn)向下一個(gè)內(nèi)存塊
        // 若沒(méi)有,p會(huì)被置空,退出while
        p = p->d.next;
    } while (p);
	
    return ngx_palloc_block(pool, size);
}

當(dāng)前內(nèi)存池的塊足夠分配:

當(dāng)前內(nèi)存池的塊不夠分配:

  1. 開(kāi)辟新的內(nèi)存塊,修改新內(nèi)存塊頭信息的last、end、next、failed
  2. 前面所有內(nèi)存塊的failed++
  3. 連接新的內(nèi)存塊以及前面的內(nèi)存塊
static void * ngx_palloc_block(ngx_pool_t *pool, size_t size){
    u_char      *m;
    size_t       psize;
    ngx_pool_t  *p, *new;
	// 開(kāi)辟與上一個(gè)內(nèi)存塊大小相同的內(nèi)存塊
    psize = (size_t) (pool->d.end - (u_char *) pool);
	
	// 將psize對(duì)齊為NGX_POOL_ALIGNMENT的整數(shù)倍后,向OS申請(qǐng)空間
    m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);
    if (m == NULL) {
        return NULL;
    }
    new = (ngx_pool_t *) m;    // 指向新開(kāi)辟內(nèi)存塊的起始地址
    new->d.end = m + psize;    // 指向新開(kāi)辟內(nèi)存塊的末尾地址
    new->d.next = NULL;		   // 下一塊內(nèi)存的地址為NULL 
    new->d.failed = 0;		   // 當(dāng)前內(nèi)存塊分配空間失敗的次數(shù)
    
	// 指向頭信息的尾部,而max,current、chain等只在第一個(gè)內(nèi)存塊有
    m += sizeof(ngx_pool_data_t);  
    m = ngx_align_ptr(m, NGX_ALIGNMENT);
    new->d.last = m + size;                // last指向當(dāng)前塊空閑空間的起始地址
	
	// 由于每次都是從pool->current開(kāi)始分配空間
	// 若執(zhí)行到這里,除了new這個(gè)內(nèi)存塊分配成功,其他的內(nèi)存塊全部分配失敗
    for (p = pool->current; p->d.next != NULL; p = p->d.next) {
    	// 對(duì)所有的內(nèi)存塊的failed都++,直到該內(nèi)存塊分配失敗的次數(shù)大于4了
    	// 就表示該內(nèi)存塊的剩余空間很小了,不能再分配空間了
    	// 就修改current指針,下次從current開(kāi)始分配空間,再次分配的時(shí)候可以不用遍歷前面的內(nèi)存塊
        if (p->d.failed++ > 4) {
            pool->current = p->d.next;
        }
    }
	
    p->d.next = new;   // 連接可分配空間的首個(gè)內(nèi)存塊 和 新開(kāi)辟的內(nèi)存塊
    return m;
}

四、大塊內(nèi)存的分配與釋放

typedef struct ngx_pool_large_s  ngx_pool_large_t;
struct ngx_pool_large_s {
    ngx_pool_large_t     *next;   // 下一個(gè)大塊內(nèi)存的起始地址
    void                 *alloc;  // 大塊內(nèi)存的起始地址
};
static void * ngx_palloc_large(ngx_pool_t *pool, size_t size){
    void              *p;
    ngx_uint_t         n;
    ngx_pool_large_t  *large;
	
	// 調(diào)用的就是malloc
    p = ngx_alloc(size, pool->log);
    if (p == NULL) {
        return NULL;
    }
    n = 0;
	// for循環(huán)遍歷存儲(chǔ)大塊內(nèi)存信息的鏈表
    for (large = pool->large; large; large = large->next) {
        if (large->alloc == NULL) {
        	// 當(dāng)大塊內(nèi)存被ngx_pfree時(shí),alloc為NULL
        	// 遍歷鏈表,若大塊內(nèi)存的首地址為空,則把當(dāng)前malloc的內(nèi)存地址寫(xiě)入alloc
            large->alloc = p;
            return p;
        }
		// 遍歷4次后,若還沒(méi)有找到被釋放過(guò)的大塊內(nèi)存對(duì)應(yīng)的信息
		// 為了提高效率,直接在小塊內(nèi)存中申請(qǐng)空間保存大塊內(nèi)存的信息
        if (n++ > 3) {
            break;
        }
    }
	// 通過(guò)指針偏移在小塊內(nèi)存池上分配存放大塊內(nèi)存*next和*alloc的空間
    large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
    if (large == NULL) {
    	// 如果在小塊內(nèi)存上分配存儲(chǔ)*next和*alloc空間失敗,則無(wú)法記錄大塊內(nèi)存
    	// 釋放大塊內(nèi)存p
        ngx_free(p);
        return NULL;
    }
	
    large->alloc = p;			   // alloc指向大塊內(nèi)存的首地址
    large->next = pool->large;	   // 這兩句采用頭插法,將新內(nèi)存塊的記錄信息存放于以large為頭結(jié)點(diǎn)的鏈表中
    pool->large = large;
    return p;
}

大塊內(nèi)存的釋放

// 釋放p指向的大塊內(nèi)存
ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p){
    ngx_pool_large_t  *l;
    for (l = pool->large; l; l = l->next) {
    	// 遍歷存儲(chǔ)大塊內(nèi)存信息的鏈表,找到p對(duì)應(yīng)的大塊內(nèi)存
        if (p == l->alloc) {
            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,   "free: %p", l->alloc);
            // 釋放大塊內(nèi)存,但不釋放存儲(chǔ)信息的內(nèi)存空間
            ngx_free(l->alloc);  // free
            l->alloc = NULL;     // alloc置空
            return NGX_OK;
        }
    }
    return NGX_DECLINED;
}

五、關(guān)于小塊內(nèi)存不釋放

就用了last和end兩個(gè)指著標(biāo)識(shí)空閑的空間,是無(wú)法將已經(jīng)使用的空間合理歸還到內(nèi)存池的,只是會(huì)重置內(nèi)存池。同時(shí)還存儲(chǔ)了指向大內(nèi)存塊large和清理函數(shù)cleanup的頭信息

考慮到nginx的效率,小塊內(nèi)存分配高效,同時(shí)也不回收內(nèi)存

void ngx_reset_pool(ngx_pool_t *pool){
    ngx_pool_t        *p;
    ngx_pool_large_t  *l;
	
	// 由于需要重置小塊內(nèi)存,而大塊內(nèi)存的控制信息在小塊內(nèi)存中保存
	// 所以需要先釋放大塊內(nèi)存,在重置小塊內(nèi)存
    for (l = pool->large; l; l = l->next) {
        if (l->alloc) {
            ngx_free(l->alloc);
        }
    }
	
	// 遍歷小塊內(nèi)存的鏈表,重置last、failed、current、chain、large等管理信息
    for (p = pool; p; p = p->d.next) {
    	// 由于只有第一個(gè)內(nèi)存塊有除了ngx_pool_data_t以外的管理信息,別的內(nèi)存塊只有ngx_pool_data_t的信息
    	// 不會(huì)出錯(cuò),但是會(huì)浪費(fèi)空間
        p->d.last = (u_char *) p + sizeof(ngx_pool_t);
        p->d.failed = 0;
    }
	
	// current指向可用于分配內(nèi)存的內(nèi)存塊
    pool->current = pool;
    pool->chain = NULL;
    pool->large = NULL;
}

nginx本質(zhì)是http服務(wù)器,通常處理的是短鏈接,間接性提供服務(wù),需要的內(nèi)存不大,所以不回收內(nèi)存,重置即可。

客戶端發(fā)起一個(gè)requests請(qǐng)求后,nginx服務(wù)器收到請(qǐng)求會(huì)返回response響應(yīng),若在keep-alive時(shí)間內(nèi)沒(méi)有收到客戶的再次請(qǐng)求,nginx服務(wù)器會(huì)主動(dòng)斷開(kāi)連接,此時(shí)會(huì)reset內(nèi)存池。下一次客戶端請(qǐng)求再到來(lái)時(shí),可以復(fù)用內(nèi)存池。

如果是處理長(zhǎng)鏈接,只要客戶端還在線,服務(wù)器的資源就無(wú)法釋放,直到系統(tǒng)資源耗盡。長(zhǎng)鏈接一般使用SGI STL內(nèi)存池的方式進(jìn)行內(nèi)存的開(kāi)辟和釋放,而這種方式分配和回收空間的效率就比nginx低

六、銷毀和清空內(nèi)存池

假設(shè)如下情況:

// 假設(shè)內(nèi)存對(duì)齊為4B
typedef struct{
	char* p;
	char data[508];
}stData;
ngx_pool_t *pool = ngx_create_pool(512, log);  // 創(chuàng)建一個(gè)總空間為512B的nginx內(nèi)存塊
stData* data_ptr = ngx_alloc(512);            // 因?yàn)榭捎玫膶?shí)際內(nèi)存大小為:512-sizeof(ngx_pool_t),所以屬于大內(nèi)存開(kāi)辟
data_ptr->p = malloc(10);                   // p指向外界堆內(nèi)存,類似于C++對(duì)象中對(duì)用占用了外部資源

當(dāng)回收大塊內(nèi)存的時(shí)候,調(diào)用ngx_free,就會(huì)導(dǎo)致內(nèi)存泄漏

以上內(nèi)存泄漏的問(wèn)題,可以通過(guò)回調(diào)函數(shù)進(jìn)行內(nèi)存釋放(通過(guò)函數(shù)指針實(shí)現(xiàn))

typedef void (*ngx_pool_cleanup_pt)(void *data);
typedef struct ngx_pool_cleanup_s  ngx_pool_cleanup_t;
// 以下結(jié)構(gòu)體由ngx_pool_s.cleanup指向,也是存放在內(nèi)存池的小塊內(nèi)存
struct ngx_pool_cleanup_s {
    ngx_pool_cleanup_pt   handler;
    void                 *data;     // 指向需要釋放的資源
    ngx_pool_cleanup_t   *next;     // 釋放資源的函數(shù)都放在一個(gè)鏈表,用next指向這個(gè)鏈表
};

nginx提供的函數(shù)接口:

// p表示內(nèi)存池的入口地址,size表示p->cleanup->data指針的大小
// p->cleanup指向含有清理函數(shù)信息的結(jié)構(gòu)體
// ngx_pool_cleanup_add返回 含有清理函數(shù)信息的結(jié)構(gòu)體 的指針
ngx_pool_cleanup_t* ngx_pool_cleanup_add(ngx_pool_t *p, size_t size){
    ngx_pool_cleanup_t  *c;
	
	// 開(kāi)辟清理函數(shù)的結(jié)構(gòu)體,實(shí)際上也是存放在內(nèi)存池的小塊內(nèi)存
    c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));
    if (c == NULL) {
        return NULL;
    }
	
    if (size) {
    	// 為c->data申請(qǐng)size的空間
        c->data = ngx_palloc(p, size);
        if (c->data == NULL) {
            return NULL;
        }
    } else {
        c->data = NULL;
    }
    c->handler = NULL;
    // 采用頭插法,將當(dāng)前結(jié)構(gòu)體串在pool->cleanup后
    c->next = p->cleanup;
    p->cleanup = c;
    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);
    return c;
}

使用方式:

void release(void* p){
	free(p);
}
ngx_pool_cleanup_t* clean_ptr = ngx_clean_cleanup_add(pool, sizeof(char*));
clean_ptr->handler = &release;   // 用戶設(shè)置銷毀內(nèi)存池前需要調(diào)用的函數(shù)
clean_ptr->data = data_ptr->p;   // 用戶設(shè)置銷毀內(nèi)存池前需要釋放的內(nèi)存的地址
ngx_destroy_pool(pool);          // 用戶銷毀內(nèi)存池

七、編譯測(cè)試內(nèi)存池接口功能

void ngx_destroy_pool(ngx_pool_t *pool)
{
    ngx_pool_t          *p, *n;
    ngx_pool_large_t    *l;
    ngx_pool_cleanup_t  *c;
	
	// 遍歷cleanup鏈表(存放的時(shí)釋放前需要調(diào)用的函數(shù)),可釋放外部占用的資源
    for (c = pool->cleanup; c; c = c->next) {
        if (c->handler) {
            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,   "run cleanup: %p", c);
            c->handler(c->data);
        }
    }
	// 釋放大塊內(nèi)存
    for (l = pool->large; l; l = l->next) {
        if (l->alloc) {
            ngx_free(l->alloc);
        }
    }
	
	// 釋放小塊內(nèi)存池
    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
        ngx_free(p);
        
        if (n == NULL) {
            break;
        }
    }
}

執(zhí)行configure生成Makefile文件(若報(bào)錯(cuò)則表示需要apt安裝軟件)

Makefile如下:

執(zhí)行make命令使用Makefile編譯源碼,在相應(yīng)目錄下生成 .o文件

#include <ngx_config.h>
#include <nginx.h>
#include <ngx_core.h>
#include <ngx_palloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
            const char *fmt, ...){
}
typedef struct Data stData;
struct Data{
    char *ptr;
    FILE *pfile;
};
void func1(char *p){
    printf("free ptr mem!\n");
    free(p);
}
void func2(FILE *pf){
    printf("close file!\n");
    fclose(pf);
}
void main(){
	// max = 512 - sizeof(ngx_pool_t)
	// 創(chuàng)建總空間為512字節(jié)的nginx內(nèi)存塊
    ngx_pool_t *pool = ngx_create_pool(512, NULL);
    if(pool == NULL){
        printf("ngx_create_pool fail...");
        return;
    }
    
	// 從小塊內(nèi)存池分配的
    void *p1 = ngx_palloc(pool, 128); 
    if(p1 == NULL){
        printf("ngx_palloc 128 bytes fail...");
        return;
    }
	
	// 從大塊內(nèi)存池分配的
    stData *p2 = ngx_palloc(pool, 512); 
    if(p2 == NULL){
        printf("ngx_palloc 512 bytes fail...");
        return;
    }
    
    // 占用外部堆內(nèi)存
    p2->ptr = malloc(12);
    strcpy(p2->ptr, "hello world");
    // 文件描述符
    p2->pfile = fopen("data.txt", "w");
    
    ngx_pool_cleanup_t *c1 = ngx_pool_cleanup_add(pool, sizeof(char*));
    c1->handler = func1;   // 設(shè)置回調(diào)函數(shù)
    c1->data = p2->ptr;    // 設(shè)置資源地址
    ngx_pool_cleanup_t *c2 = ngx_pool_cleanup_add(pool, sizeof(FILE*));
    c2->handler = func2;
    c2->data = p2->pfile;
	
	// 1.調(diào)用所有的預(yù)置的清理函數(shù) 2.釋放大塊內(nèi)存 3.釋放小塊內(nèi)存池所有內(nèi)存
    ngx_destroy_pool(pool); 
    return;
}

由于ngx_pool_cleanup_add中用頭插法將創(chuàng)建的清理塊鏈入pool->cleanup,所以ngx_destroy_pool的時(shí)候先清理文件后清理堆內(nèi)存。

相關(guān)測(cè)試代碼推送到:https://github.com/BugMaker-shen/nginx_sgistl_pool

到此這篇關(guān)于nginx內(nèi)存池源碼解析的文章就介紹到這了,更多相關(guān)nginx內(nèi)存池內(nèi)容請(qǐng)搜索本站以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持本站!

版權(quán)聲明:本站文章來(lái)源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請(qǐng)保持原文完整并注明來(lái)源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來(lái)源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來(lái),僅供學(xué)習(xí)參考,不代表本站立場(chǎng),如有內(nèi)容涉嫌侵權(quán),請(qǐng)聯(lián)系alex-e#qq.com處理。

相關(guān)文章

實(shí)時(shí)開(kāi)通

自選配置、實(shí)時(shí)開(kāi)通

免備案

全球線路精選!

全天候客戶服務(wù)

7x24全年不間斷在線

專屬顧問(wèn)服務(wù)

1對(duì)1客戶咨詢顧問(wèn)

在線
客服

在線客服:7*24小時(shí)在線

客服
熱線

400-630-3752
7*24小時(shí)客服服務(wù)熱線

關(guān)注
微信

關(guān)注官方微信
頂部